code is here :
RstJson = rfc4627:encode({obj, [{"age", 45.99}]}),
{ok, Req3} = cowboy_req:reply(200, [{<<"Content-Type">>, <<"application/json;charset=UTF-8">>}], RstJson, Req2)
then I get this wrong data from front client:
{
"age": 45.990000000000002
}
the float number precision is changed ! how can I solved this problem?
Let's have a look at the JSON that rfc4627
generates:
> io:format("~s~n", [rfc4627:encode({obj, [{"age", 45.99}]})]).
{"age":4.59900000000000019895e+01}
It turns out that rfc4627
encodes floating-point values by calling float_to_list/1
, which uses "scientific" notation with 20 digits of precision. As Per Hedeland noted on the erlang-questions mailing list in November 2007, that's an odd choice:
A reasonable question could be why float_to_list/1 generates 20 digits when a 64-bit float (a.k.a. C double), which is what is used internally, only can hold 15-16 worth of them - I don't know off-hand what a 128-bit float would have, but presumably significantly more than 20, so it's not that either. I guess way back in the dark ages, someone thought that 20 was a nice and even number (I hope it wasn't me:-). The 6.30000 form is of course just the ~p/~w formatting.
However, it turns out this is actually not the problem! In fact, 45.990000000000002
is equal to 45.99
, so you do get the correct value in the front end:
> 45.990000000000002 =:= 45.99.
true
As noted above, a 64-bit float can hold 15 or 16 significant digits, but 45.990000000000002
contains 17 digits (count them!). It looks like your front end tries to print the number with more precision than it actually contains, thereby making the number look different even though it is in fact the same number.
The answers to the question Is floating point math broken? go into much more detail about why this actually makes sense, given how computers handle floating point values.