Ответ 1
Заключение
Спецификация Java требует сложного двойного округления в этой ситуации. Число 0,6446968749999999470645661858725361526012420654296875 сначала преобразуется в 0,644696875, а затем округляется до 0,64469688.
Напротив, реализация C просто округляет 0,6446968749999999470645661858725361526012420654296875 до восьми цифр, что дает 0,64469687.
Отборочные
Для Double
Java использует базовый 64-разрядный двоичный код с плавающей точкой IEEE-754. В этом формате значение, ближайшее к числу в исходном тексте, 0,644696875, равно 0,6446968749999999470645661858725361526012420654296875, и я считаю, что это фактическое значение, которое должно быть отформатировано с помощью String.format("%10.8f",0.644696875)
. 1
Что говорит спецификация Java
Документация для форматирования с типом Double
и f
гласит:
… Если точность меньше количества цифр, которое должно появиться после десятичной точки в строке, возвращаемой
Float.toString(float)
илиDouble.toString(double)
соответственно, то значение будет округлено с использованием алгоритма округления до половины. В противном случае для достижения точности можно добавить нули...
Давайте рассмотрим "строку, возвращаемую… Double.toString(double)
". Для номера 0,6446968749999999470645661858725361526012420654296875 эта строка равна 0,644696875. Это связано с тем, что в спецификации Java говорится, что toString
выдает достаточно десятичных цифр, чтобы однозначно различать число в наборе значений Double
, а в "0,644696875" достаточно только цифр в этом случае. 2
Это число имеет девять цифр после десятичной точки, и "%10.8f"
запрашивает восемь, поэтому в приведенном выше отрывке говорится, что "значение" округлено. Какое значение это означает - фактический операнд format
, который равен 0,6446968749999999470645661858725361526012420654296875, или та строка, которую он упоминает, "0,644696875"? Поскольку последнее не является числовым значением, я ожидал бы, что "значение" будет означать первое. Тем не менее, второе предложение говорит: "В противном случае [то есть, если запрашивается больше цифр], могут добавляться нули…" Если бы мы использовали фактический операнд из format
, мы бы показывали его цифры, а не использовали нули. Но если мы возьмем строку в качестве числового значения, ее десятичное представление будет иметь только нули после цифр, показанных в ней. Так что, похоже, это и есть толкование, и реализации Java, похоже, соответствуют этому.
Итак, чтобы отформатировать это число с помощью "%10.8f"
, мы сначала конвертируем его в 0,644696875, а затем округляем его, используя правило округления до половины, что дает 0,64469688.
Это плохая спецификация, потому что:
- Требуется два округления, что может увеличить ошибку.
- Округления происходят в трудно предсказуемых и трудно контролируемых местах. Некоторые значения будут округлены после двух десятичных знаков. Некоторые из них будут округлены после 13. Программа не может легко предсказать это или скорректировать его.
(Также жаль, что они написали, что нули "могут быть" добавлены. Почему бы и нет? В противном случае нули добавляются для достижения точности "?" С "может", кажется, что они дают реализации выбор, хотя я подозреваю, что они имели в виду, что "май" основан на том, нужны ли нули для достижения точности, а не на том, решит ли разработчик добавить их.)
Сноска
1 Когда 0.644696875
в исходном тексте преобразуется в Double
, я считаю, что результатом должно быть ближайшее значение, представляемое в формате Double
. (Я не нашел этого в документации по Java, но она соответствует философии Java, согласно которой реализация должна вести себя одинаково, и я подозреваю, что преобразование выполняется в соответствии с Double.valueOf(String s)
, который требует этого.) Ближайший Double
к 0,644696875 - 0,6446968749999999470645661858725361526012420654296875.
2 При меньшем количестве цифр 0,64469687 из семи цифр недостаточно, поскольку ближайшее к нему значение Double
составляет 0,6446968 699999999774519210404832847416400909423828125. Таким образом, для однозначного различения 0,6446968 749999999470645661858725361526012420654296875 требуется, что необходимо из восьми цифр.