Compare commits

...

31 Commits
v8.2.2 ... main

Author SHA1 Message Date
Daniel Lemire
221a4920db Merge branch 'main' of github.com:fastfloat/fast_float 2026-02-04 12:13:47 -05:00
Daniel Lemire
a110aa1392 adding bug report template 2026-02-04 12:13:25 -05:00
Daniel Lemire
8d9f627710
Merge pull request #366 from 976520/main
Add more boundary test cases for double and float parsing
2026-02-04 10:55:44 -05:00
재욱
3e2b5d3dc3 refactor verification calls for double and float limits 2026-02-04 15:36:31 +09:00
재욱
f43d6711bc Add additional verification cases for double and float limits 2026-02-04 15:27:46 +09:00
Daniel Lemire
01ce95dfe4 v8.2.3 2026-02-03 11:27:40 -05:00
Daniel Lemire
d5717d4a80
Merge pull request #365 from nyoma-diamond/fix-permissive-flag
Fix overzealous build-time  `/permissive-` propagating to compile-time (cross-compilation bug)
2026-02-03 11:26:49 -05:00
N'yoma Diamond
707fccb445
compile-time generator expression fixes overzealous /permissive- compiler option usage 2026-02-02 16:44:39 +00:00
Daniel Lemire
36a2b14eed
Merge pull request #361 from fastfloat/dependabot/github_actions/github-actions-c132dce648
Bump the github-actions group with 2 updates
2026-02-02 11:19:55 -05:00
dependabot[bot]
95295d1398
Bump the github-actions group with 2 updates
Bumps the github-actions group with 2 updates: [actions/checkout](https://github.com/actions/checkout) and [actions/setup-node](https://github.com/actions/setup-node).


Updates `actions/checkout` from 5.0.1 to 6.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Commits](https://github.com/actions/checkout/compare/v5.0.1...v6.0.1)

Updates `actions/setup-node` from 6.1.0 to 6.2.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](395ad32622...6044e13b5d)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/setup-node
  dependency-version: 6.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-02-02 00:10:32 +00:00
