Merge pull request #901 from Blewzman/FixAggregateExceptionGetBaseException
[mono.git] / mcs / class / corlib / System / Double.cs
1 //
2 // System.Double.cs
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Bob Smith       (bob@thestuff.net)
7 //   Marek Safar     (marek.safar@gmail.com)
8 //
9 // (C) Ximian, Inc.  http://www.ximian.com
10 // (C) Bob Smith.    http://www.thestuff.net
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
13 //
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 // 
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 // 
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33
34 using System.Globalization;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.ConstrainedExecution;
37
38 namespace System {
39         
40         [Serializable]
41         [System.Runtime.InteropServices.ComVisible (true)]
42         public struct Double : IComparable, IFormattable, IConvertible, IComparable <double>, IEquatable <double>
43         {
44                 public const double Epsilon = 4.9406564584124650e-324;
45                 public const double MaxValue =  1.7976931348623157e308;
46                 public const double MinValue = -1.7976931348623157e308;
47                 public const double NaN = 0.0d / 0.0d;
48                 public const double NegativeInfinity = -1.0d / 0.0d;
49                 public const double PositiveInfinity = 1.0d / 0.0d;
50                 
51                 internal double m_value;
52
53                 public int CompareTo (object value)
54                 {
55                         if (value == null)
56                                 return 1;
57                         
58                         if (!(value is System.Double))
59                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Double"));
60
61                         double dv = (double)value;
62
63                         if (IsPositiveInfinity(m_value) && IsPositiveInfinity(dv))
64                                 return 0;
65
66                         if (IsNegativeInfinity(m_value) && IsNegativeInfinity(dv))
67                                 return 0;
68
69                         if (IsNaN(dv))
70                                 if (IsNaN(m_value))
71                                         return 0;
72                                 else
73                                         return 1;
74
75                         if (IsNaN(m_value))
76                                 if (IsNaN(dv))
77                                         return 0;
78                                 else
79                                         return -1;
80
81                         if (m_value > dv) return 1;
82                         else if (m_value < dv) return -1;
83                         else return 0;
84                 }
85
86                 public override bool Equals (object obj)
87                 {
88                         if (!(obj is System.Double))
89                                 return false;
90
91                         double value = (double) obj;
92
93                         if (IsNaN (value))
94                                 return IsNaN (m_value);
95
96                         return (value == m_value);
97                 }
98
99                 public int CompareTo (double value)
100                 {
101                         if (IsPositiveInfinity(m_value) && IsPositiveInfinity(value))
102                                 return 0;
103
104                         if (IsNegativeInfinity(m_value) && IsNegativeInfinity(value))
105                                 return 0;
106
107                         if (IsNaN(value))
108                                 if (IsNaN(m_value))
109                                         return 0;
110                                 else
111                                         return 1;
112
113                         if (IsNaN(m_value))
114                                 if (IsNaN(value))
115                                         return 0;
116                                 else
117                                         return -1;
118
119                         if (m_value > value) return 1;
120                         else if (m_value < value) return -1;
121                         else return 0;
122                 }
123
124                 public bool Equals (double obj)
125                 {
126                         if (IsNaN (obj)) {
127                                 if (IsNaN(m_value))
128                                         return true;
129                                 else
130                                         return false;
131                         }
132
133                         return obj == m_value;
134                 }
135
136                 public override unsafe int GetHashCode ()
137                 {
138                         double d = m_value;
139                         return (*((long*)&d)).GetHashCode ();
140                 }
141
142 #if     NET_4_0
143                 public static bool operator==(double left, double right)
144                 {
145                         return left == right;
146                 }
147
148                 public static bool operator!=(double left, double right)
149                 {
150                         return left != right;
151                 }
152
153                 public static bool operator>(double left, double right)
154                 {
155                         return left > right;
156                 }
157
158                 public static bool operator>=(double left, double right)
159                 {
160                         return left >= right;
161                 }
162
163                 public static bool operator<(double left, double right)
164                 {
165                         return left < right;
166                 }
167
168                 public static bool operator<=(double left, double right)
169                 {
170                         return left <= right;
171                 }
172 #endif
173
174                 public static bool IsInfinity (double d)
175                 {
176                         return (d == PositiveInfinity || d == NegativeInfinity);
177                 }
178
179                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
180                 public static bool IsNaN (double d)
181                 {
182 #pragma warning disable 1718
183                         return (d != d);
184 #pragma warning restore
185                 }
186
187                 public static bool IsNegativeInfinity (double d)
188                 {
189                         return (d < 0.0d && (d == NegativeInfinity || d == PositiveInfinity));
190                 }
191
192                 public static bool IsPositiveInfinity (double d)
193                 {
194                         return (d > 0.0d && (d == NegativeInfinity || d == PositiveInfinity));
195                 }
196
197                 public static double Parse (string s)
198                 {
199                         return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), null);
200                 }
201
202                 public static double Parse (string s, IFormatProvider provider)
203                 {
204                         return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), provider);
205                 }
206
207                 public static double Parse (string s, NumberStyles style) 
208                 {
209                         return Parse (s, style, null);
210                 }
211
212                 enum ParseState {
213                         AllowSign = 1,
214                         Digits = 2,
215                         Decimal = 3,
216                         ExponentSign = 4,
217                         Exponent = 5,
218                         ConsumeWhiteSpace = 6,
219                         TrailingSymbols = 7,
220                         Exit = 8
221                 };
222                 
223                 public static double Parse (string s, NumberStyles style, IFormatProvider provider)
224                 {
225                         Exception exc;
226                         double result;
227                         
228                         if (!Parse (s, style, provider, false, out result, out exc))
229                                 throw exc;
230
231                         return result;
232                 }
233                 
234                 // FIXME: check if digits are group in correct numbers between the group separators
235                 internal static bool Parse (string s, NumberStyles style, IFormatProvider provider, bool tryParse, out double result, out Exception exc)
236                 {
237                         result = 0;
238                         exc = null;
239                         
240                         if (s == null) {
241                                 if (!tryParse)
242                                         exc = new ArgumentNullException ("s");
243                                 return false;
244                         }
245                         if (s.Length == 0) {
246                                 if (!tryParse)
247                                         exc = new FormatException ();
248                                 return false;
249                         }
250                         // yes it's counter intuitive (buggy?) but even TryParse actually throws in this case
251                         if ((style & NumberStyles.AllowHexSpecifier) != 0) {
252                                 string msg = Locale.GetText ("Double doesn't support parsing with '{0}'.", "AllowHexSpecifier");
253                                 throw new ArgumentException (msg);
254                         }
255                         if (style > NumberStyles.Any) {
256                                 if (!tryParse)
257                                         exc = new ArgumentException();
258                                 return false;
259                         }
260
261                         NumberFormatInfo format = NumberFormatInfo.GetInstance(provider);
262                         if (format == null) throw new Exception("How did this happen?");
263                         
264                         //
265                         // validate and prepare string for C
266                         //
267                         int len = s.Length;
268                         int didx = 0;
269                         int sidx = 0;
270                         char c;
271                         bool allow_leading_white = (style & NumberStyles.AllowLeadingWhite) != 0;
272                         bool allow_trailing_white = ((style & NumberStyles.AllowTrailingWhite) != 0);
273                         
274                         if (allow_leading_white) {
275                                 while (sidx < len && Char.IsWhiteSpace (s [sidx]))
276                                        sidx++;
277
278                                 if (sidx == len) {
279                                         if (!tryParse)
280                                                 exc = Int32.GetFormatException ();
281                                         return false;
282                                 }
283                         }
284                         int sEndPos = s.Length - 1;
285                         if (allow_trailing_white)
286                                 while (Char.IsWhiteSpace (s [sEndPos]))
287                                         sEndPos--;
288
289                         if (TryParseStringConstant (format.NaNSymbol, s, sidx, sEndPos)) {
290                                 result = double.NaN;
291                                 return true;
292                         }
293                         if (TryParseStringConstant (format.PositiveInfinitySymbol, s, sidx, sEndPos)) {
294                                 result = double.PositiveInfinity;
295                                 return true;
296                         }
297                         if (TryParseStringConstant (format.NegativeInfinitySymbol, s, sidx, sEndPos)) {
298                                 result = double.NegativeInfinity;
299                                 return true;
300                         }
301
302                         byte [] b = new byte [len + 1];
303
304                         //
305                         // Machine state
306                         //
307                         var state = ParseState.AllowSign;
308
309                         //
310                         // Setup
311                         //
312                         string decimal_separator = null;
313                         string group_separator = null;
314                         string currency_symbol = null;
315                         int decimal_separator_len = 0;
316                         int group_separator_len = 0;
317                         int currency_symbol_len = 0;
318                         if ((style & NumberStyles.AllowDecimalPoint) != 0){
319                                 decimal_separator = format.NumberDecimalSeparator;
320                                 decimal_separator_len = decimal_separator.Length;
321                         }
322                         if ((style & NumberStyles.AllowThousands) != 0){
323                                 group_separator = format.NumberGroupSeparator;
324                                 group_separator_len = group_separator.Length;
325                         }
326                         if ((style & NumberStyles.AllowCurrencySymbol) != 0){
327                                 currency_symbol = format.CurrencySymbol;
328                                 currency_symbol_len = currency_symbol.Length;
329                         }
330                         string positive = format.PositiveSign;
331                         string negative = format.NegativeSign;
332                         bool allow_trailing_parenthes = false;
333                         
334                         for (; sidx < len; sidx++){
335                                 c = s [sidx];
336
337                                 if (c == '\0') {
338                                         sidx = len;
339                                         continue;
340                                 }
341
342                                 switch (state){
343                                 case ParseState.AllowSign:
344                                         if ((style & NumberStyles.AllowLeadingSign) != 0) {
345                                                 if (c == positive [0] &&
346                                                     s.Substring (sidx, positive.Length) == positive) {
347                                                         state = ParseState.Digits;
348                                                         sidx += positive.Length - 1;
349                                                         continue;
350                                                 }
351
352                                                 if (c == negative [0] &&
353                                                     s.Substring (sidx, negative.Length) == negative) {
354                                                         state = ParseState.Digits;
355                                                         b [didx++] = (byte)'-';
356                                                         sidx += negative.Length - 1;
357                                                         continue;
358                                                 }
359                                         }
360
361                                         if ((style & NumberStyles.AllowParentheses) != 0 && c == '(') {
362                                                 b [didx++] = (byte)'-';
363                                                 state = ParseState.Digits;
364                                                 allow_trailing_parenthes = true;
365                                                 continue;
366                                         }
367
368                                         state = ParseState.Digits;
369                                         goto case ParseState.Digits;
370                                         
371                                 case ParseState.Digits:
372                                         if (Char.IsDigit (c)) {
373                                                 b [didx++] = (byte)c;
374                                                 break;
375                                         }
376
377                                         if (c == 'e' || c == 'E')
378                                                 goto case ParseState.Decimal;
379
380                                         if (allow_trailing_parenthes && c == ')') {
381                                                 allow_trailing_parenthes = false;
382                                                 state = ParseState.ConsumeWhiteSpace;
383                                                 continue;
384                                         }
385
386                                         if (decimal_separator_len > 0 &&
387                                             decimal_separator [0] == c) {
388                                                 if (String.CompareOrdinal (s, sidx, decimal_separator, 0, decimal_separator_len) == 0) {
389                                                         b [didx++] = (byte)'.';
390                                                         sidx += decimal_separator_len - 1;
391                                                         state = ParseState.Decimal; 
392                                                         break;
393                                                 }
394                                         }
395                                         if (group_separator_len > 0 &&
396                                             group_separator [0] == c) {
397                                                 if (s.Substring (sidx, group_separator_len) ==
398                                                     group_separator) {
399                                                         sidx += group_separator_len - 1;
400                                                         break;
401                                                 }
402                                         }
403                                         if (currency_symbol_len > 0 &&
404                                                 currency_symbol [0] == c) {
405                                                 if (s.Substring (sidx, currency_symbol_len) ==
406                                                         currency_symbol) {
407                                                         sidx += currency_symbol_len - 1;
408                                                         currency_symbol_len = 0;
409                                                         break;
410                                                 }
411                                         }
412
413                                         state = ParseState.TrailingSymbols;
414                                         goto case ParseState.TrailingSymbols;
415
416                                 case ParseState.Decimal:
417                                         if (Char.IsDigit (c)){
418                                                 b [didx++] = (byte) c;
419                                                 break;
420                                         }
421
422                                         if (c == 'e' || c == 'E'){
423                                                 if ((style & NumberStyles.AllowExponent) == 0) {
424                                                         if (!tryParse)
425                                                                 exc = new FormatException ("Unknown char: " + c);
426                                                         return false;
427                                                 }
428                                                 b [didx++] = (byte) c;
429                                                 state = ParseState.ExponentSign;
430                                                 break;
431                                         }
432
433                                         state = ParseState.TrailingSymbols;
434                                         goto case ParseState.TrailingSymbols;
435
436                                 case ParseState.ExponentSign:
437                                         if (Char.IsDigit (c)){
438                                                 state = ParseState.Exponent;
439                                                 goto case ParseState.Exponent;
440                                         }
441
442                                         if (c == positive [0] &&
443                                             s.Substring (sidx, positive.Length) == positive){
444                                                 state = ParseState.Digits;
445                                                 sidx += positive.Length-1;
446                                                 continue;
447                                         }
448
449                                         if (c == negative [0] &&
450                                             s.Substring (sidx, negative.Length) == negative){
451                                                 state = ParseState.Digits;
452                                                 b [didx++] = (byte) '-';
453                                                 sidx += negative.Length-1;
454                                                 continue;
455                                         }
456
457                                         goto case ParseState.ConsumeWhiteSpace;
458
459                                 case ParseState.Exponent:
460                                         if (Char.IsDigit (c)){
461                                                 b [didx++] = (byte) c;
462                                                 break;
463                                         }
464                                         
465                                         state = ParseState.TrailingSymbols;
466                                         goto case ParseState.TrailingSymbols;
467
468                                 case ParseState.TrailingSymbols:
469                                         if ((style & NumberStyles.AllowTrailingSign) != 0) {
470                                                 if (positive != null && c == positive [0] &&
471                                                         s.Substring (sidx, positive.Length) == positive) {
472                                                         state = ParseState.ConsumeWhiteSpace;
473                                                         sidx += positive.Length - 1;
474                                                         allow_trailing_parenthes = false;
475                                                         positive = null;
476                                                         continue;
477                                                 }
478
479                                                 if (negative != null && c == negative [0] &&
480                                                         s.Substring (sidx, negative.Length) == negative) {
481                                                         state = ParseState.ConsumeWhiteSpace;
482                                                         Array.Copy (b, 0, b, 1, didx); 
483                                                         b [0] = (byte)'-';
484                                                         ++didx;
485                                                         sidx += negative.Length - 1;
486                                                         allow_trailing_parenthes = false;
487                                                         negative = null;
488                                                         continue;
489                                                 }
490                                         }
491
492                                         if (currency_symbol_len > 0 &&
493                                                 currency_symbol [0] == c) {
494                                                 if (s.Substring (sidx, currency_symbol_len) ==
495                                                         currency_symbol) {
496                                                         sidx += currency_symbol_len - 1;
497                                                         currency_symbol_len = 0;
498                                                         break;
499                                                 }
500                                         }
501
502                                         if (allow_trailing_white && Char.IsWhiteSpace (c)) {
503                                                 break;
504                                         }
505
506                                         goto case ParseState.ConsumeWhiteSpace;
507
508                                 case ParseState.ConsumeWhiteSpace:
509                                         if (allow_trailing_parenthes && c == ')') {
510                                                 allow_trailing_parenthes = false;
511                                                 state = ParseState.ConsumeWhiteSpace;
512                                                 break;
513                                         }
514
515                                         if (allow_trailing_white && Char.IsWhiteSpace (c)) {
516                                                 state = ParseState.ConsumeWhiteSpace;
517                                                 break;
518                                         }
519                                         
520                                         if (!tryParse)
521                                                 exc = new FormatException ("Unknown char");
522                                         return false;
523                                 }
524
525                                 if (state == ParseState.Exit)
526                                         break;
527                         }
528
529                         b [didx] = 0;
530                         unsafe {
531                                 fixed (byte *p = &b [0]){
532                                         double retVal;
533                                         if (!ParseImpl (p, out retVal)) {
534                                                 if (!tryParse)
535                                                         exc = Int32.GetFormatException ();
536                                                 return false;
537                                         }
538                                         if (IsPositiveInfinity(retVal) || IsNegativeInfinity(retVal)) {
539                                                 if (!tryParse)
540                                                         exc = new OverflowException ();
541                                                 return false;
542                                         }
543
544                                         result = retVal;
545                                         return true;
546                                 }
547                         }
548                 }
549
550                 static bool TryParseStringConstant (string format, string s, int start, int end)
551                 {
552                         return end - start + 1 == format.Length && String.CompareOrdinal (format, 0, s, start, format.Length) == 0;
553                 }
554
555                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
556                 unsafe private static extern bool ParseImpl (byte *byte_ptr, out double value);
557                 
558                 public static bool TryParse (string s,
559                                              NumberStyles style,
560                                              IFormatProvider provider,
561                                              out double result)
562                 {
563                         Exception exc;
564                         if (!Parse (s, style, provider, true, out result, out exc)) {
565                                 result = 0;
566                                 return false;
567                         }
568
569                         return true;
570                 }
571
572                 public static bool TryParse (string s, out double result)
573                 {
574                         return TryParse (s, NumberStyles.Any, null, out result);
575                 }
576
577                 public override string ToString ()
578                 {
579                         return NumberFormatter.NumberToString (m_value, null);
580                 }
581
582                 public string ToString (IFormatProvider provider)
583                 {
584                         return NumberFormatter.NumberToString (m_value, provider);
585                 }
586
587                 public string ToString (string format)
588                 {
589                         return ToString (format, null);
590                 }
591
592                 public string ToString (string format, IFormatProvider provider)
593                 {
594                         return NumberFormatter.NumberToString (format, m_value, provider);
595                 }
596
597                 // =========== IConvertible Methods =========== //
598
599                 public TypeCode GetTypeCode ()
600                 {
601                         return TypeCode.Double;
602                 }
603
604                 object IConvertible.ToType (Type targetType, IFormatProvider provider)
605                 {
606                         if (targetType == null)
607                                 throw new ArgumentNullException ("targetType");
608                         return System.Convert.ToType (m_value, targetType, provider, false);
609                 }
610
611                 bool IConvertible.ToBoolean (IFormatProvider provider)
612                 {
613                         return System.Convert.ToBoolean (m_value);
614                 }
615
616                 byte IConvertible.ToByte (IFormatProvider provider)
617                 {
618                         return System.Convert.ToByte (m_value);
619                 }
620
621                 char IConvertible.ToChar (IFormatProvider provider)
622                 {
623                         throw new InvalidCastException ();
624                 }
625
626                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
627                 {
628                         throw new InvalidCastException ();
629                 }
630
631                 decimal IConvertible.ToDecimal (IFormatProvider provider)
632                 {
633                         return System.Convert.ToDecimal (m_value);
634                 }
635
636                 double IConvertible.ToDouble (IFormatProvider provider)
637                 {
638                         return System.Convert.ToDouble (m_value);
639                 }
640
641                 short IConvertible.ToInt16 (IFormatProvider provider)
642                 {
643                         return System.Convert.ToInt16 (m_value);
644                 }
645
646                 int IConvertible.ToInt32 (IFormatProvider provider)
647                 {
648                         return System.Convert.ToInt32 (m_value);
649                 }
650
651                 long IConvertible.ToInt64 (IFormatProvider provider)
652                 {
653                         return System.Convert.ToInt64 (m_value);
654                 }
655
656                 sbyte IConvertible.ToSByte (IFormatProvider provider)
657                 {
658                         return System.Convert.ToSByte (m_value);
659                 }
660
661                 float IConvertible.ToSingle (IFormatProvider provider)
662                 {
663                         return System.Convert.ToSingle (m_value);
664                 }
665
666                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
667                 {
668                         return System.Convert.ToUInt16 (m_value);
669                 }
670                 uint IConvertible.ToUInt32 (IFormatProvider provider)
671                 {
672                         return System.Convert.ToUInt32 (m_value);
673                 }
674                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
675                 {
676                         return System.Convert.ToUInt64 (m_value);
677                 }
678         }
679 }