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