Browse Source

Reduce floating point errors in decoding

kusano 3 years ago
parent
commit
c32652d32a
2 changed files with 7 additions and 2 deletions
  1. 5 1
      src/jsone_decode.erl
  2. 2 1
      test/jsone_decode_tests.erl

+ 5 - 1
src/jsone_decode.erl

@@ -283,7 +283,11 @@ number_exponation_part(<<C, Bin/binary>>, N, DecimalOffset, ExpSign, Exp, _, Nex
     number_exponation_part(Bin, N, DecimalOffset, ExpSign, Exp * 10 + C - $0, false, Nexts, Buf, Opt);
 number_exponation_part(<<Bin/binary>>, N, DecimalOffset, ExpSign, Exp, false, Nexts, Buf, Opt) ->
     Pos = ExpSign * Exp - DecimalOffset,
-    try N * math:pow(10, Pos)
+    try
+        case Pos of
+            Pos when Pos >= 0 -> N * math:pow(10, Pos);
+            _                 -> N / math:pow(10, -Pos) % multiplying by decimal makes float errors larger.
+        end
     of Res -> next(Bin, Res, Nexts, Buf, Opt)
     catch error:badarith ->
         ?ERROR(number_exponation_part, [Bin, N, DecimalOffset, ExpSign, Exp, false, Nexts, Buf, Opt])

+ 2 - 1
test/jsone_decode_tests.erl

@@ -77,7 +77,8 @@ decode_test_() ->
               ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345E2">>)),
               ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345e+2">>)), % exponent part can begin with plus sign
               ?assertEqual({ok, 12.345, <<"">>}, jsone_decode:decode(<<"0.12345E+2">>)),
-              ?assertEqual({ok, -12.345, <<"">>}, jsone_decode:decode(<<"-0.012345e3">>))
+              ?assertEqual({ok, -12.345, <<"">>}, jsone_decode:decode(<<"-0.012345e3">>)),
+              ?assertEqual({ok, 123.0, <<"">>}, jsone_decode:decode(<<"1.23000000000000000000e+02">>))
       end},
      {"float: invalid format",
       fun () ->