Skip to content

json: insufficient precision for float64 serialization #15397

@slonik-az

Description

@slonik-az

Nim's standard json module serializes float numbers with 16 significant decimal digits

var n: int = c_sprintf(addr buf, "%.16g", value)

which is insufficient to represent all of 64 bit IEEE-754 floats. One needs at least 17 significant decimal digits, see https://en.wikipedia.org/wiki/Double-precision_floating-point_format for details.

The following MWE shows the problem and a possible solution that changes precision from 16 to 17 digits.

MWE

import json
import fenv
import strformat

let
    x1eps   = 1.0+epsilon(float64) # smallest float64 larger than 1
    jsnStr1 = $(%*{"fnum": x1eps }) # json uses 16 significant digits, not enough!
    jsnStr2 = fmt("""{{ "fnum": {x1eps:.17g} }}""") # need at least 17 significant digits
    jsnNd1  = parseJson(jsnStr1)
    jsnNd2  = parseJson(jsnStr2)
    x1      = jsnNd1{"fnum"}.getFloat(NaN)
    x2      = jsnNd2{"fnum"}.getFloat(NaN)

echo "jsbStr1 = ", jsnStr1
echo "jsbStr2 = ", jsnStr2
echo "jsnNd1  = ", jsnNd1
echo "jsnNd2  = ", jsnNd2
echo "x1      = ", x1
echo "x2      = ", x2

# Results:
doAssert (x1 != x1eps)
doAssert (x1 == 1.0)
doAssert (x2 == x1eps)

Additional Information

$ nim -v
Nim Compiler Version 1.2.6 [MacOSX: amd64]
Compiled at 2020-09-02
Copyright (c) 2006-2020 by Andreas Rumpf
git hash: 211868b85cf8b11baabf06d75aaf7e2015d82e2b
active boot switches: -d:release

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions