1083 Commits

Author SHA1 Message Date
Daniel Lemire
91aef729bd
Merge pull request #422 from sahvx655-wq/is-integer-signed-overflow
avoid signed overflow in is_integer on negative wchar_t units
2026-10-09 15:54:46 -04:00
Sahana Bogar
50f01dbb51 avoid signed overflow in is_integer for negative code units 2026-10-08 19:12:13 +05:30
Daniel Lemire
54571f86e3
Merge pull request #421 from sahvx655-wq/times-pow10-int64-min
avoid signed negation overflow in integer_times_pow10
2026-10-06 19:48:48 -04:00
Sahana Bogar
1271675129 avoid signed negation overflow in integer_times_pow10
Negating the most negative int64_t is undefined behaviour. GCC at -O2 uses that to turn the fast-path mantissa bound into a signed compare and returns the wrong sign, so take the magnitude in uint64_t instead.
2026-10-05 22:46:40 +05:30
github-actions[bot]
f3f02c8ad0 Version 8.3.1 v8.3.1 2026-10-04 16:26:53 +00:00
Daniel Lemire
963467e89d
Merge pull request #420 from fastfloat/release-workflow
Add manually triggered release workflow; fix on-release asset upload
2026-10-04 12:24:46 -04:00
Daniel Lemire
e62540e95f Add manually triggered release workflow; fix on-release asset upload
- New release.yml (workflow_dispatch): bumps patch/minor/major, runs
  script/release.py, tests the amalgamation and the full test suite,
  commits and tags on main, and publishes a release with fast_float.h.
- on-release.yml: the upload failed with 'Validation Failed' because
  fast_float.h was already attached to the release. Use
  'gh release upload --clobber' instead of the deprecated
  actions/upload-release-asset, and run the amalgamation test binary.
- release.py: also bump MODULE.bazel (stuck at 8.2.4) and exit non-zero
  if amalgamation fails.
