2008-06-30 Andreas Nahr <ClassDevelopment@A-SoftTech.com>
[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 #if NET_2_0
46         [System.Runtime.InteropServices.ComVisible (true)]
47 #endif
48         public struct Double : IComparable, IFormattable, IConvertible
49 #if NET_2_0
50                 , IComparable <double>, IEquatable <double>
51 #endif
52         {
53                 public const double Epsilon = 4.9406564584124650e-324;
54                 public const double MaxValue =  1.7976931348623157e308;
55                 public const double MinValue = -1.7976931348623157e308;
56                 public const double NaN = 0.0d / 0.0d;
57                 public const double NegativeInfinity = -1.0d / 0.0d;
58                 public const double PositiveInfinity = 1.0d / 0.0d;
59                 
60                 internal double m_value;
61
62                 public int CompareTo (object value)
63                 {
64                         if (value == null)
65                                 return 1;
66                         
67                         if (!(value is System.Double))
68                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Double"));
69
70                         double dv = (double)value;
71
72                         if (IsPositiveInfinity(m_value) && IsPositiveInfinity(dv))
73                                 return 0;
74
75                         if (IsNegativeInfinity(m_value) && IsNegativeInfinity(dv))
76                                 return 0;
77
78                         if (IsNaN(dv))
79                                 if (IsNaN(m_value))
80                                         return 0;
81                                 else
82                                         return 1;
83
84                         if (IsNaN(m_value))
85                                 if (IsNaN(dv))
86                                         return 0;
87                                 else
88                                         return -1;
89
90                         if (m_value > dv) return 1;
91                         else if (m_value < dv) return -1;
92                         else return 0;
93                 }
94
95                 public override bool Equals (object obj)
96                 {
97                         if (!(obj is System.Double))
98                                 return false;
99
100                         double value = (double) obj;
101
102                         if (IsNaN (value))
103                                 return IsNaN (m_value);
104
105                         return (value == m_value);
106                 }
107
108 #if NET_2_0
109                 public int CompareTo (double value)
110                 {
111                         if (IsPositiveInfinity(m_value) && IsPositiveInfinity(value))
112                                 return 0;
113
114                         if (IsNegativeInfinity(m_value) && IsNegativeInfinity(value))
115                                 return 0;
116
117                         if (IsNaN(value))
118                                 if (IsNaN(m_value))
119                                         return 0;
120                                 else
121                                         return 1;
122
123                         if (IsNaN(m_value))
124                                 if (IsNaN(value))
125                                         return 0;
126                                 else
127                                         return -1;
128
129                         if (m_value > value) return 1;
130                         else if (m_value < value) return -1;
131                         else return 0;
132                 }
133
134                 public bool Equals (double obj)
135                 {
136                         if (IsNaN (obj)) {
137                                 if (IsNaN(m_value))
138                                         return true;
139                                 else
140                                         return false;
141                         }
142
143                         return obj == m_value;
144                 }
145 #endif
146
147                 public override unsafe int GetHashCode ()
148                 {
149                         double d = m_value;
150                         return (*((long*)&d)).GetHashCode ();
151                 }
152
153                 public static bool IsInfinity (double d)
154                 {
155                         return (d == PositiveInfinity || d == NegativeInfinity);
156                 }
157
158 #if NET_2_0
159                 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
160 #endif
161                 public static bool IsNaN (double d)
162                 {
163 #pragma warning disable 1718
164                         return (d != d);
165 #pragma warning restore
166                 }
167
168                 public static bool IsNegativeInfinity (double d)
169                 {
170                         return (d < 0.0d && (d == NegativeInfinity || d == PositiveInfinity));
171                 }
172
173                 public static bool IsPositiveInfinity (double d)
174                 {
175                         return (d > 0.0d && (d == NegativeInfinity || d == PositiveInfinity));
176                 }
177
178                 public static double Parse (string s)
179                 {
180                         return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), null);
181                 }
182
183                 public static double Parse (string s, IFormatProvider provider)
184                 {
185                         return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), provider);
186                 }
187
188                 public static double Parse (string s, NumberStyles style) 
189                 {
190                         return Parse (s, style, null);
191                 }
192
193                 // We're intentionally using constants here to avoid some bigger headaches in mcs.
194                 // This struct must be compiled before System.Enum so we can't use enums here.
195                 private const int State_AllowSign = 1;
196                 private const int State_Digits = 2;
197                 private const int State_Decimal = 3;
198                 private const int State_ExponentSign = 4;
199                 private const int State_Exponent = 5;
200                 private const int State_ConsumeWhiteSpace = 6;
201                 private const int State_Exit = 7;
202                 
203                 public static double Parse (string s, NumberStyles style, IFormatProvider provider)
204                 {
205                         Exception exc;
206                         double result;
207                         
208                         if (!Parse (s, style, provider, false, out result, out exc))
209                                 throw exc;
210
211                         return result;
212                 }
213                 
214                 // FIXME: check if digits are group in correct numbers between the group separators
215                 internal static bool Parse (string s, NumberStyles style, IFormatProvider provider, bool tryParse, out double result, out Exception exc)
216                 {
217                         result = 0;
218                         exc = null;
219                         
220                         if (s == null) {
221                                 if (!tryParse)
222                                         exc = new ArgumentNullException ("s");
223                                 return false;
224                         }
225                         if (s.Length == 0) {
226                                 if (!tryParse)
227                                         exc = new FormatException ();
228                                 return false;
229                         }
230 #if NET_2_0
231                         // yes it's counter intuitive (buggy?) but even TryParse actually throws in this case
232                         if ((style & NumberStyles.AllowHexSpecifier) != 0) {
233                                 string msg = Locale.GetText ("Double doesn't support parsing with '{0}'.", "AllowHexSpecifier");
234                                 throw new ArgumentException (msg);
235                         }
236 #endif
237                         if (style > NumberStyles.Any) {
238                                 if (!tryParse)
239                                         exc = new ArgumentException();
240                                 return false;
241                         }
242
243                         NumberFormatInfo format = NumberFormatInfo.GetInstance(provider);
244                         if (format == null) throw new Exception("How did this happen?");
245                         
246                         if (s == format.NaNSymbol) {
247                                 result = Double.NaN;
248                                 return true;
249                         }
250                         if (s == format.PositiveInfinitySymbol) {
251                                 result = Double.PositiveInfinity;
252                                 return true;
253                         }
254                         if (s == format.NegativeInfinitySymbol) {
255                                 result = Double.NegativeInfinity;
256                                 return true;
257                         }
258
259                         //
260                         // validate and prepare string for C
261                         //
262                         int len = s.Length;
263                         byte [] b = new byte [len + 1];
264                         int didx = 0;
265                         int sidx = 0;
266                         char c;
267                         
268                         if ((style & NumberStyles.AllowLeadingWhite) != 0){
269                                 while (sidx < len && Char.IsWhiteSpace (c = s [sidx]))
270                                        sidx++;
271
272                                 if (sidx == len) {
273                                         if (!tryParse)
274                                                 exc = Int32.GetFormatException ();
275                                         return false;
276                                 }
277                         }
278
279                         bool allow_trailing_white = ((style & NumberStyles.AllowTrailingWhite) != 0);
280
281                         //
282                         // Machine state
283                         //
284                         int state = State_AllowSign;
285
286                         //
287                         // Setup
288                         //
289                         string decimal_separator = null;
290                         string group_separator = null;
291                         string currency_symbol = null;
292                         int decimal_separator_len = 0;
293                         int group_separator_len = 0;
294                         int currency_symbol_len = 0;
295                         if ((style & NumberStyles.AllowDecimalPoint) != 0){
296                                 decimal_separator = format.NumberDecimalSeparator;
297                                 decimal_separator_len = decimal_separator.Length;
298                         }
299                         if ((style & NumberStyles.AllowThousands) != 0){
300                                 group_separator = format.NumberGroupSeparator;
301                                 group_separator_len = group_separator.Length;
302                         }
303                         if ((style & NumberStyles.AllowCurrencySymbol) != 0){
304                                 currency_symbol = format.CurrencySymbol;
305                                 currency_symbol_len = currency_symbol.Length;
306                         }
307                         string positive = format.PositiveSign;
308                         string negative = format.NegativeSign;
309                         
310                         for (; sidx < len; sidx++){
311                                 c = s [sidx];
312
313                                 if (c == '\0') {
314                                         sidx = len;
315                                         continue;
316                                 }
317
318                                 switch (state){
319                                 case State_AllowSign:
320                                         if ((style & NumberStyles.AllowLeadingSign) != 0){
321                                                 if (c == positive [0] &&
322                                                     s.Substring (sidx, positive.Length) == positive){
323                                                         state = State_Digits;
324                                                         sidx += positive.Length-1;
325                                                         continue;
326                                                 }
327
328                                                 if (c == negative [0] &&
329                                                     s.Substring (sidx, negative.Length) == negative){
330                                                         state = State_Digits;
331                                                         b [didx++] = (byte) '-';
332                                                         sidx += negative.Length-1;
333                                                         continue;
334                                                 }
335                                         }
336                                         state = State_Digits;
337                                         goto case State_Digits;
338                                         
339                                 case State_Digits:
340                                         if (Char.IsDigit (c)){
341                                                 b [didx++] = (byte) c;
342                                                 break;
343                                         }
344                                         if (c == 'e' || c == 'E')
345                                                 goto case State_Decimal;
346                                         
347                                         if (decimal_separator != null &&
348                                             decimal_separator [0] == c) {
349                                                 if (String.CompareOrdinal (s, sidx, decimal_separator, 0, decimal_separator_len) == 0) {
350                                                         b [didx++] = (byte) '.';
351                                                         sidx += decimal_separator_len-1;
352                                                         state = State_Decimal; 
353                                                         break;
354                                                 }
355                                         }
356                                         if (group_separator != null &&
357                                             group_separator [0] == c){
358                                                 if (s.Substring (sidx, group_separator_len) ==
359                                                     group_separator){
360                                                         sidx += group_separator_len-1;
361                                                         state = State_Digits; 
362                                                         break;
363                                                 }
364                                         }
365                                         if (currency_symbol != null &&
366                                             currency_symbol [0] == c){
367                                                 if (s.Substring (sidx, currency_symbol_len) ==
368                                                     currency_symbol){
369                                                         sidx += currency_symbol_len-1;
370                                                         state = State_Digits; 
371                                                         break;
372                                                 }
373                                         }
374                                         
375                                         if (Char.IsWhiteSpace (c))
376                                                 goto case State_ConsumeWhiteSpace;
377
378                                         if (!tryParse)
379                                                 exc = new FormatException ("Unknown char: " + c);
380                                         return false;
381
382                                 case State_Decimal:
383                                         if (Char.IsDigit (c)){
384                                                 b [didx++] = (byte) c;
385                                                 break;
386                                         }
387
388                                         if (c == 'e' || c == 'E'){
389                                                 if ((style & NumberStyles.AllowExponent) == 0) {
390                                                         if (!tryParse)
391                                                                 exc = new FormatException ("Unknown char: " + c);
392                                                         return false;
393                                                 }
394                                                 b [didx++] = (byte) c;
395                                                 state = State_ExponentSign;
396                                                 break;
397                                         }
398                                         
399                                         if (Char.IsWhiteSpace (c))
400                                                 goto case State_ConsumeWhiteSpace;
401                                         
402                                         if (!tryParse)
403                                                 exc = new FormatException ("Unknown char: " + c);
404                                         return false;
405
406                                 case State_ExponentSign:
407                                         if (Char.IsDigit (c)){
408                                                 state = State_Exponent;
409                                                 goto case State_Exponent;
410                                         }
411
412                                         if (c == positive [0] &&
413                                             s.Substring (sidx, positive.Length) == positive){
414                                                 state = State_Digits;
415                                                 sidx += positive.Length-1;
416                                                 continue;
417                                         }
418
419                                         if (c == negative [0] &&
420                                             s.Substring (sidx, negative.Length) == negative){
421                                                 state = State_Digits;
422                                                 b [didx++] = (byte) '-';
423                                                 sidx += negative.Length-1;
424                                                 continue;
425                                         }
426
427                                         if (Char.IsWhiteSpace (c))
428                                                 goto case State_ConsumeWhiteSpace;
429                                         
430                                         if (!tryParse)
431                                                 exc = new FormatException ("Unknown char: " + c);
432                                         return false;
433                                         
434                                 case State_Exponent:
435                                         if (Char.IsDigit (c)){
436                                                 b [didx++] = (byte) c;
437                                                 break;
438                                         }
439                                         
440                                         if (Char.IsWhiteSpace (c))
441                                                 goto case State_ConsumeWhiteSpace;
442                                         
443                                         if (!tryParse)
444                                                 exc = new FormatException ("Unknown char: " + c);
445                                         return false;
446
447                                 case State_ConsumeWhiteSpace:
448                                         if (allow_trailing_white && Char.IsWhiteSpace (c)) {
449                                                 state = State_ConsumeWhiteSpace;
450                                                 break;
451                                         }
452                                         
453                                         if (!tryParse)
454                                                 exc = new FormatException ("Unknown char");
455                                         return false;
456                                 }
457
458                                 if (state == State_Exit)
459                                         break;
460                         }
461
462                         b [didx] = 0;
463                         unsafe {
464                                 fixed (byte *p = &b [0]){
465                                         double retVal;
466                                         if (!ParseImpl (p, out retVal)) {
467                                                 if (!tryParse)
468                                                         exc = Int32.GetFormatException ();
469                                                 return false;
470                                         }
471                                         if (IsPositiveInfinity(retVal) || IsNegativeInfinity(retVal)) {
472                                                 if (!tryParse)
473                                                         exc = new OverflowException ();
474                                                 return false;
475                                         }
476
477                                         result = retVal;
478                                         return true;
479                                 }
480                         }
481                 }
482
483                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
484                 unsafe private static extern bool ParseImpl (byte *byte_ptr, out double value);
485                 
486                 public static bool TryParse (string s,
487                                              NumberStyles style,
488                                              IFormatProvider provider,
489                                              out double result)
490                 {
491                         Exception exc;
492                         if (!Parse (s, style, provider, true, out result, out exc)) {
493                                 result = 0;
494                                 return false;
495                         }
496
497                         return true;
498                 }
499 #if NET_2_0
500                 public static bool TryParse (string s, out double result)
501                 {
502                         return TryParse (s, NumberStyles.Any, null, out result);
503                 }
504 #endif
505                 public override string ToString ()
506                 {
507                         return NumberFormatter.NumberToString (m_value, null);
508                 }
509
510                 public string ToString (IFormatProvider provider)
511                 {
512                         return NumberFormatter.NumberToString (m_value, provider);
513                 }
514
515                 public string ToString (string format)
516                 {
517                         return ToString (format, null);
518                 }
519
520                 public string ToString (string format, IFormatProvider provider)
521                 {
522                         return NumberFormatter.NumberToString (format, m_value, provider);
523                 }
524
525                 // =========== IConvertible Methods =========== //
526
527                 public TypeCode GetTypeCode ()
528                 {
529                         return TypeCode.Double;
530                 }
531
532                 object IConvertible.ToType (Type type, IFormatProvider provider)
533                 {
534                         return System.Convert.ToType (m_value, type, 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                 ushort IConvertible.ToUInt16 (IFormatProvider provider)
593                 {
594                         return System.Convert.ToUInt16 (m_value);
595                 }
596
597                 uint IConvertible.ToUInt32 (IFormatProvider provider)
598                 {
599                         return System.Convert.ToUInt32 (m_value);
600                 }
601
602                 ulong IConvertible.ToUInt64 (IFormatProvider provider)
603                 {
604                         return System.Convert.ToUInt64 (m_value);
605                 }
606         }
607 }