From a105c27b4e24ac0d29ba131eca00793bc3385dca Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Wed, 16 Dec 2009 10:55:46 -0800 Subject: [PATCH] json: Accurately parse very large real numbers. The test for whether a real number was outside the valid range was imprecise and failed at the edge of the real range. This commit changes the code to use the C library's strtod(), which presumably does better. --- lib/json.c | 23 ++++++++++------------- tests/json.at | 8 ++++++++ 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/json.c b/lib/json.c index d162fd51..6842c8ba 100644 --- a/lib/json.c +++ b/lib/json.c @@ -606,6 +606,7 @@ json_lex_number(struct json_parser *p) { const char *cp = ds_cstr(&p->buffer); unsigned long long int significand = 0; + struct json_token token; int sig_digits = 0; bool imprecise = false; bool negative = false; @@ -730,7 +731,6 @@ json_lex_number(struct json_parser *p) && significand <= (negative ? (unsigned long long int) LLONG_MAX + 1 : LLONG_MAX)) { - struct json_token token; token.type = T_INTEGER; token.u.integer = negative ? -significand : significand; json_parser_input(p, &token); @@ -738,19 +738,16 @@ json_lex_number(struct json_parser *p) } } - if (pow10 + sig_digits <= DBL_MAX_10_EXP) { - struct json_token token; - token.type = T_REAL; - token.u.real = significand * pow(10.0, pow10); - if (token.u.real <= DBL_MAX) { - if (negative && token.u.real) { - token.u.real = -token.u.real; - } - json_parser_input(p, &token); - return; - } + token.type = T_REAL; + if (!str_to_double(ds_cstr(&p->buffer), &token.u.real)) { + json_error(p, "number outside valid range"); + return; } - json_error(p, "number outside valid range"); + /* Suppress negative zero. */ + if (token.u.real == 0) { + token.u.real = 0; + } + json_parser_input(p, &token); } static bool diff --git a/tests/json.at b/tests/json.at index 3096b264..371bf2d2 100644 --- a/tests/json.at +++ b/tests/json.at @@ -158,6 +158,14 @@ JSON_CHECK_POSITIVE( [scientific notation], [[[1e3, 1E3, 2.5E2, 1e+3, 125e-3, 3.125e-2, 3125e-05, 1.525878906e-5]]], [[[1000,1000,250,1000,0.125,0.03125,0.03125,1.525878906e-05]]]) +# It seems likely that the following test will fail on some system that +# rounds slightly differently in arithmetic or in printf, but I'd like +# to keep it this way until we run into such a system. +JSON_CHECK_POSITIVE( + [+/- DBL_MAX], + [[[1.7976931348623157e+308, -1.7976931348623157e+308]]], + [[[1.79769313486232e+308,-1.79769313486232e+308]]]) + JSON_CHECK_POSITIVE( [negative reals], [[[-0, -1.0, -2.0, -3.0, -3.5, -8.1250]]], -- 2.30.2