From d0f1ea00fc037d3936e80b30ba933f4d5ab415d0 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Sun, 24 Feb 2013 16:34:48 +0100 Subject: [PATCH 1/2] Encode floating point numbers as short as possible Floating point numbers are no longer encoded as a one to one mapping of their binary representation, but as short as possible (while still being acurate). The double-conversion library [1] is used to do the hard work. The ECMAScript compatible conversion is used. [1] https://code.google.com/p/double-conversion/ --- Makefile | 2 +- README.md | 2 +- c_src/c_api.cc | 31 +++++++++++++++++++++++++++++++ c_src/encoder.c | 3 +-- c_src/jiffy.h | 2 ++ rebar.config | 13 +++++++++++-- rebar.config.script | 13 ++++++++++--- test/003-numbers.t | 12 ++++++------ 8 files changed, 63 insertions(+), 15 deletions(-) create mode 100644 c_src/c_api.cc diff --git a/Makefile b/Makefile index 1aa9a1b..7227ce4 100644 --- a/Makefile +++ b/Makefile @@ -20,7 +20,7 @@ devmarker: depends: devmarker - @if test ! -d ./deps; then \ + @if test ! -d ./deps/proper; then \ $(REBAR) get-deps; \ fi 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 diff --git a/c_src/c_api.cc b/c_src/c_api.cc new file mode 100644 index 0000000..88d7323 --- /dev/null +++ b/c_src/c_api.cc @@ -0,0 +1,31 @@ +#include + +#include "double-conversion.h" + + +using namespace double_conversion; + + +#ifdef __cplusplus +extern "C" { +#endif + +// Returns the length of the string +int +double_to_shortest(char *buf, size_t size, double val) +{ + int len = -1; + + StringBuilder builder(buf, size); + const DoubleToStringConverter& dc = + DoubleToStringConverter::EcmaScriptConverter(); + + dc.ToShortest(val, &builder); + len = builder.position(); + buf = builder.Finalize(); + return len; +} + +#ifdef __cplusplus +} +#endif diff --git a/c_src/encoder.c b/c_src/encoder.c index 640aa68..ad35007 100644 --- a/c_src/encoder.c +++ b/c_src/encoder.c @@ -399,8 +399,7 @@ enc_double(Encoder* e, double val) start = &(e->p[e->i]); - sprintf(start, "%0.20g", val); - len = strlen(start); + len = double_to_shortest(start, e->curr->size, val); // Check if we have a decimal point for(i = 0; i < len; i++) { diff --git a/c_src/jiffy.h b/c_src/jiffy.h index c477a43..1fdce39 100644 --- a/c_src/jiffy.h +++ b/c_src/jiffy.h @@ -41,4 +41,6 @@ int unicode_to_utf8(int c, unsigned char* buf); int unicode_from_pair(int hi, int lo); int unicode_uescape(int c, char* buf); +int double_to_shortest(char *buf, size_t size, double val); + #endif // Included JIFFY_H diff --git a/rebar.config b/rebar.config index 18e62a0..1d7100a 100644 --- a/rebar.config +++ b/rebar.config @@ -1,7 +1,12 @@ -{port_specs, [{"priv/jiffy.so", ["c_src/*.c"]}]}. +{port_specs, [ + {"priv/jiffy.so", ["c_src/*.c*", "deps/double-conversion/src/*.cc"]} +]}. {port_env, [ - {".*", "CFLAGS", "$CFLAGS -g -Wall"}, + {".*", "CXXFLAGS", "$CXXFLAGS -g -Wall -Ideps/double-conversion/src"}, + + {"(linux|solaris|freebsd|netbsd|openbsd|dragonfly|darwin)", + "LDFLAGS", "$LDFLAGS -lstdc++"}, %% OS X Leopard flags for 64-bit {"darwin9.*-64$", "CXXFLAGS", "-m64"}, @@ -23,3 +28,7 @@ }} ]}. +{deps, [ + {'double-conversion', ".*", + {git, "https://code.google.com/p/double-conversion/", "master"}, [raw]} +]}. diff --git a/rebar.config.script b/rebar.config.script index 4b71f42..58d4ff7 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -11,16 +11,23 @@ ErlOpts = {erl_opts, [ {d, 'JIFFY_DEV'} ]}, -Deps = {deps, [ +Proper = [ {proper, ".*", {git, "git://github.com/manopapad/proper.git", "master"}} -]}, +], ConfigPath = filename:dirname(SCRIPT), DevMarker = filename:join([ConfigPath, ".jiffy.dev"]), case filelib:is_file(DevMarker) of true -> - CONFIG ++ [ErlOpts, Deps]; + % Don't override existing dependencies + NewConfig = case lists:keyfind(deps, 1, CONFIG) of + false -> + CONFIG ++ [{deps, Proper}]; + {deps, DepsList} -> + lists:keyreplace(deps, 1, CONFIG, {deps, DepsList ++ Proper}) + end, + NewConfig ++ [ErlOpts]; false -> CONFIG end. diff --git a/test/003-numbers.t b/test/003-numbers.t index f580faf..9196706 100755 --- a/test/003-numbers.t +++ b/test/003-numbers.t @@ -23,12 +23,12 @@ good() -> { <<"1234567890123456789012345.0">>, 1.23456789012345678e24, - <<"1.2345678901234568245e+24">> + <<"1.2345678901234568e+24">> }, { <<"1234567890123456789012345.0E3">>, 1.2345678901234569e27, - <<"1.2345678901234568502e+27">> + <<"1.2345678901234569e+27">> }, { <<"1234567890123456789012345012">>, @@ -39,12 +39,12 @@ good() -> { <<"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.0123456789, <<"2.0123456789">>}, + {<<"2.4234324E24">>, 2.4234324E24, <<"2.4234324e+24">>}, + {<<"-3.1416">>, -3.1416, <<"-3.1416">>}, {<<"1E4">>, 10000.0, <<"10000.0">>}, {<<"1.0E+01">>, 10.0, <<"10.0">>}, {<<"1e1">>, 10.0, <<"10.0">>}, From ba954631049c12e0a24e01a16f03b8f6fd907b51 Mon Sep 17 00:00:00 2001 From: Volker Mische Date: Sun, 24 Feb 2013 20:49:34 +0100 Subject: [PATCH 2/2] Use v1.1 of the double-conversion library Using a fixed version of the double-conversion library rather then master prevents accidental future breakage. --- rebar.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config b/rebar.config index 1d7100a..037e371 100644 --- a/rebar.config +++ b/rebar.config @@ -30,5 +30,5 @@ {deps, [ {'double-conversion', ".*", - {git, "https://code.google.com/p/double-conversion/", "master"}, [raw]} + {git, "https://code.google.com/p/double-conversion/", "v1.1"}, [raw]} ]}.