Merge pull request #409 from Alkarex/patch-1
[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 using System.Runtime.ConstrainedExecution;
41 using System.Runtime.Serialization;
42
43 #if MSTEST
44 using System.Runtime.InteropServices;
45 #endif
46
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         [System.Runtime.InteropServices.ComVisible (true)]
56         public struct Decimal: IFormattable, IConvertible, IComparable, IComparable<Decimal>, IEquatable <Decimal>
57 #if NET_4_0
58                 , IDeserializationCallback
59 #endif
60         {
61                 public const decimal MinValue = -79228162514264337593543950335m;
62                 public const decimal MaxValue =  79228162514264337593543950335m;
63
64                 public const decimal MinusOne = -1;
65                 public const decimal One = 1;
66                 public const decimal Zero = 0;
67
68                 private static readonly Decimal MaxValueDiv10 = MaxValue / 10;
69
70                 // some constants
71                 private const uint MAX_SCALE = 28;
72                 private const uint SIGN_FLAG = 0x80000000;
73                 private const int SCALE_SHIFT = 16;
74                 private const uint RESERVED_SS32_BITS = 0x7F00FFFF;
75
76                 // internal representation of decimal
77                 private uint flags;
78                 private uint hi;
79                 private uint lo;
80                 private uint mid;
81
82                 public Decimal (int lo, int mid, int hi, bool isNegative, byte scale)
83                 {
84                         unchecked 
85                         {
86                                 this.lo = (uint) lo;
87                                 this.mid = (uint) mid;
88                                 this.hi = (uint) hi;
89
90                                 if (scale > MAX_SCALE) 
91                                         throw new ArgumentOutOfRangeException (Locale.GetText ("scale must be between 0 and 28"));
92
93                                 flags = scale;
94                                 flags <<= SCALE_SHIFT;
95                                 if (isNegative) flags |= SIGN_FLAG;
96                         }
97                 }
98
99                 public Decimal (int value) 
100                 {
101                         unchecked 
102                         {
103                                 hi = mid = 0;
104                                 if (value < 0) 
105                                 {
106                                         flags = SIGN_FLAG;
107                                         lo = ((uint)~value) + 1;
108                                 }
109                                 else 
110                                 {
111                                         flags = 0;
112                                         lo = (uint) value;
113                                 }
114                         }
115                 }
116
117                 [CLSCompliant(false)]
118                 public Decimal (uint value) 
119                 {
120                         lo = value;
121                         flags = hi = mid = 0;
122                 }
123
124                 public Decimal (long value) 
125                 {
126                         unchecked 
127                         {
128                                 hi = 0;
129                                 if (value < 0) 
130                                 {
131                                         flags = SIGN_FLAG;
132                                         ulong u = ((ulong)~value) + 1;
133                                         lo = (uint)u;
134                                         mid = (uint)(u >> 32);
135                                 }
136                                 else 
137                                 {
138                                         flags = 0;
139                                         ulong u = (ulong)value;
140                                         lo = (uint)u;
141                                         mid = (uint)(u >> 32);
142                                 }
143                         }
144                 }
145
146                 [CLSCompliant(false)]
147                 public Decimal (ulong value) 
148                 {
149                         unchecked 
150                         {
151                                 flags = hi = 0;
152                                 lo = (uint)value;
153                                 mid = (uint)(value >> 32);
154                         }
155                 }
156
157                 public Decimal (float value) 
158                 {
159 #if false
160                         //
161                         // We cant use the double2decimal method
162                         // because it incorrectly turns the floating point
163                         // value 1.23456789E-25F which should be:
164                         //    0.0000000000000000000000001235
165                         // into the incorrect:
166                         //   0.0000000000000000000000001234
167                         //
168                         //    The code currently parses the double value 0.6 as
169                         //    0.600000000000000
170                         //
171                         // And we have a patch for that called (trim
172                         if (double2decimal (out this, value, 7) != 0)
173                                 throw new OverflowException ();
174 #else
175                         if (value > (float)Decimal.MaxValue || value < (float)Decimal.MinValue ||
176                                 float.IsNaN (value) || float.IsNegativeInfinity (value) || float.IsPositiveInfinity (value)) {
177                                 throw new OverflowException (Locale.GetText (
178                                         "Value {0} is greater than Decimal.MaxValue or less than Decimal.MinValue", value));
179                         }
180                         
181                         // we must respect the precision (double2decimal doesn't)
182                         Decimal d = Decimal.Parse (value.ToString (CultureInfo.InvariantCulture),
183                                         NumberStyles.Float, CultureInfo.InvariantCulture);
184                         flags = d.flags;
185                         hi = d.hi;
186                         lo = d.lo;
187                         mid = d.mid;
188 #endif
189                 }
190
191                 public Decimal (double value) 
192                 {
193 #if true
194                         //
195                         // We cant use the double2decimal method
196                         // because it incorrectly turns the floating point
197                         // value 1.23456789E-25F which should be:
198                         //    0.0000000000000000000000001235
199                         // into the incorrect:
200                         //   0.0000000000000000000000001234
201                         //
202                         //    The code currently parses the double value 0.6 as
203                         //    0.600000000000000
204                         //
205                         // And we have a patch for that called (trim
206                         if (double2decimal (out this, value, 15) != 0)
207                                 throw new OverflowException ();
208 #else
209                         if (value > (double)Decimal.MaxValue || value < (double)Decimal.MinValue ||
210                                 double.IsNaN (value) || double.IsNegativeInfinity (value) || double.IsPositiveInfinity (value)) {
211                                 throw new OverflowException (Locale.GetText (
212                                         "Value {0} is greater than Decimal.MaxValue or less than Decimal.MinValue", value));
213                         }
214                         // we must respect the precision (double2decimal doesn't)
215                         Decimal d = Decimal.Parse (value.ToString (CultureInfo.InvariantCulture),
216                                         NumberStyles.Float, CultureInfo.InvariantCulture);
217                         flags = d.flags;
218                         hi = d.hi;
219                         lo = d.lo;
220                         mid = d.mid;
221 #endif
222                 }
223
224                 public Decimal (int[] bits) 
225                 {
226                         if (bits == null) 
227                         {
228                                 throw new ArgumentNullException (Locale.GetText ("bits is a null reference"));
229                         }
230
231                         if (bits.Length != 4) 
232                         {
233                                 throw new ArgumentException (Locale.GetText ("bits does not contain four values"));
234                         }
235
236                         unchecked {
237                                 lo = (uint) bits[0];
238                                 mid = (uint) bits[1];
239                                 hi = (uint) bits[2];
240                                 flags = (uint) bits[3];
241                                 byte scale = (byte)(flags >> SCALE_SHIFT);
242                                 if (scale > MAX_SCALE || (flags & RESERVED_SS32_BITS) != 0) 
243                                 {
244                                         throw new ArgumentException (Locale.GetText ("Invalid bits[3]"));
245                                 }
246                         }
247                 }
248
249                 public static decimal FromOACurrency (long cy)
250                 {
251                         return (decimal)cy / (decimal)10000;
252                 }
253
254                 public static int[] GetBits (Decimal d) 
255                 {
256                         unchecked 
257                         {
258                                 return new int[] { (int)d.lo, (int)d.mid, (int)d.hi, (int)d.flags };
259                         }
260                 }
261
262                 public static Decimal Negate (Decimal d) 
263                 {
264                         d.flags ^= SIGN_FLAG;
265                         return d;
266                 }
267
268                 public static Decimal Add (Decimal d1, Decimal d2) 
269                 {
270                         if (decimalIncr (ref d1, ref d2) == 0)
271                                 return d1;
272                         else
273                                 throw new OverflowException (Locale.GetText ("Overflow on adding decimal number"));
274                 }
275
276                 public static Decimal Subtract (Decimal d1, Decimal d2) 
277                 {
278                         d2.flags ^= SIGN_FLAG;
279                         int result = decimalIncr (ref d1, ref d2);
280                         if (result == 0)
281                                 return d1;
282                         else
283                                 throw new OverflowException (Locale.GetText ("Overflow on subtracting decimal numbers ("+result+")"));
284                 }
285
286                 public override int GetHashCode () 
287                 {
288                         return (int) (flags ^ hi ^ lo ^ mid);
289                 }
290
291                 public static Decimal operator + (Decimal d1, Decimal d2)
292                 {
293                         return Add (d1, d2);
294                 }
295
296                 public static Decimal operator -- (Decimal d) 
297                 {
298                         return Add(d, MinusOne);
299                 }
300
301                 public static Decimal operator ++ (Decimal d) 
302                 {
303                         return Add (d, One);
304                 }
305
306                 public static Decimal operator - (Decimal d1, Decimal d2) 
307                 {
308                         return Subtract (d1, d2);
309                 }
310
311                 public static Decimal operator - (Decimal d) 
312                 {
313                         return Negate (d);
314                 }
315
316                 public static Decimal operator + (Decimal d) 
317                 {
318                         return d;
319                 }
320
321                 public static Decimal operator * (Decimal d1, Decimal d2)
322                 {
323                         return Multiply (d1, d2);
324                 }
325                 
326                 public static Decimal operator / (Decimal d1, Decimal d2) 
327                 {
328                         return Divide (d1, d2);
329                 }
330                 
331                 public static Decimal operator % (Decimal d1, Decimal d2) 
332                 {
333                         return Remainder (d1, d2);
334                 }
335
336                 private static ulong u64 (Decimal value) 
337                 {
338                         ulong result;
339
340                         decimalFloorAndTrunc (ref value, 0);
341                         if (decimal2UInt64 (ref value, out result) != 0) {
342                                 throw new System.OverflowException ();
343                         }
344                         return result;
345                 }
346
347                 private static long s64 (Decimal value) 
348                 {
349                         long result;
350
351                         decimalFloorAndTrunc (ref value, 0);
352                         if (decimal2Int64 (ref value, out result) != 0) {
353                                 throw new System.OverflowException ();
354                         }
355                         return result;
356                 }
357
358                 public static explicit operator byte (Decimal value)
359                 {
360                         ulong result = u64 (value);
361                         return checked ((byte) result);
362                 }
363
364                 [CLSCompliant (false)]
365                 public static explicit operator sbyte (Decimal value)
366                 {
367                         long result = s64 (value);
368                         return checked ((sbyte) result);
369                 }
370
371                 public static explicit operator char (Decimal value) 
372                 {
373                         ulong result = u64 (value);
374                         return checked ((char) result);
375                 }
376
377                 public static explicit operator short (Decimal value) 
378                 {
379                         long result = s64 (value);
380                         return checked ((short) result);
381                 }
382
383                 [CLSCompliant (false)]
384                 public static explicit operator ushort (Decimal value) 
385                 {
386                         ulong result = u64 (value);
387                         return checked ((ushort) result);
388                 }
389
390                 public static explicit operator int (Decimal value) 
391                 {
392                         long result = s64 (value);
393                         return checked ((int) result);
394                 }
395
396                 [CLSCompliant(false)]
397                 public static explicit operator uint (Decimal value) 
398                 {
399                         ulong result = u64 (value);
400                         return checked ((uint) result);
401                 }
402
403                 public static explicit operator long (Decimal value) 
404                 {
405                         return s64 (value);
406                 }
407
408                 [CLSCompliant(false)]
409                 public static explicit operator ulong (Decimal value) 
410                 {
411                         return u64 (value);
412                 }
413
414                 public static implicit operator Decimal (byte value) 
415                 {
416                         return new Decimal (value);
417                 }
418
419                 [CLSCompliant(false)]
420                 public static implicit operator Decimal (sbyte value) 
421                 {
422                         return new Decimal (value);
423                 }
424
425                 public static implicit operator Decimal (short value) 
426                 {
427                         return new Decimal (value);
428                 }
429
430                 [CLSCompliant(false)]
431                 public static implicit operator Decimal (ushort value) 
432                 {
433                         return new Decimal (value);
434                 }
435
436                 public static implicit operator Decimal (char value) 
437                 {
438                         return new Decimal (value);
439                 }
440
441                 public static implicit operator Decimal (int value) 
442                 {
443                         return new Decimal (value);
444                 }
445
446                 [CLSCompliant(false)]
447                 public static implicit operator Decimal (uint value) 
448                 {
449                         return new Decimal (value);
450                 }
451
452                 public static implicit operator Decimal (long value) 
453                 {
454                         return new Decimal (value);
455                 }
456
457                 [CLSCompliant(false)]
458                 public static implicit operator Decimal (ulong value) 
459                 {
460                         return new Decimal (value);
461                 }
462
463                 public static explicit operator Decimal (float value) 
464                 {
465                         return new Decimal (value);
466                 }
467
468                 public static explicit operator Decimal (double value)
469                 {
470                         return new Decimal (value);
471                 }
472
473                 public static explicit operator float (Decimal value)
474                 {
475                         return (float) (double) value;
476                 }
477
478                 public static explicit operator double (Decimal value)
479                 {
480                         return decimal2double (ref value);
481                 }
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 Equals (d1, d2);
492                 }
493
494                 public static bool operator > (Decimal d1, Decimal d2) 
495                 {
496                         return Compare (d1, d2) > 0;
497                 }
498
499                 public static bool operator >= (Decimal d1, Decimal d2) 
500                 {
501                         return Compare (d1, d2) >= 0;
502                 }
503
504                 public static bool operator < (Decimal d1, Decimal d2) 
505                 {
506                         return Compare (d1, d2) < 0;
507                 }
508
509                 public static bool operator <= (Decimal d1, Decimal d2) 
510                 {
511                         return Compare (d1, d2) <= 0;
512                 }
513
514                 public static bool Equals (Decimal d1, Decimal d2) 
515                 {
516                         return Compare (d1, d2) == 0;
517                 }
518
519                 public override bool Equals (object value) 
520                 {
521                         if (!(value is Decimal))
522                                 return false;
523
524                         return Equals ((Decimal) value, this);
525                 }
526
527                 // avoid unmanaged call
528                 private bool IsZero () 
529                 {
530                         return ((hi == 0) && (lo == 0) && (mid == 0));
531                 }
532
533                 // avoid unmanaged call
534                 private bool IsNegative () 
535                 {
536                         return ((flags & 0x80000000) == 0x80000000);
537                 }
538
539                 public static Decimal Floor (Decimal d) 
540                 {
541                         decimalFloorAndTrunc (ref d, 1);
542                         return d;
543                 }
544
545                 public static Decimal Truncate (Decimal d) 
546                 {
547                         decimalFloorAndTrunc (ref d, 0);
548                         return d;
549                 }
550
551                 public static Decimal Round (Decimal d, int decimals) 
552                 {
553                         return Round (d, decimals, MidpointRounding.ToEven);
554                 }
555
556                 public static Decimal Round (Decimal d, int decimals, MidpointRounding mode) 
557                 {
558                         if ((mode != MidpointRounding.ToEven) && (mode != MidpointRounding.AwayFromZero))
559                                 throw new ArgumentException ("The value '" + mode + "' is not valid for this usage of the type MidpointRounding.", "mode");
560
561                         if (decimals < 0 || decimals > 28) {
562                                 throw new ArgumentOutOfRangeException ("decimals", "[0,28]");
563                         }
564
565                         bool negative = d.IsNegative ();
566                         if (negative)
567                                 d.flags ^= SIGN_FLAG;
568
569                         // Moved from Math.cs because it's easier to fix the "sign"
570                         // issue here :( as the logic is OK only for positive numbers
571                         decimal p = (decimal) Math.Pow (10, decimals);
572                         decimal int_part = Decimal.Floor (d);
573                         decimal dec_part = d - int_part;
574                         dec_part *= 10000000000000000000000000000M;
575                         dec_part = Decimal.Floor(dec_part);
576                         dec_part /= (10000000000000000000000000000M / p);
577                         dec_part = Math.Round (dec_part, mode);
578                         dec_part /= p;
579                         decimal result = int_part + dec_part;
580
581                         // that fixes the precision/scale (which we must keep for output)
582                         // (moved and adapted from System.Data.SqlTypes.SqlMoney)
583                         long scaleDiff = decimals - ((result.flags & 0x7FFF0000) >> 16);
584                         // integrify
585                         if (scaleDiff > 0) {
586                                 // note: here we always work with positive numbers
587                                 while (scaleDiff > 0) {
588                                         if (result > MaxValueDiv10)
589                                                 break;
590                                         result *= 10;
591                                         scaleDiff--;
592                                 }
593                         }
594                         else if (scaleDiff < 0) {
595                                 while (scaleDiff < 0) {
596                                         result /= 10;
597                                         scaleDiff++;
598                                 }
599                         }
600                         result.flags = (uint)((decimals - scaleDiff) << SCALE_SHIFT);
601
602                         if (negative)
603                                 result.flags ^= SIGN_FLAG;
604                         return result;
605                 }
606
607                 public static Decimal Round (Decimal d)
608                 {
609                         return Math.Round (d);
610                 }
611
612                 public static Decimal Round (Decimal d, MidpointRounding mode)
613                 {
614                         return Math.Round (d, mode);
615                 }
616
617                 public static Decimal Multiply (Decimal d1, Decimal d2) 
618                 {
619                         if (d1.IsZero () || d2.IsZero ())
620                                 return Decimal.Zero;
621
622                         if (decimalMult (ref d1, ref d2) != 0)
623                                 throw new OverflowException ();
624                         return d1;
625                 }
626
627                 public static Decimal Divide (Decimal d1, Decimal d2) 
628                 {
629                         if (d2.IsZero ())
630                                 throw new DivideByZeroException ();
631                         if (d1.IsZero ())
632                                 return Decimal.Zero;
633
634                         d1.flags ^= SIGN_FLAG;
635                         d1.flags ^= SIGN_FLAG;
636
637                         Decimal result;
638                         if (decimalDiv (out result, ref d1, ref d2) != 0)
639                                 throw new OverflowException ();
640
641                         return result;
642                 }
643
644                 public static Decimal Remainder (Decimal d1, Decimal d2) 
645                 {
646                         if (d2.IsZero ())
647                                 throw new DivideByZeroException ();
648                         if (d1.IsZero ())
649                                 return Decimal.Zero;
650
651                         bool negative = d1.IsNegative ();
652                         if (negative)
653                                 d1.flags ^= SIGN_FLAG;
654                         if (d2.IsNegative ())
655                                 d2.flags ^= SIGN_FLAG;
656
657                         Decimal result;
658                         if (d1 == d2) {
659                                 return Decimal.Zero;
660                         }
661                         else if (d2 > d1) {
662                                 result = d1;
663                         }
664                         else {
665                                 if (decimalDiv (out result, ref d1, ref d2) != 0)
666                                         throw new OverflowException ();
667                                 result = Decimal.Truncate (result);
668
669                                 // FIXME: not really performant here
670                                 result = d1 - result * d2;
671                         }
672
673                         if (negative)
674                                 result.flags ^= SIGN_FLAG;
675                         return result;
676                 }
677
678                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
679                 public static int Compare (Decimal d1, Decimal d2) 
680                 {
681                         return decimalCompare (ref d1, ref d2);
682                 }
683
684                 public int CompareTo (object value)
685                 {
686                         if (value == null)
687                                 return 1;
688
689                         if (!(value is Decimal))
690                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Decimal"));
691
692                         return Compare (this, (Decimal)value);
693                 }
694
695                 public int CompareTo (Decimal value)
696                 {
697                         return Compare (this, value);
698                 }
699
700                 public bool Equals (Decimal value) 
701                 {
702                         return Equals (value, this);
703                 }
704
705                 public static Decimal Ceiling (Decimal d)
706                 {
707                         return Math.Ceiling (d);
708                 }
709
710                 public static Decimal Parse (string s) 
711                 {
712                         return Parse (s, NumberStyles.Number, null);
713                 }
714
715                 public static Decimal Parse (string s, NumberStyles style) 
716                 {
717                         return Parse (s, style, null);
718                 }
719
720                 public static Decimal Parse (string s, IFormatProvider provider) 
721                 {
722                         return Parse (s, NumberStyles.Number, provider);
723                 }
724
725                 static void ThrowAtPos (int pos)
726                 {
727                         throw new FormatException (String.Format (Locale.GetText ("Invalid character at position {0}"), pos));
728                 }
729
730                 static void ThrowInvalidExp ()
731                 {
732                         throw new FormatException (Locale.GetText ("Invalid exponent"));
733                 }
734
735                 private static string stripStyles (string s, NumberStyles style, NumberFormatInfo nfi, 
736                   out int decPos, out bool isNegative, out bool expFlag, out int exp, bool throwex)
737                 {
738                         isNegative = false;
739                         expFlag = false;
740                         exp = 0;
741                         decPos = -1;
742
743                         bool hasSign = false;
744                         bool hasOpeningParentheses = false;
745                         bool hasDecimalPoint = false;
746                         bool allowedLeadingWhiteSpace = ((style & NumberStyles.AllowLeadingWhite) != 0);
747                         bool allowedTrailingWhiteSpace = ((style & NumberStyles.AllowTrailingWhite) != 0);
748                         bool allowedLeadingSign = ((style & NumberStyles.AllowLeadingSign) != 0);
749                         bool allowedTrailingSign = ((style & NumberStyles.AllowTrailingSign) != 0);
750                         bool allowedParentheses = ((style & NumberStyles.AllowParentheses) != 0);
751                         bool allowedThousands = ((style & NumberStyles.AllowThousands) != 0);
752                         bool allowedDecimalPoint = ((style & NumberStyles.AllowDecimalPoint) != 0);
753                         bool allowedExponent = ((style & NumberStyles.AllowExponent) != 0);
754
755                         /* get rid of currency symbol */
756                         bool hasCurrency = false;
757                         if ((style & NumberStyles.AllowCurrencySymbol) != 0)
758                         {
759                                 int index = s.IndexOf (nfi.CurrencySymbol);
760                                 if (index >= 0) 
761                                 {
762                                         s = s.Remove (index, nfi.CurrencySymbol.Length);
763                                         hasCurrency = true;
764                                 }
765                         }
766
767                         string decimalSep = (hasCurrency) ? nfi.CurrencyDecimalSeparator : nfi.NumberDecimalSeparator;
768                         string groupSep = (hasCurrency) ? nfi.CurrencyGroupSeparator : nfi.NumberGroupSeparator;
769                         string negativeSign = nfi.NegativeSign;
770                         string positiveSign = nfi.PositiveSign;
771
772                         // If we don't have a group separator defined, it has the same effect as if it wasn't allowed.
773                         if (string.IsNullOrEmpty(groupSep)) allowedThousands = false;
774
775                         int pos = 0;
776                         int len = s.Length;
777
778                         StringBuilder sb = new StringBuilder (len);
779
780                         // leading
781                         while (pos < len) 
782                         {
783                                 char ch = s[pos];
784                                 if (Char.IsDigit (ch))
785                                 {
786                                         break; // end of leading
787                                 }
788                                 else if (allowedLeadingWhiteSpace && Char.IsWhiteSpace (ch))
789                                 {
790                                         pos++;
791                                 }
792                                 else if (allowedParentheses && ch == '(' && !hasSign && !hasOpeningParentheses) 
793                                 {
794                                         hasOpeningParentheses = true;
795                                         hasSign = true;
796                                         isNegative = true;
797                                         pos++;
798                                 }
799                                 else if (allowedLeadingSign && !string.IsNullOrEmpty (negativeSign) && ch == negativeSign[0] && !hasSign)
800                                 {
801                                         int slen = negativeSign.Length;
802                                         if (slen == 1 || s.IndexOf (negativeSign, pos, slen) == pos)
803                                         {
804                                                 hasSign = true;
805                                                 isNegative = true;
806                                                 pos += slen;
807                                         }
808                                 }
809                                 else if (allowedLeadingSign && !string.IsNullOrEmpty (positiveSign) && ch == positiveSign[0] && !hasSign)
810                                 {
811                                         int slen = positiveSign.Length;
812                                         if (slen == 1 || s.IndexOf (positiveSign, pos, slen) == pos)
813                                         {
814                                                 hasSign = true;
815                                                 pos += slen;
816                                         }
817                                 }
818                                 else if (allowedDecimalPoint && ch == decimalSep[0])
819                                 {
820                                         int slen = decimalSep.Length;
821                                         if (slen != 1 && s.IndexOf (decimalSep, pos, slen) != pos) 
822                                         {
823                                                 if (throwex)
824                                                         ThrowAtPos (pos);
825                                                 else
826                                                         return null;
827                                         }
828                                         break;
829                                 }
830                                 else
831                                 {
832                                         if (throwex)
833                                                 ThrowAtPos (pos);
834                                         else
835                                                 return null;
836                                 }
837                         }
838
839                         if (pos == len) {
840                                 if (throwex)
841                                         throw new FormatException (Locale.GetText ("No digits found"));
842                                 else
843                                         return null;
844                         }
845
846                         // digits 
847                         while (pos < len)
848                         {
849                                 char ch = s[pos];
850                                 if (Char.IsDigit (ch)) 
851                                 {
852                                         sb.Append(ch);
853                                         pos++;
854                                 }
855                                 else if (allowedThousands && ch == groupSep[0] && ch != decimalSep [0]) 
856                                 {
857                                         int slen = groupSep.Length;
858                                         if (slen != 1 && s.IndexOf(groupSep, pos, slen) != pos) 
859                                         {
860                                                 if (throwex)
861                                                         ThrowAtPos (pos);
862                                                 else
863                                                         return null;
864                                         }
865                                         pos += slen;
866                                 }
867                                 else if (allowedDecimalPoint && ch == decimalSep[0] && !hasDecimalPoint)
868                                 {
869                                         int slen = decimalSep.Length;
870                                         if (slen == 1 || s.IndexOf(decimalSep, pos, slen) == pos) 
871                                         {
872                                                 decPos = sb.Length;
873                                                 hasDecimalPoint = true;
874                                                 pos += slen;
875                                         }
876                                 }
877                                 else
878                                 {
879                                         break;
880                                 }
881                         }
882
883                         // exponent
884                         if (pos < len)
885                         {
886                                 char ch = s[pos];
887                                 if (allowedExponent && Char.ToUpperInvariant (ch) == 'E')
888                                 {
889                                         expFlag = true;
890                                         pos++;
891                                         if (pos >= len){
892                                                 if (throwex)
893                                                         ThrowInvalidExp ();
894                                                 else
895                                                         return null;
896                                         }
897                                         ch = s[pos];
898                                         bool isNegativeExp = false;
899                                         if (!string.IsNullOrEmpty (positiveSign) && ch == positiveSign[0])
900                                         {
901                                                 int slen = positiveSign.Length;
902                                                 if (slen == 1 || s.IndexOf (positiveSign, pos, slen) == pos)
903                                                 {
904                                                         pos += slen;
905                                                         if (pos >= len) {
906                                                                 if (throwex)
907                                                                         ThrowInvalidExp ();
908                                                                 else
909                                                                         return null;
910                                                         }
911                                                 }
912                                         }
913                                         else if (!string.IsNullOrEmpty (negativeSign) && ch == negativeSign[0])
914                                         {
915                                                 int slen = negativeSign.Length;
916                                                 if (slen == 1 || s.IndexOf (negativeSign, pos, slen) == pos)
917                                                 {
918                                                         pos += slen;
919                                                         if (pos >= len) {
920                                                                 if (throwex)
921                                                                         ThrowInvalidExp ();
922                                                                 else
923                                                                         return null;
924                                                         }
925                                                         isNegativeExp = true;
926                                                 }
927                                         }
928                                         ch = s[pos];
929                                         if (!Char.IsDigit(ch)) {
930                                                 if (throwex)
931                                                         ThrowInvalidExp ();
932                                                 else
933                                                         return null;
934                                         }
935
936                                         exp = ch - '0';
937                                         pos++;
938                                         while (pos < len && Char.IsDigit (s[pos])) 
939                                         {
940                                                 exp *= 10;
941                                                 exp += s[pos] - '0';
942                                                 pos++;
943                                         }
944                                         if (isNegativeExp) exp *= -1;
945                                 }
946                         }
947
948                         // trailing
949                         while (pos < len)
950                         {
951                                 char ch = s[pos];
952                                 if (allowedTrailingWhiteSpace && Char.IsWhiteSpace (ch)) 
953                                 {
954                                         pos++;
955                                 }
956                                 else if (allowedParentheses && ch == ')' && hasOpeningParentheses) 
957                                 {
958                                         hasOpeningParentheses = false;
959                                         pos++;
960                                 }
961                                 else if (allowedTrailingSign && !string.IsNullOrWhiteSpace (negativeSign) && ch == negativeSign[0] && !hasSign)
962                                 {
963                                         int slen = negativeSign.Length;
964                                         if (slen == 1 || s.IndexOf(negativeSign, pos, slen) == pos)
965                                         {
966                                                 hasSign = true;
967                                                 isNegative = true;
968                                                 pos += slen;
969                                         }
970                                 }
971                                 else if (allowedTrailingSign && !string.IsNullOrWhiteSpace (positiveSign) && ch == positiveSign[0] && !hasSign)
972                                 {
973                                         int slen = positiveSign.Length;
974                                         if (slen == 1 || s.IndexOf (positiveSign, pos, slen) == pos)
975                                         {
976                                                 hasSign = true;
977                                                 pos += slen;
978                                         }
979                                 }
980                                 else
981                                 {
982                                         // trailing zero characters are allowed
983                                         if (ch == 0){
984                                                 while (++pos < len && s [pos] == 0)
985                                                         ;
986                                                 if (pos == len)
987                                                         break;
988                                         }
989                                         
990                                         if (throwex)
991                                                 ThrowAtPos (pos);
992                                         else
993                                                 return null;
994                                 }
995                         }
996
997                         if (hasOpeningParentheses) {
998                                 if (throwex)
999                                         throw new FormatException (Locale.GetText ("Closing Parentheses not found"));
1000                                 else
1001                                         return null;
1002                         }
1003
1004                         if (!hasDecimalPoint)
1005                                 decPos = sb.Length;
1006
1007                         return sb.ToString ();
1008                 }
1009
1010                 public static Decimal Parse (string s, NumberStyles style, IFormatProvider provider) 
1011                 {
1012                         if (s == null)
1013                                 throw new ArgumentNullException ("s");
1014
1015                         if ((style & NumberStyles.AllowHexSpecifier) != 0)
1016                                 throw new ArgumentException ("Decimal.TryParse does not accept AllowHexSpecifier", "style");
1017
1018                         Decimal result;
1019                         PerformParse (s, style, provider, out result, true);
1020                         return result;
1021                 }
1022         
1023                 public static bool TryParse (string s, out Decimal result)
1024                 {
1025                         if (s == null){
1026                                 result = 0;
1027                                 return false;
1028                         }
1029                         return PerformParse (s, NumberStyles.Number, null, out result, false);
1030                 }
1031
1032                 public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out decimal result)
1033                 {
1034                         if (s == null || (style & NumberStyles.AllowHexSpecifier) != 0){
1035                                 result = 0;
1036                                 return false;
1037                         }
1038
1039                         return PerformParse (s, style, provider, out result, false);
1040                 }
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 d)
1164                 {
1165                         return Convert.ToDouble (d);
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 d)
1179                 {
1180                         if (d > Int32.MaxValue || d < 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 (d));
1186                 }
1187         
1188                 public static long ToInt64 (decimal d)
1189                 {
1190                         if (d > Int64.MaxValue || d < 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 (d));
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 d)
1215                 {
1216                         return Convert.ToSingle (d);
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 d)
1232                 {
1233                         if (d > UInt32.MaxValue || d < 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 (d));
1239                 }
1240
1241                 [CLSCompliant(false)]
1242                 public static ulong ToUInt64 (decimal d)
1243                 {
1244                         if (d > UInt64.MaxValue || d < 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 (d));
1250                 }
1251
1252                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
1253                 {
1254                         if (targetType == null)
1255                                 throw new ArgumentNullException ("targetType");
1256                         return Convert.ToType (this, targetType, provider, false);
1257                 }
1258
1259                 bool IConvertible.ToBoolean (IFormatProvider provider)
1260                 {
1261                         return Convert.ToBoolean (this);
1262                 }
1263
1264                 byte IConvertible.ToByte (IFormatProvider provider)
1265                 {
1266                         return Convert.ToByte (this);
1267                 }
1268
1269                 char IConvertible.ToChar (IFormatProvider provider)
1270                 {
1271                         throw new InvalidCastException ();
1272                 }
1273
1274                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
1275                 {
1276                         throw new InvalidCastException ();
1277                 }
1278
1279                 decimal IConvertible.ToDecimal (IFormatProvider provider)
1280                 {
1281                         return this;
1282                 }
1283
1284                 double IConvertible.ToDouble (IFormatProvider provider)
1285                 {
1286                         return Convert.ToDouble (this);
1287                 }
1288
1289                 short IConvertible.ToInt16 (IFormatProvider provider)
1290                 {
1291                         return Convert.ToInt16 (this);
1292                 }
1293
1294                 int IConvertible.ToInt32 (IFormatProvider provider)
1295                 {
1296                         return Convert.ToInt32 (this);
1297                 }
1298
1299                 long IConvertible.ToInt64 (IFormatProvider provider)
1300                 {
1301                         return Convert.ToInt64 (this);
1302                 }
1303
1304                 sbyte IConvertible.ToSByte (IFormatProvider provider)
1305                 {
1306                         return Convert.ToSByte (this);
1307                 }
1308
1309                 float IConvertible.ToSingle (IFormatProvider provider)
1310                 {
1311                         return Convert.ToSingle (this);
1312                 }
1313
1314                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
1315                 {
1316                         return Convert.ToUInt16 (this);
1317                 }
1318
1319                 uint IConvertible.ToUInt32 (IFormatProvider provider)
1320                 {
1321                         return Convert.ToUInt32 (this);
1322                 }
1323
1324                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
1325                 {
1326                         return Convert.ToUInt64 (this);
1327                 }
1328
1329                 public string ToString (string format, IFormatProvider provider) 
1330                 {
1331                         return NumberFormatter.NumberToString (format, this, provider);
1332                 }
1333
1334                 public override string ToString () 
1335                 {
1336                         return ToString ("G", null);
1337                 }
1338
1339                 public string ToString (string format) 
1340                 {
1341                         return ToString (format, null);
1342                 }
1343
1344                 public string ToString (IFormatProvider provider) 
1345                 {
1346                         return ToString ("G", provider);
1347                 }
1348                 
1349 #if NET_4_0
1350                 void IDeserializationCallback.OnDeserialization(object sender)
1351                 {
1352                 }
1353 #endif
1354
1355 #if !MSTEST
1356                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1357                 private static extern int decimal2UInt64 (ref Decimal val, out ulong result);
1358
1359                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1360                 private static extern int decimal2Int64 (ref Decimal val, out long result);
1361
1362                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1363                 private static extern int double2decimal (out Decimal erg, double val, int digits);
1364
1365                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1366                 private static extern int decimalIncr (ref Decimal d1, ref Decimal d2);
1367
1368                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1369                 internal static extern int string2decimal (out Decimal val, String sDigits, uint decPos, int sign);
1370
1371                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1372                 internal static extern int decimalSetExponent (ref Decimal val, int exp);
1373
1374                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1375                 private static extern double decimal2double (ref Decimal val);
1376
1377                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1378                 private static extern void decimalFloorAndTrunc (ref Decimal val, 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, out ulong result);
1398
1399                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1400                 [DllImport("libdec", EntryPoint="decimal2Int64")]
1401                 private static extern int decimal2Int64 (ref Decimal val, out long result);
1402
1403                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1404                 [DllImport("libdec", EntryPoint="double2decimal")]
1405                 private static extern int double2decimal (out Decimal erg, double val, int digits);
1406
1407                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1408                 [DllImport("libdec", EntryPoint="decimalIncr")]
1409                 private static extern int decimalIncr (ref Decimal d1, ref Decimal d2);
1410
1411                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1412                 [DllImport("libdec", EntryPoint="string2decimal")]
1413                 internal static extern int string2decimal (out Decimal val,
1414                     [MarshalAs(UnmanagedType.LPWStr)]String sDigits,
1415                     uint decPos, int sign);
1416
1417                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1418                 [DllImport("libdec", EntryPoint="decimalSetExponent")]
1419                 internal static extern int decimalSetExponent (ref Decimal val, int exp);
1420
1421                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1422                 [DllImport("libdec", EntryPoint="decimal2double")]
1423                 private static extern double decimal2double (ref Decimal val);
1424
1425                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1426                 [DllImport("libdec", EntryPoint="decimalFloorAndTrunc")]
1427                 private static extern void decimalFloorAndTrunc (ref Decimal val, int floorFlag);
1428
1429                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1430                 [DllImport("libdec", EntryPoint="decimalRound")]
1431                 private static extern void decimalRound (ref Decimal val, int decimals);
1432
1433                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1434                 [DllImport("libdec", EntryPoint="decimalMult")]
1435                 private static extern int decimalMult (ref Decimal pd1, ref Decimal pd2);
1436
1437                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1438                 [DllImport("libdec", EntryPoint="decimalDiv")]
1439                 private static extern int decimalDiv (out Decimal pc, ref Decimal pa, ref Decimal pb);
1440
1441                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1442                 [DllImport("libdec", EntryPoint="decimalIntDiv")]
1443                 private static extern int decimalIntDiv (out Decimal pc, ref Decimal pa, ref Decimal pb);
1444
1445                 //![MethodImplAttribute(MethodImplOptions.InternalCall)]
1446                 [DllImport("libdec", EntryPoint="decimalCompare")]
1447                 private static extern int decimalCompare (ref Decimal d1, ref Decimal d2);
1448
1449 #endif
1450         }
1451 }