json: Accurately parse very large real numbers.
authorBen Pfaff <blp@nicira.com>
Wed, 16 Dec 2009 18:55:46 +0000 (10:55 -0800)
committerBen Pfaff <blp@nicira.com>
Wed, 16 Dec 2009 18:56:04 +0000 (10:56 -0800)
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
tests/json.at

index d162fd5143bfc9e8cd3994007655c46ccca989f8..6842c8baaf6b7f3d14b9f35c03f886473680620a 100644 (file)
@@ -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
index 3096b26439a3b7eaf948e6707f098fe0709db13c..371bf2d2512ef6b8a02395d92c39c0745f38bb91 100644 (file)
@@ -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]]],