Преобразовать “1.79769313486232E+308” в double без исключения OverflowException?
Вопрос
У меня есть эта строка "1.79769313486232E + 308", и я пытаюсь преобразовать ее в числовое значение .NET (double?), но получаю приведенное ниже исключение.Я использую Convert.ToDouble()
.Как правильно выполнить это преобразование?
Исключение OverflowException:Значение было либо слишком большим, либо слишком маленьким для Двойного
Решение
Проблема, скорее всего, связана с тем, что Double.MaxValue
был преобразован в строку, и когда строка выводится, выводятся не все цифры, вместо этого они округляются.Синтаксический анализ этого значения приводит к переполнению double.
Используя Double.TryParse
и впоследствии проверяет равенство в строке "1.79769313486232E +308" в случае сбоя и заменяет Double.MaxValue
должно быть быстрое решение проблемы, если вам нужно сохранить строку такой, какая она есть.
Редактировать:Конечно, если вам не нужно сохранять строку такой, какая она есть, используйте Спецификатор формата поездки туда и обратно чтобы создать строку в первую очередь, как Джон описывает в своем ответе.
Другие советы
К сожалению, это значение больше, чем double.MaxValue
, отсюда и исключение.
Как предлагает codekaizen, вы могли бы жестко запрограммировать тест для строки.Лучшая альтернатива (IMO), если вы тот самый производящий строка в первую очередь предназначена для использования спецификатора формата "r".Тогда вместо этого вы создадите строку "1.7976931348623157E + 308", которая затем будет правильно проанализирована:
string s = double.MaxValue.ToString("r");
double d = double.Parse(s); // No exception
Очевидно, что это не поможет, если у вас нет контроля над данными, но тогда вы должны понимать, что в этом случае вы, скорее всего, уже потеряете данные.
Вы можете попробовать double.Parse()
или double.TryParse()
вместо того , чтобы Convert.ToDouble()
, но я не уверен, что вы добьетесь лучших результатов.Кстати, строка, которую вы предоставляете, равна double.MaxValue
, что является (конечно) максимальным значением, которое может содержаться в double , так что, скорее всего, именно отсюда возникает ваша ошибка.Числовые типы с плавающей запятой привередливы, поэтому я бы предположил, что происходит какое-то округление и вывод его за пределы типа.
Вы также могли бы попробовать decimal
тип данных.Возможно, там вам повезет больше.
Вот что у меня получилось.Спасибо Джону Скиту и 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;
}
Демонстрирует проблему и ее решение:
var s = double.MaxValue.ToString();
double d;
if (!double.TryParse(s, out d)) {
d = s.Equals(double.MaxValue) ? double.MaxValue : double.MinValue;
}
Здесь представлена более общая реализация, представляющая различное форматирование и культуры и более терпимая:
#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;
}
Это число слишком велико для удвоения, как указано в исключении.Вам нужно будет найти библиотеку больших чисел, чтобы справиться с этим за вас, поскольку я ничего не знаю о библиотеке .Net, которая обрабатывает очень большие числа.