Erlang ковбой ответ json данные, точность числа с плавающей запятой неверна?

код здесь:

RstJson = rfc4627:encode({obj, [{"age", 45.99}]}),
{ok, Req3} = cowboy_req:reply(200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], RstJson, Req2)

то я получаю эти неправильные данные от фронт-клиента:

{
  "age": 45.990000000000002
}

точность числа с плавающей запятой изменена! как я могу решить эту проблему?


person Ensk    schedule 20.02.2017    source источник
comment
Не могли бы вы опубликовать код на клиенте, который получает эти данные? Я бы рискнул, что это как-то связано с JavaScript.   -  person Asier Azkuenaga    schedule 20.02.2017


Ответы (2)


Давайте посмотрим на 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

функция кодирования чисел с плавающей запятой в rfc4627:

encode_number(Num, Acc) when is_float(Num) ->
  lists:reverse(float_to_list(Num), Acc).

Я изменил это так:

encode_number(Num, Acc) when is_float(Num) ->
  lists:reverse(io_lib:format("~p",[Num]), Acc).

Проблема решена.

person Ensk    schedule 21.02.2017