Давайте посмотрим на JSON, который генерирует rfc4627
:
> io:format("~s~n", [rfc4627:encode({obj, [{"age", 45.99}]})]).
{"age":4.59900000000000019895e+01}
Оказывается, rfc4627
кодирует значения с плавающей запятой, вызывая float_to_list/1
, который использует «научную» запись с точностью до 20 цифр. Как отметил Пер Хеделанд в списке рассылки erlang-questions в ноябре 2007 г., это странный выбор:
Разумный вопрос может заключаться в том, почему float_to_list/1 генерирует 20 цифр, когда 64-битное число с плавающей запятой (также известное как C double), которое используется внутри, может содержать только 15-16 из них - я не знаю, что навскидку у 128-битного числа с плавающей запятой было бы, но предположительно значительно больше 20, так что это тоже не то. Думаю, в далеком средневековье кто-то думал, что 20 — красивое и четное число (надеюсь, это был не я :-). Форма 6.30000 — это, конечно, просто форматирование ~p/~w.
Однако оказывается, что это на самом деле не проблема! На самом деле 45.990000000000002
равно 45.99
, поэтому вы действительно получаете правильное значение во внешнем интерфейсе:
> 45.990000000000002 =:= 45.99.
true
Как отмечалось выше, 64-битное число с плавающей запятой может содержать 15 или 16 значащих цифр, но 45.990000000000002
содержит 17 цифр (сосчитайте их!). Похоже, что ваш внешний интерфейс пытается напечатать число с большей точностью, чем оно есть на самом деле, в результате чего число выглядит по-другому, хотя на самом деле это одно и то же число.
Ответы на вопрос Разрушена ли математика с плавающей запятой? содержат гораздо более подробные сведения о том, почему это на самом деле имеет смысл, учитывая, как компьютеры обрабатывать значения с плавающей запятой.
person
legoscia
schedule
20.02.2017