Daniel Lemire
c5256521ae
Merge pull request #363 from pitrou/patch-1
Clarify behavior for underflow
2026-01-28 13:53:54 -05:00
Antoine Pitrou
cb299bdecc
Clarify behavior for underflow
In https://github.com/fastfloat/fast_float/pull/189 the behavior on underflow was changed to better match the standard's recommendations, but the README does not mention underflow explicitly.
2026-01-28 10:55:48 +01:00
Daniel Lemire
2e4ed7d6fd
Merge pull request #362 from sleepingeight/surya/fix-opt-cmp
fix early return error in fastfloat_strncasecmp
2026-01-21 10:19:49 -05:00
sleepingieght
4fa83ccff4 fix early return error in fastfloat_strncasecmp 2026-01-21 19:21:06 +05:30
Daniel Lemire
71ab1cce81
Fix error message to display input instead of result 2026-01-19 20:36:29 -05:00
Daniel Lemire
64a68590fd
Clarify std::from_chars conversion method
Updated explanation of `std::from_chars` conversion checks.
2026-01-18 19:05:51 -05:00
Daniel Lemire
cffb71db98
Merge pull request #344 from fastfloat/dependabot/github_actions/github-actions-397fd93db7
Bump the github-actions group across 1 directory with 4 updates
2026-01-18 19:00:04 -05:00
Daniel Lemire
babb1f3335
Merge pull request #356 from sleepingeight/surya/opt-cmp
optimize fastfloat_strncasecmp
2026-01-18 18:56:05 -05:00
Daniel Lemire
fd9cad9f0c
Merge pull request #359 from shikharish/uint16
optimize uint16 parsing
2026-01-13 12:52:22 +01:00
dependabot[bot]
d1af5b18ac
Bump the github-actions group across 1 directory with 4 updates
Bumps the github-actions group with 4 updates in the / directory: [actions/checkout](https://github.com/actions/checkout), [actions/upload-artifact](https://github.com/actions/upload-artifact), [actions/setup-node](https://github.com/actions/setup-node) and [jidicula/clang-format-action](https://github.com/jidicula/clang-format-action).


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

Updates `actions/upload-artifact` from 5 to 6
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

Updates `actions/setup-node` from 6.0.0 to 6.1.0
- [Release notes](https://github.com/actions/setup-node/releases)
- [Commits](2028fbc5c2...395ad32622)

Updates `jidicula/clang-format-action` from 4.15.0 to 4.16.0
- [Release notes](https://github.com/jidicula/clang-format-action/releases)
- [Commits](4726374d1a...6cd220de46)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/setup-node
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
- dependency-name: jidicula/clang-format-action
  dependency-version: 4.16.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-05 00:11:02 +00:00
Shikhar
b14e6a466a simpler optimizations
Signed-off-by: Shikhar <shikharish05@gmail.com>
2026-01-02 05:21:01 +05:30
Shikhar
13d4b94183 small fix 2026-01-02 05:21:01 +05:30
Shikhar
d0af1cfdbd optimize uint16 parsing
Signed-off-by: Shikhar <shikharish05@gmail.com>
2026-01-02 05:21:01 +05:30
Shikhar
36d3441dc0 add bench_uint16
Signed-off-by: Shikhar <shikharish05@gmail.com>
2026-01-02 05:21:01 +05:30
Daniel Lemire
42ae960d95
Merge pull request #360 from fastfloat/fixs390
see if this fixes the issue with s390x.
2026-01-01 18:35:53 -05:00
Daniel Lemire
6440936afb see if this fixes the issue with s390x. 2026-01-01 17:49:15 -05:00
Daniel Lemire
011763f31c adding tests 2025-12-31 13:46:53 -05:00
Daniel Lemire
d5bc4e1b2e
Merge pull request #358 from shikharish/uint8-base-fix
add base check for uint8
2025-12-31 13:44:12 -05:00
Shikhar
4dc5225797 add base check for uint8 parsing
Signed-off-by: Shikhar <shikharish05@gmail.com>
2025-12-31 22:07:45 +05:30
sleepingieght
4eb0d806fa add specialisations 2025-12-30 20:27:45 +05:30
sleepingieght
265cb849f3 optimise fastfloat_strncasecmp 2025-12-30 01:15:22 +05:30
29 changed files with 528 additions and 130 deletions

24
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,24 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Ideally, you should provide a reproducible test case.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Additional context**
Add any other context about the problem here.
**Note:** Bug reports should come with a test case or, at least, an analysis.
**Automated tool policy**: If you use an automated tool (e.g., static analysis,
LLM, etc.), you need to demonstrate an understanding of the issue you are raising. Usually, a bug is demonstrated by a test case. Do not copy-paste what a tool is telling you.

View File

@ -18,7 +18,7 @@ jobs:
- riscv64
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6.0.2
- name: Install latest Alpine Linux for ${{ matrix.arch }}
uses: jirutka/setup-alpine@v1

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Compile with amalgamation
run: |
mkdir build &&

View File

@ -20,7 +20,7 @@ jobs:
fuzz-seconds: 300
output-sarif: true
- name: Upload Crash
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts

View File

@ -4,13 +4,13 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.2.2
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v4.2.2
- uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6.2.0
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
- name: Verify
run: emcc -v
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v3.6.0
uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v3.6.0
- name: Configure
run: emcmake cmake -B build
- name: Build # We build but do not test

View File

@ -24,10 +24,10 @@ jobs:
lint-and-format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
- uses: actions/checkout@0c366fd6a839edf440554fa01a7085ccba70ac98 # v4.1.7
- name: Run clang-format
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # v4.15.0
uses: jidicula/clang-format-action@6cd220de46c89139a0365edae93eee8eb30ca8fe # v4.16.0
with:
clang-format-version: '17'

View File

@ -23,7 +23,7 @@ jobs:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- uses: msys2/setup-msys2@v2
with:
update: true

View File

@ -29,7 +29,7 @@ jobs:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- uses: msys2/setup-msys2@v2
with:
update: true

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Amalgamate fast_float.h
run: |

View File

@ -6,7 +6,7 @@ jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Install packages
run: |
sudo apt-get update -q -y

View File

@ -12,7 +12,7 @@ jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- uses: uraimo/run-on-arch-action@v3
name: Test
id: runcmd
@ -22,9 +22,9 @@ jobs:
distro: ubuntu_latest
install: |
apt-get update -q -y
apt-get install -y cmake make g++
apt-get install -y cmake make g++ git
run: |
cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake -DCMAKE_BUILD_TYPE=Release -B build -DFASTFLOAT_TEST=ON
cmake --build build -j=2
ctest --output-on-failure --test-dir build

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Install clang++-14
run: sudo apt-get install -y clang++-14
- name: Use cmake

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Use cmake
run: |
mkdir build &&

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Use cmake
run: |
mkdir build &&

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Use cmake
run: |
mkdir build &&

View File

@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Use cmake
run: |
mkdir build &&

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6.0.2
- name: Use cmake
run: |
set -xe

View File

@ -14,7 +14,7 @@ jobs:
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
uses: actions/checkout@v6.0.2
- name: configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON

View File

@ -16,7 +16,7 @@ jobs:
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
uses: actions/checkout@v6.0.2
- name: configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination

View File

@ -16,7 +16,7 @@ jobs:
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
uses: actions/checkout@v6.0.2
- name: Configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -T ClangCL -DFASTFLOAT_TEST=ON

View File

@ -16,7 +16,7 @@ jobs:
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v5
uses: actions/checkout@v6.0.2
- name: configure
run: >-
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.14)
project(fast_float VERSION 8.2.2 LANGUAGES CXX)
project(fast_float VERSION 8.2.3 LANGUAGES CXX)
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
option(FASTFLOAT_TEST "Enable tests" OFF)
@ -57,13 +57,7 @@ if(FASTFLOAT_SANITIZE)
endif()
endif()
include(CheckCXXCompilerFlag)
unset(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
CHECK_CXX_COMPILER_FLAG(/permissive- FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
if(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
target_compile_options(fast_float INTERFACE /permissive-)
endif()
target_compile_options(fast_float INTERFACE $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,19.10>>:/permissive->)
if(FASTFLOAT_INSTALL)
include(CMakePackageConfigHelpers)

View File

@ -69,7 +69,7 @@ int main() {
}
```
Though the C++17 standard has you do a comparison with `std::errc()` to check whether the conversion worked, you can avoid it by casting the result to a `bool` like so:
Prior to C++26, checking for a successful `std::from_chars` conversion requires comparing the `from_chars_result::ec` member to `std::errc()`. As an extension `fast_float::from_chars` supports the improved C++26 API that allows checking the result by converting it to `bool`, like so:
```cpp
#include "fast_float/fast_float.h"
@ -83,7 +83,7 @@ int main() {
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
std::cerr << "failed to parse " << result << std::endl;
std::cerr << "failed to parse " << input << std::endl;
return EXIT_FAILURE;
}
```
@ -141,9 +141,12 @@ Furthermore, we have the following restrictions:
fixed-width floating-point types such as `std::float64_t`, `std::float32_t`,
`std::float16_t`, and `std::bfloat16_t`.
* We only support the decimal format: we do not support hexadecimal strings.
* For values that are either very large or very small (e.g., `1e9999`), we
represent it using the infinity or negative infinity value and the returned
* For values that are very large positives or negatives (e.g., `1e9999`), we
represent them using a positive or negative infinity and the returned
`ec` is set to `std::errc::result_out_of_range`.
* For values that are very close to zero (e.g., `1e-9999`), we represent them
using a positive or negative zero and the returned `ec` is set to
`std::errc::result_out_of_range`.
We support Visual Studio, macOS, Linux, freeBSD. We support big and little
endian. We support 32-bit and 64-bit systems.
@ -533,7 +536,7 @@ sufficiently recent version of CMake (3.11 or better at least):
FetchContent_Declare(
fast_float
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
GIT_TAG tags/v8.2.2
GIT_TAG tags/v8.2.3
GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float)
@ -549,7 +552,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v8.2.2)
GIT_TAG v8.2.3)
```
## Using as single header
@ -561,7 +564,7 @@ if desired as described in the command line help.
You may directly download automatically generated single-header files:
<https://github.com/fastfloat/fast_float/releases/download/v8.2.2/fast_float.h>
<https://github.com/fastfloat/fast_float/releases/download/v8.2.3/fast_float.h>
## Benchmarking

View File

@ -11,7 +11,9 @@ FetchContent_MakeAvailable(counters)
add_executable(realbenchmark benchmark.cpp)
target_link_libraries(realbenchmark PRIVATE counters::counters)
add_executable(bench_ip bench_ip.cpp)
add_executable(bench_uint16 bench_uint16.cpp)
target_link_libraries(bench_ip PRIVATE counters::counters)
target_link_libraries(bench_uint16 PRIVATE counters::counters)
set_property(
TARGET realbenchmark
@ -19,8 +21,12 @@ set_property(
set_property(
TARGET bench_ip
PROPERTY CXX_STANDARD 17)
set_property(
TARGET bench_uint16
PROPERTY CXX_STANDARD 17)
target_link_libraries(realbenchmark PUBLIC fast_float)
target_link_libraries(bench_ip PUBLIC fast_float)
target_link_libraries(bench_uint16 PUBLIC fast_float)
include(ExternalProject)

139
benchmarks/bench_uint16.cpp Normal file
View File

@ -0,0 +1,139 @@
#include "counters/bench.h"
#include "fast_float/fast_float.h"
#include <charconv>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <random>
#include <atomic>
#include <string>
#include <vector>
void pretty_print(size_t volume, size_t bytes, std::string name,
counters::event_aggregate agg) {
if (agg.inner_count > 1) {
printf("# (inner count: %d)\n", agg.inner_count);
}
printf("%-40s : ", name.c_str());
printf(" %5.2f GB/s ", bytes / agg.fastest_elapsed_ns());
printf(" %5.1f Mip/s ", volume * 1000.0 / agg.fastest_elapsed_ns());
printf(" %5.2f ns/ip ", agg.fastest_elapsed_ns() / volume);
if (counters::event_collector().has_events()) {
printf(" %5.2f GHz ", agg.fastest_cycles() / agg.fastest_elapsed_ns());
printf(" %5.2f c/ip ", agg.fastest_cycles() / volume);
printf(" %5.2f i/ip ", agg.fastest_instructions() / volume);
printf(" %5.2f c/b ", agg.fastest_cycles() / bytes);
printf(" %5.2f i/b ", agg.fastest_instructions() / bytes);
printf(" %5.2f i/c ", agg.fastest_instructions() / agg.fastest_cycles());
}
printf("\n");
}
enum class parse_method { standard, fast_float };
void validate(const std::string &buffer, const std::vector<uint16_t> &expected,
char delimiter) {
const char *p = buffer.data();
const char *pend = p + buffer.size();
for (size_t i = 0; i < expected.size(); i++) {
uint16_t val;
auto r = fast_float::from_chars(p, pend, val);
if (r.ec != std::errc() || val != expected[i]) {
printf("Validation failed at index %zu: expected %u, got %u\n", i,
expected[i], val);
std::abort();
}
p = r.ptr;
if (i + 1 < expected.size()) {
if (p >= pend || *p != delimiter) {
printf("Validation failed at index %zu: delimiter mismatch\n", i);
std::abort();
}
++p;
}
}
if (p != pend) {
printf("Validation failed: trailing bytes remain\n");
std::abort();
}
printf("Validation passed!\n");
}
int main() {
constexpr size_t N = 500000;
constexpr char delimiter = ',';
std::mt19937 rng(1234);
std::uniform_int_distribution<int> dist(0, 65535);
std::vector<uint16_t> expected;
expected.reserve(N);
std::string buffer;
buffer.reserve(N * 6); // up to 5 digits + delimiter
for (size_t i = 0; i < N; ++i) {
uint16_t val = (uint16_t)dist(rng);
expected.push_back(val);
std::string s = std::to_string(val);
buffer.append(s);
if (i + 1 < N) {
buffer.push_back(delimiter);
}
}
size_t total_bytes = buffer.size();
validate(buffer, expected, delimiter);
volatile uint64_t sink = 0;
pretty_print(N, total_bytes, "parse_uint16_std_fromchars",
counters::bench([&]() {
uint64_t sum = 0;
const char *p = buffer.data();
const char *pend = p + buffer.size();
for (size_t i = 0; i < N; ++i) {
uint16_t value = 0;
auto r = std::from_chars(p, pend, value);
if (r.ec != std::errc())
std::abort();
sum += value;
p = r.ptr;
if (i + 1 < N) {
if (p >= pend || *p != delimiter)
std::abort();
++p;
}
}
if (p != pend)
std::abort();
sink += sum;
}));
pretty_print(N, total_bytes, "parse_uint16_fastfloat", counters::bench([&]() {
uint64_t sum = 0;
const char *p = buffer.data();
const char *pend = p + buffer.size();
for (size_t i = 0; i < N; ++i) {
uint16_t value = 0;
auto r = fast_float::from_chars(p, pend, value);
if (r.ec != std::errc())
std::abort();
sum += value;
p = r.ptr;
if (i + 1 < N) {
if (p >= pend || *p != delimiter)
std::abort();
++p;
}
}
if (p != pend)
std::abort();
sink += sum;
}));
return EXIT_SUCCESS;
}

View File

@ -32,7 +32,7 @@ template <typename UC> fastfloat_really_inline constexpr bool has_simd_opt() {
// able to optimize it well.
template <typename UC>
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
return !(c > UC('9') || c < UC('0'));
return (unsigned)(c - UC('0')) <= 9u;
}
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
@ -68,6 +68,25 @@ read8_to_u64(UC const *chars) {
return val;
}
// Read 4 UC into a u32. Truncates UC if not char.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t
read4_to_u32(UC const *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
uint32_t val = 0;
for (int i = 0; i < 4; ++i) {
val |= uint32_t(uint8_t(*chars)) << (i * 8);
++chars;
}
return val;
}
uint32_t val;
::memcpy(&val, chars, sizeof(uint32_t));
#if FASTFLOAT_IS_BIG_ENDIAN == 1
val = byteswap_32(val);
#endif
return val;
}
#ifdef FASTFLOAT_SSE2
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
@ -149,6 +168,18 @@ is_made_of_eight_digits_fast(uint64_t val) noexcept {
0x8080808080808080));
}
fastfloat_really_inline constexpr bool
is_made_of_four_digits_fast(uint32_t val) noexcept {
return !((((val + 0x46464646) | (val - 0x30303030)) & 0x80808080));
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
parse_four_digits_unrolled(uint32_t val) noexcept {
val -= 0x30303030;
val = (val * 10) + (val >> 8);
return (((val & 0x00FF00FF) * 0x00640001) >> 16) & 0xFFFF;
}
#ifdef FASTFLOAT_HAS_SIMD
// Call this if chars might not be 8 digits.
@ -515,6 +546,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
UC const *const start_digits = p;
FASTFLOAT_IF_CONSTEXPR17((std::is_same<T, std::uint8_t>::value)) {
if (base == 10) {
const size_t len = (size_t)(pend - p);
if (len == 0) {
if (has_leading_zeros) {
@ -603,6 +635,57 @@ parse_int_string(UC const *p, UC const *pend, T &value,
answer.ptr = p + nd;
return answer;
}
}
FASTFLOAT_IF_CONSTEXPR17((std::is_same<T, std::uint16_t>::value)) {
if (base == 10) {
const size_t len = size_t(pend - p);
if (len == 0) {
if (has_leading_zeros) {
value = 0;
answer.ec = std::errc();
answer.ptr = p;
} else {
answer.ec = std::errc::invalid_argument;
answer.ptr = first;
}
return answer;
}
if (len >= 4) {
uint32_t digits = read4_to_u32(p);
if (is_made_of_four_digits_fast(digits)) {
uint32_t v = parse_four_digits_unrolled(digits);
if (len >= 5 && is_integer(p[4])) {
v = v * 10 + uint32_t(p[4] - '0');
if (len >= 6 && is_integer(p[5])) {
answer.ec = std::errc::result_out_of_range;
const UC *q = p + 5;
while (q != pend && is_integer(*q)) {
q++;
}
answer.ptr = q;
return answer;
}
if (v > 65535) {
answer.ec = std::errc::result_out_of_range;
answer.ptr = p + 5;
return answer;
}
value = uint16_t(v);
answer.ec = std::errc();
answer.ptr = p + 5;
return answer;
}
// 4 digits
value = uint16_t(v);
answer.ec = std::errc();
answer.ptr = p + 4;
return answer;
}
}
}
}
uint64_t i = 0;
if (base == 10) {

View File

@ -2,6 +2,7 @@
#define FASTFLOAT_FLOAT_COMMON_H
#include <cfloat>
#include <cstddef>
#include <cstdint>
#include <cassert>
#include <cstring>
@ -17,7 +18,7 @@
#define FASTFLOAT_VERSION_MAJOR 8
#define FASTFLOAT_VERSION_MINOR 2
#define FASTFLOAT_VERSION_PATCH 2
#define FASTFLOAT_VERSION_PATCH 3
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
@ -267,18 +268,151 @@ struct is_supported_char_type
> {
};
template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp3(UC const *actual_mixedcase,
UC const *expected_lowercase) {
uint64_t mask{0};
FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 1) { mask = 0x2020202020202020; }
else FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 2) {
mask = 0x0020002000200020;
}
else FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 4) {
mask = 0x0000002000000020;
}
else {
return false;
}
uint64_t val1{0}, val2{0};
if (cpp20_and_in_constexpr()) {
for (size_t i = 0; i < 3; i++) {
if ((actual_mixedcase[i] | 32) != expected_lowercase[i]) {
return false;
}
}
return true;
} else {
FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 1 || sizeof(UC) == 2) {
::memcpy(&val1, actual_mixedcase, 3 * sizeof(UC));
::memcpy(&val2, expected_lowercase, 3 * sizeof(UC));
val1 |= mask;
val2 |= mask;
return val1 == val2;
}
else FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 4) {
::memcpy(&val1, actual_mixedcase, 2 * sizeof(UC));
::memcpy(&val2, expected_lowercase, 2 * sizeof(UC));
val1 |= mask;
if (val1 != val2) {
return false;
}
return (actual_mixedcase[2] | 32) == (expected_lowercase[2]);
}
else {
return false;
}
}
return true;
}
template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp5(UC const *actual_mixedcase,
UC const *expected_lowercase) {
uint64_t mask{0};
uint64_t val1{0}, val2{0};
if (cpp20_and_in_constexpr()) {
for (size_t i = 0; i < 5; i++) {
if ((actual_mixedcase[i] | 32) != expected_lowercase[i]) {
return false;
}
}
return true;
} else {
FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 1) {
mask = 0x2020202020202020;
::memcpy(&val1, actual_mixedcase, 5 * sizeof(UC));
::memcpy(&val2, expected_lowercase, 5 * sizeof(UC));
val1 |= mask;
val2 |= mask;
return val1 == val2;
}
else FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 2) {
mask = 0x0020002000200020;
::memcpy(&val1, actual_mixedcase, 4 * sizeof(UC));
::memcpy(&val2, expected_lowercase, 4 * sizeof(UC));
val1 |= mask;
if (val1 != val2) {
return false;
}
return (actual_mixedcase[4] | 32) == (expected_lowercase[4]);
}
else FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 4) {
mask = 0x0000002000000020;
::memcpy(&val1, actual_mixedcase, 2 * sizeof(UC));
::memcpy(&val2, expected_lowercase, 2 * sizeof(UC));
val1 |= mask;
if (val1 != val2) {
return false;
}
::memcpy(&val1, actual_mixedcase + 2, 2 * sizeof(UC));
::memcpy(&val2, expected_lowercase + 2, 2 * sizeof(UC));
val1 |= mask;
if (val1 != val2) {
return false;
}
return (actual_mixedcase[4] | 32) == (expected_lowercase[4]);
}
else {
return false;
}
}
return true;
}
// Compares two ASCII strings in a case insensitive manner.
template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
size_t length) {
for (size_t i = 0; i < length; ++i) {
UC const actual = actual_mixedcase[i];
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
uint64_t mask{0};
FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 1) { mask = 0x2020202020202020; }
else FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 2) {
mask = 0x0020002000200020;
}
else FASTFLOAT_IF_CONSTEXPR17(sizeof(UC) == 4) {
mask = 0x0000002000000020;
}
else {
return false;
}
if (cpp20_and_in_constexpr()) {
for (size_t i = 0; i < length; i++) {
if ((actual_mixedcase[i] | 32) != expected_lowercase[i]) {
return false;
}
}
return true;
} else {
uint64_t val1{0}, val2{0};
size_t sz{8 / (sizeof(UC))};
for (size_t i = 0; i < length; i += sz) {
val1 = val2 = 0;
sz = std::min(sz, length - i);
::memcpy(&val1, actual_mixedcase + i, sz * sizeof(UC));
::memcpy(&val2, expected_lowercase + i, sz * sizeof(UC));
val1 |= mask;
val2 |= mask;
if (val1 != val2) {
return false;
}
}
return true;
}
}
#ifndef FLT_EVAL_METHOD

