Comma Delimited String of Doubles to Array -- How to keep Precision

88 Views Asked by At

I am working with the JSON-C Library v0.11 to output JSON. I have a string of doubles in a char * that I need to output as an array.

I have: const char* doubles = "0.1234, 0.5678, 1.1234, 1.5678";

I want to output: "doubles", [0.1234, 0.5678, 1.1234, 1.5678]

My issue is that the JSON-C library when using json_object_new_double adds trailing zeroes and I want my doubles to be exact.

Is there a workaround to this?

char *token = strtok(doubles, ",");
while (token != NULL) {
    double current = atof(token);
    json_object_array_add(my_array, json_object_new_double(current));
    token = strtok(NULL, ",");
}

// Add the key-value pair as an array
json_object_object_add(root_obj, "my_array", my_array);
1

There are 1 best solutions below

1
On

The json-c library uses snprintf with a format "%.17g" to convert the double values as strings for output. This produces an accurate conversion such that any 2 different double values produce different output and converting back this output produces the original double value. Javascript interpreters use more sophisticated logic to produce minimal strings when converting numbers to strings. The library should use that too, but it seems the authors are not aware of the issue. They do however provide a hack to modify the conversion behavior: json_c_set_serialization_double_format.

There is no exact binary representation of 0.5678 as a double in the ubiquitous IEEE binary 64 bit format used in most systems. The closest double value in this format is 5114287736841935 * 2-53 whose exact decimal representation is 0.56779999999999997140065488565596751868724822998046875 (and 0x1.22b6ae7d566cfp-1 in hexadecimal floating point format). If you convert this value to decimal with 4 to 16 decimal places, the conversion produces 0.5678, but the library uses 17 places to ensure that all double values produce different output, so on your system, the string produced by the library is 0.56779999999999997, yet atof("0.56779999999999997") and atof("0.5678") should produce the same double value.

To avoid this problem, you would need to use decimal floats, which json-c does not seem to support or a way to specify the maximum number of decimals in the conversion to string operated by json-c, and the package does provide an API to specify a conversion format: json_c_set_serialization_double_format in the current version 0.17, much more recent than your old 0.11 version.

Here are my recommendations:

  • you should probably upgrade your version of json-c to a more recent release.
  • you could ignore the change in representation of the double values as they will convert back to the same double value upon reading in the destination system.
  • you could use json_c_set_serialization_double_format to specify a conversion format that produces the expected output.
  • you might consider a simpler package to handle the json format.