2026-10-04 12:05:40 -04:00
Daniel Lemire
3e4f80fcc4
Merge pull request #419 from fastfloat/fix-418-struct-layout
Reorder parsed_number_string_t fields to avoid padding (fixes #418)
2026-10-04 11:59:23 -04:00
Daniel Lemire
fe5e97eeac Reorder parsed_number_string_t fields to avoid padding (fixes #418)
Placing the parse_error field next to the booleans shrinks the struct
from 72 to 64 bytes on 64-bit systems, recovering a large performance
regression observed with MSVC. Add a static_assert to guard the size.
2026-10-04 09:34:39 -04:00
Daniel Lemire
9498cc361e
Merge pull request #417 from fastfloat/fix-alpine-ci
CI: pin Alpine to v3.22 to fix setup-alpine failures
2026-09-26 15:30:01 -04:00
Daniel Lemire
c31e0294c3 CI: pin Alpine to v3.22 (setup-alpine v1 fails with apk-tools v3) 2026-09-26 14:53:33 -04:00
Daniel Lemire
1dc46a6b8f
Merge pull request #409 from caolanm/main
drop the unreachable tail of parse_mantissa
2026-09-25 18:21:25 -04:00
Daniel Lemire
f103228650
Merge pull request #415 from Algunenano/fix-benchmark-branch-misses
Average branch misses over the repetitions in the benchmark
2026-09-23 14:12:38 -04:00
Raúl Marín
38168208f6 Average branch misses over the repetitions in the benchmark
bm/f printed the total over all repetitions per float, unlike the other counters.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-23 19:12:41 +02:00
Daniel Lemire
cdc385d058
Merge pull request #408 from jadidbourbaki/float16-exactness
Fix std::float16_t rounding of subnormal ties and fast-path overflow
2026-09-22 14:11:44 -04:00
Daniel Lemire
82623d83c5 Avoid right-shifting a negative value in subnormal_ties_possible() 2026-09-22 13:57:31 -04:00
Daniel Lemire
7969d5089c
Merge pull request #413 from fastfloat/clang-format-fold
Fold the format at compile time under clang, mark cold checks unlikely
2026-09-21 22:51:08 -04:00
Daniel Lemire
b57a12270e Fold the format at compile time under clang, mark cold checks unlikely
Clang generated ~35% more instructions per float than GCC on identical
code (canada.txt, 64-bit: 276 vs 200 i/f on a Xeon Gold 6548N). Two
causes, confirmed with exact instruction traces:

1. Clang declines to inline the parser (from_chars_caller<T>::call) into
   its callers, so parse_options stays a runtime value and every format
   flag is re-tested on every conversion: javascript, skip_white_space,
   allow_leading_plus, fixed/scientific, the decimal point, the JSON
   dispatch. GCC inlines the whole parser into each call site, where
   chars_format::general is a constant, and all of it folds away.

   Fix: from_chars dispatches chars_format::general to a
   from_chars_fixed_format<T, UC, Fmt> instantiation, so the format is a
   compile-time constant inside the parser body whether or not the user's
   call is inlined. The call forwarder gets fastfloat_clang_really_inline
   so that it folds into that instantiation.

2. Clang if-converts the rare over/underflow fix-ups at the end of
   compute_float and the result_out_of_range test into cmov chains that
   every conversion executes. Marking them fastfloat_clang_unlikely keeps
   them as never-taken branches.

Both are applied under clang only, and GCC's generated code is identical
to before (verified on the object code). That is on purpose: forcing
always_inline on from_chars, or even on the trivial call forwarder,
regressed GCC by up to 14% on mesh.txt by changing its inlining order;
dispatching to the fixed-format instantiation made it stop inlining into
the loop; and the unlikely hints alone cost it 2-6% on mesh.txt by
reordering blocks (GCC already emits branches there).

realbenchmark, cycles per float, 5 interleaved rounds, medians:

clang 21, Xeon Gold 6548N        before   after
  canada.txt ASCII  (64)          49.7    44.6   -10%
  canada.txt UTF-16 (64)          57.4    52.0    -9%
  mesh.txt   ASCII  (64)          26.5    22.4   -16%
  mesh.txt   UTF-16 (64)          28.1    24.0   -15%
  (32-bit: -8% to -14%)
gcc 14, same machine: generated code unchanged.
Apple clang 17, arm64: +3% to +18% Mfloat/s.
2026-09-21 22:30:43 -04:00
Daniel Lemire
e9e04dec19
Merge pull request #412 from fastfloat/strncasecmp-simple
Restore the simple loop in fastfloat_strncasecmp
2026-09-21 22:17:08 -04:00
Daniel Lemire
7e45de0c3b Restore the simple loop in fastfloat_strncasecmp
PR #356 replaced the 3/5-character case-insensitive compare used by
parse_infnan with SWAR helpers (fastfloat_strncasecmp3/5 plus a generic
memcpy-based fastfloat_strncasecmp). That code only runs for "nan",
"inf" and "infinity" inputs, so it cannot help ordinary parsing, but it
grew the inlined inf/nan tail of the parser: on x86-64 (GCC 14 and
clang 21) the per-commit benchmarks dropped by 1.5-2.5% because the
extra dead code changed inlining decisions and register allocation on
the hot path (+7 instructions/float on canada.txt).

Go back to the plain loop, which the compiler unrolls for the constant
lengths 3 and 5, and drop the now-unused generic SWAR function. The
`actual < 256` guard of the original is unnecessary: OR-ing 0x20 into
a value can only equal an ASCII lowercase letter when the value is that
letter or its uppercase form, so the plain OR is exact for every
character type.
2026-09-21 22:05:34 -04:00
Daniel Lemire
6373592899
Merge pull request #410 from Algunenano/javascript-fmt-hot-path
Keep chars_format::javascript out of the common hot path
2026-09-21 19:01:45 -04:00
Raúl Marín
b2e41e8b88 Keep chars_format::javascript out of the common hot path
8.3.0 added the ECMAScript leading-zero rule as a runtime test on fmt inside
parse_number_string, so unlike basic_json_fmt its error returns and Annex B
octal scan land in the parser body that every from_chars instantiation
force-inlines. from_chars_caller<double>::call grew from 935 to 1031
instructions and other formats lost about 3% on short inputs.

Make the rule a compile-time parameter of the parser body, selected once at the
entry point, and take chars_format::javascript out to a cold conversion before
anything else is live so the branch is a tail call. parse_number_string keeps
its signature and behaviour.

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

              canada.txt    mesh.txt
  8.2.10         47.03        24.78
  8.3.0          47.34        26.08
  this           46.28        24.86
2026-09-21 19:47:21 +00:00
Daniel Lemire
b0ab987b3d Version 8.3.0 v8.3.0 2026-09-15 11:03:18 -04:00
Daniel Lemire
3254622f74 Add chars_format::javascript_sloppy (Annex B NonOctalDecimalIntegerLiteral)
chars_format::javascript follows the strict-mode grammar, which rejects
any leading zero. In sloppy mode, JavaScript also accepts a leading zero
when one of the digits is 8 or 9 ("08.5" is 8.5, Annex B's
NonOctalDecimalIntegerLiteral). A leading zero followed only by octal
digits is a LegacyOctalIntegerLiteral ("0775"), which is not a decimal
number: it is rejected with the new parse_error::legacy_octal_integer_part
so the caller can parse it in base 8.

javascript_sloppy sets one extra bit on top of javascript, and the check
lives inside the existing runtime JavaScript branch of
parse_number_string<false>, so the general path is unchanged. An
interleaved A/B with realbenchmark on canada.txt and mesh.txt (ASCII and
UTF-16, 32/64-bit) shows identical instructions per float and cycles
within noise.

Document both formats in the README.
2026-09-15 10:52:49 -04:00
Daniel Lemire
e0e355c073
Merge pull request #407 from fastfloat/javascript-format
Add chars_format::javascript (ECMAScript DecimalLiteral)
2026-09-15 10:44:07 -04:00
jadidbourbaki
20a9b35333 Gate the subnormal tie test on a compile-time predicate 2026-09-15 09:11:27 -04:00
Caolán McNamara
27b2a9dd06 drop the unreachable tail of parse_mantissa
counter is always zero when the loops end, so the final
add_native never runs

seen in Coverity as cid#488376 DEADCODE dead_error_line
2026-09-15 09:50:00 +01:00
jadidbourbaki
abb4c8659a Fix std::float16_t rounding of subnormal ties and fast-path overflow 2026-09-14 22:15:45 -04:00
Daniel Lemire
4fcb7e3249 Fix clang-format 17 on FASTFLOAT_ASSERT macros 2026-09-14 20:46:52 -04:00
Daniel Lemire
f837a78057 Add chars_format::javascript (ECMAScript DecimalLiteral)
The JSON format rejects leading zeros, which JavaScript also rejects,
but it further requires digits on both sides of the decimal point,
which JavaScript does not: ".5", "5." and "5.e3" are all valid
DecimalLiteral productions. Users lexing JavaScript therefore could not
use chars_format::json and had to re-check the integer span themselves
after a general-mode parse.

chars_format::javascript is general mode plus the leading-zero rule
(reported as leading_zeros_in_integer_part), with no_infnan. It is a
runtime check on the format, like fortran, so it reuses the existing
parse_number_string<false> instantiation. A leading minus sign is
accepted, and a leading plus sign with allow_leading_plus.

Claude-Session: https://claude.ai/code/session_01KbAWRBpE3MPy5cXECc5vWZ
2026-09-14 19:36:49 -04:00
Daniel Lemire
a8a02f7748
Merge pull request #405 from fastfloat/old-style-cast
Avoid old-style casts
2026-08-20 14:00:30 -04:00
Daniel Lemire
12e310aa8f Avoid old-style casts
Replace C-style casts with static_cast/const_cast so -Wold-style-cast
is clean, and enable that warning in the Unix test builds and the
amalgamated-header CI job.
2026-08-20 13:30:32 -04:00
Daniel Lemire
13ecd70727
Merge pull request #399 from dg0yt/max
Fix compatibility with min()/max() macros
2026-08-17 14:13:58 -04:00
Daniel Lemire
a1f63a883d
Merge pull request #404 from latent-9/docs/fix-strtod-typo
docs: fix strtod function name in README
2026-08-08 15:48:33 -04:00
latent-9
37fb7d73da
docs: fix strtod function name in README 2026-08-09 00:06:35 +12:00
Kai Pastor
c335514c6e Fix compat with min() macro 2026-07-23 09:58:09 +02:00
Kai Pastor
ec239e0e04 Fix compat with max() macro 2026-07-21 11:26:09 +02:00
Daniel Lemire
f6df0f2917
Merge pull request #398 from redis-performance/exp062-063-combo
Enable the 4-digit SWAR follow-up on GCC + skip the rounding-mode probe for long mantissas
2026-07-13 15:13:16 -04:00
Daniel Lemire
11c390b4dc
Merge pull request #396 from fastfloat/dependabot/github_actions/github-actions-640176b5ab
Bump actions/checkout from 6 to 7 in the github-actions group across 1 directory
2026-07-13 09:09:20 -04:00
dependabot[bot]
1fa146ed1d
Bump actions/checkout in the github-actions group across 1 directory
Bumps the github-actions group with 1 update in the / directory: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 6 to 7
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-07-13 00:06:02 +00:00
Daniel Lemire
ce25f0d195
Remove Stars section from README
Removed the Stars section and related chart from README.
2026-07-10 09:20:48 -04:00
fcostaoliveira
46b2fbaabb Reconcile redis-perf/optim: content superseded by upstream #381/#382/#387
EXP-050/052/053 were upstreamed in final form via #381 (integer-scan unroll)
and #382 (4-digit follow-up, clang-gated); the #387 span-elision restructure
then rewrote the surrounding code. This merge carries the old branch history
while the tree = upstream/main (8.2.7) + EXP-062 (ungate 4-digit SWAR on gcc)
+ EXP-063 (mantissa bound before rounds_to_nearest probe).

NOTE: EXP-052's 2x unroll of loop_parse_if_eight_digits was never upstreamed
and is dropped here pending post-#387 revalidation (see EXPERIMENTS.md).
2026-07-03 14:44:24 +01:00
fcostaoliveira
5082489e35 EXP-063: test the mode-independent mantissa bound before the rounds_to_nearest probe 2026-07-03 14:34:47 +01:00
fcostaoliveira
f4f36e04f7 EXP-062: enable the 4-digit SWAR fraction follow-up on all compilers (drop __clang__ gate) 2026-07-03 14:34:47 +01:00
Daniel Lemire
8ec5d236e2
Merge pull request #395 from sahvx655-wq/is-space-signed-compare
guard is_space against negative signed code units
2026-06-18 16:33:25 -04:00
sahvx655-wq
c539b5399c guard is_space against negative signed code units 2026-06-18 19:51:56 +05:30
Daniel Lemire
34164f547b 8.2.10 v8.2.10 2026-06-14 09:52:47 -04:00
Daniel Lemire
4eec7bec38
Merge pull request #394 from fastfloat/int-overflow-simdjson-approach
Int overflow check with a faster approach
2026-06-14 09:52:09 -04:00
Daniel Lemire
fd970ab05e updating visual studio 2026-06-13 21:41:53 -04:00
Daniel Lemire
a7249f86ed replace checked re-parse with O(1) simdjson-style overflow check
The previous commit detects multi-wrap u64 overflow at the max_digits
boundary by re-parsing the digits through a checked multiply-add loop
(O(max_digits)). Replace that with the constant-time check used in
simdjson: the leading digit plus a single threshold comparison.

For a max_digits-length value, min_safe_u64(base) == base^(max_digits-1)
is the smallest such value and also the width of each leading-digit band
[d*ms, (d+1)*ms). Since that width is < 2^64, the only band that can
straddle 2^64 is d == dmax (the largest leading digit that still fits),
and there it straddles at most once, so a single threshold dmax*ms
separates wrapped from non-wrapped values. A leading digit above dmax
always overflows; below dmax always fits. dmax and the threshold derive
from the existing min_safe_u64 table, so no new tables are needed and
dmax*ms cannot itself overflow.

Add a programmatic, self-verifying test for parse_int_string overflow
detection covering bases 2..36, complementing the hand-picked strings
added earlier. Every generated input is cross-checked against an
independent trusted oracle (a plain 64-bit checked multiply-add); on
success the parsed value is also compared exactly and full consumption
of the input is asserted.

Per base it exercises:
  - an exact-boundary sweep of the 64 values straddling 2^64
    (UINT64_MAX-31 .. 2^64+31), built by walking the digit string;
  - UINT64_MAX, 2^64 and the all-max-digit value, each also with
    leading zeros;
  - random max_digits-length values across every leading digit, with
    the heaviest sampling on the lead == dmax band that straddles 2^64,
    and full coverage of lead > dmax (the multi-wrap region the naive
    min_safe check accepted by mistake);
  - max_digits-1 (never overflows) and max_digits+1 (always overflows).
A small signed (int64_t) section checks the exact INT64_MIN/INT64_MAX
limits round-trip and that INT64_MAX+1 / INT64_MIN-1 are rejected in
every base.
2026-06-13 21:34:34 -04:00