Encode reals correctly
authorPetri Lehtinen <petri@digip.org>
Thu, 17 Dec 2009 21:42:13 +0000 (23:42 +0200)
committerPetri Lehtinen <petri@digip.org>
Thu, 17 Dec 2009 22:05:06 +0000 (00:05 +0200)
This patch changes the sprintf format from "%0.17f" to "%.17g", as the
f format specifier doesn't print the exponent at all. This caused
losing precision in all but the most simple cases.

Because the g specifier doesn't print the decimal fraction or exponent
if they're not needed, a ".0" has to be appended by hand in these
cases. Otherwise the value's type changes from real to integer when
decoding again.

Thanks to Philip Grandinetti for reporting this issue.

src/dump.c

index 7dbc9f2..bd12a7b 100644 (file)
@@ -145,10 +145,25 @@ static int do_dump(const json_t *json, unsigned long flags, int depth,
             char buffer[MAX_REAL_STR_LENGTH];
             int size;
 
-            size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%0.17f", json_real_value(json));
+            size = snprintf(buffer, MAX_REAL_STR_LENGTH, "%.17g",
+                            json_real_value(json));
             if(size >= MAX_REAL_STR_LENGTH)
                 return -1;
 
+            /* Make sure there's a dot or 'e' in the output. Otherwise
+               a real is converted to an integer when decoding */
+            if(strchr(buffer, '.') == NULL &&
+               strchr(buffer, 'e') == NULL)
+            {
+                if(size + 2 >= MAX_REAL_STR_LENGTH) {
+                    /* No space to append ".0" */
+                    return -1;
+                }
+                buffer[size] = '.';
+                buffer[size + 1] = '0';
+                size += 2;
+            }
+
             return dump(buffer, size, data);
         }