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