Skip to content

Skip redundant fractional truncation scans - #402

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

Skip redundant fractional truncation scans#402
perfloop-agent wants to merge 2 commits into
fastfloat:mainfrom
perfloop:perfloop-pr-open-f5be9f04qk

Conversation

@perfloop-agent

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

Copy link
Copy Markdown

Summary

parse_mantissa unconditionally scans the fraction span for nonzero digits to decide truncation, even when the integer-suffix scan has already found one. For inputs with a long integer part and a very long fraction, that second scan is pure overhead: once any nonzero digit is known, the rounding decision is already determined.

This PR short-circuits the fraction scan when truncated is already true:

bool truncated = is_truncated(p, pend);
// A nonzero integer suffix already determines the rounding direction.
if (!truncated && num.fraction.ptr != nullptr) {
  truncated = is_truncated(num.fraction);
}

Semantics are unchanged by construction (a |= f()if (!a) a = f() for a side-effect-free f): the same inputs round the same way, and zero-integer-suffix inputs still scan the fraction as before.

Performance

Measured with the opt-in benchmark added in this PR (-DFASTFLOAT_TRUNCATED_FRACTION_BENCHMARK=ON), on a 770-digit integer with a nonzero suffix followed by a 4 MiB zero fraction:

shape before after
direct parse_mantissa (GCC 14.2, x86-64) ~229 µs ~1.2 µs
end-to-end exact from_chars ~2.56 ms ~2.36 ms
zero-suffix guardrails (direct + end-to-end) unchanged unchanged

Independently reproduced on Apple Silicon (clang, arm64): ~150 µs → ~0.85 µs on the direct shape.

This path is only reached by long ambiguous decimals that fall back to big-integer comparison, so ordinary parsing is unaffected; the win matters for adversarially long inputs, where the removed scan is O(fraction length) per parse.

Testing

  • Full CMake test suite passes (including C++20 constexpr basictest).
  • New test coverage in basictest.cpp for long nonzero-suffix + long-zero-fraction inputs, including wchar_t/char16_t/char32_t.
  • Differential run over generated decimal inputs (boundary lengths, exponents, long suffixes, float/double) produced byte-identical results before and after.

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

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