From 1eac92b03fcb17fff72ac13441b7181b49775506 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Wed, 23 Sep 2026 22:18:11 +0200 Subject: [PATCH] Parse the fraction tail and the exponent with one subtraction per digit GCC computed the digit twice, and the in-loop exponent saturation became a select on the path to the power of ten. Saturate only for more than 18 exponent digits. Co-Authored-By: Claude Opus 5.5 (1M context) --- include/fast_float/ascii_number.h | 32 +++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/include/fast_float/ascii_number.h b/include/fast_float/ascii_number.h index eb6a348..a2cd38d 100644 --- a/include/fast_float/ascii_number.h +++ b/include/fast_float/ascii_number.h @@ -459,8 +459,13 @@ parse_number_string_impl(UC const *p, UC const *pend, // for integers with many digits, digit parsing is the primary bottleneck. loop_parse_if_eight_digits(p, pend, i); - while ((p != pend) && is_integer(*p)) { - uint8_t digit = uint8_t(*p - UC('0')); + // One subtraction serves the digit test and the digit (GCC computes it + // twice when written separately); in 64 bits it needs no extension. + while (p != pend) { + uint64_t const digit = uint64_t(*p) - uint64_t(UC('0')); + if (digit > 9) { + break; + } ++p; i = i * 10 + digit; // in rare cases, this will overflow, but that's ok } @@ -509,13 +514,28 @@ parse_number_string_impl(UC const *p, UC const *pend, // Otherwise, we will be ignoring the 'e'. p = location_of_e; } else { - while ((p != pend) && is_integer(*p)) { - uint8_t digit = uint8_t(*p - UC('0')); - if (exp_number < 0x10000000) { - exp_number = 10 * exp_number + digit; + // Same digit test as the fraction tail. No saturation in the loop: it + // would add a select per digit on the path to the power of ten, and 18 + // digits fit in int64_t. + UC const *const exp_start = p; + uint64_t exp_digits = 0; + while (p != pend) { + uint64_t const digit = uint64_t(*p) - uint64_t(UC('0')); + if (digit > 9) { + break; } + exp_digits = 10 * exp_digits + digit; ++p; } + exp_number = int64_t(exp_digits); + if fastfloat_unlikely (p - exp_start > 18) { + exp_number = 0; + for (UC const *q = exp_start; q != p; ++q) { + if (exp_number < 0x10000000) { + exp_number = 10 * exp_number + (*q - UC('0')); + } + } + } if (neg_exp) { exp_number = -exp_number; }