2010-04-07 Jb Evain <jbevain@novell.com>
[mcs.git] / class / corlib / System / Decimal.cs
blobed7eaa252f7d4da28b71cd230a914159b25f3c54
1 //
2 // System.Decimal.cs
3 //
4 // Represents a floating-point decimal data type with up to 29
5 // significant digits, suitable for financial and commercial calculations.
6 //
7 // Author:
8 // Martin Weindel (martin.weindel@t-online.de)
9 //
10 // (C) 2001 Martin Weindel
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 //
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 //
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 using System;
37 using System.Globalization;
38 using System.Text;
39 using System.Runtime.CompilerServices;
40 using System.Runtime.ConstrainedExecution;
42 #if MSTEST
43 using System.Runtime.InteropServices;
44 #endif
47 namespace System
49 /// <summary>
50 /// Represents a floating-point decimal data type with up to 29 significant
51 /// digits, suitable for financial and commercial calculations
52 /// </summary>
53 [Serializable]
54 [System.Runtime.InteropServices.ComVisible (true)]
55 public struct Decimal: IFormattable, IConvertible, IComparable, IComparable<Decimal>, IEquatable <Decimal>
57 public const decimal MinValue = -79228162514264337593543950335m;
58 public const decimal MaxValue = 79228162514264337593543950335m;
60 public const decimal MinusOne = -1;
61 public const decimal One = 1;
62 public const decimal Zero = 0;
64 private static readonly Decimal MaxValueDiv10 = MaxValue / 10;
66 // some constants
67 private const uint MAX_SCALE = 28;
68 private const uint SIGN_FLAG = 0x80000000;
69 private const int SCALE_SHIFT = 16;
70 private const uint RESERVED_SS32_BITS = 0x7F00FFFF;
72 // internal representation of decimal
73 private uint flags;
74 private uint hi;
75 private uint lo;
76 private uint mid;
78 public Decimal (int lo, int mid, int hi, bool isNegative, byte scale)
80 unchecked
82 this.lo = (uint) lo;
83 this.mid = (uint) mid;
84 this.hi = (uint) hi;
86 if (scale > MAX_SCALE)
87 throw new ArgumentOutOfRangeException (Locale.GetText ("scale must be between 0 and 28"));
89 flags = scale;
90 flags <<= SCALE_SHIFT;
91 if (isNegative) flags |= SIGN_FLAG;
95 public Decimal (int value)
97 unchecked
99 hi = mid = 0;
100 if (value < 0)
102 flags = SIGN_FLAG;
103 lo = ((uint)~value) + 1;
105 else
107 flags = 0;
108 lo = (uint) value;
113 [CLSCompliant(false)]
114 public Decimal (uint value)
116 lo = value;
117 flags = hi = mid = 0;
120 public Decimal (long value)
122 unchecked
124 hi = 0;
125 if (value < 0)
127 flags = SIGN_FLAG;
128 ulong u = ((ulong)~value) + 1;
129 lo = (uint)u;
130 mid = (uint)(u >> 32);
132 else
134 flags = 0;
135 ulong u = (ulong)value;
136 lo = (uint)u;
137 mid = (uint)(u >> 32);
142 [CLSCompliant(false)]
143 public Decimal (ulong value)
145 unchecked
147 flags = hi = 0;
148 lo = (uint)value;
149 mid = (uint)(value >> 32);
153 public Decimal (float value)
155 #if false
157 // We cant use the double2decimal method
158 // because it incorrectly turns the floating point
159 // value 1.23456789E-25F which should be:
160 // 0.0000000000000000000000001235
161 // into the incorrect:
162 // 0.0000000000000000000000001234
164 // The code currently parses the double value 0.6 as
165 // 0.600000000000000
167 // And we have a patch for that called (trim
168 if (double2decimal (out this, value, 7) != 0)
169 throw new OverflowException ();
170 #else
171 if (value > (float)Decimal.MaxValue || value < (float)Decimal.MinValue ||
172 float.IsNaN (value) || float.IsNegativeInfinity (value) || float.IsPositiveInfinity (value)) {
173 throw new OverflowException (Locale.GetText (
174 "Value {0} is greater than Decimal.MaxValue or less than Decimal.MinValue", value));
177 // we must respect the precision (double2decimal doesn't)
178 Decimal d = Decimal.Parse (value.ToString (CultureInfo.InvariantCulture),
179 NumberStyles.Float, CultureInfo.InvariantCulture);
180 flags = d.flags;
181 hi = d.hi;
182 lo = d.lo;
183 mid = d.mid;
184 #endif
187 public Decimal (double value)
189 #if false
191 // We cant use the double2decimal method
192 // because it incorrectly turns the floating point
193 // value 1.23456789E-25F which should be:
194 // 0.0000000000000000000000001235
195 // into the incorrect:
196 // 0.0000000000000000000000001234
198 // The code currently parses the double value 0.6 as
199 // 0.600000000000000
201 // And we have a patch for that called (trim
202 if (double2decimal (out this, value, 15) != 0)
203 throw new OverflowException ();
204 #else
205 if (value > (double)Decimal.MaxValue || value < (double)Decimal.MinValue ||
206 double.IsNaN (value) || double.IsNegativeInfinity (value) || double.IsPositiveInfinity (value)) {
207 throw new OverflowException (Locale.GetText (
208 "Value {0} is greater than Decimal.MaxValue or less than Decimal.MinValue", value));
210 // we must respect the precision (double2decimal doesn't)
211 Decimal d = Decimal.Parse (value.ToString (CultureInfo.InvariantCulture),
212 NumberStyles.Float, CultureInfo.InvariantCulture);
213 flags = d.flags;
214 hi = d.hi;
215 lo = d.lo;
216 mid = d.mid;
217 #endif
220 public Decimal (int[] bits)
222 if (bits == null)
224 throw new ArgumentNullException (Locale.GetText ("Bits is a null reference"));
227 if (bits.GetLength(0) != 4)
229 throw new ArgumentException (Locale.GetText ("bits does not contain four values"));
232 unchecked {
233 lo = (uint) bits[0];
234 mid = (uint) bits[1];
235 hi = (uint) bits[2];
236 flags = (uint) bits[3];
237 byte scale = (byte)(flags >> SCALE_SHIFT);
238 if (scale > MAX_SCALE || (flags & RESERVED_SS32_BITS) != 0)
240 throw new ArgumentException (Locale.GetText ("Invalid bits[3]"));
245 public static decimal FromOACurrency (long cy)
247 return (decimal)cy / (decimal)10000;
250 public static int[] GetBits (Decimal d)
252 unchecked
254 return new int[] { (int)d.lo, (int)d.mid, (int)d.hi, (int)d.flags };
258 public static Decimal Negate (Decimal d)
260 d.flags ^= SIGN_FLAG;
261 return d;
264 public static Decimal Add (Decimal d1, Decimal d2)
266 if (decimalIncr (ref d1, ref d2) == 0)
267 return d1;
268 else
269 throw new OverflowException (Locale.GetText ("Overflow on adding decimal number"));
272 public static Decimal Subtract (Decimal d1, Decimal d2)
274 d2.flags ^= SIGN_FLAG;
275 int result = decimalIncr (ref d1, ref d2);
276 if (result == 0)
277 return d1;
278 else
279 throw new OverflowException (Locale.GetText ("Overflow on subtracting decimal numbers ("+result+")"));
282 public override int GetHashCode ()
284 return (int) (flags ^ hi ^ lo ^ mid);
287 public static Decimal operator + (Decimal d1, Decimal d2)
289 return Add (d1, d2);
292 public static Decimal operator -- (Decimal d)
294 return Add(d, MinusOne);
297 public static Decimal operator ++ (Decimal d)
299 return Add (d, One);
302 public static Decimal operator - (Decimal d1, Decimal d2)
304 return Subtract (d1, d2);
307 public static Decimal operator - (Decimal d)
309 return Negate (d);
312 public static Decimal operator + (Decimal d)
314 return d;
317 public static Decimal operator * (Decimal d1, Decimal d2)
319 return Multiply (d1, d2);
322 public static Decimal operator / (Decimal d1, Decimal d2)
324 return Divide (d1, d2);
327 public static Decimal operator % (Decimal d1, Decimal d2)
329 return Remainder (d1, d2);
332 private static ulong u64 (Decimal value)
334 ulong result;
336 decimalFloorAndTrunc (ref value, 0);
337 if (decimal2UInt64 (ref value, out result) != 0) {
338 throw new System.OverflowException ();
340 return result;
343 private static long s64 (Decimal value)
345 long result;
347 decimalFloorAndTrunc (ref value, 0);
348 if (decimal2Int64 (ref value, out result) != 0) {
349 throw new System.OverflowException ();
351 return result;
354 public static explicit operator byte (Decimal value)
356 ulong result = u64 (value);
357 return checked ((byte) result);
360 [CLSCompliant (false)]
361 public static explicit operator sbyte (Decimal value)
363 long result = s64 (value);
364 return checked ((sbyte) result);
367 public static explicit operator char (Decimal value)
369 ulong result = u64 (value);
370 return checked ((char) result);
373 public static explicit operator short (Decimal value)
375 long result = s64 (value);
376 return checked ((short) result);
379 [CLSCompliant (false)]
380 public static explicit operator ushort (Decimal value)
382 ulong result = u64 (value);
383 return checked ((ushort) result);
386 public static explicit operator int (Decimal value)
388 long result = s64 (value);
389 return checked ((int) result);
392 [CLSCompliant(false)]
393 public static explicit operator uint (Decimal value)
395 ulong result = u64 (value);
396 return checked ((uint) result);
399 public static explicit operator long (Decimal value)
401 return s64 (value);
404 [CLSCompliant(false)]
405 public static explicit operator ulong (Decimal value)
407 return u64 (value);
410 public static implicit operator Decimal (byte value)
412 return new Decimal (value);
415 [CLSCompliant(false)]
416 public static implicit operator Decimal (sbyte value)
418 return new Decimal (value);
421 public static implicit operator Decimal (short value)
423 return new Decimal (value);
426 [CLSCompliant(false)]
427 public static implicit operator Decimal (ushort value)
429 return new Decimal (value);
432 public static implicit operator Decimal (char value)
434 return new Decimal (value);
437 public static implicit operator Decimal (int value)
439 return new Decimal (value);
442 [CLSCompliant(false)]
443 public static implicit operator Decimal (uint value)
445 return new Decimal (value);
448 public static implicit operator Decimal (long value)
450 return new Decimal (value);
453 [CLSCompliant(false)]
454 public static implicit operator Decimal (ulong value)
456 return new Decimal (value);
459 public static explicit operator Decimal (float value)
461 return new Decimal (value);
464 public static explicit operator Decimal (double value)
466 return new Decimal (value);
469 public static explicit operator float (Decimal value)
471 return (float) (double) value;
474 public static explicit operator double (Decimal value)
476 return decimal2double (ref value);
480 public static bool operator != (Decimal d1, Decimal d2)
482 return !Equals (d1, d2);
485 public static bool operator == (Decimal d1, Decimal d2)
487 return Equals (d1, d2);
490 public static bool operator > (Decimal d1, Decimal d2)
492 return Compare (d1, d2) > 0;
495 public static bool operator >= (Decimal d1, Decimal d2)
497 return Compare (d1, d2) >= 0;
500 public static bool operator < (Decimal d1, Decimal d2)
502 return Compare (d1, d2) < 0;
505 public static bool operator <= (Decimal d1, Decimal d2)
507 return Compare (d1, d2) <= 0;
510 public static bool Equals (Decimal d1, Decimal d2)
512 return Compare (d1, d2) == 0;
515 public override bool Equals (object value)
517 if (!(value is Decimal))
518 return false;
520 return Equals ((Decimal) value, this);
523 // avoid unmanaged call
524 private bool IsZero ()
526 return ((hi == 0) && (lo == 0) && (mid == 0));
529 // avoid unmanaged call
530 private bool IsNegative ()
532 return ((flags & 0x80000000) == 0x80000000);
535 public static Decimal Floor (Decimal d)
537 decimalFloorAndTrunc (ref d, 1);
538 return d;
541 public static Decimal Truncate (Decimal d)
543 decimalFloorAndTrunc (ref d, 0);
544 return d;
547 public static Decimal Round (Decimal d, int decimals)
549 return Round (d, decimals, MidpointRounding.ToEven);
552 public static Decimal Round (Decimal d, int decimals, MidpointRounding mode)
554 if ((mode != MidpointRounding.ToEven) && (mode != MidpointRounding.AwayFromZero))
555 throw new ArgumentException ("The value '" + mode + "' is not valid for this usage of the type MidpointRounding.", "mode");
557 if (decimals < 0 || decimals > 28) {
558 throw new ArgumentOutOfRangeException ("decimals", "[0,28]");
561 bool negative = d.IsNegative ();
562 if (negative)
563 d.flags ^= SIGN_FLAG;
565 // Moved from Math.cs because it's easier to fix the "sign"
566 // issue here :( as the logic is OK only for positive numbers
567 decimal p = (decimal) Math.Pow (10, decimals);
568 decimal int_part = Decimal.Floor (d);
569 decimal dec_part = d - int_part;
570 dec_part *= 10000000000000000000000000000M;
571 dec_part = Decimal.Floor(dec_part);
572 dec_part /= (10000000000000000000000000000M / p);
573 dec_part = Math.Round (dec_part, mode);
574 dec_part /= p;
575 decimal result = int_part + dec_part;
577 // that fixes the precision/scale (which we must keep for output)
578 // (moved and adapted from System.Data.SqlTypes.SqlMoney)
579 long scaleDiff = decimals - ((result.flags & 0x7FFF0000) >> 16);
580 // integrify
581 if (scaleDiff > 0) {
582 // note: here we always work with positive numbers
583 while (scaleDiff > 0) {
584 if (result > MaxValueDiv10)
585 break;
586 result *= 10;
587 scaleDiff--;
590 else if (scaleDiff < 0) {
591 while (scaleDiff < 0) {
592 result /= 10;
593 scaleDiff++;
596 result.flags = (uint)((decimals - scaleDiff) << SCALE_SHIFT);
598 if (negative)
599 result.flags ^= SIGN_FLAG;
600 return result;
603 public static Decimal Round (Decimal d)
605 return Math.Round (d);
608 public static Decimal Round (Decimal d, MidpointRounding mode)
610 return Math.Round (d, mode);
613 public static Decimal Multiply (Decimal d1, Decimal d2)
615 if (d1.IsZero () || d2.IsZero ())
616 return Decimal.Zero;
618 if (decimalMult (ref d1, ref d2) != 0)
619 throw new OverflowException ();
620 return d1;
623 public static Decimal Divide (Decimal d1, Decimal d2)
625 if (d2.IsZero ())
626 throw new DivideByZeroException ();
627 if (d1.IsZero ())
628 return Decimal.Zero;
630 d1.flags ^= SIGN_FLAG;
631 d1.flags ^= SIGN_FLAG;
633 Decimal result;
634 if (decimalDiv (out result, ref d1, ref d2) != 0)
635 throw new OverflowException ();
637 return result;
640 public static Decimal Remainder (Decimal d1, Decimal d2)
642 if (d2.IsZero ())
643 throw new DivideByZeroException ();
644 if (d1.IsZero ())
645 return Decimal.Zero;
647 bool negative = d1.IsNegative ();
648 if (negative)
649 d1.flags ^= SIGN_FLAG;
650 if (d2.IsNegative ())
651 d2.flags ^= SIGN_FLAG;
653 Decimal result;
654 if (d1 == d2) {
655 return Decimal.Zero;
657 else if (d2 > d1) {
658 result = d1;
660 else {
661 if (decimalDiv (out result, ref d1, ref d2) != 0)
662 throw new OverflowException ();
663 result = Decimal.Truncate (result);
665 // FIXME: not really performant here
666 result = d1 - result * d2;
669 if (negative)
670 result.flags ^= SIGN_FLAG;
671 return result;
674 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
675 public static int Compare (Decimal d1, Decimal d2)
677 return decimalCompare (ref d1, ref d2);
680 public int CompareTo (object value)
682 if (value == null)
683 return 1;
685 if (!(value is Decimal))
686 throw new ArgumentException (Locale.GetText ("Value is not a System.Decimal"));
688 return Compare (this, (Decimal)value);
691 public int CompareTo (Decimal value)
693 return Compare (this, value);
696 public bool Equals (Decimal value)
698 return Equals (value, this);
701 public static Decimal Ceiling (Decimal d)
703 return Math.Ceiling (d);
706 public static Decimal Parse (string s)
708 return Parse (s, NumberStyles.Number, null);
711 public static Decimal Parse (string s, NumberStyles style)
713 return Parse (s, style, null);
716 public static Decimal Parse (string s, IFormatProvider provider)
718 return Parse (s, NumberStyles.Number, provider);
721 static void ThrowAtPos (int pos)
723 throw new FormatException (String.Format (Locale.GetText ("Invalid character at position {0}"), pos));
726 static void ThrowInvalidExp ()
728 throw new FormatException (Locale.GetText ("Invalid exponent"));
731 private static string stripStyles (string s, NumberStyles style, NumberFormatInfo nfi,
732 out int decPos, out bool isNegative, out bool expFlag, out int exp, bool throwex)
734 isNegative = false;
735 expFlag = false;
736 exp = 0;
737 decPos = -1;
739 bool hasSign = false;
740 bool hasOpeningParentheses = false;
741 bool hasDecimalPoint = false;
742 bool allowedLeadingWhiteSpace = ((style & NumberStyles.AllowLeadingWhite) != 0);
743 bool allowedTrailingWhiteSpace = ((style & NumberStyles.AllowTrailingWhite) != 0);
744 bool allowedLeadingSign = ((style & NumberStyles.AllowLeadingSign) != 0);
745 bool allowedTrailingSign = ((style & NumberStyles.AllowTrailingSign) != 0);
746 bool allowedParentheses = ((style & NumberStyles.AllowParentheses) != 0);
747 bool allowedThousands = ((style & NumberStyles.AllowThousands) != 0);
748 bool allowedDecimalPoint = ((style & NumberStyles.AllowDecimalPoint) != 0);
749 bool allowedExponent = ((style & NumberStyles.AllowExponent) != 0);
751 /* get rid of currency symbol */
752 bool hasCurrency = false;
753 if ((style & NumberStyles.AllowCurrencySymbol) != 0)
755 int index = s.IndexOf (nfi.CurrencySymbol);
756 if (index >= 0)
758 s = s.Remove (index, nfi.CurrencySymbol.Length);
759 hasCurrency = true;
763 string decimalSep = (hasCurrency) ? nfi.CurrencyDecimalSeparator : nfi.NumberDecimalSeparator;
764 string groupSep = (hasCurrency) ? nfi.CurrencyGroupSeparator : nfi.NumberGroupSeparator;
766 int pos = 0;
767 int len = s.Length;
769 StringBuilder sb = new StringBuilder (len);
771 // leading
772 while (pos < len)
774 char ch = s[pos];
775 if (Char.IsDigit (ch))
777 break; // end of leading
779 else if (allowedLeadingWhiteSpace && Char.IsWhiteSpace (ch))
781 pos++;
783 else if (allowedParentheses && ch == '(' && !hasSign && !hasOpeningParentheses)
785 hasOpeningParentheses = true;
786 hasSign = true;
787 isNegative = true;
788 pos++;
790 else if (allowedLeadingSign && ch == nfi.NegativeSign[0] && !hasSign)
792 int slen = nfi.NegativeSign.Length;
793 if (slen == 1 || s.IndexOf (nfi.NegativeSign, pos, slen) == pos)
795 hasSign = true;
796 isNegative = true;
797 pos += slen;
800 else if (allowedLeadingSign && ch == nfi.PositiveSign[0] && !hasSign)
802 int slen = nfi.PositiveSign.Length;
803 if (slen == 1 || s.IndexOf (nfi.PositiveSign, pos, slen) == pos)
805 hasSign = true;
806 pos += slen;
809 else if (allowedDecimalPoint && ch == decimalSep[0])
811 int slen = decimalSep.Length;
812 if (slen != 1 && s.IndexOf (decimalSep, pos, slen) != pos)
814 if (throwex)
815 ThrowAtPos (pos);
816 else
817 return null;
819 break;
821 else
823 if (throwex)
824 ThrowAtPos (pos);
825 else
826 return null;
830 if (pos == len) {
831 if (throwex)
832 throw new FormatException (Locale.GetText ("No digits found"));
833 else
834 return null;
837 // digits
838 while (pos < len)
840 char ch = s[pos];
841 if (Char.IsDigit (ch))
843 sb.Append(ch);
844 pos++;
846 else if (allowedThousands && ch == groupSep[0] && ch != decimalSep [0])
848 int slen = groupSep.Length;
849 if (slen != 1 && s.IndexOf(groupSep, pos, slen) != pos)
851 if (throwex)
852 ThrowAtPos (pos);
853 else
854 return null;
856 pos += slen;
858 else if (allowedDecimalPoint && ch == decimalSep[0] && !hasDecimalPoint)
860 int slen = decimalSep.Length;
861 if (slen == 1 || s.IndexOf(decimalSep, pos, slen) == pos)
863 decPos = sb.Length;
864 hasDecimalPoint = true;
865 pos += slen;
868 else
870 break;
874 // exponent
875 if (pos < len)
877 char ch = s[pos];
878 if (allowedExponent && Char.ToUpperInvariant (ch) == 'E')
880 expFlag = true;
881 pos++;
882 if (pos >= len){
883 if (throwex)
884 ThrowInvalidExp ();
885 else
886 return null;
888 ch = s[pos];
889 bool isNegativeExp = false;
890 if (ch == nfi.PositiveSign[0])
892 int slen = nfi.PositiveSign.Length;
893 if (slen == 1 || s.IndexOf (nfi.PositiveSign, pos, slen) == pos)
895 pos += slen;
896 if (pos >= len) {
897 if (throwex)
898 ThrowInvalidExp ();
899 else
900 return null;
904 else if (ch == nfi.NegativeSign[0])
906 int slen = nfi.NegativeSign.Length;
907 if (slen == 1 || s.IndexOf (nfi.NegativeSign, pos, slen) == pos)
909 pos += slen;
910 if (pos >= len) {
911 if (throwex)
912 ThrowInvalidExp ();
913 else
914 return null;
916 isNegativeExp = true;
919 ch = s[pos];
920 if (!Char.IsDigit(ch)) {
921 if (throwex)
922 ThrowInvalidExp ();
923 else
924 return null;
927 exp = ch - '0';
928 pos++;
929 while (pos < len && Char.IsDigit (s[pos]))
931 exp *= 10;
932 exp += s[pos] - '0';
933 pos++;
935 if (isNegativeExp) exp *= -1;
939 // trailing
940 while (pos < len)
942 char ch = s[pos];
943 if (allowedTrailingWhiteSpace && Char.IsWhiteSpace (ch))
945 pos++;
947 else if (allowedParentheses && ch == ')' && hasOpeningParentheses)
949 hasOpeningParentheses = false;
950 pos++;
952 else if (allowedTrailingSign && ch == nfi.NegativeSign[0] && !hasSign)
954 int slen = nfi.NegativeSign.Length;
955 if (slen == 1 || s.IndexOf (nfi.NegativeSign, pos, slen) == pos)
957 hasSign = true;
958 isNegative = true;
959 pos += slen;
962 else if (allowedTrailingSign && ch == nfi.PositiveSign[0] && !hasSign)
964 int slen = nfi.PositiveSign.Length;
965 if (slen == 1 || s.IndexOf(nfi.PositiveSign, pos, slen) == pos)
967 hasSign = true;
968 pos += slen;
971 else
973 // trailing zero characters are allowed
974 if (ch == 0){
975 while (++pos < len && s [pos] == 0)
977 if (pos == len)
978 break;
981 if (throwex)
982 ThrowAtPos (pos);
983 else
984 return null;
988 if (hasOpeningParentheses) {
989 if (throwex)
990 throw new FormatException (Locale.GetText ("Closing Parentheses not found"));
991 else
992 return null;
995 if (!hasDecimalPoint)
996 decPos = sb.Length;
998 return sb.ToString ();
1001 public static Decimal Parse (string s, NumberStyles style, IFormatProvider provider)
1003 if (s == null)
1004 throw new ArgumentNullException ("s");
1006 if ((style & NumberStyles.AllowHexSpecifier) != 0)
1007 throw new ArgumentException ("Decimal.TryParse does not accept AllowHexSpecifier", "style");
1009 Decimal result;
1010 PerformParse (s, style, provider, out result, true);
1011 return result;
1014 public static bool TryParse (string s, out Decimal result)
1016 if (s == null){
1017 result = 0;
1018 return false;
1020 return PerformParse (s, NumberStyles.Number, null, out result, false);
1023 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out decimal result)
1025 if (s == null || (style & NumberStyles.AllowHexSpecifier) != 0){
1026 result = 0;
1027 return false;
1030 return PerformParse (s, style, provider, out result, false);
1033 static bool PerformParse (string s, NumberStyles style, IFormatProvider provider, out Decimal res, bool throwex)
1035 NumberFormatInfo nfi = NumberFormatInfo.GetInstance (provider);
1037 int iDecPos, exp;
1038 bool isNegative, expFlag;
1039 s = stripStyles(s, style, nfi, out iDecPos, out isNegative, out expFlag, out exp, throwex);
1040 if (s == null){
1041 res = 0;
1042 return false;
1045 if (iDecPos < 0){
1046 if (throwex)
1047 throw new Exception (Locale.GetText ("Error in System.Decimal.Parse"));
1048 res = 0;
1049 return false;
1052 // first we remove leading 0
1053 int len = s.Length;
1054 int i = 0;
1055 while ((i < iDecPos) && (s [i] == '0'))
1056 i++;
1057 if ((i > 1) && (len > 1)) {
1058 s = s.Substring (i, len - i);
1059 iDecPos -= i;
1062 // first 0. may not be here but is part of the maximum length
1063 int max = ((iDecPos == 0) ? 27 : 28);
1064 len = s.Length;
1065 if (len >= max + 1) {
1066 // number lower than MaxValue (base-less) can have better precision
1067 if (String.Compare (s, 0, "79228162514264337593543950335", 0, max + 1,
1068 false, CultureInfo.InvariantCulture) <= 0) {
1069 max++;
1073 // then we trunc the string
1074 if ((len > max) && (iDecPos < len)) {
1075 int round = (s [max] - '0');
1076 s = s.Substring (0, max);
1078 bool addone = false;
1079 if (round > 5) {
1080 addone = true;
1082 else if (round == 5) {
1083 if (isNegative) {
1084 addone = true;
1086 else {
1087 // banker rounding applies :(
1088 int previous = (s [max - 1] - '0');
1089 addone = ((previous & 0x01) == 0x01);
1092 if (addone) {
1093 char[] array = s.ToCharArray ();
1094 int p = max - 1;
1095 while (p >= 0) {
1096 int b = (array [p] - '0');
1097 if (array [p] != '9') {
1098 array [p] = (char)(b + '1');
1099 break;
1101 else {
1102 array [p--] = '0';
1105 if ((p == -1) && (array [0] == '0')) {
1106 iDecPos++;
1107 s = "1".PadRight (iDecPos, '0');
1109 else
1110 s = new String (array);
1114 Decimal result;
1115 // always work in positive (rounding issues)
1116 if (string2decimal (out result, s, (uint)iDecPos, 0) != 0){
1117 if (throwex)
1118 throw new OverflowException ();
1119 res = 0;
1120 return false;
1123 if (expFlag) {
1124 if (decimalSetExponent (ref result, exp) != 0){
1125 if (throwex)
1126 throw new OverflowException ();
1127 res = 0;
1128 return false;
1132 if (isNegative)
1133 result.flags ^= SIGN_FLAG;
1135 res = result;
1136 return true;
1139 public TypeCode GetTypeCode ()
1141 return TypeCode.Decimal;
1144 public static byte ToByte (decimal value)
1146 if (value > Byte.MaxValue || value < Byte.MinValue)
1147 throw new OverflowException (Locale.GetText (
1148 "Value is greater than Byte.MaxValue or less than Byte.MinValue"));
1150 // return truncated value
1151 return (byte)(Decimal.Truncate (value));
1154 public static double ToDouble (decimal d)
1156 return Convert.ToDouble (d);
1159 public static short ToInt16 (decimal value)
1161 if (value > Int16.MaxValue || value < Int16.MinValue)
1162 throw new OverflowException (Locale.GetText (
1163 "Value is greater than Int16.MaxValue or less than Int16.MinValue"));
1165 // return truncated value
1166 return (Int16)(Decimal.Truncate (value));
1169 public static int ToInt32 (decimal d)
1171 if (d > Int32.MaxValue || d < Int32.MinValue)
1172 throw new OverflowException (Locale.GetText (
1173 "Value is greater than Int32.MaxValue or less than Int32.MinValue"));
1175 // return truncated value
1176 return (Int32)(Decimal.Truncate (d));
1179 public static long ToInt64 (decimal d)
1181 if (d > Int64.MaxValue || d < Int64.MinValue)
1182 throw new OverflowException (Locale.GetText (
1183 "Value is greater than Int64.MaxValue or less than Int64.MinValue"));
1185 // return truncated value
1186 return (Int64)(Decimal.Truncate (d));
1189 public static long ToOACurrency (decimal value)
1191 return (long) (value * 10000);
1194 [CLSCompliant(false)]
1195 public static sbyte ToSByte (decimal value)
1197 if (value > SByte.MaxValue || value < SByte.MinValue)
1198 throw new OverflowException (Locale.GetText (
1199 "Value is greater than SByte.MaxValue or less than SByte.MinValue"));
1201 // return truncated value
1202 return (SByte)(Decimal.Truncate (value));
1205 public static float ToSingle (decimal d)
1207 return Convert.ToSingle (d);
1210 [CLSCompliant(false)]
1211 public static ushort ToUInt16 (decimal value)
1213 if (value > UInt16.MaxValue || value < UInt16.MinValue)
1214 throw new OverflowException (Locale.GetText (
1215 "Value is greater than UInt16.MaxValue or less than UInt16.MinValue"));
1217 // return truncated value
1218 return (UInt16)(Decimal.Truncate (value));
1221 [CLSCompliant(false)]
1222 public static uint ToUInt32 (decimal d)
1224 if (d > UInt32.MaxValue || d < UInt32.MinValue)
1225 throw new OverflowException (Locale.GetText (
1226 "Value is greater than UInt32.MaxValue or less than UInt32.MinValue"));
1228 // return truncated value
1229 return (UInt32)(Decimal.Truncate (d));
1232 [CLSCompliant(false)]
1233 public static ulong ToUInt64 (decimal d)
1235 if (d > UInt64.MaxValue || d < UInt64.MinValue)
1236 throw new OverflowException (Locale.GetText (
1237 "Value is greater than UInt64.MaxValue or less than UInt64.MinValue"));
1239 // return truncated value
1240 return (UInt64)(Decimal.Truncate (d));
1243 object IConvertible.ToType (Type targetType, IFormatProvider provider)
1245 if (targetType == null)
1246 throw new ArgumentNullException ("targetType");
1247 return Convert.ToType (this, targetType, provider, false);
1250 bool IConvertible.ToBoolean (IFormatProvider provider)
1252 return Convert.ToBoolean (this);
1255 byte IConvertible.ToByte (IFormatProvider provider)
1257 return Convert.ToByte (this);
1260 char IConvertible.ToChar (IFormatProvider provider)
1262 throw new InvalidCastException ();
1265 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1267 throw new InvalidCastException ();
1270 decimal IConvertible.ToDecimal (IFormatProvider provider)
1272 return this;
1275 double IConvertible.ToDouble (IFormatProvider provider)
1277 return Convert.ToDouble (this);
1280 short IConvertible.ToInt16 (IFormatProvider provider)
1282 return Convert.ToInt16 (this);
1285 int IConvertible.ToInt32 (IFormatProvider provider)
1287 return Convert.ToInt32 (this);
1290 long IConvertible.ToInt64 (IFormatProvider provider)
1292 return Convert.ToInt64 (this);
1295 sbyte IConvertible.ToSByte (IFormatProvider provider)
1297 return Convert.ToSByte (this);
1300 float IConvertible.ToSingle (IFormatProvider provider)
1302 return Convert.ToSingle (this);
1305 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1307 return Convert.ToUInt16 (this);
1310 uint IConvertible.ToUInt32 (IFormatProvider provider)
1312 return Convert.ToUInt32 (this);
1315 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1317 return Convert.ToUInt64 (this);
1320 public string ToString (string format, IFormatProvider provider)
1322 return NumberFormatter.NumberToString (format, this, provider);
1325 public override string ToString ()
1327 return ToString ("G", null);
1330 public string ToString (string format)
1332 return ToString (format, null);
1335 public string ToString (IFormatProvider provider)
1337 return ToString ("G", provider);
1340 #if !MSTEST
1341 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1342 private static extern int decimal2UInt64 (ref Decimal val, out ulong result);
1344 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1345 private static extern int decimal2Int64 (ref Decimal val, out long result);
1347 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1348 private static extern int double2decimal (out Decimal erg, double val, int digits);
1350 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1351 private static extern int decimalIncr (ref Decimal d1, ref Decimal d2);
1353 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1354 internal static extern int decimal2string (ref Decimal val,
1355 int digits, int decimals, char[] bufDigits, int bufSize, out int decPos, out int sign);
1357 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1358 internal static extern int string2decimal (out Decimal val, String sDigits, uint decPos, int sign);
1360 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1361 internal static extern int decimalSetExponent (ref Decimal val, int exp);
1363 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1364 private static extern double decimal2double (ref Decimal val);
1366 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1367 private static extern void decimalFloorAndTrunc (ref Decimal val, int floorFlag);
1369 // [MethodImplAttribute(MethodImplOptions.InternalCall)]
1370 // private static extern void decimalRound (ref Decimal val, int decimals);
1372 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1373 private static extern int decimalMult (ref Decimal pd1, ref Decimal pd2);
1375 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1376 private static extern int decimalDiv (out Decimal pc, ref Decimal pa, ref Decimal pb);
1378 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1379 private static extern int decimalIntDiv (out Decimal pc, ref Decimal pa, ref Decimal pb);
1381 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1382 private static extern int decimalCompare (ref Decimal d1, ref Decimal d2);
1383 #else
1384 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1385 [DllImport("libdec", EntryPoint="decimal2UInt64")]
1386 private static extern int decimal2UInt64 (ref Decimal val, out ulong result);
1388 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1389 [DllImport("libdec", EntryPoint="decimal2Int64")]
1390 private static extern int decimal2Int64 (ref Decimal val, out long result);
1392 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1393 [DllImport("libdec", EntryPoint="double2decimal")]
1394 private static extern int double2decimal (out Decimal erg, double val, int digits);
1396 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1397 [DllImport("libdec", EntryPoint="decimalIncr")]
1398 private static extern int decimalIncr (ref Decimal d1, ref Decimal d2);
1400 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1401 [DllImport("libdec", EntryPoint="decimal2string")]
1402 internal static extern int decimal2string (ref Decimal val,
1403 int digits, int decimals,
1404 [MarshalAs(UnmanagedType.LPWStr)]StringBuilder bufDigits,
1405 int bufSize, out int decPos, out int sign);
1407 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1408 [DllImport("libdec", EntryPoint="string2decimal")]
1409 internal static extern int string2decimal (out Decimal val,
1410 [MarshalAs(UnmanagedType.LPWStr)]String sDigits,
1411 uint decPos, int sign);
1413 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1414 [DllImport("libdec", EntryPoint="decimalSetExponent")]
1415 internal static extern int decimalSetExponent (ref Decimal val, int exp);
1417 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1418 [DllImport("libdec", EntryPoint="decimal2double")]
1419 private static extern double decimal2double (ref Decimal val);
1421 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1422 [DllImport("libdec", EntryPoint="decimalFloorAndTrunc")]
1423 private static extern void decimalFloorAndTrunc (ref Decimal val, int floorFlag);
1425 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1426 [DllImport("libdec", EntryPoint="decimalRound")]
1427 private static extern void decimalRound (ref Decimal val, int decimals);
1429 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1430 [DllImport("libdec", EntryPoint="decimalMult")]
1431 private static extern int decimalMult (ref Decimal pd1, ref Decimal pd2);
1433 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1434 [DllImport("libdec", EntryPoint="decimalDiv")]
1435 private static extern int decimalDiv (out Decimal pc, ref Decimal pa, ref Decimal pb);
1437 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1438 [DllImport("libdec", EntryPoint="decimalIntDiv")]
1439 private static extern int decimalIntDiv (out Decimal pc, ref Decimal pa, ref Decimal pb);
1441 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1442 [DllImport("libdec", EntryPoint="decimalCompare")]
1443 private static extern int decimalCompare (ref Decimal d1, ref Decimal d2);
1445 #endif