Skip to content

Commit 4eec7be

Browse files
authored
Merge pull request #394 from fastfloat/int-overflow-simdjson-approach
Int overflow check with a faster approach
2 parents 8234a89 + fd970ab commit 4eec7be

6 files changed

Lines changed: 364 additions & 17 deletions

File tree

.github/workflows/vs17-arm-ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
include:
13-
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Release}
14-
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug}
13+
- {gen: Visual Studio 18 2026, arch: ARM64, cfg: Release}
14+
- {gen: Visual Studio 18 2026, arch: ARM64, cfg: Debug}
1515
steps:
1616
- name: checkout
1717
uses: actions/checkout@v6.0.2

.github/workflows/vs17-ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
include:
13-
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
14-
#- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
15-
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
16-
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
13+
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release}
14+
#- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug}
15+
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release}
16+
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug}
1717
steps:
1818
- name: checkout
1919
uses: actions/checkout@v6.0.2

.github/workflows/vs17-clang-ci.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
include:
13-
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
14-
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
15-
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
16-
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
13+
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release}
14+
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug}
15+
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release}
16+
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug}
1717
steps:
1818
- name: checkout
1919
uses: actions/checkout@v6.0.2

.github/workflows/vs17-cxx20.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ jobs:
1010
fail-fast: false
1111
matrix:
1212
include:
13-
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
14-
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
15-
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
16-
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
13+
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release}
14+
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug}
15+
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release}
16+
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug}
1717
steps:
1818
- name: checkout
1919
uses: actions/checkout@v6.0.2

include/fast_float/ascii_number.h

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -781,9 +781,27 @@ parse_int_string(UC const *p, UC const *pend, T &value,
781781
}
782782
// this check can be eliminated for all other types, but they will all require
783783
// a max_digits(base) equivalent
784-
if (digit_count == max_digits && i < min_safe_u64(base)) {
785-
answer.ec = std::errc::result_out_of_range;
786-
return answer;
784+
if (digit_count == max_digits) {
785+
// At the max_digits boundary the accumulator `i` may have wrapped around
786+
// 2^64. A plain `i < min_safe_u64(base)` test is not sufficient: for any
787+
// base whose max_digits-length range exceeds 2^64 (base 10 reaches
788+
// ~5.4 * 2^64 at 20 digits) the value can wrap a whole multiple of 2^64 and
789+
// land back above min_safe, slipping through. Decide exactly in O(1) using
790+
// the leading digit, following the approach used in simdjson:
791+
// ms == min_safe_u64(base) == base^(max_digits-1), the smallest
792+
// max_digits-length value.
793+
// dmax == the largest leading digit whose number can still fit in u64.
794+
// The leading-digit band [d*ms, (d+1)*ms) has width ms < 2^64, so within
795+
// the single band where d == dmax the value straddles 2^64 at most once,
796+
// and a single threshold separates wrapped from non-wrapped values. A
797+
// leading digit above dmax always overflows; below dmax always fits.
798+
uint64_t const ms = min_safe_u64(base);
799+
uint64_t const dmax = (std::numeric_limits<uint64_t>::max)() / ms;
800+
uint64_t const lead = ch_to_digit(*start_digits);
801+
if (lead > dmax || (lead == dmax && i < dmax * ms)) {
802+
answer.ec = std::errc::result_out_of_range;
803+
return answer;
804+
}
787805
}
788806

789807
// check other types overflow

0 commit comments

Comments
 (0)