Недавно мне пришлось сериализовать двойник в текст, а затем вернуть его обратно. Значение кажется не эквивалентным:
double d1 = 0.84551240822557006;
string s = d1.ToString("R");
double d2 = double.Parse(s);
bool s1 = d1 == d2;
// -> s1 is False
Но в соответствии с MSDN: Стандартные строки числового формата , опция «R» должна гарантировать безопасность в обоих направлениях.
Спецификатор формата туда-обратно ("R") используется для гарантии того, что числовое значение, преобразованное в строку, будет проанализировано обратно в то же числовое значение
Почему это случилось?
Ответы:
Я нашел ошибку.
.NET делает следующее в
clr\src\vm\comnumber.cpp
:DoubleToNumber
это довольно просто - это просто вызовы_ecvt
, которые находятся во время выполнения C:Оказывается,
_ecvt
возвращает строку845512408225570
.Заметьте конечный ноль? Оказывается, в этом вся разница!
Когда присутствует ноль, результат фактически анализируется обратно
0.84551240822557006
, который является вашим исходным числом - так что он сравнивается равным, и, следовательно, возвращаются только 15 цифр.Однако, если я усекаю строку с этим нулем до
84551240822557
, то я получаю обратно0.84551240822556994
, который не является вашим исходным числом, и, следовательно, он вернет 17 цифр.Доказательство: запустите следующий 64-разрядный код (большую часть которого я извлек из Microsoft Shared Source CLI 2.0) в своем отладчике и проверьте
v
в концеmain
:источник
+1
. Этот код из shared-source-cli-2.0, верно? Это единственная мысль, которую я нашел.Мне кажется, что это просто ошибка. Ваши ожидания вполне разумны. Я воспроизвел его с помощью .NET 4.5.1 (x64), запустив следующее консольное приложение, которое использует мой
DoubleConverter
класс.DoubleConverter.ToExactString
показывает точное значение, представленноеdouble
:Результаты в .NET:
Результаты в Mono 3.3.0:
Если вы вручную укажете строку из Mono (которая содержит «006» в конце), .NET проанализирует ее обратно к исходному значению. Похоже, проблема в
ToString("R")
обработке, а не в разборе.Как отмечалось в других комментариях, похоже, что это специфично для работы под x64 CLR. Если вы скомпилируете и запустите приведенный выше код для x86, это нормально:
... вы получаете те же результаты, что и с Mono. Было бы интересно узнать, обнаружена ли ошибка в RyuJIT - у меня она сейчас не установлена. В частности, я могу представить, что это, возможно , ошибка JIT, или вполне возможно, что существуют совершенно разные реализации внутренних компонентов,
double.ToString
основанных на архитектуре.Я предлагаю вам сообщить об ошибке на http://connect.microsoft.com
источник
ToString()
? Пока я пытался заменить жестко закодированное значение,rand.NextDouble()
проблем не было.ToString("R")
конверсии. ПопробуйтеToString("G32")
и обратите внимание, что печатает правильное значение.В последнее время я пытаюсь решить эту проблему . Как указано в коде , double.ToString ("R") имеет следующую логику:
В этом случае double.ToString ("R") ошибочно выбрал результат с точностью до 15, поэтому ошибка возникает. В документе MSDN есть официальный обходной путь:
Поэтому, если эта проблема не будет решена, вы должны использовать double.ToString ("G17") для циклического отключения.
Обновление : теперь есть конкретная проблема, чтобы отследить эту ошибку.
источник