Parse a long input once instead of twice

A mantissa of more than 19 significant digits needs more than 19 characters,
so only a longer input can come back too_many_digits. Today that is discovered
after a full parse without spans, and the input is then parsed a second time
with them. Testing the length up front sends it straight to the parse that
materializes the spans.

benchmarks/benchmark.cpp, Intel Xeon 6975P-C, clang 22.1.8, cycles per float,
best of 5:

                 canada  mesh   long fractions  20-38 digit ints  >38 digit ints
  before          47.30  26.20          233.94            259.73          447.73
  after           46.90  25.68          195.68            181.65          273.11

Results are bit-identical over 400k adversarial inputs.

Caveat: the test looks at the range, not at the number. A caller that passes
the whole remaining buffer for a short value takes the slow path on every
value, measured at 20% for 7-character numbers in a large buffer. Deciding on
the token instead costs more than it saves: a 20-character probe loop is
slower than the second parse it avoids.
This commit is contained in:
Raúl Marín 2026-09-21 22:08:58 +00:00
parent b0ab987b3d
commit 510ea1fa8a

View File

@ -336,6 +336,14 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
}
bool const bjf = uint64_t(fmt & detail::basic_json_fmt) != 0;
// A mantissa of more than 19 significant digits needs more than 19
// characters, so only a longer input can be too_many_digits. Predicting it
// from the length sends it straight to the span-materializing parse instead
// of parsing once without spans and then re-parsing with them.
if fastfloat_unlikely ((last - first) > 19) {
return parse_number_slow_path<T, UC>(first, last, value, options, bjf);
}
// Fast path: parse WITHOUT materializing the integer/fraction spans (read
// only by the rare slow paths). Skipping their stores keeps the fat
// parsed_number_string_t off the hot path. store_spans is a runtime argument,