merge 99422:99508
[mono.git] / mcs / class / corlib / System / Decimal.cs
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
11 //
12
13 //
14 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
15 //
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.
34 //
35
36 using System;
37 using System.Globalization;
38 using System.Text;
39 using System.Runtime.CompilerServices;
40 #if MSTEST
41 using System.Runtime.InteropServices;
42 #endif
43
44 #if NET_2_0
45 using System.Runtime.ConstrainedExecution;
46 #endif
47
48 namespace System
49 {
50     /// <summary>
51     /// Represents a floating-point decimal data type with up to 29 significant
52     /// digits, suitable for financial and commercial calculations
53     /// </summary>
54         [Serializable]
55 #if NET_2_0
56         [System.Runtime.InteropServices.ComVisible (true)]
57 #endif
58     public struct Decimal: IFormattable, IConvertible, IComparable
59 #if NET_2_0
60         , IComparable<Decimal>, IEquatable <Decimal>
61 #endif
62     {
63 #if BOOTSTRAP_WITH_OLDLIB
64
65         // LAMESPEC: the attributes aren't mentioned, but show up in CorCompare
66         // Unfortunately, corcompare starts throwing security exceptions when
67         // these attributes are present...
68
69         [DecimalConstantAttribute(0, 1, unchecked((uint)-1), unchecked((uint)-1), unchecked((uint)-1))]
70         public static readonly Decimal MinValue = new Decimal(-1, -1, -1, true, 0);
71         [DecimalConstantAttribute(0, 0, unchecked((uint)-1), unchecked((uint)-1), unchecked((uint)-1))]
72         public static readonly Decimal MaxValue = new Decimal(-1, -1, -1, false, 0);
73         [DecimalConstantAttribute(0, 1, 0, 0, 1)]
74         public static readonly Decimal MinusOne = new Decimal(1, 0, 0, true, 0);
75         [DecimalConstantAttribute(0, 0, 0, 0, 1)]
76         public static readonly Decimal One = new Decimal(1, 0, 0, false, 0);
77         [DecimalConstantAttribute(0, 0, 0, 0, 0)]
78         public static readonly Decimal Zero = new Decimal(0, 0, 0, false, 0);
79 #else
80         public const decimal MinValue = -79228162514264337593543950335m;
81         public const decimal MaxValue =  79228162514264337593543950335m;
82         
83         public const decimal MinusOne = -1;
84         public const decimal One = 1;
85         public const decimal Zero = 0;
86 #endif
87
88         private static readonly Decimal MaxValueDiv10 = MaxValue / 10;
89
90         // maximal decimal value as double
91         private static readonly double dDecMaxValue = 7.922816251426433759354395033e28;
92         // epsilon decimal value as double
93         private static readonly double dDecEpsilon = 0.5e-28;  // == 0.5 * 1 / 10^28
94
95         // some constants
96         private const int DECIMAL_DIVIDE_BY_ZERO = 5;
97         private const uint MAX_SCALE = 28;
98         private const int iMAX_SCALE = 28;
99         private const uint SIGN_FLAG = 0x80000000;
100         private const uint SCALE_MASK = 0x00FF0000;
101         private const int SCALE_SHIFT = 16;
102         private const uint RESERVED_SS32_BITS = 0x7F00FFFF;
103
104         // internal representation of decimal
105         private uint flags;
106         private uint hi;
107         private uint lo;
108         private uint mid;
109
110         public Decimal(int lo, int mid, int hi, bool isNegative, byte scale)
111         {
112             unchecked 
113             {
114                 this.lo = (uint) lo;
115                 this.mid = (uint) mid;
116                 this.hi = (uint) hi;
117             
118                 if (scale > MAX_SCALE) 
119                 {
120                         throw new ArgumentOutOfRangeException (Locale.GetText ("scale must be between 0 and 28"));
121                 }
122
123                 flags = scale;
124                 flags <<= SCALE_SHIFT;
125                 if (isNegative) flags |= SIGN_FLAG;
126             }
127         }
128
129         public Decimal(int val) 
130         {
131             unchecked 
132             {
133                 hi = mid = 0;
134                 if (val < 0) 
135                 {
136                     flags = SIGN_FLAG;
137                     lo = ((uint)~val) + 1;
138                 }
139                 else 
140                 {
141                     flags = 0;
142                     lo = (uint) val;
143                 }
144             }
145         }
146
147         [CLSCompliant(false)]
148         public Decimal(uint val) 
149         {
150             lo = val;
151             flags = hi = mid = 0;
152         }
153
154         public Decimal(long val) 
155         {
156             unchecked 
157             {
158                 hi = 0;
159                 if (val < 0) 
160                 {
161                     flags = SIGN_FLAG;
162                     ulong u = ((ulong)~val) + 1;
163                     lo = (uint)u;
164                     mid = (uint)(u >> 32);
165                 }
166                 else 
167                 {
168                     flags = 0;
169                     ulong u = (ulong)val;
170                     lo = (uint)u;
171                     mid = (uint)(u >> 32);
172                 }
173             }
174         }
175
176         [CLSCompliant(false)]
177         public Decimal(ulong uval) 
178         {
179             unchecked 
180             {
181                 flags = hi = 0;
182                 lo = (uint)uval;
183                 mid = (uint)(uval >> 32);
184             }
185         }
186
187         public Decimal (float val) 
188         {
189                 if (val > (float)Decimal.MaxValue || val < (float)Decimal.MinValue) {
190                         throw new OverflowException (Locale.GetText (
191                                 "Value {0} is greater than Decimal.MaxValue or less than Decimal.MinValue", val));
192                 }
193                 // we must respect the precision (double2decimal doesn't)
194                 Decimal d = Decimal.Parse (val.ToString (CultureInfo.InvariantCulture),
195                                 NumberStyles.Float, CultureInfo.InvariantCulture);
196                 flags = d.flags;
197                 hi = d.hi;
198                 lo = d.lo;
199                 mid = d.mid;
200         }
201
202         public Decimal (double val) 
203         {
204                 if (val > (double)Decimal.MaxValue || val < (double)Decimal.MinValue) {
205                         throw new OverflowException (Locale.GetText (
206                                 "Value {0} is greater than Decimal.MaxValue or less than Decimal.MinValue", val));
207                 }
208                 // we must respect the precision (double2decimal doesn't)
209                 Decimal d = Decimal.Parse (val.ToString (CultureInfo.InvariantCulture),
210                                 NumberStyles.Float, CultureInfo.InvariantCulture);
211                 flags = d.flags;
212                 hi = d.hi;
213                 lo = d.lo;
214                 mid = d.mid;
215         }
216
217         public Decimal(int[] bits) 
218         {
219             if (bits == null) 
220             {
221                 throw new ArgumentNullException(Locale.GetText ("Bits is a null reference"));
222             }
223
224             if (bits.GetLength(0) != 4) 
225             {
226                 throw new ArgumentException(Locale.GetText ("bits does not contain four values"));
227             }
228
229             unchecked {
230                 lo = (uint) bits[0];
231                 mid = (uint) bits[1];
232                 hi = (uint) bits[2];
233                 flags = (uint) bits[3];
234                 byte scale = (byte)(flags >> SCALE_SHIFT);
235                 if (scale > MAX_SCALE || (flags & RESERVED_SS32_BITS) != 0) 
236                 {
237                     throw new ArgumentException(Locale.GetText ("Invalid bits[3]"));
238                 }
239             }
240         }
241
242                 public static decimal FromOACurrency(long cy)
243                 {
244                         return (decimal)cy / (decimal)10000;
245                 }
246         
247         public static int[] GetBits(Decimal d) 
248         {
249             unchecked 
250             {
251                 return new int[] { (int)d.lo, (int)d.mid, (int)d.hi, 
252                                      (int)d.flags };
253             }
254         }
255
256         public static Decimal Negate(Decimal d) 
257         {
258             d.flags ^= SIGN_FLAG;
259             return d;
260         }
261
262
263         public static Decimal Add(Decimal d1, Decimal d2) 
264         {
265             if (decimalIncr(ref d1, ref d2) == 0)
266                 return d1;
267             else
268                 throw new OverflowException(Locale.GetText ("Overflow on adding decimal number"));
269         }
270
271         public static Decimal Subtract(Decimal d1, Decimal d2) 
272         {
273             d2.flags ^= SIGN_FLAG;
274             int result = decimalIncr(ref d1, ref d2);
275             if (result == 0)
276                 return d1;
277             else
278                 throw new OverflowException(Locale.GetText ("Overflow on subtracting decimal numbers ("+result+")"));
279         }
280
281         public override int GetHashCode () 
282         {
283                 return (int) (flags ^ hi ^ lo ^ mid);
284         }
285
286         public static Decimal operator +(Decimal d1, Decimal d2)
287         {
288             return Add(d1, d2);
289         }
290
291         public static Decimal operator --(Decimal d) 
292         {
293             return Add(d, MinusOne);
294         }
295
296         public static Decimal operator ++(Decimal d) 
297         {
298             return Add(d, One);
299         }
300
301         public static Decimal operator -(Decimal d1, Decimal d2) 
302         {
303             return Subtract(d1, d2);
304         }
305
306         public static Decimal operator -(Decimal d) 
307         {
308             return Negate(d);
309         }
310
311         public static Decimal operator +(Decimal d) 
312         {
313             return d;
314         }
315
316         public static Decimal operator *(Decimal d1, Decimal d2)
317         {
318             return Multiply(d1, d2);
319         }
320
321         public static Decimal operator /(Decimal d1, Decimal d2) 
322         {
323             return Divide(d1, d2);
324         }
325
326         public static Decimal operator %(Decimal d1, Decimal d2) 
327         {
328             return Remainder(d1, d2);
329         }
330
331         private static ulong u64 (Decimal value) 
332         {
333                 ulong result;
334
335                 decimalFloorAndTrunc (ref value, 0);
336                 if (decimal2UInt64 (ref value, out result) != 0) {
337                         throw new System.OverflowException ();
338                 }
339                 return result;
340         }
341
342         private static long s64 (Decimal value) 
343         {
344                 long result;
345
346                 decimalFloorAndTrunc (ref value, 0);
347                 if (decimal2Int64 (ref value, out result) != 0) {
348                         throw new System.OverflowException ();
349                 }
350                 return result;
351         }
352
353         public static explicit operator byte (Decimal val)
354         {
355                 ulong result = u64 (val);
356                 return checked ((byte) result);
357         }
358
359         [CLSCompliant (false)]
360         public static explicit operator sbyte (Decimal val)
361         {
362                 long result = s64 (val);
363                 return checked ((sbyte) result);
364         }
365
366         public static explicit operator char (Decimal val) 
367         {
368                 ulong result = u64 (val);
369                 return checked ((char) result);
370         }
371
372         public static explicit operator short (Decimal val) 
373         {
374                 long result = s64 (val);
375                 return checked ((short) result);
376         }
377
378         [CLSCompliant (false)]
379         public static explicit operator ushort (Decimal val) 
380         {
381                 ulong result = u64 (val);
382                 return checked ((ushort) result);
383         }
384
385         public static explicit operator int (Decimal val) 
386         {
387                 long result = s64 (val);
388                 return checked ((int) result);
389         }
390
391         [CLSCompliant(false)]
392         public static explicit operator uint (Decimal val) 
393         {
394                 ulong result = u64 (val);
395                 return checked ((uint) result);
396         }
397
398         public static explicit operator long (Decimal val) 
399         {
400                 return s64 (val);
401         }
402
403         [CLSCompliant(false)]
404         public static explicit operator ulong (Decimal val) 
405         {
406                 return u64 (val);
407         }
408
409         public static implicit operator Decimal(byte val) 
410         {
411             return new Decimal(val);
412         }
413
414         [CLSCompliant(false)]
415         public static implicit operator Decimal(sbyte val) 
416         {
417             return new Decimal(val);
418         }
419
420         public static implicit operator Decimal(short val) 
421         {
422             return new Decimal(val);
423         }
424
425         [CLSCompliant(false)]
426         public static implicit operator Decimal(ushort val) 
427         {
428             return new Decimal(val);
429         }
430
431         public static implicit operator Decimal(char val) 
432         {
433             return new Decimal(val);
434         }
435
436         public static implicit operator Decimal(int val) 
437         {
438             return new Decimal(val);
439         }
440
441         [CLSCompliant(false)]
442         public static implicit operator Decimal(uint val) 
443         {
444             return new Decimal(val);
445         }
446
447         public static implicit operator Decimal(long val) 
448         {
449             return new Decimal(val);
450         }
451
452         [CLSCompliant(false)]
453         public static implicit operator Decimal(ulong val) 
454         {
455             return new Decimal(val);
456         }
457
458         public static explicit operator Decimal(float val) 
459         {
460             return new Decimal(val);
461         }
462
463         public static explicit operator Decimal(double val)
464         {
465             return new Decimal(val);
466         }
467
468         public static explicit operator float(Decimal val)
469         {
470             return (float) (double) val;
471         }
472
473         public static explicit operator double(Decimal val)
474         {
475             return decimal2double(ref val);
476         }
477
478
479         public static bool operator !=(Decimal d1, Decimal d2) 
480         {
481             return !Equals(d1, d2);
482         }
483
484         public static bool operator ==(Decimal d1, Decimal d2) 
485         {
486             return Equals(d1, d2);
487         }
488
489         public static bool operator >(Decimal d1, Decimal d2) 
490         {
491             return decimalCompare(ref d1, ref d2) > 0;
492         }
493
494         public static bool operator >=(Decimal d1, Decimal d2) 
495         {
496             return decimalCompare(ref d1, ref d2) >= 0;
497         }
498
499         public static bool operator <(Decimal d1, Decimal d2) 
500         {
501             return decimalCompare(ref d1, ref d2) < 0;
502         }
503
504         public static bool operator <=(Decimal d1, Decimal d2) 
505         {
506             return decimalCompare(ref d1, ref d2) <= 0;
507         }
508
509         public static bool Equals(Decimal d1, Decimal d2) 
510         {
511             return decimalCompare(ref d1, ref d2) == 0;
512         }
513
514         public override bool Equals(object o) 
515         {
516             if (!(o is Decimal))
517                 return false;
518
519             return Equals((Decimal) o, this);
520         }
521
522         // avoid unmanaged call
523         private bool IsZero () 
524         {
525                 return ((hi == 0) && (lo == 0) && (mid == 0));
526         }
527
528         // avoid unmanaged call
529         private bool IsNegative () 
530         {
531                 return ((flags & 0x80000000) == 0x80000000);
532         }
533
534         public static Decimal Floor(Decimal d) 
535         {
536             decimalFloorAndTrunc(ref d, 1);
537             return d;
538         }
539
540         public static Decimal Truncate(Decimal d) 
541         {
542             decimalFloorAndTrunc(ref d, 0);
543             return d;
544         }
545
546         public static Decimal Round (Decimal d, int decimals) 
547         {
548 #if NET_2_0
549                 return Round (d, decimals, MidpointRounding.ToEven);
550         }
551
552         public static Decimal Round (Decimal d, int decimals, MidpointRounding mode) 
553         {
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");
556
557 #endif
558                 if (decimals < 0 || decimals > 28) {
559                         throw new ArgumentOutOfRangeException ("decimals", "[0,28]");
560                 }
561
562                 bool negative = d.IsNegative ();
563                 if (negative)
564                         d.flags ^= SIGN_FLAG;
565
566                 // Moved from Math.cs because it's easier to fix the "sign"
567                 // issue here :( as the logic is OK only for positive numbers
568                 decimal p = (decimal) Math.Pow (10, decimals);
569                 decimal int_part = Decimal.Floor (d);
570                 decimal dec_part = d - int_part;
571                 dec_part *= 10000000000000000000000000000M;
572                 dec_part = Decimal.Floor(dec_part);
573                 dec_part /= (10000000000000000000000000000M / p);
574 #if NET_2_0
575                 dec_part = Math.Round (dec_part, mode);
576 #else
577                 dec_part = Math.Round (dec_part);
578 #endif
579                 dec_part /= p;
580                 decimal result = int_part + dec_part;
581
582                 // that fixes the precision/scale (which we must keep for output)
583                 // (moved and adapted from System.Data.SqlTypes.SqlMoney)
584                 long scaleDiff = decimals - ((result.flags & 0x7FFF0000) >> 16);
585                 // integrify
586                 if (scaleDiff > 0) {
587                         // note: here we always work with positive numbers
588                         while (scaleDiff > 0) {
589                                 if (result > MaxValueDiv10)
590                                         break;
591                                 result *= 10;
592                                 scaleDiff--;
593                         }
594                 }
595                 else if (scaleDiff < 0) {
596                         while (scaleDiff < 0) {
597                                 result /= 10;
598                                 scaleDiff++;
599                         }
600                 }
601                 result.flags = (uint)((decimals - scaleDiff) << SCALE_SHIFT);
602
603                 if (negative)
604                         result.flags ^= SIGN_FLAG;
605                 return result;
606         }
607
608 #if NET_2_0
609         public static Decimal Round (Decimal d)
610         {
611           return Math.Round (d);
612         }
613
614         public static Decimal Round (Decimal d, MidpointRounding mode)
615         {
616           return Math.Round (d, mode);
617         }
618 #endif
619
620         public static Decimal Multiply (Decimal d1, Decimal d2) 
621         {
622                 if (d1.IsZero () || d2.IsZero ())
623                         return Decimal.Zero;
624
625                 if (decimalMult (ref d1, ref d2) != 0)
626                         throw new OverflowException ();
627                 return d1;
628         }
629
630         public static Decimal Divide (Decimal d1, Decimal d2) 
631         {
632                 if (d2.IsZero ())
633                         throw new DivideByZeroException ();
634                 if (d1.IsZero ())
635                         return Decimal.Zero;
636                 if (d1 == d2)
637                         return Decimal.One;
638
639                 d1.flags ^= SIGN_FLAG;
640                 if (d1 == d2)
641                         return Decimal.MinusOne;
642                 d1.flags ^= SIGN_FLAG;
643
644                 Decimal result;
645                 if (decimalDiv (out result, ref d1, ref d2) != 0)
646                         throw new OverflowException ();
647
648                 return result;
649         }
650
651         public static Decimal Remainder (Decimal d1, Decimal d2) 
652         {
653                 if (d2.IsZero ())
654                         throw new DivideByZeroException ();
655                 if (d1.IsZero ())
656                         return Decimal.Zero;
657
658                 bool negative = d1.IsNegative ();
659                 if (negative)
660                         d1.flags ^= SIGN_FLAG;
661                 if (d2.IsNegative ())
662                         d2.flags ^= SIGN_FLAG;
663
664                 Decimal result;
665                 if (d1 == d2) {
666                         return Decimal.Zero;
667                 }
668                 else if (d2 > d1) {
669                         result = d1;
670                 }
671                 else {
672                         if (decimalIntDiv (out result, ref d1, ref d2) != 0)
673                                 throw new OverflowException ();
674
675                         // FIXME: not really performant here
676                         result = d1 - result * d2;
677                 }
678
679                 if (negative)
680                         result.flags ^= SIGN_FLAG;
681                 return result;
682         }
683
684 #if NET_2_0
685                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
686 #endif
687         public static int Compare(Decimal d1, Decimal d2) 
688         {
689             return decimalCompare(ref d1, ref d2);
690         }
691
692         public int CompareTo(object val)
693         {
694             if (val == null)
695                 return 1;
696             
697             if (!(val is Decimal))
698                 throw new ArgumentException (Locale.GetText ("Value is not a System.Decimal"));
699
700             Decimal d2 = (Decimal)val;
701             return decimalCompare(ref this, ref d2);
702         }
703
704 #if NET_2_0
705         public int CompareTo(Decimal value)
706         {
707             return decimalCompare(ref this, ref value);
708         }
709
710         public bool Equals(Decimal value) 
711         {
712             return Equals(value, this);
713         }
714
715         public static Decimal Ceiling(Decimal d)
716         {
717             return Math.Ceiling (d);
718         }
719 #endif
720
721         public static Decimal Parse(string s) 
722         {
723             return Parse(s, NumberStyles.Number, null);
724         }
725
726         public static Decimal Parse(string s, NumberStyles style) 
727         {
728             return Parse(s, style, null);
729         }
730
731         public static Decimal Parse(string s, IFormatProvider provider) 
732         {
733             return Parse(s, NumberStyles.Number, provider);
734         }
735
736         static void ThrowAtPos (int pos)
737         {
738                 throw new FormatException (String.Format (Locale.GetText ("Invalid character at position {0}"), pos));
739         }
740         
741         static void ThrowInvalidExp ()
742         {
743                 throw new FormatException (Locale.GetText ("Invalid exponent"));
744         }
745         
746         private static string stripStyles(string s, NumberStyles style, NumberFormatInfo nfi, 
747             out int decPos, out bool isNegative, out bool expFlag, out int exp, bool throwex)
748         {
749             isNegative = false;
750             expFlag = false;
751             exp = 0;
752             decPos = -1;
753
754             bool hasSign = false;
755             bool hasOpeningParentheses = false;
756             bool hasDecimalPoint = false;
757             bool allowedLeadingWhiteSpace = ((style & NumberStyles.AllowLeadingWhite) != 0);
758             bool allowedTrailingWhiteSpace = ((style & NumberStyles.AllowTrailingWhite) != 0);
759             bool allowedLeadingSign = ((style & NumberStyles.AllowLeadingSign) != 0);
760             bool allowedTrailingSign = ((style & NumberStyles.AllowTrailingSign) != 0);
761             bool allowedParentheses = ((style & NumberStyles.AllowParentheses) != 0);
762             bool allowedThousands = ((style & NumberStyles.AllowThousands) != 0);
763             bool allowedDecimalPoint = ((style & NumberStyles.AllowDecimalPoint) != 0);
764             bool allowedExponent = ((style & NumberStyles.AllowExponent) != 0);
765
766             /* get rid of currency symbol */
767             bool hasCurrency = false;
768             if ((style & NumberStyles.AllowCurrencySymbol) != 0)
769             {
770                 int index = s.IndexOf(nfi.CurrencySymbol);
771                 if (index >= 0) 
772                 {
773                     s = s.Remove(index, nfi.CurrencySymbol.Length);
774                     hasCurrency = true;
775                 }
776             }
777
778             string decimalSep = (hasCurrency) ? nfi.CurrencyDecimalSeparator : nfi.NumberDecimalSeparator;
779             string groupSep = (hasCurrency) ? nfi.CurrencyGroupSeparator : nfi.NumberGroupSeparator;
780
781             int pos = 0;
782             int len = s.Length;
783
784             StringBuilder sb = new StringBuilder(len);
785
786             // leading
787             while (pos < len) 
788             {
789                 char ch = s[pos];
790                 if (Char.IsDigit(ch)) 
791                 {
792                     break; // end of leading
793                 }
794                 else if (allowedLeadingWhiteSpace && Char.IsWhiteSpace(ch)) 
795                 {
796                     pos++;
797                 }
798                 else if (allowedParentheses && ch == '(' && !hasSign && !hasOpeningParentheses) 
799                 {
800                     hasOpeningParentheses = true;
801                     hasSign = true;
802                     isNegative = true;
803                     pos++;
804                 }
805                 else if (allowedLeadingSign && ch == nfi.NegativeSign[0] && !hasSign) 
806                 {
807                     int slen = nfi.NegativeSign.Length;
808                     if (slen == 1 || s.IndexOf(nfi.NegativeSign, pos, slen) == pos) 
809                     {
810                         hasSign = true;
811                         isNegative = true;
812                         pos += slen;
813                     }
814                 }
815                 else if (allowedLeadingSign && ch == nfi.PositiveSign[0] && !hasSign) 
816                 {
817                     int slen = nfi.PositiveSign.Length;
818                     if (slen == 1 || s.IndexOf(nfi.PositiveSign, pos, slen) == pos) 
819                     {
820                         hasSign = true;
821                         pos += slen;
822                     }
823                 }
824                 else if (allowedDecimalPoint && ch == decimalSep[0])
825                 {
826                     int slen = decimalSep.Length;
827                     if (slen != 1 && s.IndexOf(decimalSep, pos, slen) != pos) 
828                     {
829                             if (throwex)
830                                     ThrowAtPos (pos);
831                             else
832                                     return null;
833                     }
834                     break;
835                 }
836                 else
837                 {
838                         if (throwex)
839                                 ThrowAtPos (pos);
840                         else
841                                 return null;
842                 }
843             }
844
845             if (pos == len){
846                     if (throwex)
847                             throw new FormatException(Locale.GetText ("No digits found"));
848                     else
849                             return null;
850             }
851
852             // digits 
853             while (pos < len)
854             {
855                 char ch = s[pos];
856                 if (Char.IsDigit(ch)) 
857                 {
858                     sb.Append(ch);
859                     pos++;
860                 }
861                 else if (allowedThousands && ch == groupSep[0]) 
862                 {
863                     int slen = groupSep.Length;
864                     if (slen != 1 && s.IndexOf(groupSep, pos, slen) != pos) 
865                     {
866                             if (throwex)
867                                     ThrowAtPos (pos);
868                             else
869                                     return null;
870                     }
871                     pos += slen;
872                 }
873                 else if (allowedDecimalPoint && ch == decimalSep[0] && !hasDecimalPoint)
874                 {
875                     int slen = decimalSep.Length;
876                     if (slen == 1 || s.IndexOf(decimalSep, pos, slen) == pos) 
877                     {
878                         decPos = sb.Length;
879                         hasDecimalPoint = true;
880                         pos += slen;
881                     }
882                 }
883                 else
884                 {
885                     break;
886                 }
887             }
888
889             // exponent
890             if (pos < len)
891             {
892                 char ch = s[pos];
893                 if (allowedExponent && Char.ToUpperInvariant (ch) == 'E')
894                 {
895                     expFlag = true;
896                     pos++;
897                     if (pos >= len){
898                             if (throwex)
899                                     ThrowInvalidExp ();
900                             else
901                                     return null;
902                     }
903                     ch = s[pos];
904                     bool isNegativeExp = false;
905                     if (ch == nfi.PositiveSign[0])
906                     {
907                         int slen = nfi.PositiveSign.Length;
908                         if (slen == 1 || s.IndexOf(nfi.PositiveSign, pos, slen) == pos) 
909                         {
910                             pos += slen;
911                             if (pos >= len){
912                                     if (throwex)
913                                             ThrowInvalidExp ();
914                                     else
915                                             return null;
916                             }
917                         }
918                     }
919                     else if (ch == nfi.NegativeSign[0])
920                     {
921                         int slen = nfi.NegativeSign.Length;
922                         if (slen == 1 || s.IndexOf(nfi.NegativeSign, pos, slen) == pos) 
923                         {
924                             pos += slen;
925                             if (pos >= len){
926                                     if (throwex)
927                                             ThrowInvalidExp ();
928                                     else
929                                             return null;
930                             }
931                             isNegativeExp = true;
932                         }
933                     }
934                     ch = s[pos];
935                     if (!Char.IsDigit(ch)){
936                             if (throwex)
937                                     ThrowInvalidExp ();
938                             else
939                                     return null;
940                     }
941
942                     exp = ch - '0';
943                     pos++;
944                     while (pos < len && Char.IsDigit(s[pos])) 
945                     {
946                         exp *= 10;
947                         exp += s[pos] - '0';
948                         pos++;
949                     }
950                     if (isNegativeExp) exp *= -1;
951                 }
952             }
953
954             // trailing
955             while (pos < len)
956             {
957                 char ch = s[pos];
958                 if (allowedTrailingWhiteSpace && Char.IsWhiteSpace(ch)) 
959                 {
960                     pos++;
961                 }
962                 else if (allowedParentheses && ch == ')' && hasOpeningParentheses) 
963                 {
964                     hasOpeningParentheses = false;
965                     pos++;
966                 }
967                 else if (allowedTrailingSign && ch == nfi.NegativeSign[0] && !hasSign) 
968                 {
969                     int slen = nfi.NegativeSign.Length;
970                     if (slen == 1 || s.IndexOf(nfi.NegativeSign, pos, slen) == pos) 
971                     {
972                         hasSign = true;
973                         isNegative = true;
974                         pos += slen;
975                     }
976                 }
977                 else if (allowedTrailingSign && ch == nfi.PositiveSign[0] && !hasSign) 
978                 {
979                     int slen = nfi.PositiveSign.Length;
980                     if (slen == 1 || s.IndexOf(nfi.PositiveSign, pos, slen) == pos) 
981                     {
982                         hasSign = true;
983                         pos += slen;
984                     }
985                 }
986                 else
987                 {
988                         if (throwex)
989                                 ThrowAtPos (pos);
990                         else
991                                 return null;
992                 }
993             }
994
995             if (hasOpeningParentheses){
996                     if (throwex)
997                             throw new FormatException (Locale.GetText ("Closing Parentheses not found"));
998                     else
999                             return null;
1000             }
1001             
1002             if (!hasDecimalPoint)
1003                     decPos = sb.Length;
1004
1005             return sb.ToString();
1006         }
1007
1008         public static Decimal Parse (string s, NumberStyles style, IFormatProvider provider) 
1009         {
1010                 if (s == null)
1011                         throw new ArgumentNullException ("s");
1012
1013                 if ((style & NumberStyles.AllowHexSpecifier) != 0)
1014                         throw new ArgumentException ("Decimal.TryParse does not accept AllowHexSpecifier", "style");
1015
1016                 Decimal result;
1017                 PerformParse (s, style, provider, out result, true);
1018                 return result;
1019         }
1020         
1021 #if NET_2_0
1022         public static bool TryParse (string s, out Decimal result)
1023         {
1024                 if (s == null){
1025                         result = 0;
1026                         return false;
1027                 }
1028                 return PerformParse (s, NumberStyles.Number, null, out result, false);
1029         }
1030
1031         public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out decimal result)
1032         {
1033                 if (s == null || (style & NumberStyles.AllowHexSpecifier) != 0){
1034                         result = 0;
1035                         return false;
1036                 }
1037                 
1038                 return PerformParse (s, style, provider, out result, false);
1039         }
1040 #endif
1041
1042         static bool PerformParse (string s, NumberStyles style, IFormatProvider provider, out Decimal res, bool throwex) 
1043         {
1044                 NumberFormatInfo nfi = NumberFormatInfo.GetInstance (provider);
1045
1046                 int iDecPos, exp;
1047                 bool isNegative, expFlag;
1048                 s = stripStyles(s, style, nfi, out iDecPos, out isNegative, out expFlag, out exp, throwex);
1049                 if (s == null){
1050                         res = 0;
1051                         return false;
1052                 }
1053
1054                 if (iDecPos < 0){
1055                         if (throwex)
1056                                 throw new Exception (Locale.GetText ("Error in System.Decimal.Parse"));
1057                         res = 0;
1058                         return false;
1059                 }
1060
1061                 // first we remove leading 0
1062                 int len = s.Length;
1063                 int i = 0;
1064                 while ((i < iDecPos) && (s [i] == '0'))
1065                         i++;
1066                 if ((i > 1) && (len > 1)) {
1067                         s = s.Substring (i, len - i);
1068                         iDecPos -= i;
1069                 }
1070
1071                 // first 0. may not be here but is part of the maximum length
1072                 int max = ((iDecPos == 0) ? 27 : 28);
1073                 len = s.Length;
1074                 if (len >= max + 1) {
1075                         // number lower than MaxValue (base-less) can have better precision
1076                         if (String.Compare (s, 0, "79228162514264337593543950335", 0, max + 1,
1077                                 false, CultureInfo.InvariantCulture) <= 0) {
1078                                 max++;
1079                         }
1080                 }
1081
1082                 // then we trunc the string
1083                 if ((len > max) && (iDecPos < len)) {
1084                         int round = (s [max] - '0');
1085                         s = s.Substring (0, max);
1086
1087                         bool addone = false;
1088                         if (round > 5) {
1089                                 addone = true;
1090                         }
1091                         else if (round == 5) {
1092                                 if (isNegative) {
1093                                         addone = true;
1094                                 }
1095                                 else {
1096                                         // banker rounding applies :(
1097                                         int previous = (s [max - 1] - '0');
1098                                         addone = ((previous & 0x01) == 0x01);
1099                                 }
1100                         }
1101                         if (addone) {
1102                                 char[] array = s.ToCharArray ();
1103                                 int p = max - 1;
1104                                 while (p >= 0) {
1105                                         int b = (array [p] - '0');
1106                                         if (array [p] != '9') {
1107                                                 array [p] = (char)(b + '1');
1108                                                 break;
1109                                         }
1110                                         else {
1111                                                 array [p--] = '0';
1112                                         }
1113                                 }
1114                                 if ((p == -1) && (array [0] == '0')) {
1115                                         iDecPos++;
1116                                         s = "1".PadRight (iDecPos, '0');
1117                                 }
1118                                 else
1119                                         s = new String (array);
1120                         }
1121                 }
1122
1123                 Decimal result;
1124                 // always work in positive (rounding issues)
1125                 if (string2decimal (out result, s, (uint)iDecPos, 0) != 0){
1126                         if (throwex)
1127                                 throw new OverflowException ();
1128                         res = 0;
1129                         return false;
1130                 }
1131
1132                 if (expFlag) {
1133                         if (decimalSetExponent (ref result, exp) != 0){
1134                                 if (throwex)
1135                                         throw new OverflowException ();
1136                                 res = 0;
1137                                 return false;
1138                         }
1139                 }
1140
1141                 if (isNegative)
1142                         result.flags ^= SIGN_FLAG;
1143
1144                 res = result;
1145                 return true;
1146         }
1147
1148         public TypeCode GetTypeCode ()
1149         {
1150             return TypeCode.Decimal;
1151         }
1152
1153         public static byte ToByte (decimal value)
1154         {
1155                 if (value > Byte.MaxValue || value < Byte.MinValue)
1156                         throw new OverflowException (Locale.GetText (
1157                                 "Value is greater than Byte.MaxValue or less than Byte.MinValue"));
1158           
1159                 // return truncated value
1160                 return (byte)(Decimal.Truncate (value));
1161         }
1162
1163         public static double ToDouble (decimal value)
1164         {
1165                 return Convert.ToDouble (value);
1166         }
1167
1168         public static short ToInt16 (decimal value)
1169         {
1170                 if (value > Int16.MaxValue || value < Int16.MinValue)
1171                         throw new OverflowException (Locale.GetText (
1172                                 "Value is greater than Int16.MaxValue or less than Int16.MinValue"));
1173           
1174                 // return truncated value
1175                 return (Int16)(Decimal.Truncate (value));
1176         }
1177
1178         public static int ToInt32 (decimal value)
1179         {
1180                 if (value > Int32.MaxValue || value < Int32.MinValue)
1181                         throw new OverflowException (Locale.GetText (
1182                                 "Value is greater than Int32.MaxValue or less than Int32.MinValue"));
1183           
1184                 // return truncated value
1185                 return (Int32)(Decimal.Truncate (value));
1186         }
1187         
1188         public static long ToInt64 (decimal value)
1189         {
1190                 if (value > Int64.MaxValue || value < Int64.MinValue)
1191                         throw new OverflowException (Locale.GetText (
1192                                 "Value is greater than Int64.MaxValue or less than Int64.MinValue"));
1193           
1194                 // return truncated value
1195                 return (Int64)(Decimal.Truncate (value));
1196         }
1197
1198         public static long ToOACurrency (decimal value)
1199         {
1200                 return (long) (value * 10000);
1201         }
1202
1203         [CLSCompliant(false)]
1204         public static sbyte ToSByte (decimal value)
1205         {
1206                 if (value > SByte.MaxValue || value < SByte.MinValue)
1207                         throw new OverflowException (Locale.GetText (
1208                                 "Value is greater than SByte.MaxValue or less than SByte.MinValue"));
1209           
1210                 // return truncated value
1211                 return (SByte)(Decimal.Truncate (value));
1212         }
1213         
1214         public static float ToSingle (decimal value)
1215         {
1216                 return Convert.ToSingle (value);
1217         }
1218
1219         [CLSCompliant(false)]
1220         public static ushort ToUInt16 (decimal value)
1221         {
1222                 if (value > UInt16.MaxValue || value < UInt16.MinValue)
1223                         throw new OverflowException (Locale.GetText (
1224                                 "Value is greater than UInt16.MaxValue or less than UInt16.MinValue"));
1225           
1226                 // return truncated value
1227                 return (UInt16)(Decimal.Truncate (value));
1228         }
1229
1230         [CLSCompliant(false)]
1231         public static uint ToUInt32 (decimal value)
1232         {
1233                 if (value > UInt32.MaxValue || value < UInt32.MinValue)
1234                         throw new OverflowException (Locale.GetText (
1235                                 "Value is greater than UInt32.MaxValue or less than UInt32.MinValue"));
1236           
1237                 // return truncated value
1238                 return (UInt32)(Decimal.Truncate (value));
1239         }
1240
1241         [CLSCompliant(false)]
1242         public static ulong ToUInt64 (decimal value)
1243         {
1244                 if (value > UInt64.MaxValue || value < UInt64.MinValue)
1245                         throw new OverflowException (Locale.GetText (
1246                                 "Value is greater than UInt64.MaxValue or less than UInt64.MinValue"));
1247           
1248                 // return truncated value
1249                 return (UInt64)(Decimal.Truncate (value));
1250         }
1251                 
1252         object IConvertible.ToType (Type conversionType, IFormatProvider provider)
1253         {
1254             return Convert.ToType (this, conversionType, provider);
1255         }
1256
1257         bool IConvertible.ToBoolean (IFormatProvider provider)
1258         {
1259             return Convert.ToBoolean (this);
1260         }
1261
1262         byte IConvertible.ToByte (IFormatProvider provider)
1263         {
1264             return Convert.ToByte (this);
1265         }
1266
1267         char IConvertible.ToChar (IFormatProvider provider)
1268         {
1269             throw new InvalidCastException ();
1270         }
1271
1272         DateTime IConvertible.ToDateTime (IFormatProvider provider)
1273         {
1274             throw new InvalidCastException ();
1275         }
1276
1277         decimal IConvertible.ToDecimal (IFormatProvider provider)
1278         {
1279             return this;
1280         }
1281
1282         double IConvertible.ToDouble (IFormatProvider provider)
1283         {
1284             return Convert.ToDouble (this);
1285         }
1286
1287         short IConvertible.ToInt16 (IFormatProvider provider)
1288         {
1289             return Convert.ToInt16 (this);
1290         }
1291
1292         int IConvertible.ToInt32 (IFormatProvider provider)
1293         {
1294             return Convert.ToInt32 (this);
1295         }
1296
1297         long IConvertible.ToInt64 (IFormatProvider provider)
1298         {
1299             return Convert.ToInt64 (this);
1300         }
1301
1302         sbyte IConvertible.ToSByte (IFormatProvider provider)
1303         {
1304             return Convert.ToSByte (this);
1305         }
1306
1307         float IConvertible.ToSingle (IFormatProvider provider)
1308         {
1309             return Convert.ToSingle (this);
1310         }
1311
1312         ushort IConvertible.ToUInt16 (IFormatProvider provider)
1313         {
1314             return Convert.ToUInt16 (this);
1315         }
1316
1317         uint IConvertible.ToUInt32 (IFormatProvider provider)
1318         {
1319             return Convert.ToUInt32 (this);
1320         }
1321
1322         ulong IConvertible.ToUInt64 (IFormatProvider provider)
1323         {
1324             return Convert.ToUInt64 (this);
1325         }
1326
1327         public string ToString (string format, IFormatProvider provider) 
1328         {
1329             return NumberFormatter.NumberToString (format, this, provider);
1330         }
1331
1332         public override string ToString() 
1333         {
1334             return ToString("G", null);
1335         }
1336
1337         public string ToString(string format) 
1338         {
1339             return ToString(format, null);
1340         }
1341
1342         public string ToString(IFormatProvider provider) 
1343         {
1344             return ToString("G", provider);
1345         }
1346
1347 #if !MSTEST
1348         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1349         private static extern int decimal2UInt64(ref Decimal val, 
1350             out ulong result);
1351
1352         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1353         private static extern int decimal2Int64(ref Decimal val, 
1354             out long result);
1355
1356         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1357         private static extern int double2decimal(out Decimal erg, 
1358             double val, int digits);
1359
1360         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1361         private static extern int decimalIncr(ref Decimal d1, ref Decimal d2);
1362
1363         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1364         internal static extern int decimal2string(ref Decimal val, 
1365             int digits, int decimals, char[] bufDigits, int bufSize, out int decPos, out int sign);
1366
1367         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1368         internal static extern int string2decimal(out Decimal val, String sDigits, uint decPos, int sign);
1369
1370         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1371         internal static extern int decimalSetExponent(ref Decimal val, int exp);
1372
1373         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1374         private static extern double decimal2double(ref Decimal val);
1375
1376         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1377         private static extern void decimalFloorAndTrunc(ref Decimal val, 
1378             int floorFlag);
1379         
1380         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1381         private static extern void decimalRound(ref Decimal val, int decimals);
1382
1383         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1384         private static extern int decimalMult(ref Decimal pd1, ref Decimal pd2);
1385         
1386         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1387         private static extern int decimalDiv(out Decimal pc, ref Decimal pa, ref Decimal pb);
1388
1389         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1390         private static extern int decimalIntDiv(out Decimal pc, ref Decimal pa, ref Decimal pb);
1391
1392         [MethodImplAttribute(MethodImplOptions.InternalCall)]
1393         private static extern int decimalCompare(ref Decimal d1, ref Decimal d2);
1394 #else
1395         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1396         [DllImport("libdec", EntryPoint="decimal2UInt64")]
1397         private static extern int decimal2UInt64(ref Decimal val, 
1398             out ulong result);
1399
1400         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1401         [DllImport("libdec", EntryPoint="decimal2Int64")]
1402         private static extern int decimal2Int64(ref Decimal val, 
1403             out long result);
1404
1405         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1406         [DllImport("libdec", EntryPoint="double2decimal")]
1407         private static extern int double2decimal(out Decimal erg, 
1408             double val, int digits);
1409
1410         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1411         [DllImport("libdec", EntryPoint="decimalIncr")]
1412         private static extern int decimalIncr(ref Decimal d1, ref Decimal d2);
1413
1414         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1415         [DllImport("libdec", EntryPoint="decimal2string")]
1416         internal static extern int decimal2string(ref Decimal val, 
1417             int digits, int decimals,
1418             [MarshalAs(UnmanagedType.LPWStr)]StringBuilder bufDigits, 
1419             int bufSize, out int decPos, out int sign);
1420
1421         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1422         [DllImport("libdec", EntryPoint="string2decimal")]
1423         internal static extern int string2decimal(out Decimal val,
1424             [MarshalAs(UnmanagedType.LPWStr)]String sDigits,
1425             uint decPos, int sign);
1426
1427         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1428         [DllImport("libdec", EntryPoint="decimalSetExponent")]
1429         internal static extern int decimalSetExponent(ref Decimal val, int exp);
1430
1431         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1432         [DllImport("libdec", EntryPoint="decimal2double")]
1433         private static extern double decimal2double(ref Decimal val);
1434
1435         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1436         [DllImport("libdec", EntryPoint="decimalFloorAndTrunc")]
1437         private static extern void decimalFloorAndTrunc(ref Decimal val, 
1438             int floorFlag);
1439         
1440         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1441         [DllImport("libdec", EntryPoint="decimalRound")]
1442         private static extern void decimalRound(ref Decimal val, int decimals);
1443
1444         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1445         [DllImport("libdec", EntryPoint="decimalMult")]
1446         private static extern int decimalMult(ref Decimal pd1, ref Decimal pd2);
1447         
1448         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1449         [DllImport("libdec", EntryPoint="decimalDiv")]
1450         private static extern int decimalDiv(out Decimal pc, ref Decimal pa, ref Decimal pb);
1451
1452         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1453         [DllImport("libdec", EntryPoint="decimalIntDiv")]
1454         private static extern int decimalIntDiv(out Decimal pc, ref Decimal pa, ref Decimal pb);
1455
1456         //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1457         [DllImport("libdec", EntryPoint="decimalCompare")]
1458         private static extern int decimalCompare(ref Decimal d1, ref Decimal d2);
1459
1460 #endif
1461     }
1462 }
1463