View File

@ -35,7 +35,7 @@ from_chars_result_t<UC>
++first;
}
if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
if (fastfloat_strncasecmp3(first, str_const_nan<UC>())) {
answer.ptr = (first += 3);
value = minusSign ? -std::numeric_limits<T>::quiet_NaN()
: std::numeric_limits<T>::quiet_NaN();
@ -54,9 +54,9 @@ from_chars_result_t<UC>
}
return answer;
}
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
if (fastfloat_strncasecmp3(first, str_const_inf<UC>())) {
if ((last - first >= 8) &&
fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
fastfloat_strncasecmp5(first + 3, str_const_inf<UC>() + 3)) {
answer.ptr = first + 8;
} else {
answer.ptr = first + 3;

View File

@ -1167,6 +1167,9 @@ TEST_CASE("double.general") {
// DBL_TRUE_MIN / 2 + 0.0000000000000001e-324
verify("2.4703282292062328e-324", 0x0.0000000000001p-1022);
verify("0.2470328229206232720e-323", 0.0, std::errc::result_out_of_range);
verify("0.2470328229206232721e-323", 0x0.0000000000001p-1022);
verify("-2.2222222222223e-322", -0x1.68p-1069);
verify("9007199254740993.0", 0x1p+53);
verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328);
@ -1262,8 +1265,13 @@ TEST_CASE("double.general") {
verify("4.9406564584124654e-324", 0x0.0000000000001p-1022);
verify("2.2250738585072009e-308", 0x0.fffffffffffffp-1022);
verify("2.2250738585072014e-308", 0x1p-1022);
verify("0.2225073858507201136e-307", 0x0.fffffffffffffp-1022);
verify("0.2225073858507201137e-307", 0x1p-1022);
verify("1.7976931348623157e308", 0x1.fffffffffffffp+1023);
verify("1.7976931348623158e308", 0x1.fffffffffffffp+1023);
verify("1.7976931348623158079e308", std::numeric_limits<double>::max());
verify("1.7976931348623158080e308", std::numeric_limits<double>::infinity(),
std::errc::result_out_of_range);
verify("4503599627370496.5", 4503599627370496.5);
verify("4503599627475352.5", 4503599627475352.5);
verify("4503599627475353.5", 4503599627475353.5);
@ -1543,6 +1551,8 @@ TEST_CASE("float.general") {
verify("0.7006492e-45", 0.f, std::errc::result_out_of_range);
// FLT_TRUE_MIN / 2 + 0.0000001e-45
verify("0.7006493e-45", 0x1p-149f);
verify("0.7006492321624085354e-45", 0.f, std::errc::result_out_of_range);
verify("0.7006492321624085355e-45", 0x1p-149f);
// max
verify("340282346638528859811704183484516925440", 0x1.fffffep+127f);
@ -1553,6 +1563,9 @@ TEST_CASE("float.general") {
// that rounds to FLT_MAX
verify("340282356779733661637539395458142568447",
std::numeric_limits<float>::max());
verify("0.3402823567797336616e39", std::numeric_limits<float>::max());
verify("0.3402823567797336617e39", std::numeric_limits<float>::infinity(),
std::errc::result_out_of_range);
verify("-1e-999", -0.0f, std::errc::result_out_of_range);
verify("1."
@ -1563,6 +1576,8 @@ TEST_CASE("float.general") {
"175494140627517859246175898662808184331245864732796240031385942718174"
"6759860647699724722770042717456817626953125e-38",
0x1.fffff8p-127f);
verify("1.1754942807573642917e-38", 0x1.fffffcp-127f);
verify("1.1754942807573642918e-38", std::numeric_limits<float>::min());
verify_runtime(
append_zeros("1."
"17549414062751785924617589866280818433124586473279624003138"