From b045a37391a2b2e2d674cc6967b16e57538dd786 Mon Sep 17 00:00:00 2001 From: Ryan Flynn Date: Sat, 3 Nov 2012 22:20:54 -0700 Subject: [PATCH 1/5] issue #31: encode floating point numbers with less precision if possible. fails a few etap tests, though I believe it's due to the specific values expected --- c_src/encoder.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/c_src/encoder.c b/c_src/encoder.c index ce71c1b..00a9dba 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "erl_nif.h" #include "jiffy.h" @@ -401,7 +402,12 @@ enc_double(Encoder* e, double val) start = &(e->p[e->i]); - sprintf(start, "%0.20g", val); + // try to encode doubles using the fewest digits possible... + if (snprintf(start, 32, "%.*g", DBL_DIG, val) > FLT_DIG) + { + // ...fall back to full expansion to be safe + snprintf(start, 32, "%.*g", LDBL_DIG, val); + } len = strlen(start); // Check if we have a decimal point From bc58d281c08ee47f87d40ff53b1138cd6611265d Mon Sep 17 00:00:00 2001 From: Ryan Flynn Date: Sat, 3 Nov 2012 22:47:16 -0700 Subject: [PATCH 2/5] base float buflen on float.h, not hardcoded assumptions --- c_src/encoder.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/c_src/encoder.c b/c_src/encoder.c index 00a9dba..297e2e8 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -11,6 +11,8 @@ #define BIN_INC_SIZE 2048 +#define FLOAT_BUFLEN (LDBL_DIG*2) + #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) #define MAYBE_PRETTY(e) \ @@ -396,17 +398,17 @@ enc_double(Encoder* e, double val) size_t len; size_t i; - if(!enc_ensure(e, 32)) { + if(!enc_ensure(e, FLOAT_BUFLEN)) { return 0; } start = &(e->p[e->i]); // try to encode doubles using the fewest digits possible... - if (snprintf(start, 32, "%.*g", DBL_DIG, val) > FLT_DIG) + if (snprintf(start, FLOAT_BUFLEN, "%.*g", DBL_DIG, val) > FLT_DIG) { // ...fall back to full expansion to be safe - snprintf(start, 32, "%.*g", LDBL_DIG, val); + snprintf(start, FLOAT_BUFLEN, "%.*g", LDBL_DIG, val); } len = strlen(start); @@ -416,7 +418,7 @@ enc_double(Encoder* e, double val) goto done; } - if(len > 29) return 0; + if(len >= FLOAT_BUFLEN-2) return 0; // Force a decimal point start[len++] = '.'; From 62c0a25f9b0ddbbc802e63d124be20405bd588fa Mon Sep 17 00:00:00 2001 From: Ryan Flynn Date: Sat, 3 Nov 2012 22:59:32 -0700 Subject: [PATCH 3/5] decouple float tests from a specific representation in some cases; test round-trip equality only --- test/003-numbers.t | 19 +++++++------------ test/util.erl | 4 ++++ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/test/003-numbers.t b/test/003-numbers.t index f580faf..916b43e 100755 --- a/test/003-numbers.t +++ b/test/003-numbers.t @@ -21,30 +21,25 @@ good() -> {<<"1234567890123456789012345">>, 1234567890123456789012345}, {<<"1310050760199">>, 1310050760199}, { - <<"1234567890123456789012345.0">>, - 1.23456789012345678e24, - <<"1.2345678901234568245e+24">> + 1.23456789012345678e24 }, { - <<"1234567890123456789012345.0E3">>, - 1.2345678901234569e27, - <<"1.2345678901234568502e+27">> + 1.2345678901234569e27 }, { <<"1234567890123456789012345012">>, - 1234567890123456789012345012, - <<"1234567890123456789012345012">> + 1234567890123456789012345012 }, {<<"1.0">>, 1.0}, { <<"0.000000000000000000000000000000000001">>, 1.0E-36, - <<"9.9999999999999994104e-37">> + <<"1e-36">> }, {<<"0.75">>, 0.75}, - {<<"2.0123456789">>, 2.0123456789, <<"2.0123456789000000455">>}, - {<<"2.4234324E24">>, 2.4234324E24, <<"2.4234323999999998107e+24">>}, - {<<"-3.1416">>, -3.1416, <<"-3.1415999999999999481">>}, + {2.0123456789}, + {2.4234324E24}, + {-3.1416}, {<<"1E4">>, 10000.0, <<"10000.0">>}, {<<"1.0E+01">>, 10.0, <<"10.0">>}, {<<"1e1">>, 10.0, <<"10.0">>}, diff --git a/test/util.erl b/test/util.erl index ceaef89..8c0e855 100644 --- a/test/util.erl +++ b/test/util.erl @@ -22,6 +22,10 @@ do_encode(E, Options) -> error_mesg(J) -> lists:flatten(io_lib:format("Decoding ~p returns an error.", [J])). +check_good({E}, Options) -> + J = jiffy:encode(E), + etap:is(jiffy:decode(J), E, ok_dec(J, E)), + etap:is(do_encode(E, Options), J, ok_enc(E, J)); check_good({J, E}, Options) -> etap:is(jiffy:decode(J), E, ok_dec(J, E)), etap:is(do_encode(E, Options), J, ok_enc(E, J)); From 642a6fb3e2e6ec61b7a2212d70edff77a01dc948 Mon Sep 17 00:00:00 2001 From: Ryan Flynn Date: Sun, 4 Nov 2012 14:59:52 -0800 Subject: [PATCH 4/5] simplify check_good --- test/util.erl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test/util.erl b/test/util.erl index 8c0e855..8c07762 100644 --- a/test/util.erl +++ b/test/util.erl @@ -23,9 +23,7 @@ error_mesg(J) -> lists:flatten(io_lib:format("Decoding ~p returns an error.", [J])). check_good({E}, Options) -> - J = jiffy:encode(E), - etap:is(jiffy:decode(J), E, ok_dec(J, E)), - etap:is(do_encode(E, Options), J, ok_enc(E, J)); + check_good({jiffy:encode(E), E}, Options); check_good({J, E}, Options) -> etap:is(jiffy:decode(J), E, ok_dec(J, E)), etap:is(do_encode(E, Options), J, ok_enc(E, J)); From aed0a5d6264cd074a4c2b33b1c23cbe2b895270d Mon Sep 17 00:00:00 2001 From: Ryan Flynn Date: Mon, 5 Nov 2012 19:29:04 -0800 Subject: [PATCH 5/5] update README to reflect shorter float serialization --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 36fc870..efb5300 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Errors are raised as exceptions. 2> Doc = {[{foo, [<<"bing">>, 2.3, true]}]}. {[{foo,[<<"bing">>,2.3,true]}]} 3> jiffy:encode(Doc). - <<"{\"foo\":[\"bing\",2.2999999999999998224,true]}">> + <<"{\"foo\":[\"bing\",2.3,true]}">> Data Format