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