Erlang牛仔回复json数据,浮点数精度是错误的?

时间:2017-02-20 01:43:34

标签: json erlang cowboy rfc4627

代码在这里:

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
}

浮点数精度改变了! 我怎么能解决这个问题?

2 个答案:

答案 0 :(得分:2)

让我们看一下rfc4627生成的JSON:

> io:format("~s~n", [rfc4627:encode({obj, [{"age", 45.99}]})]).
{"age":4.59900000000000019895e+01}

事实证明rfc4627通过调用float_to_list/1来编码浮点值,As Per Hedeland noted on the erlang-questions mailing list in November 2007使用20位精度的“科学”符号。 Is floating point math broken?,这是一个奇怪的选择:

  

一个合理的问题可能是float_to_list / 1生成20位数的原因   当一个64位浮点数(a.k.a。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位数(计算它们!)。看起来您的前端尝试以比实际包含的更精确的方式打印数字,从而使得看起来的数量不同,即使它实际上是相同的数字。

问题{{3}}的答案更详细地说明了为什么这实际上有意义,考虑到计算机如何处理浮点值。

答案 1 :(得分:0)

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).

解决了问题。