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