diff --git a/src/main/java/net/openhft/chronicle/wire/JSONWire.java b/src/main/java/net/openhft/chronicle/wire/JSONWire.java index 1e8a152ac..e35eec526 100644 --- a/src/main/java/net/openhft/chronicle/wire/JSONWire.java +++ b/src/main/java/net/openhft/chronicle/wire/JSONWire.java @@ -1193,29 +1193,41 @@ public void writeComment(@NotNull CharSequence s) { } /** - * Write a special double value (e.g. NaN) as a string to the given bytes. + * Write a double value to the given bytes. IEEE754 non-finite values (NaN, Infinity) are + * quoted as JSON strings since they have no numeric representation in JSON. Finite values, + * including large magnitudes, are written as unquoted JSON numbers. * * @param bytes The bytes to append the stringified double value to * @param value The double value to convert to a string */ @Override protected void writeSpecialDoubleValueToBytes(Bytes bytes, double value) { - bytes.append('"'); - bytes.append(Double.toString(value)); - bytes.append('"'); + if (Double.isNaN(value) || Double.isInfinite(value)) { + bytes.append('"'); + bytes.append(Double.toString(value)); + bytes.append('"'); + } else { + bytes.append(Double.toString(value)); + } } /** - * Write a special float value (e.g. NaN) as a string to the given bytes. + * Write a float value to the given bytes. IEEE754 non-finite values (NaN, Infinity) are + * quoted as JSON strings since they have no numeric representation in JSON. Finite values, + * including large magnitudes, are written as unquoted JSON numbers. * * @param bytes The bytes to append the stringified float value to * @param value The float value to convert to a string */ @Override protected void writeSpecialFloatValueToBytes(Bytes bytes, float value) { - bytes.append('"'); - bytes.append(Float.toString(value)); - bytes.append('"'); + if (Float.isNaN(value) || Float.isInfinite(value)) { + bytes.append('"'); + bytes.append(Float.toString(value)); + bytes.append('"'); + } else { + bytes.append(Float.toString(value)); + } } @NotNull diff --git a/src/test/java/net/openhft/chronicle/wire/JsonWireDoubleAndFloatSpecialValuesAcceptanceTests.java b/src/test/java/net/openhft/chronicle/wire/JsonWireDoubleAndFloatSpecialValuesAcceptanceTests.java index e0e105384..9aa3816de 100644 --- a/src/test/java/net/openhft/chronicle/wire/JsonWireDoubleAndFloatSpecialValuesAcceptanceTests.java +++ b/src/test/java/net/openhft/chronicle/wire/JsonWireDoubleAndFloatSpecialValuesAcceptanceTests.java @@ -89,6 +89,30 @@ void floatRoundTrip(FloatTestInput floatTestInput) { Assertions.assertTrue(floatTestInput.expectOutputFloatToMatchThisPredicate.test(object.value)); } + @ParameterizedTest + @MethodSource("largeFiniteFloatInputs") + void largeFiniteFloatSerialiseAsJsonNumber(float value) { + String json = toJson(value); + Assertions.assertFalse(json.startsWith("\""), + "Finite float " + value + " must serialise as a JSON number, not a quoted string literal"); + } + + @ParameterizedTest + @MethodSource("largeFiniteDoubleInputs") + void largeFiniteDoubleSerialiseAsJsonNumber(double value) { + String json = toJson(value); + Assertions.assertFalse(json.startsWith("\""), + "Finite double " + value + " must serialise as a JSON number, not a quoted string literal"); + } + + private static Stream largeFiniteFloatInputs() { + return Stream.of(5_000_000.0f, 1e7f, -5_000_000.0f, 1.23456789e10f); + } + + private static Stream largeFiniteDoubleInputs() { + return Stream.of(1e15, 1e16, -5e15, 1.23456789e20); + } + private static Stream doubleTestInputs() { return Stream.of( new DoubleTestInput(Double.NaN, "NaN", Double::isNaN),