Strange precision issue with Jansson (and non-Jansson) real numbers

30 views
Skip to first unread message

Jeff on Gmail

unread,
Oct 24, 2022, 8:53:28 PM10/24/22
to Jansson users
I'm running Jansson 14 on macOS 16.0 with Xcode 14.0 on an x86 Mac. I've run into a bizarre issue for the json_dump*() functions where some simple real numbers aren't "rounded" correctly on output. Most numbers are fine. For example 1963.3 is indeed displayed as "1963.3". However, there are a few strange ones. For example, 1963.4 is dumped as "1963.4000000000001".

I snooped around the Jansson source and found the strconv.c file with the jsonp_dtostr() function. I did not know about the "%.*g" format available available for the *printf(3) standard library functions that allow a maximum precision to be specified with floating point numbers. That's kind of cool! The code agrees with the Jansson encoding documentation with the JSON_REAL_PRECISION(n) flag. The default precision is 17 which apparently agrees with the IEEE 754 specification. I glanced at IEEE 754, but it was mostly over my head!

Anyway, for my project, I can simply specify a precision of 16 and get the output I expect. Outside of Jansson, I can use long double instead of double to avoid the issue.

I'm curious if anyone else has seen something like this. I don't think this a Jansson bug. I'm wondering if this could be an OS or compiler or standard library or IEEE 754 issue. I've been too lazy to try this on another platform! Here's a simple program to demonstrate the issue both with and without Jansson:

#include <stdio.h>
#include <string.h>

#include <jansson.h>

int
main(int argc, char **argv)
{
const long double ldvalue = 1963.4L;
const double dvalue =1963.4;
const double dvaluex = 1963.3;
json_t *js_obj = json_object();
json_object_set_new(js_obj, "value", json_real(dvalue));
printf("json_dumpf with value %0.1lf with default precision 17\n ", dvalue);
json_dumpf(js_obj, stdout, 0);
printf("\n");
printf("json_dumpf with value %0.1lf with precision 16\n ", dvalue);
json_dumpf(js_obj, stdout, JSON_REAL_PRECISION(16));
printf("\n");
printf("printf of double value %0.1lf with precision 17\n %.*g\n", dvalue, 17, dvalue);
printf("printf of double value %0.1lf with precision 16\n %.*g\n", dvalue, 16, dvalue);
printf("printf of long double value %0.1Lf with precision 17\n %.*Lg\n", ldvalue, 17, ldvalue);
json_object_set_new(js_obj, "valuex", json_real(dvaluex));
printf("json_dumpf with valuex %0.1lf with default precision 17\n ", dvaluex);
json_dumpf(js_obj, stdout, 0);
printf("\n");
exit(0);
}

And here's the output:

json_dumpf with value 1963.4 with default precision 17
{"value": 1963.4000000000001}
json_dumpf with value 1963.4 with precision 16
{"value": 1963.4}
printf of double value 1963.4 with precision 17
1963.4000000000001
printf of double value 1963.4 with precision 16
1963.4
printf of long double value 1963.4 with precision 17
1963.4
json_dumpf with valuex 1963.3 with default precision 17
{"value": 1963.4000000000001, "valuex": 1963.3}

Anyone want to try it with your favorite platform?

Jeff

Reply all
Reply to author
Forward
0 new messages