diff --git a/changelog.md b/changelog.md index 98a7a8c446c1c..fd57f89e57d3e 100644 --- a/changelog.md +++ b/changelog.md @@ -70,6 +70,8 @@ - `jsonutils` now serializes/deserializes holey enums as regular enums (via `ord`) instead of as strings. Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period. +- `json` and `jsonutils` now serialize NaN, Inf, -Inf as "nan", "inf", "-inf", using raw strings (the same + that are used to encode numbers that can't fit in native number types). ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 96ee61fd512d9..0f30f3f75bcff 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -333,7 +333,10 @@ proc `%`*(n: BiggestInt): JsonNode = proc `%`*(n: float): JsonNode = ## Generic constructor for JSON data. Creates a new `JFloat JsonNode`. - result = JsonNode(kind: JFloat, fnum: n) + if n != n: newJRawNumber("nan") + elif n == Inf: newJRawNumber("inf") + elif n == -Inf: newJRawNumber("-inf") + else: JsonNode(kind: JFloat, fnum: n) proc `%`*(b: bool): JsonNode = ## Generic constructor for JSON data. Creates a new `JBool JsonNode`. @@ -1087,11 +1090,32 @@ when defined(nimFixedForwardGeneric): dst = cast[T](jsonNode.num) proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) - if jsonNode.kind == JFloat: - dst = T(jsonNode.fnum) + if jsonNode.isUnquoted: + assert jsonNode.kind == JString # instead of exception, because isUnquoted is internal logic + # when nimvm: + when true: + case jsonNode.str + of "nan": + let b = NaN + dst = T(b) + # dst = NaN # would fail some tests because range conversions would cause CT error + # in some cases; but this is not a hot-spot inside this branch and backend can optimize + # this. + of "inf": + let b = Inf + dst = T(b) + of "-inf": + let b = -Inf + dst = T(b) + else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str) + else: + discard else: - dst = T(jsonNode.num) + verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) + if jsonNode.kind == JFloat: + dst = T(jsonNode.fnum) + else: + dst = T(jsonNode.num) proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = verifyJsonKind(jsonNode, {JString}, jsonPath) diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index e538baf4faf3d..dd79d6b205d6a 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -301,6 +301,12 @@ block: # bug #17383 testRoundtrip(int64.high): "9223372036854775807" testRoundtrip(uint64.high): "18446744073709551615" +block: # bug #18007 + testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): "[nan,inf,-inf,0.0,-0.0,1.0]" + # pending https://github.com/nim-lang/Nim/issues/18025 use: + # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0]): "[nan,inf,-inf,0.0,-0.0,1.0]" + let inf = float32(Inf) + testRoundtrip([float32(NaN), inf, -inf, 0.0, -0.0, 1.0]): "[nan,inf,-inf,0.0,-0.0,1.0]" block: let a = "18446744073709551615" diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 18d0aa325fb0a..89dda2ad98c03 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -91,6 +91,10 @@ template fn() = else: testRoundtrip(a): "[9223372036854775807,18446744073709551615]" + block: # bug #18007 + testRoundtrip((NaN, Inf, -Inf, 0.0, -0.0, 1.0)): "[nan,inf,-inf,0.0,-0.0,1.0]" + testRoundtrip((float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0)): "[nan,inf,-inf,0.0,-0.0,1.0]" + block: # case object type Foo = object x0: float