Skip to content

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

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#1324
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) 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:

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

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

  • Verified against C printf and Python %g for: 0, ±1000000, 1e10, 1e-10, 123456, 999999, 3.14, various precision specifiers
  • No regression in existing %f, %d, %x, %o, %c, %s format codes

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. The epsilon is large enough
to correct floating-point error (~1e-15) but small enough to not affect
non-power-of-10 values.

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.
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