Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: fastfloat/fast_float
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: main
Choose a base ref
...
head repository: fastfloat/fast_float
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: pr369-split-default-path
Choose a head ref
Checking mergeability… Don’t worry, you can still create the pull request.
  • 3 commits
  • 5 files changed
  • 3 contributors

Commits on Jun 18, 2026

  1. Make digit separator / prefix support performance-neutral

    Re-implements the optional digit-separator and base-prefix parsing
    (originally PR #369) on top of the current store_spans hot-path
    architecture, with the goal of zero overhead when the features are not
    used.
    
    Key changes vs. the original PR:
    
    - has_separator is now a *compile-time* template parameter on
      parse_number_string, dispatched once (from_chars_advanced ->
      parse_number_string_options) based on options.digit_separator. The
      has_separator==false instantiation that every default caller uses is
      byte-for-byte the separator-free parser: no separator comparison ever
      enters a digit loop and the SIMD eight-digit fast path is preserved.
      The separator-aware code lives only in the cold true instantiation.
    
    - The store_spans no-span hot path (added to main after the original PR
      was branched) is preserved.
    
    - parse_options_t fields are ordered so the two new single-byte fields
      (digit_separator, format_options) fall into the existing padding for
      UC == char. sizeof(parse_options_t<char>) stays 16 bytes, so the
      struct is still register-passed and the call boundary is unchanged.
    
    Result: for the default (no separator, no prefix) path, the generated
    assembly of from_chars<double> is identical to main, and benchmarks show
    no measurable regression (the original PR was 5-10% slower).
    
    Adds basictest coverage for digit separators (including the >19-digit
    overflow re-scan paths) and prefix skipping.
    zaewc committed Jun 18, 2026
    Configuration menu
    Copy the full SHA
    1d35d26 View commit details
    Browse the repository at this point in the history

Commits on Jun 21, 2026

  1. Keep the digit-separator feature off the default hot path

    PR #369's "performance-neutral" claim did not hold: the default
    (no-separator) parse was ~70% larger in the hot frame (parse_number_string
    inlined into from_chars: 833 -> 1414 x86/arm64 instructions), because
    
      1. parse_number_string is force-inlined (always_inline), and the runtime
         dispatch inlined BOTH the has_separator==true and ==false instantiations
         into the default caller -- dragging the entire separator-aware scanner
         into the hot frame; and
      2. the runtime separator branch survived in from_chars() even though a
         default-constructed parse_options provably has no separator, because the
         thin from_chars_caller forwarder was not inlined, so the compile-time
         '\0' was lost across the call boundary and the branch (plus two calls)
         could not be folded away.
    
    Fixes:
      * FASTFLOAT_NOINLINE + a cold parse_number_string_with_separator trampoline
        so the separator instantiation stays strictly out of line; the default
        caller only ever materializes the has_separator==false code.
      * Force-inline from_chars_caller::call (all three specializations) so the
        separator branch constant-folds away on the common from_chars() path.
    
    Net: the default hot frame returns to 844 instructions (vs 833 on main; the
    +11 is the unrelated parse_number_string refactor and is unchanged by this
    commit). The separator path is unaffected and all tests pass.
    
    Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
    zaewc and claude committed Jun 21, 2026
    Configuration menu
    Copy the full SHA
    8428fd7 View commit details
    Browse the repository at this point in the history

Commits on Jul 13, 2026

  1. Split default path into verbatim separator-free scanner

    PR #369 gated the digit-separator/prefix feature on a compile-time
    has_separator flag and claimed the has_separator==false instantiation
    "compiles to exactly the same code as if the feature did not exist".
    Instruction-count measurement (benchmark i/f, deterministic +/- 0.0%)
    disproved this: routing the default path through the shared, restructured
    body cost GCC 14 ~1.5-2.3 i/f on short doubles, a reproducible regression
    on mesh/double (+0.77 i/f ASCII, +1.35 UTF-16) even as canada improved.
    
    Fix: the has_separator==false instantiation now delegates to
    parse_number_string_nosep, a verbatim copy of the original separator-free
    scanner, so the default path's codegen is byte-for-byte the pre-feature
    parser. The shared parse_number_string body becomes separator-only (the
    dead !has_separator branches are removed). The inaccurate doc comment is
    corrected.
    
    Measured vs pre-feature baseline (34164f5), instructions per float:
      canada/f64 ASCII  242.32 -> 238.87   (PR: 240.87)
      canada/f64 UTF16  251.05 -> 241.81   (PR: 245.79)
      mesh/f64   ASCII  107.69 -> 107.57   (PR: 108.46, +0.77 regression)
      mesh/f64   UTF16  110.10 -> 109.74   (PR: 111.45, +1.35 regression)
    All eight fastfloat rows now match or beat both the baseline and the PR;
    the mesh/double regression is eliminated. All 14 ctest suites pass.
    lemire committed Jul 13, 2026
    Configuration menu
    Copy the full SHA
    f1ea455 View commit details
    Browse the repository at this point in the history
Loading