Почему Double.TryParse() возвращает false для строки, содержащей double.MaxValue или double.MinValue?
У меня есть статический метод, который берет строку для ввода и возвращает исходную строку ввода, если строка представляет число. Если строка не представляет число, входная строка обрабатывается и возвращается преобразованная строка. Я пишу тестовые примеры. Я пытаюсь проверить, что строка ввода, содержащая либо double.MinValue
, либо double.MaxValue
, возвращается без изменений. Я прочитал несколько форумов, включая StackOverflow, и придумал следующую логику:
string doubleMax = double.MaxValue.ToString();
double d;
CultureInfo cultureInfo = new CultureInfo("en-US", true);
if (Double.TryParse(doubleMax, NumberStyles.Any, cultureInfo.NumberFormat, out d))
{
Console.WriteLine("parsed");
}
else
{
Console.WriteLine("couldn't parse");
}
Проблема: Double.TryParse()
всегда возвращает false. Я вызвал TryParse()
по-разному, но результат всегда один и тот же, false.
Эта логика работает, если я использую decimal.MinValue()
, int.MinValue()
или float.MinValue()
.
Может кто-нибудь сказать мне, почему моя логика не работает для double.MinValue
?
Ответы
Ответ 1
Это из-за того, как хранятся и отображаются номера с плавающей запятой. Значение округляется, когда оно превращается в читаемую пользователем строку, а для double.MaxValue
оно округляется, так что оно больше не подходит для double
.
Вы можете использовать формат "R" в обратном направлении, чтобы превратить значение в строку, которая всегда может быть проанализирована с тем же значением, поскольку она добавляет дополнительную точность до тех пор, пока не вернется правильное значение. Кроме того, используйте ту же культуру при форматировании числа в качестве синтаксического анализа, чтобы избежать различий в культуре:
CultureInfo cultureInfo = new CultureInfo("en-US", true);
string doubleMax = Double.MaxValue.ToString("R", cultureInfo);
Console.WriteLine(doubleMax);
double d;
if (Double.TryParse(doubleMax, NumberStyles.Any, cultureInfo.NumberFormat, out d)) {
if (d == Double.MaxValue) {
Console.WriteLine("parsed");
} else {
Console.WriteLine("value changed");
}
} else {
Console.WriteLine("couldn't parse");
}
Вывод:
1.7976931348623157E+308
parsed
Edit:
Я добавил вывод строки и проверил, что анализируемое значение на самом деле все еще является MaxValue.
Ответ 2
В вашей логике нет ничего плохого, это просто ограничение API Double.TryParse
. Из документации
Обычно, если вы передаете метод Double.TryParse строку, созданную вызовом метода Double.ToString, возвращается исходное двойное значение. Однако из-за потери точности значения могут быть не равными. Кроме того, попытка проанализировать строковое представление MinValue или MaxValue вызывает исключение OverflowException, как показано в следующем примере.
Документация, по-видимому, взята из метода Parse
и применяется к TryParse
, поскольку TryParse
не выбрасывает. Но пример кода, следующий за pargraph, использует TryParse
и ожидает, что он потерпит неудачу для MaxValue
и MinValue
Ответ 3
Я заинтересовался этим вопросом и обнаружил, что если вы добавите E20 в ToString, тогда он будет работать.
double.MaxValue.ToString("E20")
Интересный вопрос.
UPDATE:
Я тестирую равенство между d (разобранным двойным) и double.MaxValue, и они равны.
var abc = Double.Parse(double.MaxValue.ToString("E20"));
abc.Dump();
double d;
CultureInfo cultureInfo = new CultureInfo("en-US", true);
if (Double.TryParse(double.MaxValue.ToString("E20"), NumberStyles.Any, cultureInfo.NumberFormat, out d)) Console.WriteLine("parsed");
else Console.WriteLine("couldn't parse");
if(d == double.MaxValue) Console.WriteLine("parsed value is equal to MaxValue");
else Console.WriteLine("parsed value is NOT equal to MaxValue");
Ответ 4
Проблема заключается в округлении метода ToString(). Фактический MaxValue в соответствии с документацией составляет 1.7976931348623157E + 308, но ToString() этого дает вам 1,79769313486232E + 308, который округлен и, следовательно, слишком велик для синтаксического анализа.