5 // Miguel de Icaza (miguel@ximian.com)
6 // Bob Smith (bob@thestuff.net)
7 // Marek Safar (marek.safar@gmail.com)
9 // (C) Ximian, Inc. http://www.ximian.com
10 // (C) Bob Smith. http://www.thestuff.net
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
12 // Copyright (C) 2014 Xamarin Inc (http://www.xamarin.com)
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
25 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Globalization;
35 using System.Runtime.CompilerServices;
36 using System.Runtime.ConstrainedExecution;
41 [System.Runtime.InteropServices.ComVisible (true)]
42 public struct Double : IComparable, IFormattable, IConvertible, IComparable <double>, IEquatable <double>
44 public const double Epsilon = 4.9406564584124650e-324;
45 public const double MaxValue = 1.7976931348623157e308;
46 public const double MinValue = -1.7976931348623157e308;
47 public const double NaN = 0.0d / 0.0d;
48 public const double NegativeInfinity = -1.0d / 0.0d;
49 public const double PositiveInfinity = 1.0d / 0.0d;
51 internal double m_value;
53 public int CompareTo (object value)
58 if (!(value is System.Double))
59 throw new ArgumentException (Locale.GetText ("Value is not a System.Double"));
61 double dv = (double)value;
63 if (IsPositiveInfinity(m_value) && IsPositiveInfinity(dv))
66 if (IsNegativeInfinity(m_value) && IsNegativeInfinity(dv))
81 if (m_value > dv) return 1;
82 else if (m_value < dv) return -1;
86 public override bool Equals (object obj)
88 if (!(obj is System.Double))
91 double value = (double) obj;
94 return IsNaN (m_value);
96 return (value == m_value);
99 public int CompareTo (double value)
101 if (IsPositiveInfinity(m_value) && IsPositiveInfinity(value))
104 if (IsNegativeInfinity(m_value) && IsNegativeInfinity(value))
119 if (m_value > value) return 1;
120 else if (m_value < value) return -1;
124 public bool Equals (double obj)
133 return obj == m_value;
136 public override unsafe int GetHashCode ()
139 return (*((long*)&d)).GetHashCode ();
143 public static bool operator==(double left, double right)
145 return left == right;
148 public static bool operator!=(double left, double right)
150 return left != right;
153 public static bool operator>(double left, double right)
158 public static bool operator>=(double left, double right)
160 return left >= right;
163 public static bool operator<(double left, double right)
168 public static bool operator<=(double left, double right)
170 return left <= right;
174 public static bool IsInfinity (double d)
176 return (d == PositiveInfinity || d == NegativeInfinity);
179 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
180 public static bool IsNaN (double d)
182 #pragma warning disable 1718
184 #pragma warning restore
187 public static bool IsNegativeInfinity (double d)
189 return (d < 0.0d && (d == NegativeInfinity || d == PositiveInfinity));
192 public static bool IsPositiveInfinity (double d)
194 return (d > 0.0d && (d == NegativeInfinity || d == PositiveInfinity));
197 public static double Parse (string s)
199 return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), null);
202 public static double Parse (string s, IFormatProvider provider)
204 return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), provider);
207 public static double Parse (string s, NumberStyles style)
209 return Parse (s, style, null);
218 ConsumeWhiteSpace = 6,
223 public static double Parse (string s, NumberStyles style, IFormatProvider provider)
228 if (!Parse (s, style, provider, false, out result, out exc))
234 // FIXME: check if digits are group in correct numbers between the group separators
235 internal static bool Parse (string s, NumberStyles style, IFormatProvider provider, bool tryParse, out double result, out Exception exc)
242 exc = new ArgumentNullException ("s");
247 exc = new FormatException ();
250 // yes it's counter intuitive (buggy?) but even TryParse actually throws in this case
251 if ((style & NumberStyles.AllowHexSpecifier) != 0) {
252 string msg = Locale.GetText ("Double doesn't support parsing with '{0}'.", "AllowHexSpecifier");
253 throw new ArgumentException (msg);
255 if (style > NumberStyles.Any) {
257 exc = new ArgumentException();
261 NumberFormatInfo format = NumberFormatInfo.GetInstance(provider);
262 if (format == null) throw new Exception("How did this happen?");
265 // validate and prepare string for C
271 bool allow_leading_white = (style & NumberStyles.AllowLeadingWhite) != 0;
272 bool allow_trailing_white = ((style & NumberStyles.AllowTrailingWhite) != 0);
274 if (allow_leading_white) {
275 while (sidx < len && Char.IsWhiteSpace (s [sidx]))
280 exc = Int32.GetFormatException ();
284 int sEndPos = s.Length - 1;
285 if (allow_trailing_white)
286 while (Char.IsWhiteSpace (s [sEndPos]))
289 if (TryParseStringConstant (format.NaNSymbol, s, sidx, sEndPos)) {
293 if (TryParseStringConstant (format.PositiveInfinitySymbol, s, sidx, sEndPos)) {
294 result = double.PositiveInfinity;
297 if (TryParseStringConstant (format.NegativeInfinitySymbol, s, sidx, sEndPos)) {
298 result = double.NegativeInfinity;
302 byte [] b = new byte [len + 1];
307 var state = ParseState.AllowSign;
312 string decimal_separator = null;
313 string group_separator = null;
314 string currency_symbol = null;
315 int decimal_separator_len = 0;
316 int group_separator_len = 0;
317 int currency_symbol_len = 0;
318 if ((style & NumberStyles.AllowDecimalPoint) != 0){
319 decimal_separator = format.NumberDecimalSeparator;
320 decimal_separator_len = decimal_separator.Length;
322 if ((style & NumberStyles.AllowThousands) != 0){
323 group_separator = format.NumberGroupSeparator;
324 group_separator_len = group_separator.Length;
326 if ((style & NumberStyles.AllowCurrencySymbol) != 0){
327 currency_symbol = format.CurrencySymbol;
328 currency_symbol_len = currency_symbol.Length;
330 string positive = format.PositiveSign;
331 string negative = format.NegativeSign;
332 bool allow_trailing_parenthes = false;
334 for (; sidx < len; sidx++){
343 case ParseState.AllowSign:
344 if ((style & NumberStyles.AllowLeadingSign) != 0) {
345 if (c == positive [0] &&
346 s.Substring (sidx, positive.Length) == positive) {
347 state = ParseState.Digits;
348 sidx += positive.Length - 1;
352 if (c == negative [0] &&
353 s.Substring (sidx, negative.Length) == negative) {
354 state = ParseState.Digits;
355 b [didx++] = (byte)'-';
356 sidx += negative.Length - 1;
361 if ((style & NumberStyles.AllowParentheses) != 0 && c == '(') {
362 b [didx++] = (byte)'-';
363 state = ParseState.Digits;
364 allow_trailing_parenthes = true;
368 state = ParseState.Digits;
369 goto case ParseState.Digits;
371 case ParseState.Digits:
372 if (Char.IsDigit (c)) {
373 b [didx++] = (byte)c;
377 if (c == 'e' || c == 'E')
378 goto case ParseState.Decimal;
380 if (allow_trailing_parenthes && c == ')') {
381 allow_trailing_parenthes = false;
382 state = ParseState.ConsumeWhiteSpace;
386 if (decimal_separator_len > 0 &&
387 decimal_separator [0] == c) {
388 if (String.CompareOrdinal (s, sidx, decimal_separator, 0, decimal_separator_len) == 0) {
389 b [didx++] = (byte)'.';
390 sidx += decimal_separator_len - 1;
391 state = ParseState.Decimal;
395 if (group_separator_len > 0 &&
396 group_separator [0] == c) {
397 if (s.Substring (sidx, group_separator_len) ==
399 sidx += group_separator_len - 1;
403 if (currency_symbol_len > 0 &&
404 currency_symbol [0] == c) {
405 if (s.Substring (sidx, currency_symbol_len) ==
407 sidx += currency_symbol_len - 1;
408 currency_symbol_len = 0;
413 state = ParseState.TrailingSymbols;
414 goto case ParseState.TrailingSymbols;
416 case ParseState.Decimal:
417 if (Char.IsDigit (c)){
418 b [didx++] = (byte) c;
422 if (c == 'e' || c == 'E'){
423 if ((style & NumberStyles.AllowExponent) == 0) {
425 exc = new FormatException ("Unknown char: " + c);
428 b [didx++] = (byte) c;
429 state = ParseState.ExponentSign;
433 state = ParseState.TrailingSymbols;
434 goto case ParseState.TrailingSymbols;
436 case ParseState.ExponentSign:
437 if (Char.IsDigit (c)){
438 state = ParseState.Exponent;
439 goto case ParseState.Exponent;
442 if (c == positive [0] &&
443 s.Substring (sidx, positive.Length) == positive){
444 state = ParseState.Digits;
445 sidx += positive.Length-1;
449 if (c == negative [0] &&
450 s.Substring (sidx, negative.Length) == negative){
451 state = ParseState.Digits;
452 b [didx++] = (byte) '-';
453 sidx += negative.Length-1;
457 goto case ParseState.ConsumeWhiteSpace;
459 case ParseState.Exponent:
460 if (Char.IsDigit (c)){
461 b [didx++] = (byte) c;
465 state = ParseState.TrailingSymbols;
466 goto case ParseState.TrailingSymbols;
468 case ParseState.TrailingSymbols:
469 if ((style & NumberStyles.AllowTrailingSign) != 0) {
470 if (positive != null && c == positive [0] &&
471 s.Substring (sidx, positive.Length) == positive) {
472 state = ParseState.ConsumeWhiteSpace;
473 sidx += positive.Length - 1;
474 allow_trailing_parenthes = false;
479 if (negative != null && c == negative [0] &&
480 s.Substring (sidx, negative.Length) == negative) {
481 state = ParseState.ConsumeWhiteSpace;
482 Array.Copy (b, 0, b, 1, didx);
485 sidx += negative.Length - 1;
486 allow_trailing_parenthes = false;
492 if (currency_symbol_len > 0 &&
493 currency_symbol [0] == c) {
494 if (s.Substring (sidx, currency_symbol_len) ==
496 sidx += currency_symbol_len - 1;
497 currency_symbol_len = 0;
502 if (allow_trailing_white && Char.IsWhiteSpace (c)) {
506 goto case ParseState.ConsumeWhiteSpace;
508 case ParseState.ConsumeWhiteSpace:
509 if (allow_trailing_parenthes && c == ')') {
510 allow_trailing_parenthes = false;
511 state = ParseState.ConsumeWhiteSpace;
515 if (allow_trailing_white && Char.IsWhiteSpace (c)) {
516 state = ParseState.ConsumeWhiteSpace;
521 exc = new FormatException ("Unknown char");
525 if (state == ParseState.Exit)
531 fixed (byte *p = &b [0]){
533 if (!ParseImpl (p, out retVal)) {
535 exc = Int32.GetFormatException ();
538 if (IsPositiveInfinity(retVal) || IsNegativeInfinity(retVal)) {
540 exc = new OverflowException ();
550 static bool TryParseStringConstant (string format, string s, int start, int end)
552 return end - start + 1 == format.Length && String.CompareOrdinal (format, 0, s, start, format.Length) == 0;
555 [MethodImplAttribute(MethodImplOptions.InternalCall)]
556 unsafe private static extern bool ParseImpl (byte *byte_ptr, out double value);
558 public static bool TryParse (string s,
560 IFormatProvider provider,
564 if (!Parse (s, style, provider, true, out result, out exc)) {
572 public static bool TryParse (string s, out double result)
574 return TryParse (s, NumberStyles.Any, null, out result);
577 public override string ToString ()
579 return NumberFormatter.NumberToString (m_value, null);
582 public string ToString (IFormatProvider provider)
584 return NumberFormatter.NumberToString (m_value, provider);
587 public string ToString (string format)
589 return ToString (format, null);
592 public string ToString (string format, IFormatProvider provider)
594 return NumberFormatter.NumberToString (format, m_value, provider);
597 // =========== IConvertible Methods =========== //
599 public TypeCode GetTypeCode ()
601 return TypeCode.Double;
604 object IConvertible.ToType (Type targetType, IFormatProvider provider)
606 if (targetType == null)
607 throw new ArgumentNullException ("targetType");
608 return System.Convert.ToType (m_value, targetType, provider, false);
611 bool IConvertible.ToBoolean (IFormatProvider provider)
613 return System.Convert.ToBoolean (m_value);
616 byte IConvertible.ToByte (IFormatProvider provider)
618 return System.Convert.ToByte (m_value);
621 char IConvertible.ToChar (IFormatProvider provider)
623 throw new InvalidCastException ();
626 DateTime IConvertible.ToDateTime (IFormatProvider provider)
628 throw new InvalidCastException ();
631 decimal IConvertible.ToDecimal (IFormatProvider provider)
633 return System.Convert.ToDecimal (m_value);
636 double IConvertible.ToDouble (IFormatProvider provider)
638 return System.Convert.ToDouble (m_value);
641 short IConvertible.ToInt16 (IFormatProvider provider)
643 return System.Convert.ToInt16 (m_value);
646 int IConvertible.ToInt32 (IFormatProvider provider)
648 return System.Convert.ToInt32 (m_value);
651 long IConvertible.ToInt64 (IFormatProvider provider)
653 return System.Convert.ToInt64 (m_value);
656 sbyte IConvertible.ToSByte (IFormatProvider provider)
658 return System.Convert.ToSByte (m_value);
661 float IConvertible.ToSingle (IFormatProvider provider)
663 return System.Convert.ToSingle (m_value);
666 ushort IConvertible.ToUInt16 (IFormatProvider provider)
668 return System.Convert.ToUInt16 (m_value);
670 uint IConvertible.ToUInt32 (IFormatProvider provider)
672 return System.Convert.ToUInt32 (m_value);
674 ulong IConvertible.ToUInt64 (IFormatProvider provider)
676 return System.Convert.ToUInt64 (m_value);