Skip to content

Normalize long trailing decimal zero suffixes - #403

Open
perfloop-agent wants to merge 2 commits into
fastfloat:mainfrom
perfloop:perfloop-pr-open-dz12ma0n75
Open

Normalize long trailing decimal zero suffixes#403
perfloop-agent wants to merge 2 commits into
fastfloat:mainfrom
perfloop:perfloop-pr-open-dz12ma0n75

Conversation

@perfloop-agent

@perfloop-agent perfloop-agent commented Aug 4, 2026

Copy link
Copy Markdown

Summary

When a long ambiguous decimal falls back to big-integer comparison, digit_comp materializes every digit into the bigint — including an arbitrarily long suffix of trailing zeros, which inflates limb construction and the subsequent comparison for no informational gain.

This PR trims a long trailing-zero suffix before materializing the coefficient:

  • has_trailing_zero is an O(1) last-character check, so inputs without a trailing zero pay a single branch.
  • trim_zeros_from_end scans backward (word-at-a-time, then per-character); suffixes shorter than 16 code units are kept as-is (reverse-scan setup doesn't pay for itself below that).
  • Trimming K zeros shortens the digit count by K, which raises the derived exponent (sci_exp + 1 - digits) by exactly K — the represented value mantissa × 10^exp is unchanged, so rounding decisions are identical.
  • Integer-part zeros are only trimmed when the entire fraction was zeros (e.g. 120.3000 keeps the zero in 120), and an all-zero coefficient is never reduced to empty spans.

The scientific exponent is computed from the original parsed number before trimming; only the local spans passed to parse_mantissa change.

Performance

Measured with the opt-in benchmark added in this PR (-DFASTFLOAT_TRAILING_ZERO_BENCHMARK=ON), over ambiguous 20-, 30-, and 120-digit cores with 64–4096 trailing zeros:

metric before after
direct digit_comp (per conversion) ~1445 ns ~200 ns
public from_chars<double> (per parse) ~2789 ns ~1526 ns

Independently reproduced on Apple Silicon (clang, arm64): ~1035 ns → ~114 ns per digit comparison on the same shapes.

Testing

  • Full CMake test suite passes across C++11/C++17/C++20 modes, including the constexpr basictest and a sanitizer run of the new trailing-zeros test.
  • New trailing_zeros_test covers the 16-unit cutoff boundary, integer-vs-fraction trimming (120.3000-style), all-zero coefficients, and wide-character variants.
  • Deterministic differential probes over several hundred thousand long/malformed/trailing/boundary inputs: double, float, and char16_t/char32_t/wchar_t outputs (bits, end pointer, and error code) are identical before and after.

Note: this touches the same slow-path file as #402; whichever lands second will need a trivial test-file rebase.

Full verification record: https://app.perfloop.ai/t/oss/case_xwm9h4yejp

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