@@ -289,6 +289,23 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
289289 return answer;
290290}
291291
292+ // Slow path: re-parse materializing the integer/fraction spans the hot no-span
293+ // parse skipped, then run the full algorithm. The two callers reach it only
294+ // through a fastfloat_unlikely branch, so the optimizer keeps this re-parse off
295+ // the hot path on its own (no function-level noinline needed).
296+ // from_chars_advanced already handles both the too_many_digits disambiguation
297+ // and the am.power2<0 digit_comp recompute, so both slow branches collapse to
298+ // one helper call.
299+ template <typename T, typename UC >
300+ FASTFLOAT_CONSTEXPR20 from_chars_result_t <UC >
301+ parse_number_slow_path (UC const *first, UC const *last, T &value,
302+ parse_options_t <UC > options, bool bjf) noexcept {
303+ parsed_number_string_t <UC > pns =
304+ bjf ? parse_number_string<true , UC >(first, last, options, true )
305+ : parse_number_string<false , UC >(first, last, options, true );
306+ return from_chars_advanced (pns, value);
307+ }
308+
292309template <typename T, typename UC >
293310fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t <UC >
294311from_chars_float_advanced (UC const *first, UC const *last, T &value,
@@ -312,10 +329,15 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
312329 answer.ptr = first;
313330 return answer;
314331 }
332+ bool const bjf = uint64_t (fmt & detail::basic_json_fmt) != 0 ;
333+
334+ // Fast path: parse WITHOUT materializing the integer/fraction spans (read
335+ // only by the rare slow paths). Skipping their stores keeps the fat
336+ // parsed_number_string_t off the hot path. store_spans is a runtime argument,
337+ // so this reuses the single parse_number_string instantiation.
315338 parsed_number_string_t <UC > pns =
316- uint64_t (fmt & detail::basic_json_fmt)
317- ? parse_number_string<true , UC >(first, last, options)
318- : parse_number_string<false , UC >(first, last, options);
339+ bjf ? parse_number_string<true , UC >(first, last, options, false )
340+ : parse_number_string<false , UC >(first, last, options, false );
319341 if (!pns.valid ) {
320342 if (uint64_t (fmt & chars_format::no_infnan)) {
321343 answer.ec = std::errc::invalid_argument;
@@ -326,8 +348,47 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
326348 }
327349 }
328350
329- // call overload that takes parsed_number_string_t directly.
330- return from_chars_advanced (pns, value);
351+ // Slow path A (rare): > 19 significant digits. The no-span parse left the
352+ // mantissa un-truncated and skipped the span-based recompute; the cold helper
353+ // re-parses with spans and runs the full algorithm.
354+ //
355+ // We have to disable -Wc++20-extensions for the [[unlikely]] attribute
356+ // See comment for @jwakely at
357+ // https://github.com/fastfloat/fast_float/pull/387#discussion_r3366943539
358+ // This is unfortunate.
359+ #ifdef __clang__
360+ #pragma clang diagnostic push
361+ #pragma clang diagnostic ignored "-Wc++20-extensions"
362+ #endif
363+ if fastfloat_unlikely (pns.too_many_digits ) {
364+ return parse_number_slow_path<T, UC >(first, last, value, options, bjf);
365+ }
366+ answer.ec = std::errc (); // be optimistic
367+ answer.ptr = pns.lastmatch ;
368+
369+ if (clinger_fast_path_impl (pns.mantissa , pns.exponent , pns.negative , value)) {
370+ return answer;
371+ }
372+
373+ adjusted_mantissa am =
374+ compute_float<binary_format<T>>(pns.exponent , pns.mantissa );
375+ // Slow path B (rare): Eisel-Lemire could not resolve; digit_comp needs the
376+ // integer/fraction spans. Route to the cold helper (clinger there is a
377+ // dead-effect since it already failed here; the cold re-parse + digit_comp
378+ // via from_chars_advanced reproduces this branch).
379+ if fastfloat_unlikely (am.power2 < 0 ) {
380+ return parse_number_slow_path<T, UC >(first, last, value, options, bjf);
381+ }
382+ #ifdef __clang__
383+ #pragma clang diagnostic pop
384+ #endif
385+ to_float (pns.negative , am, value);
386+ // Test for over/underflow.
387+ if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0 ) ||
388+ am.power2 == binary_format<T>::infinite_power ()) {
389+ answer.ec = std::errc::result_out_of_range;
390+ }
391+ return answer;
331392}
332393
333394template <typename T, typename UC , typename >
0 commit comments