3c1f479b9d
Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
681 lines
17 KiB
C#
681 lines
17 KiB
C#
//
|
|
// System.Double.cs
|
|
//
|
|
// Authors:
|
|
// Miguel de Icaza (miguel@ximian.com)
|
|
// Bob Smith (bob@thestuff.net)
|
|
// Marek Safar (marek.safar@gmail.com)
|
|
//
|
|
// (C) Ximian, Inc. http://www.ximian.com
|
|
// (C) Bob Smith. http://www.thestuff.net
|
|
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
|
|
// Copyright (C) 2014 Xamarin Inc (http://www.xamarin.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;
|
|
using System.Runtime.ConstrainedExecution;
|
|
|
|
namespace System {
|
|
|
|
[Serializable]
|
|
[System.Runtime.InteropServices.ComVisible (true)]
|
|
public struct Double : IComparable, IFormattable, IConvertible, IComparable <double>, IEquatable <double>
|
|
{
|
|
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 value)
|
|
{
|
|
if (value == null)
|
|
return 1;
|
|
|
|
if (!(value is System.Double))
|
|
throw new ArgumentException (Locale.GetText ("Value is not a System.Double"));
|
|
|
|
double dv = (double)value;
|
|
|
|
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 obj)
|
|
{
|
|
if (!(obj is System.Double))
|
|
return false;
|
|
|
|
double value = (double) obj;
|
|
|
|
if (IsNaN (value))
|
|
return IsNaN (m_value);
|
|
|
|
return (value == m_value);
|
|
}
|
|
|
|
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 obj)
|
|
{
|
|
if (IsNaN (obj)) {
|
|
if (IsNaN(m_value))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
return obj == m_value;
|
|
}
|
|
|
|
public override unsafe int GetHashCode ()
|
|
{
|
|
double d = m_value;
|
|
return (*((long*)&d)).GetHashCode ();
|
|
}
|
|
|
|
public static bool operator==(double left, double right)
|
|
{
|
|
return left == right;
|
|
}
|
|
|
|
public static bool operator!=(double left, double right)
|
|
{
|
|
return left != right;
|
|
}
|
|
|
|
public static bool operator>(double left, double right)
|
|
{
|
|
return left > right;
|
|
}
|
|
|
|
public static bool operator>=(double left, double right)
|
|
{
|
|
return left >= right;
|
|
}
|
|
|
|
public static bool operator<(double left, double right)
|
|
{
|
|
return left < right;
|
|
}
|
|
|
|
public static bool operator<=(double left, double right)
|
|
{
|
|
return left <= right;
|
|
}
|
|
|
|
public static bool IsInfinity (double d)
|
|
{
|
|
return (d == PositiveInfinity || d == NegativeInfinity);
|
|
}
|
|
|
|
[ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.Success)]
|
|
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 provider)
|
|
{
|
|
return Parse (s, (NumberStyles.Float | NumberStyles.AllowThousands), provider);
|
|
}
|
|
|
|
public static double Parse (string s, NumberStyles style)
|
|
{
|
|
return Parse (s, style, null);
|
|
}
|
|
|
|
enum ParseState {
|
|
AllowSign = 1,
|
|
Digits = 2,
|
|
Decimal = 3,
|
|
ExponentSign = 4,
|
|
Exponent = 5,
|
|
ConsumeWhiteSpace = 6,
|
|
TrailingSymbols = 7,
|
|
Exit = 8
|
|
};
|
|
|
|
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;
|
|
}
|
|
// 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);
|
|
}
|
|
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?");
|
|
|
|
//
|
|
// validate and prepare string for C
|
|
//
|
|
int len = s.Length;
|
|
int didx = 0;
|
|
int sidx = 0;
|
|
char c;
|
|
bool allow_leading_white = (style & NumberStyles.AllowLeadingWhite) != 0;
|
|
bool allow_trailing_white = ((style & NumberStyles.AllowTrailingWhite) != 0);
|
|
|
|
if (allow_leading_white) {
|
|
while (sidx < len && Char.IsWhiteSpace (s [sidx]))
|
|
sidx++;
|
|
|
|
if (sidx == len) {
|
|
if (!tryParse)
|
|
exc = GetFormatException ();
|
|
return false;
|
|
}
|
|
}
|
|
int sEndPos = s.Length - 1;
|
|
if (allow_trailing_white)
|
|
while (Char.IsWhiteSpace (s [sEndPos]))
|
|
sEndPos--;
|
|
|
|
if (TryParseStringConstant (format.NaNSymbol, s, sidx, sEndPos)) {
|
|
result = double.NaN;
|
|
return true;
|
|
}
|
|
if (TryParseStringConstant (format.PositiveInfinitySymbol, s, sidx, sEndPos)) {
|
|
result = double.PositiveInfinity;
|
|
return true;
|
|
}
|
|
if (TryParseStringConstant (format.NegativeInfinitySymbol, s, sidx, sEndPos)) {
|
|
result = double.NegativeInfinity;
|
|
return true;
|
|
}
|
|
|
|
byte [] b = new byte [len + 1];
|
|
|
|
//
|
|
// Machine state
|
|
//
|
|
var state = ParseState.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;
|
|
bool allow_trailing_parenthes = false;
|
|
|
|
for (; sidx < len; sidx++){
|
|
c = s [sidx];
|
|
|
|
if (c == '\0') {
|
|
sidx = len;
|
|
continue;
|
|
}
|
|
|
|
switch (state){
|
|
case ParseState.AllowSign:
|
|
if ((style & NumberStyles.AllowLeadingSign) != 0) {
|
|
if (c == positive [0] &&
|
|
s.Substring (sidx, positive.Length) == positive) {
|
|
state = ParseState.Digits;
|
|
sidx += positive.Length - 1;
|
|
continue;
|
|
}
|
|
|
|
if (c == negative [0] &&
|
|
s.Substring (sidx, negative.Length) == negative) {
|
|
state = ParseState.Digits;
|
|
b [didx++] = (byte)'-';
|
|
sidx += negative.Length - 1;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ((style & NumberStyles.AllowParentheses) != 0 && c == '(') {
|
|
b [didx++] = (byte)'-';
|
|
state = ParseState.Digits;
|
|
allow_trailing_parenthes = true;
|
|
continue;
|
|
}
|
|
|
|
state = ParseState.Digits;
|
|
goto case ParseState.Digits;
|
|
|
|
case ParseState.Digits:
|
|
if (Char.IsDigit (c)) {
|
|
b [didx++] = (byte)c;
|
|
break;
|
|
}
|
|
|
|
if (c == 'e' || c == 'E')
|
|
goto case ParseState.Decimal;
|
|
|
|
if (allow_trailing_parenthes && c == ')') {
|
|
allow_trailing_parenthes = false;
|
|
state = ParseState.ConsumeWhiteSpace;
|
|
continue;
|
|
}
|
|
|
|
if (decimal_separator_len > 0 &&
|
|
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 = ParseState.Decimal;
|
|
break;
|
|
}
|
|
}
|
|
if (group_separator_len > 0 &&
|
|
group_separator [0] == c) {
|
|
if (s.Substring (sidx, group_separator_len) ==
|
|
group_separator) {
|
|
sidx += group_separator_len - 1;
|
|
break;
|
|
}
|
|
}
|
|
if (currency_symbol_len > 0 &&
|
|
currency_symbol [0] == c) {
|
|
if (s.Substring (sidx, currency_symbol_len) ==
|
|
currency_symbol) {
|
|
sidx += currency_symbol_len - 1;
|
|
currency_symbol_len = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
state = ParseState.TrailingSymbols;
|
|
goto case ParseState.TrailingSymbols;
|
|
|
|
case ParseState.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 = ParseState.ExponentSign;
|
|
break;
|
|
}
|
|
|
|
state = ParseState.TrailingSymbols;
|
|
goto case ParseState.TrailingSymbols;
|
|
|
|
case ParseState.ExponentSign:
|
|
if (Char.IsDigit (c)){
|
|
state = ParseState.Exponent;
|
|
goto case ParseState.Exponent;
|
|
}
|
|
|
|
if (c == positive [0] &&
|
|
s.Substring (sidx, positive.Length) == positive){
|
|
state = ParseState.Digits;
|
|
sidx += positive.Length-1;
|
|
continue;
|
|
}
|
|
|
|
if (c == negative [0] &&
|
|
s.Substring (sidx, negative.Length) == negative){
|
|
state = ParseState.Digits;
|
|
b [didx++] = (byte) '-';
|
|
sidx += negative.Length-1;
|
|
continue;
|
|
}
|
|
|
|
goto case ParseState.ConsumeWhiteSpace;
|
|
|
|
case ParseState.Exponent:
|
|
if (Char.IsDigit (c)){
|
|
b [didx++] = (byte) c;
|
|
break;
|
|
}
|
|
|
|
state = ParseState.TrailingSymbols;
|
|
goto case ParseState.TrailingSymbols;
|
|
|
|
case ParseState.TrailingSymbols:
|
|
if ((style & NumberStyles.AllowTrailingSign) != 0) {
|
|
if (positive != null && c == positive [0] &&
|
|
s.Substring (sidx, positive.Length) == positive) {
|
|
state = ParseState.ConsumeWhiteSpace;
|
|
sidx += positive.Length - 1;
|
|
allow_trailing_parenthes = false;
|
|
positive = null;
|
|
continue;
|
|
}
|
|
|
|
if (negative != null && c == negative [0] &&
|
|
s.Substring (sidx, negative.Length) == negative) {
|
|
state = ParseState.ConsumeWhiteSpace;
|
|
Array.Copy (b, 0, b, 1, didx);
|
|
b [0] = (byte)'-';
|
|
++didx;
|
|
sidx += negative.Length - 1;
|
|
allow_trailing_parenthes = false;
|
|
negative = null;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (currency_symbol_len > 0 &&
|
|
currency_symbol [0] == c) {
|
|
if (s.Substring (sidx, currency_symbol_len) ==
|
|
currency_symbol) {
|
|
sidx += currency_symbol_len - 1;
|
|
currency_symbol_len = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (allow_trailing_white && Char.IsWhiteSpace (c)) {
|
|
break;
|
|
}
|
|
|
|
goto case ParseState.ConsumeWhiteSpace;
|
|
|
|
case ParseState.ConsumeWhiteSpace:
|
|
if (allow_trailing_parenthes && c == ')') {
|
|
allow_trailing_parenthes = false;
|
|
state = ParseState.ConsumeWhiteSpace;
|
|
break;
|
|
}
|
|
|
|
if (allow_trailing_white && Char.IsWhiteSpace (c)) {
|
|
state = ParseState.ConsumeWhiteSpace;
|
|
break;
|
|
}
|
|
|
|
if (!tryParse)
|
|
exc = new FormatException ("Unknown char");
|
|
return false;
|
|
}
|
|
|
|
if (state == ParseState.Exit)
|
|
break;
|
|
}
|
|
|
|
b [didx] = 0;
|
|
unsafe {
|
|
fixed (byte *p = &b [0]){
|
|
double retVal;
|
|
if (!ParseImpl (p, out retVal)) {
|
|
if (!tryParse)
|
|
exc = GetFormatException ();
|
|
return false;
|
|
}
|
|
if (IsPositiveInfinity(retVal) || IsNegativeInfinity(retVal)) {
|
|
if (!tryParse)
|
|
exc = new OverflowException ();
|
|
return false;
|
|
}
|
|
|
|
result = retVal;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool TryParseStringConstant (string format, string s, int start, int end)
|
|
{
|
|
return end - start + 1 == format.Length && String.CompareOrdinal (format, 0, s, start, format.Length) == 0;
|
|
}
|
|
|
|
[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;
|
|
}
|
|
|
|
public static bool TryParse (string s, out double result)
|
|
{
|
|
return TryParse (s, NumberStyles.Any, null, out result);
|
|
}
|
|
|
|
public override string ToString ()
|
|
{
|
|
return NumberFormatter.NumberToString (m_value, null);
|
|
}
|
|
|
|
public string ToString (IFormatProvider provider)
|
|
{
|
|
return NumberFormatter.NumberToString (m_value, provider);
|
|
}
|
|
|
|
public string ToString (string format)
|
|
{
|
|
return ToString (format, null);
|
|
}
|
|
|
|
public string ToString (string format, IFormatProvider provider)
|
|
{
|
|
return NumberFormatter.NumberToString (format, m_value, provider);
|
|
}
|
|
|
|
static Exception GetFormatException ()
|
|
{
|
|
return new FormatException ("Input string was not in the correct format");
|
|
}
|
|
|
|
// =========== IConvertible Methods =========== //
|
|
|
|
public TypeCode GetTypeCode ()
|
|
{
|
|
return TypeCode.Double;
|
|
}
|
|
|
|
object IConvertible.ToType (Type type, IFormatProvider provider)
|
|
{
|
|
return Convert.DefaultToType ((IConvertible)this, type, 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|