Skip to content

fix: correct %g and %e exponent for powers of 10#882

Open
He-Pin wants to merge 1 commit into
google:masterfrom
He-Pin:fix-g-format-exponent
Open

fix: correct %g and %e exponent for powers of 10#882
He-Pin wants to merge 1 commit into
google:masterfrom
He-Pin:fix-g-format-exponent

Conversation

@He-Pin

@He-Pin He-Pin commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Motivation

%g % 1000000 produces "1000000" instead of "1e+06", and %.2g % 1000 produces "10e+02" instead of "1e+03". C printf and Python both produce the correct output.

Root cause: std.log(x)/std.log(10) in the standard library suffers from floating-point imprecision for powers of 10:

  • log(1000000)/log(10) = 5.9999999999999991 (should be 6)
  • log(1000)/log(10) = 2.9999999999999996 (should be 3)

std.floor then rounds these down incorrectly.

Modification

Added 1e-12 epsilon before std.floor in the exponent calculation in two places in cpp-jsonnet/stdlib/std.jsonnet:

  1. render_float_sci (used by %e/%E)
  2. %g/%G format handler

Regenerated astgen/stdast.go from the updated standard library.

The epsilon is ~1000x the floating-point error (~1e-15) but far too small to affect non-power-of-10 values.

Result

Expression Before After C/Python
"%g" % 1000000 "1000000" "1e+06" "1e+06"
"%.2g" % 1000 "10e+02" "1e+03" "1e+03"
"%e" % 1000000 "1.000000e+05" (wrong) "1.000000e+06" "1.000000e+06"
"%G" % 1000000 "10E+05" "1E+06" "1E+06"

All other %g/%e cases produce identical output before and after this fix.

Test plan

  • go test ./... — all tests pass
  • Verified against C printf and Python %g for: 0, ±1000000, 1e10, 1e-10, 123456, 999999, 3.14, various precision specifiers
  • No regression in existing format tests

Related

Motivation:
%g % 1000000 produced "1000000" instead of "1e+06", and %.2g % 1000
produced "10e+02" instead of "1e+03". Root cause: std.log(x)/std.log(10)
suffers from floating-point imprecision for powers of 10 (e.g.
log(1000000)/log(10) = 5.999999... instead of 6), causing std.floor
to round down incorrectly.

Modification:
Added 1e-12 epsilon before std.floor in exponent calculation in both
render_float_sci and the %g format handler in the standard library.
Regenerated astgen/stdast.go from the updated stdlib.

Result:
- "%g" % 1000000 → "1e+06" (was "1000000")
- "%.2g" % 1000 → "1e+03" (was "10e+02")
- "%e" % 1000000 → "1.000000e+06" (was "1.000000e+05" with wrong mantissa)
- All other %g/%e cases unchanged.

Upstream PR: google/jsonnet#1324
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant