diff --git a/json_object.c b/json_object.c index 4d6e2e7..52e8b76 100644 --- a/json_object.c +++ b/json_object.c @@ -15,6 +15,7 @@ #include "strerror_override.h" #include +#include #include #include #include @@ -790,7 +791,6 @@ static int json_object_double_to_json_string_format(struct json_object* jso, { char buf[128], *p, *q; int size; - double dummy; /* needed for modf() */ /* Although JSON RFC does not support NaN or Infinity as numeric values ECMA 262 section 9.8.1 defines @@ -810,44 +810,60 @@ static int json_object_double_to_json_string_format(struct json_object* jso, { const char *std_format = "%.17g"; + if (!format) + { #if defined(HAVE___THREAD) - if (tls_serialization_float_format) - std_format = tls_serialization_float_format; - else + if (tls_serialization_float_format) + format = tls_serialization_float_format; + else #endif - if (global_serialization_float_format) - std_format = global_serialization_float_format; - if (!format) - format = std_format; + if (global_serialization_float_format) + format = global_serialization_float_format; + else + format = std_format; + } size = snprintf(buf, sizeof(buf), format, jso->o.c_double); - if (modf(jso->o.c_double, &dummy) == 0 && size >= 0 && size < (int)sizeof(buf) - 2) + + if (size < 0) + return -1; + + p = strchr(buf, ','); + if (p) + *p = '.'; + else + p = strchr(buf, '.'); + + int format_drops_decimals = 0; + if (format == std_format || strstr(format, ".0f") == NULL) + format_drops_decimals = 1; + + if (size < (int)sizeof(buf) - 2 && + isdigit((int)buf[0]) && /* Looks like *some* kind of number */ + !p && /* Has no decimal point */ + strchr(buf, 'e') == NULL && /* Not scientific notation */ + format_drops_decimals) { - // Ensure it looks like a float, even if snprintf didn't. + // Ensure it looks like a float, even if snprintf didn't, + // unless a custom format is set to omit the decimal. strcat(buf, ".0"); size += 2; } + if (p && (flags & JSON_C_TO_STRING_NOZERO)) + { + /* last useful digit, always keep 1 zero */ + p++; + for (q=p ; *q ; q++) { + if (*q!='0') p=q; + } + /* drop trailing zeroes */ + *(++p) = 0; + size = p-buf; + } } // although unlikely, snprintf can fail if (size < 0) return -1; - p = strchr(buf, ','); - if (p) - *p = '.'; - else - p = strchr(buf, '.'); - if (p && (flags & JSON_C_TO_STRING_NOZERO)) - { - /* last useful digit, always keep 1 zero */ - p++; - for (q=p ; *q ; q++) { - if (*q!='0') p=q; - } - /* drop trailing zeroes */ - *(++p) = 0; - size = p-buf; - } - if (size >= (int)sizeof(buf)) // The standard formats are guaranteed not to overrun the buffer, // but if a custom one happens to do so, just silently truncate. diff --git a/tests/test_double_serializer.c b/tests/test_double_serializer.c index c7b229e..0f7a60e 100644 --- a/tests/test_double_serializer.c +++ b/tests/test_double_serializer.c @@ -54,4 +54,24 @@ int main() printf("obj.to_string(back to default format)=%s\n", json_object_to_json_string(obj)); json_object_put(obj); + + obj = json_object_new_double(12.0); + printf("obj(12.0).to_string(default format)=%s\n", json_object_to_json_string(obj)); + if (json_c_set_serialization_double_format("%.0f", JSON_C_OPTION_GLOBAL) < 0) + printf("ERROR: json_c_set_serialization_double_format() failed"); + printf("obj(12.0).to_string(%%.0f)=%s\n", json_object_to_json_string(obj)); + + if (json_c_set_serialization_double_format("%.0g", JSON_C_OPTION_GLOBAL) < 0) + printf("ERROR: json_c_set_serialization_double_format() failed"); + printf("obj(12.0).to_string(%%.0g)=%s\n", json_object_to_json_string(obj)); + + if (json_c_set_serialization_double_format("%.2g", JSON_C_OPTION_GLOBAL) < 0) + printf("ERROR: json_c_set_serialization_double_format() failed"); + printf("obj(12.0).to_string(%%.1g)=%s\n", json_object_to_json_string(obj)); + + // Reset to default to free memory + if (json_c_set_serialization_double_format(NULL, JSON_C_OPTION_GLOBAL) < 0) + printf("ERROR: json_c_set_serialization_double_format() failed"); + + json_object_put(obj); } diff --git a/tests/test_double_serializer.expected b/tests/test_double_serializer.expected index cd5f02b..98eea1e 100644 --- a/tests/test_double_serializer.expected +++ b/tests/test_double_serializer.expected @@ -12,3 +12,7 @@ obj.to_string(with thread format)=T0.52X obj.to_string(long thread format)=Ttttttttttttt0.52xxxxxxxxxxxxxxxxxxX obj.to_string(back to global format)=x0.524y obj.to_string(back to default format)=0.52381 +obj(12.0).to_string(default format)=12.0 +obj(12.0).to_string(%.0f)=12 +obj(12.0).to_string(%.0g)=1e+01 +obj(12.0).to_string(%.1g)=12.0