0

代码在这里:

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
}

浮点数精度改变了!我该如何解决这个问题?

4

2 回答 2

2

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

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

事实证明,rfc4627通过调用 对浮点值进行编码float_to_list/1,它使用具有 20 位精度的“科学”表示法。 正如 Per Hedeland 在 2007 年 11 月的 erlang-questions 邮件列表中指出的那样,这是一个奇怪的选择:

一个合理的问题可能是为什么 float_to_list/1 在内部使用的 64 位浮点数(又名 C 双精度)只能容纳 15-16 个数字时生成 20 位数字 - 我不知道副手是什么一个 128 位的浮点数会有,但可能远远超过 20,所以也不是这样。我猜想早在黑暗时代,有人认为 20 是一个不错的偶数(我希望不是我:-)。6.30000 形式当然只是 ~p/~w 格式。


然而,事实证明这实际上不是问题!事实上,45.990000000000002等于45.99,所以你确实在前端得到了正确的值:

> 45.990000000000002 =:= 45.99.
true

如上所述,64 位浮点数可以容纳 15 或 16 位有效数字,但45.990000000000002包含 17 位数字(数一数!)。看起来您的前端尝试以比实际包含的精度更高的精度打印数字,从而使数字看起来不同,即使它实际上是相同的数字。


问题的答案是浮点数学坏了吗?考虑到计算机如何处理浮点值,更详细地了解为什么这实际上是有意义的。

于 2017-02-20T10:16:38.710 回答
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).

问题解决了。

于 2017-02-21T10:22:40.220 回答