From 7c209d736907e18ec8d79a57328c1e3bad7b2786 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Mon, 9 Oct 2023 15:21:05 +0200 Subject: [PATCH] types: ensure double serializatiion with decimal places The `%g` printf format used for serializing double values into strings will not include any decimal place if the value happens to be integral, leading to an unwanted double to integer conversion when serializing and subsequently deserializing an integral double value as JSON. Solve this issue by checking the serialized string result for a decimal point or exponential notation and appending `.0` if neither is found. Ref: #173 Suggested-by: Felix Fietkau Signed-off-by: Jo-Philipp Wich --- .../99_bugs/43_types_json_double_format | 24 +++++++++++++++++++ types.c | 17 ++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/custom/99_bugs/43_types_json_double_format diff --git a/tests/custom/99_bugs/43_types_json_double_format b/tests/custom/99_bugs/43_types_json_double_format new file mode 100644 index 00000000..9b90e373 --- /dev/null +++ b/tests/custom/99_bugs/43_types_json_double_format @@ -0,0 +1,24 @@ +When formatting integral double values as JSON, ensure that at least one +decimal place is retained. + +-- Testcase -- +printf("%.J\n", [ + 1e100, + 1.23, + 4.00, + 1.0/3*3, +]); +-- End -- + +-- Args -- +-R +-- End -- + +-- Expect stdout -- +[ + 1e+100, + 1.23, + 4.0, + 1.0 +] +-- End -- diff --git a/types.c b/types.c index c9472f7e..eeac8c74 100644 --- a/types.c +++ b/types.c @@ -1561,6 +1561,21 @@ ucv_to_stringbuf_add_padding(uc_stringbuf_t *pb, char pad_char, size_t pad_size) } } +static void +ucv_to_stringbuf_add_double(uc_stringbuf_t *pb, double val, bool json) +{ + int len = ucv_stringbuf_printf(pb, "%.14g", val); + + if (!json) + return; + + for (char *p = pb->buf + pb->bpos - len; len > 0; len--, p++) + if (*p == '.' || *p == 'e') + return; + + ucv_stringbuf_append(pb, ".0"); +} + void ucv_to_stringbuf_formatted(uc_vm_t *vm, uc_stringbuf_t *pb, uc_value_t *uv, size_t depth, char pad_char, size_t pad_size) { @@ -1624,7 +1639,7 @@ ucv_to_stringbuf_formatted(uc_vm_t *vm, uc_stringbuf_t *pb, uc_value_t *uv, size else if (d == -INFINITY) ucv_stringbuf_append(pb, "-Infinity"); else - ucv_stringbuf_printf(pb, "%.14g", d); + ucv_to_stringbuf_add_double(pb, d, json); break;