Skip to content

std.format: wrong digits for %f/%d on large values; malformed output for non-integer * precision #1331

Description

@He-Pin

Description

The numeric conversion codes of std.format, as implemented in std.jsonnet, produce wrong digits for large values and accept non-integer * width/precision, producing malformed output. Verified on go-jsonnet v0.22.0 (which vendors std.jsonnet); since the implementation lives in the shared stdlib, I expect the C++ implementation is affected the same way (I did not test a C++ binary locally).

Per https://jsonnet.org/ref/stdlib.html#std-format, "string formatting follows the same rules as Python", so CPython is used as ground truth below.

1. %f wrong fractional digits for large integral doubles

std.format("%f", 1e20)
  • Actual: "100000000000000000000.729344"
  • Expected: "100000000000000000000.000000"

CPython:

>>> "%f" % 1e20
'100000000000000000000.000000'

1e20 is an exactly-representable integer double, so the fraction must be all zeros. The .729344 suggests the fraction is extracted with floating-point arithmetic that loses precision at this magnitude.

2. %d wrong digits for large doubles

std.format("%d", 1e308)
  • Actual (first digits): 100000000000000080820288064406668826404226602626644826268666088682448264248040264260482460802080684648080264248284000422088262400264842228240488280204820482448600044082048226820886002048022200064204444666088046048242682004206408240604042624800242804426482484064246602062860886000088448826682264040666006840886
  • Expected (exact decimal expansion of the binary64 value): 100000000000000001097906362944045541740492309677311846336810682903157585404911491537163328978494688899061249669721172515611590283743140088328307009198146046031271664502933027185697489699588559043338384466165001178426897626212945177628091195786707458122783970171784415105291802893207873272974885715430223118336

CPython agrees with Expected:

>>> "%d" % 1e308
100000000000000001097906362944045541740492309677311846336810682903157585404911491537163328978494688899061249669721172515611590283743140088328307009198146046031271664502933027185697489699588559043338384466165001178426897626212945177628091195786707458122783970171784415105291802893207873272974885715430223118336

3. Non-integer * width/precision yields malformed output instead of an error

std.format("%.*f", [2.5, 1.234])
  • Actual: "1.73.772233983161982" (malformed: two decimal points)
  • Expected: an error; CPython raises TypeError: * wants int

Similarly std.format("%*d", [2.5, 42]) silently truncates the width to 2 instead of erroring.

Cross-check

sjsonnet produces the Expected output in all three cases (its %d/%f paths expand the exact binary64 value and validate * arguments are integers).

Environment

  • go-jsonnet v0.22.0 (Homebrew), used as the vehicle to exercise the shared std.jsonnet formatting code.

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