Подтвердить что ты не робот

Почему 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?

4b9b3361

Ответ 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, который округлен и, следовательно, слишком велик для синтаксического анализа.