Skip to content

Lossy json serialization/deserialization round-trip for floats #13196

@bluenote10

Description

@bluenote10

A json serialization + deserialization round-trip does not give back the original value for floats.

Example

import json
import strformat

# some arbitrary float, not caring about actually significant places here
let x = 0.12345678901234567890123456789
let y = ($(%* x)).parseJson().getFloat()

echo &"{x} == {x} => {x == y}"
doAssert x == y

(EDIT(timotheecour): see another example in #15397 (comment))

Current Output

0.1234567890123457 == 0.1234567890123457 => false
/tmp/test.nim(9)         test
/nim/lib/system/assertions.nim(27) failedAssertImpl
/nim/lib/system/assertions.nim(20) raiseAssert
/nim/lib/system/fatal.nim(55) sysFatal
Error: unhandled exception: /tmp/test.nim(9, 10) `x == y`  [AssertionError]

Expected Output

No assertion error at least.

Possible Solution

This is basically a consequence that parseFloat and $ are not supposed be lossless (see #7717), but the json module builds on these.

I made a quick check with NimYaml and it suffers from exactly the same problem.

It is a natural expectation to rely on $ to produce an exact string representation. In my opinion the easiest solution would be to improve $ for floats. Perhaps it would make sense to implement Ryū: fast float-to-string conversion in the Nim standard library to solve the problem for good. A reference implementation is available in C and other languages. This would probably give float-to-string conversion a nice performance boost as well.

$ nim -v
Nim Compiler Version 1.1.1 [Linux: amd64]
Compiled at 2020-01-19
Copyright (c) 2006-2019 by Andreas Rumpf

git hash: 13ddbc46fc255978ebaf4233009b44db29583cb4
active boot switches: -d:release

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions