From 4f04f7245854ccbfadb86ef7774f53477f71da87 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 11 Mar 2026 16:19:52 +0300 Subject: [PATCH] json: output of json encode/decode of structs containing f64 arrays is not equivalent to the input (fixes #22860) --- vlib/json/json_primitives.c.v | 22 +++++++++++++++++-- .../tests/json_f64_array_roundtrip_test.v | 14 ++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 vlib/json/tests/json_f64_array_roundtrip_test.v diff --git a/vlib/json/json_primitives.c.v b/vlib/json/json_primitives.c.v index 081fd37be..e1e718d11 100644 --- a/vlib/json/json_primitives.c.v +++ b/vlib/json/json_primitives.c.v @@ -3,6 +3,8 @@ // that can be found in the LICENSE file. module json +import math + #flag -I @VEXEROOT/thirdparty/cJSON #flag @VEXEROOT/thirdparty/cJSON/cJSON.o #include "cJSON.h" @@ -28,6 +30,7 @@ fn C.cJSON_IsObject(&C.cJSON) bool fn C.cJSON_IsArray(&C.cJSON) bool fn C.cJSON_CreateNumber(f64) &C.cJSON +fn C.cJSON_CreateRaw(&char) &C.cJSON fn C.cJSON_CreateBool(bool) &C.cJSON @@ -233,12 +236,12 @@ fn encode_u64(val u64) &C.cJSON { @[markused] fn encode_f32(val f32) &C.cJSON { - return C.cJSON_CreateNumber(val) + return C.cJSON_CreateRaw(&char(json_float_to_raw_string(val).str)) } @[markused] fn encode_f64(val f64) &C.cJSON { - return C.cJSON_CreateNumber(val) + return C.cJSON_CreateRaw(&char(json_float_to_raw_string(val).str)) } @[markused] @@ -256,6 +259,21 @@ fn encode_string(val string) &C.cJSON { return C.cJSON_CreateString(&char(val.str)) } +// json_float_to_raw_string uses V's float formatter so json.encode keeps exact float round-trips. +fn json_float_to_raw_string[T](val T) string { + if val == 0 { + return '0' + } + if math.is_nan(f64(val)) || math.is_inf(f64(val), 0) { + return 'null' + } + mut raw := val.str() + if raw.len > 2 && raw[raw.len - 2] == `.` && raw[raw.len - 1] == `0` { + raw = raw[..raw.len - 2] + } + return raw +} + // /////////////////////// // user := decode_User(json_parse(js_string_var)) @[markused] diff --git a/vlib/json/tests/json_f64_array_roundtrip_test.v b/vlib/json/tests/json_f64_array_roundtrip_test.v new file mode 100644 index 000000000..c85989962 --- /dev/null +++ b/vlib/json/tests/json_f64_array_roundtrip_test.v @@ -0,0 +1,14 @@ +import json + +struct F64ArrayPayload { + arr []f64 +} + +fn test_encode_decode_struct_with_f64_array_roundtrips() ! { + original := F64ArrayPayload{ + arr: [0.9716157205240175, 0.9336099585062241] + } + encoded := json.encode(original) + assert encoded == '{"arr":[0.9716157205240175,0.9336099585062241]}' + assert json.decode(F64ArrayPayload, encoded)! == original +} -- 2.39.5