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
- 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
- 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.
Description
The numeric conversion codes of
std.format, as implemented instd.jsonnet, produce wrong digits for large values and accept non-integer*width/precision, producing malformed output. Verified on go-jsonnet v0.22.0 (which vendorsstd.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.
%fwrong fractional digits for large integral doubles"100000000000000000000.729344""100000000000000000000.000000"CPython:
1e20is an exactly-representable integer double, so the fraction must be all zeros. The.729344suggests the fraction is extracted with floating-point arithmetic that loses precision at this magnitude.2.
%dwrong digits for large doubles100000000000000080820288064406668826404226602626644826268666088682448264248040264260482460802080684648080264248284000422088262400264842228240488280204820482448600044082048226820886002048022200064204444666088046048242682004206408240604042624800242804426482484064246602062860886000088448826682264040666006840886100000000000000001097906362944045541740492309677311846336810682903157585404911491537163328978494688899061249669721172515611590283743140088328307009198146046031271664502933027185697489699588559043338384466165001178426897626212945177628091195786707458122783970171784415105291802893207873272974885715430223118336CPython agrees with Expected:
3. Non-integer
*width/precision yields malformed output instead of an error"1.73.772233983161982"(malformed: two decimal points)TypeError: * wants intSimilarly
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/%fpaths expand the exact binary64 value and validate*arguments are integers).Environment
std.jsonnetformatting code.