Convert “1.79769313486232E+308” to double without

2020-02-01 18:06发布

问题:

I have this string "1.79769313486232E+308" and am trying to convert it to a .NET numeric value (double?) but am getting the below exception. I am using Convert.ToDouble(). What is the proper way to do this conversion?

OverflowException: Value was either too large or too small for a Double

回答1:

The problem is likely due to the fact that Double.MaxValue was converted to a string, and when the string is output, not all the digits are output, instead it is rounded. Parsing this value overflows the double.

Using Double.TryParse and subsequently checking equality on the string "1.79769313486232E+308" in case of failure and substituting Double.MaxValue should be a quick workaround, if you need to keep the string the way it is.

EDIT: Of course, if you don't need to keep the string the way it is, use the Round Trip format specifier to produce the string in the first place, as Jon describes in his answer.



回答2:

Unfortunately this value is greater than double.MaxValue, hence the exception.

As codekaizen suggests, you could hard-code a test for the string. A better (IMO) alternative if you're the one producing the string in the first place is to use the "r" format specifier. Then the string you produce will be "1.7976931348623157E+308" instead, which then parses correctly:

string s = double.MaxValue.ToString("r");
double d = double.Parse(s); // No exception

Obviously that's no help if you don't have control over the data - but then you should understand you're likely to be losing data already in that case.



回答3:

You may try double.Parse() or double.TryParse() rather than Convert.ToDouble(), but I'm not certain you'll get better results. Incidentally, the string that you provide is equal to double.MaxValue, which is (of course) the maximum value that can be contained in a double, so that's likely where your error is coming from. Floating-point numeric types are finicky, so I would assume that some sort of rounding is taking place and pushing it outside the bounds of the type.

You could also try the decimal data type. You may have better luck there.



回答4:

Here is what I came up with. Thanks Jon Skeet and codekaizen.

private double convertToDouble(string str)
{
    double dbl;

    if (double.TryParse(str, out dbl))
        return dbl;

    if (str == "1.79769313486232E+308")
        return double.MaxValue;

    return double.MinValue;
}


回答5:

Demonstrates the issue and a solution:

var s = double.MaxValue.ToString();
double d;
if (!double.TryParse(s, out d)) {
    d = s.Equals(double.MaxValue) ? double.MaxValue : double.MinValue;
}


回答6:

Here a more generic implementation which representing different formatting and cultures and is more tolerant:

#region MatchDoubleMinMaxValuesRegex
/// <summary>
/// This regex matches strings which represents either a <see cref="double.MinValue"/> or a <see cref="double.MaxValue"/>.
/// If it is a <see cref="double.MinValue"/> then the group "isNegative" will be matched as <see cref="Group.Success"/>.
/// </summary>
private static readonly Regex MatchDoubleMinMaxValuesRegex = new Regex(
    @"
        ^
        (?>(?<isNegative>-)|\+?)
        1
        (?>[,.]?)
        79769313486232
        (?>
            [eE]\+308|
            0{294}(?>[,.]|$)
        )
    ",
    RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace
);
#endregion

/// <summary>
/// Converts the string representation of a number in a specified culture-specific format to its double-precision floating-point number equivalent.
/// <para>This implementation is more tolerant compared to the native double.Parse implementation:
/// strings representing <see cref="double.MinValue"/> and <see cref="double.MaxValue"/> can be parsed without <see cref="OverflowException"/>.</para>
/// </summary>
/// <param name="s">A string that contains a number to convert.</param>
/// <param name="cultureInfo">For some type conversions optional culture information that shall be used to parse the value.
/// If not specified, then the Current Culture will be used.</param>
/// <param name="numberStyles">For some type conversions optional number style configuration that shall be used to parse the value.
/// If not specified, then the default will be used.</param>
/// <returns>A double-precision floating-point number that is equivalent to the numeric value or symbol specified in <paramref name="s"/>.</returns>
/// <exception cref="ArgumentNullException"><paramref name="s"/> is <c>null</c>.</exception>
/// <exception cref="FormatException"><paramref name="s"/> does not represent a number in a valid format.</exception>
/// <exception cref="OverflowException"><paramref name="s"/> represents a number that is less than <see cref="double.MinValue"/> or greater than <see cref="double.MaxValue"/>.</exception>
public static double ParseDoubleEx(string s, CultureInfo cultureInfo = null, NumberStyles? numberStyles = null)
{
    // Try parse
    double tempValue;
    bool parseSuccess = (numberStyles != null)
        ? double.TryParse(s, numberStyles.Value, cultureInfo, out tempValue)
        : double.TryParse(s, NumberStyles.Any, cultureInfo, out tempValue);

    // If parsing failed, check for Min or Max Value (by pattern)
    if (parseSuccess == false)
    {
        Match match = MatchDoubleMinMaxValuesRegex.Match(s);
        if (match.Success == true)
            tempValue = (match.Groups["isNegative"].Success == false)
                ? double.MaxValue
                : double.MinValue;
        else
            throw new OverflowException("A double-precision floating-point number that is equivalent to the numeric value or symbol specified in s.");
    }

    return tempValue;
}


回答7:

That number is too big for a double, as the exception says. You are going to have to find a large number library to handle that for you, as there is nothing that I know of in the .Net library that handles very large numbers.



标签: c# .net double