// // System.Double.cs // // Author: // Miguel de Icaza (miguel@ximian.com) // Bob Smith (bob@thestuff.net) // // (C) Ximian, Inc. http://www.ximian.com // (C) Bob Smith. http://www.thestuff.net // // // Copyright (C) 2004 Novell, Inc (http://www.novell.com) // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System.Globalization; using System.Runtime.CompilerServices; #if NET_2_0 using System.Runtime.ConstrainedExecution; #endif namespace System { [Serializable] #if NET_2_0 [System.Runtime.InteropServices.ComVisible (true)] #endif public struct Double : IComparable, IFormattable, IConvertible #if NET_2_0 , IComparable , IEquatable #endif { public const double Epsilon = 4.9406564584124650e-324; public const double MaxValue = 1.7976931348623157e308; public const double MinValue = -1.7976931348623157e308; public const double NaN = 0.0d / 0.0d; public const double NegativeInfinity = -1.0d / 0.0d; public const double PositiveInfinity = 1.0d / 0.0d; internal double m_value; public int CompareTo (object v) { if (v == null) return 1; if (!(v is System.Double)) throw new ArgumentException (Locale.GetText ("Value is not a System.Double")); double dv = (double)v; if (IsPositiveInfinity(m_value) && IsPositiveInfinity(dv)) return 0; if (IsNegativeInfinity(m_value) && IsNegativeInfinity(dv)) return 0; if (IsNaN(dv)) if (IsNaN(m_value)) return 0; else return 1; if (IsNaN(m_value)) if (IsNaN(dv)) return 0; else return -1; if (m_value > dv) return 1; else if (m_value < dv) return -1; else return 0; } public override bool Equals (object o) { if (!(o is System.Double)) return false; if (IsNaN ((double)o)) { if (IsNaN(m_value)) return true; else return false; } return ((double) o) == m_value; } #if NET_2_0 public int CompareTo (double value) { if (IsPositiveInfinity(m_value) && IsPositiveInfinity(value)) return 0; if (IsNegativeInfinity(m_value) && IsNegativeInfinity(value)) return 0; if (IsNaN(value)) if (IsNaN(m_value)) return 0; else return 1; if (IsNaN(m_value)) if (IsNaN(value)) return 0; else return -1; if (m_value > value) return 1; else if (m_value < value) return -1; else return 0; } public bool Equals (double value) { if (IsNaN (value)) { if (IsNaN(m_value)) return true; else return false; } return value == m_value; } #endif public override unsafe int GetHashCode () { double d = m_value; return (*((long*)&d)).GetHashCode (); } public static bool IsInfinity (double d) { return (d == PositiveInfinity || d == NegativeInfinity); } #if NET_2_0 [ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)] #endif public static bool IsNaN (double d) { #pragma warning disable 1718 return (d != d); #pragma warning restore } public static bool IsNegativeInfinity (double d) { return (d < 0.0d && (d == NegativeInfinity || d == PositiveInfinity)); } public static bool IsPositiveInfinity (double d) { return (d > 0.0d && (d == NegativeInfinity || d == PositiveInfinity)); } public static double Parse (string s) { return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), null); } public static double Parse (string s, IFormatProvider fp) { return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), fp); } public static double Parse (string s, NumberStyles style) { return Parse (s, style, null); } // We're intentionally using constants here to avoid some bigger headaches in mcs. // This struct must be compiled before System.Enum so we can't use enums here. private const int State_AllowSign = 1; private const int State_Digits = 2; private const int State_Decimal = 3; private const int State_ExponentSign = 4; private const int State_Exponent = 5; private const int State_ConsumeWhiteSpace = 6; private const int State_Exit = 7; public static double Parse (string s, NumberStyles style, IFormatProvider provider) { Exception exc; double result; if (!Parse (s, style, provider, false, out result, out exc)) throw exc; return result; } // FIXME: check if digits are group in correct numbers between the group separators internal static bool Parse (string s, NumberStyles style, IFormatProvider provider, bool tryParse, out double result, out Exception exc) { result = 0; exc = null; if (s == null) { if (!tryParse) exc = new ArgumentNullException ("s"); return false; } if (s.Length == 0) { if (!tryParse) exc = new FormatException (); return false; } #if NET_2_0 // yes it's counter intuitive (buggy?) but even TryParse actually throws in this case if ((style & NumberStyles.AllowHexSpecifier) != 0) { string msg = Locale.GetText ("Double doesn't support parsing with '{0}'.", "AllowHexSpecifier"); throw new ArgumentException (msg); } #endif if (style > NumberStyles.Any) { if (!tryParse) exc = new ArgumentException(); return false; } NumberFormatInfo format = NumberFormatInfo.GetInstance(provider); if (format == null) throw new Exception("How did this happen?"); if (s == format.NaNSymbol) { result = Double.NaN; return true; } if (s == format.PositiveInfinitySymbol) { result = Double.PositiveInfinity; return true; } if (s == format.NegativeInfinitySymbol) { result = Double.NegativeInfinity; return true; } // // validate and prepare string for C // int len = s.Length; byte [] b = new byte [len + 1]; int didx = 0; int sidx = 0; char c; if ((style & NumberStyles.AllowLeadingWhite) != 0){ while (sidx < len && Char.IsWhiteSpace (c = s [sidx])) sidx++; if (sidx == len) { if (!tryParse) exc = Int32.GetFormatException (); return false; } } bool allow_trailing_white = ((style & NumberStyles.AllowTrailingWhite) != 0); // // Machine state // int state = State_AllowSign; // // Setup // string decimal_separator = null; string group_separator = null; string currency_symbol = null; int decimal_separator_len = 0; int group_separator_len = 0; int currency_symbol_len = 0; if ((style & NumberStyles.AllowDecimalPoint) != 0){ decimal_separator = format.NumberDecimalSeparator; decimal_separator_len = decimal_separator.Length; } if ((style & NumberStyles.AllowThousands) != 0){ group_separator = format.NumberGroupSeparator; group_separator_len = group_separator.Length; } if ((style & NumberStyles.AllowCurrencySymbol) != 0){ currency_symbol = format.CurrencySymbol; currency_symbol_len = currency_symbol.Length; } string positive = format.PositiveSign; string negative = format.NegativeSign; for (; sidx < len; sidx++){ c = s [sidx]; if (c == '\0') { sidx = len; continue; } switch (state){ case State_AllowSign: if ((style & NumberStyles.AllowLeadingSign) != 0){ if (c == positive [0] && s.Substring (sidx, positive.Length) == positive){ state = State_Digits; sidx += positive.Length-1; continue; } if (c == negative [0] && s.Substring (sidx, negative.Length) == negative){ state = State_Digits; b [didx++] = (byte) '-'; sidx += negative.Length-1; continue; } } state = State_Digits; goto case State_Digits; case State_Digits: if (Char.IsDigit (c)){ b [didx++] = (byte) c; break; } if (c == 'e' || c == 'E') goto case State_Decimal; if (decimal_separator != null && decimal_separator [0] == c) { if (String.CompareOrdinal (s, sidx, decimal_separator, 0, decimal_separator_len) == 0) { b [didx++] = (byte) '.'; sidx += decimal_separator_len-1; state = State_Decimal; break; } } if (group_separator != null && group_separator [0] == c){ if (s.Substring (sidx, group_separator_len) == group_separator){ sidx += group_separator_len-1; state = State_Digits; break; } } if (currency_symbol != null && currency_symbol [0] == c){ if (s.Substring (sidx, currency_symbol_len) == currency_symbol){ sidx += currency_symbol_len-1; state = State_Digits; break; } } if (Char.IsWhiteSpace (c)) goto case State_ConsumeWhiteSpace; if (!tryParse) exc = new FormatException ("Unknown char: " + c); return false; case State_Decimal: if (Char.IsDigit (c)){ b [didx++] = (byte) c; break; } if (c == 'e' || c == 'E'){ if ((style & NumberStyles.AllowExponent) == 0) { if (!tryParse) exc = new FormatException ("Unknown char: " + c); return false; } b [didx++] = (byte) c; state = State_ExponentSign; break; } if (Char.IsWhiteSpace (c)) goto case State_ConsumeWhiteSpace; if (!tryParse) exc = new FormatException ("Unknown char: " + c); return false; case State_ExponentSign: if (Char.IsDigit (c)){ state = State_Exponent; goto case State_Exponent; } if (c == positive [0] && s.Substring (sidx, positive.Length) == positive){ state = State_Digits; sidx += positive.Length-1; continue; } if (c == negative [0] && s.Substring (sidx, negative.Length) == negative){ state = State_Digits; b [didx++] = (byte) '-'; sidx += negative.Length-1; continue; } if (Char.IsWhiteSpace (c)) goto case State_ConsumeWhiteSpace; if (!tryParse) exc = new FormatException ("Unknown char: " + c); return false; case State_Exponent: if (Char.IsDigit (c)){ b [didx++] = (byte) c; break; } if (Char.IsWhiteSpace (c)) goto case State_ConsumeWhiteSpace; if (!tryParse) exc = new FormatException ("Unknown char: " + c); return false; case State_ConsumeWhiteSpace: if (allow_trailing_white && Char.IsWhiteSpace (c)) { state = State_ConsumeWhiteSpace; break; } if (!tryParse) exc = new FormatException ("Unknown char"); return false; } if (state == State_Exit) break; } b [didx] = 0; unsafe { fixed (byte *p = &b [0]){ double retVal; if (!ParseImpl (p, out retVal)) { if (!tryParse) exc = Int32.GetFormatException (); return false; } if (IsPositiveInfinity(retVal) || IsNegativeInfinity(retVal)) { if (!tryParse) exc = new OverflowException (); return false; } result = retVal; return true; } } } [MethodImplAttribute(MethodImplOptions.InternalCall)] unsafe private static extern bool ParseImpl (byte *byte_ptr, out double value); public static bool TryParse (string s, NumberStyles style, IFormatProvider provider, out double result) { Exception exc; if (!Parse (s, style, provider, true, out result, out exc)) { result = 0; return false; } return true; } #if NET_2_0 public static bool TryParse (string s, out double result) { return TryParse (s, NumberStyles.Any, null, out result); } #endif public override string ToString () { return ToString (null, null); } public string ToString (IFormatProvider fp) { return ToString (null, fp); } public string ToString (string format) { return ToString (format, null); } public string ToString (string format, IFormatProvider fp) { NumberFormatInfo nfi = NumberFormatInfo.GetInstance (fp); return NumberFormatter.NumberToString (format, m_value, nfi); } // =========== IConvertible Methods =========== // public TypeCode GetTypeCode () { return TypeCode.Double; } object IConvertible.ToType (Type conversionType, IFormatProvider provider) { return System.Convert.ToType(m_value, conversionType, provider); } bool IConvertible.ToBoolean (IFormatProvider provider) { return System.Convert.ToBoolean(m_value); } byte IConvertible.ToByte (IFormatProvider provider) { return System.Convert.ToByte(m_value); } char IConvertible.ToChar (IFormatProvider provider) { throw new InvalidCastException(); } DateTime IConvertible.ToDateTime (IFormatProvider provider) { throw new InvalidCastException(); } decimal IConvertible.ToDecimal (IFormatProvider provider) { return System.Convert.ToDecimal(m_value); } double IConvertible.ToDouble (IFormatProvider provider) { return System.Convert.ToDouble(m_value); } short IConvertible.ToInt16 (IFormatProvider provider) { return System.Convert.ToInt16(m_value); } int IConvertible.ToInt32 (IFormatProvider provider) { return System.Convert.ToInt32(m_value); } long IConvertible.ToInt64 (IFormatProvider provider) { return System.Convert.ToInt64(m_value); } sbyte IConvertible.ToSByte (IFormatProvider provider) { return System.Convert.ToSByte(m_value); } float IConvertible.ToSingle (IFormatProvider provider) { return System.Convert.ToSingle(m_value); } /* string IConvertible.ToString (IFormatProvider provider) { return ToString(provider); } */ ushort IConvertible.ToUInt16 (IFormatProvider provider) { return System.Convert.ToUInt16(m_value); } uint IConvertible.ToUInt32 (IFormatProvider provider) { return System.Convert.ToUInt32(m_value); } ulong IConvertible.ToUInt64 (IFormatProvider provider) { return System.Convert.ToUInt64(m_value); } } }