Merge c982adabbdb797411477eec57e878effdec030c0 into 0de7a21da582b6c84b0546772a96549882905d2b

This commit is contained in:
HedgehogInTheCPP 2026-10-27 20:41:45 +00:00 committed by GitHub
commit 146faa39b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
55 changed files with 2760 additions and 1646 deletions

View File

@ -1,4 +1,4 @@
We have benchmarks, please consider running them: [see our README for details](https://github.com/fastfloat/fast_float/blob/main/README.md#benchmarking). We expect you to run our benchmarks if you make claims with respect to the performance. We have benchmarks, please consider running them: [see our README for details](https://github.com/irainman/fast_float/blob/main/README.md#benchmarking). We expect you to run our benchmarks if you make claims with respect to the performance.
Our CI tests check formatting automating. If such a test fails, please consider running the bash script: Our CI tests check formatting automating. If such a test fails, please consider running the bash script:

View File

@ -1,13 +1,20 @@
name: Alpine Linux name: Alpine Linux
on: on:
- push push:
- pull_request pull_request:
permissions:
contents: read
jobs: jobs:
build: build:
name: Build on Alpine ${{ matrix.arch }} name: Build on Alpine ${{ matrix.arch }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
arch: arch:
- x86_64 - x86_64
@ -16,34 +23,44 @@ jobs:
- armv7 - armv7
- ppc64le - ppc64le
- riscv64 - riscv64
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v7 uses: actions/checkout@v7
- name: Install latest Alpine Linux for ${{ matrix.arch }} - name: Install Alpine Linux for ${{ matrix.arch }}
uses: jirutka/setup-alpine@v1 uses: jirutka/setup-alpine@v1
with: with:
arch: ${{ matrix.arch }} arch: ${{ matrix.arch }}
# v3.22 is the last release with apk-tools v2; setup-alpine v1 bootstraps
# with apk.static v2, which cannot set up the apk v3 releases. # Keep CI reproducible.
branch: v3.22 # riscv64 currently uses edge because stable Alpine support
# for this architecture can lag behind the other architectures.
branch: ${{ matrix.arch == 'riscv64' && 'edge' || 'v3.23' }}
packages: > packages: >
build-base build-base
cmake cmake
g++
linux-headers linux-headers
git git
bash bash
build-base
- name: Prepare - name: Prepare
run: | run: |
cmake -DFASTFLOAT_TEST=ON -B build cmake \
-DFASTFLOAT_TEST=ON \
-B build
shell: alpine.sh {0} shell: alpine.sh {0}
- name: Build - name: Build
run: | run: |
cmake --build build cmake --build build
shell: alpine.sh {0} shell: alpine.sh {0}
- name: Test - name: Test
run: | run: |
ctest --test-dir build -R basictest ctest \
--test-dir build \
-R basictest \
--output-on-failure
shell: alpine.sh {0} shell: alpine.sh {0}

View File

@ -1,10 +1,13 @@
name: Amalgamate Ubuntu 24.04 CI name: Amalgamate Ubuntu CI
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Compile with amalgamation - name: Compile with amalgamation

View File

@ -1,11 +1,14 @@
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@e8d4307400f9427dba7cb98e488d6ab85f1cec5f # v4.2.2 - uses: actions/checkout@e8d4307400f9427dba7cb98e488d6ab85f1cec5f # v4.2.2
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
- uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16 - uses: mymindstorm/setup-emsdk@4528d102f7230f0e7b276855c01ea1159be0e984 # v16
- name: Verify - name: Verify
run: emcc -v run: emcc -v

View File

@ -29,5 +29,5 @@ jobs:
- name: Run clang-format - name: Run clang-format
uses: jidicula/clang-format-action@654a770daa28443dd111d133e4083e21c1075674 # v4.18.0 uses: jidicula/clang-format-action@654a770daa28443dd111d133e4083e21c1075674 # v4.18.0
with: with:
clang-format-version: '17' clang-format-version: '18'

View File

@ -1,36 +0,0 @@
name: MSYS2-CLANG-CI
on: [push, pull_request]
jobs:
windows-mingw:
name: ${{ matrix.msystem }}
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
strategy:
fail-fast: false
matrix:
include:
- msystem: "MINGW64"
install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang
type: Release
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v7
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: ${{ matrix.msystem }}
install: ${{ matrix.install }}
- name: Prepare build dir
run: mkdir build
- name: Configure
run: cd build && cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON ..
- name: Build
run: cmake --build build
- name: Run basic tests
run: cd build && ctest --output-on-failure -R basictest

View File

@ -1,39 +0,0 @@
name: MSYS2-CI
on: [push, pull_request]
jobs:
windows-mingw:
name: ${{ matrix.msystem }}
runs-on: windows-latest
defaults:
run:
shell: msys2 {0}
strategy:
fail-fast: false
matrix:
include:
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: Release
- msystem: "MINGW64"
install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc
type: Debug
env:
CMAKE_GENERATOR: Ninja
steps:
- uses: actions/checkout@v7
- uses: msys2/setup-msys2@v2
with:
update: true
msystem: ${{ matrix.msystem }}
install: ${{ matrix.install }}
- name: Prepare build dir
run: mkdir build
- name: Configure
run: cd build && cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON ..
- name: Build
run: cmake --build build
- name: Run basic tests
run: cd build && ctest --output-on-failure -R basictest

View File

@ -2,6 +2,9 @@ name: Ubuntu RISC-V rvv VLEN=128 (clang 17)
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
build: build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04

View File

@ -7,6 +7,9 @@ on:
pull_request: pull_request:
branches: branches:
- main - main
permissions:
contents: read
jobs: jobs:
build: build:

View File

@ -1,10 +1,13 @@
name: Ubuntu 22.04 CI Sanitized (GCC 11) name: Ubuntu latest CI Sanitized
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Use cmake - name: Use cmake

19
.github/workflows/ubuntu.yml vendored Normal file
View File

@ -0,0 +1,19 @@
name: Ubuntu latest CI
on: [push, pull_request]
permissions:
contents: read
jobs:
ubuntu-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DFASTFLOAT_TEST=ON .. &&
cmake --build . &&
ctest --output-on-failure

View File

@ -1,25 +1,28 @@
name: Ubuntu 22.04 CI (clang 14) name: Ubuntu 22.04 CI (clang 15)
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- name: Install clang++-14 - name: Install clang++-15
run: sudo apt-get install -y clang++-14 run: sudo apt-get install -y clang++-15
- name: Use cmake - name: Use cmake
run: | run: |
mkdir build && mkdir build &&
cd build && cd build &&
CXX=clang++-14 cmake -DFASTFLOAT_TEST=ON .. && CXX=clang++-15 cmake -DFASTFLOAT_TEST=ON .. &&
cmake --build . && cmake --build . &&
ctest --output-on-failure ctest --output-on-failure
- name: Use cmake CXX20 - name: Use cmake CXX20
run: | run: |
mkdir build20 && mkdir build20 &&
cd build20 && cd build20 &&
CXX=clang++-14 cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. && CXX=clang++-15 cmake -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. &&
cmake --build . && cmake --build . &&
ctest --output-on-failure ctest --output-on-failure

View File

@ -2,6 +2,9 @@ name: Ubuntu 22.04 CI (GCC 12)
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04

View File

@ -2,6 +2,9 @@ name: Ubuntu 22.04 CI (GCC 11)
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04

View File

@ -2,6 +2,9 @@ name: Ubuntu 24.04 CI
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04

View File

@ -2,6 +2,9 @@ name: Ubuntu 24.04 CI (GCC 13)
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04

View File

@ -2,22 +2,25 @@ name: VS17-ARM-CI
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ci: ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}} name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest runs-on: windows-2022
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- {gen: Visual Studio 18 2026, arch: ARM64, cfg: Release} - {gen: Visual Studio 17 2022, arch: ARM64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: ARM64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v7 uses: actions/checkout@v7
- name: configure - name: configure
run: | run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_CXX_STANDARD=14 -DFASTFLOAT_TEST=ON
- name: build - name: build
run: | run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel cmake --build build --verbose --config ${{matrix.cfg}} --parallel

View File

@ -2,24 +2,27 @@ name: VS17-CI
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ci: ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}} name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest runs-on: windows-2022
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
#- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release} - {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v7 uses: actions/checkout@v7
- name: configure - name: configure
run: | run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_CXX_STANDARD=14 -DFASTFLOAT_BENCHMARKS=ON -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination
- name: build - name: build
run: | run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel cmake --build build --verbose --config ${{matrix.cfg}} --parallel

View File

@ -2,18 +2,21 @@ name: VS17-CLANG-CI
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ci: ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}} name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest runs-on: windows-2022
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release} - {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v7 uses: actions/checkout@v7

View File

@ -2,18 +2,21 @@ name: VS17-CI C++20
on: [push, pull_request] on: [push, pull_request]
permissions:
contents: read
jobs: jobs:
ci: ci:
name: vs17/${{matrix.arch}}/${{matrix.cfg}} name: vs17/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-latest runs-on: windows-2022
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
include: include:
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release} - {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v7 uses: actions/checkout@v7

31
.github/workflows/vs18-arm-ci.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: VS18-ARM-CI
on: [push, pull_request]
permissions:
contents: read
jobs:
ci:
name: vs18/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-2025
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 18 2026, arch: ARM64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: ARM64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v7
- name: configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_CXX_STANDARD=14 -DFASTFLOAT_TEST=ON
- name: build
run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
# disabled because it requires a toolchain
#- name: test
# run: |
# cd build &&
# ctest --output-on-failure -C ${{matrix.cfg}}

46
.github/workflows/vs18-ci.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: VS18-CI
on: [push, pull_request]
permissions:
contents: read
jobs:
ci:
name: vs18/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-2025
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release}
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v7
- name: configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_CXX_STANDARD=14 -DFASTFLOAT_BENCHMARKS=OFF -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination
- name: build
run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
- name: test
run: |
cd build &&
ctest --output-on-failure -C ${{matrix.cfg}}
- name: install
run: |
cd build &&
cmake --install .
- name: test install (find)
run: |
cd tests/installation_tests/find &&
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination &&
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
- name: test install (issue 72)
run: |
cd tests/installation_tests/issue72_installation &&
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination &&
cmake --build build --verbose --config ${{matrix.cfg}} --parallel

31
.github/workflows/vs18-clang-ci.yml vendored Normal file
View File

@ -0,0 +1,31 @@
name: VS18-CLANG-CI
on: [push, pull_request]
permissions:
contents: read
jobs:
ci:
name: vs18/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-2025
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release}
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v7
- name: Configure
run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -T ClangCL -DFASTFLOAT_TEST=ON
- name: Build
run: cmake --build build --config ${{matrix.cfg}} --parallel --verbose
- name: Run basic tests
run: |
cd build
ctest -C ${{matrix.cfg}} --output-on-failure -R basictest

36
.github/workflows/vs18-cxx23.yml vendored Normal file
View File

@ -0,0 +1,36 @@
name: VS18-CI C++23
on: [push, pull_request]
permissions:
contents: read
jobs:
ci:
name: vs18/${{matrix.arch}}/${{matrix.cfg}}
runs-on: windows-2025
strategy:
fail-fast: false
matrix:
include:
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Release}
- {gen: Visual Studio 18 2026, arch: Win32, cfg: Debug}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Release}
- {gen: Visual Studio 18 2026, arch: x64, cfg: Debug}
steps:
- name: checkout
uses: actions/checkout@v7
- name: configure
run: >-
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}
-DFASTFLOAT_CXX_STANDARD=23
-DFASTFLOAT_TEST=ON
-DFASTFLOAT_CONSTEXPR_TESTS=ON
-DCMAKE_INSTALL_PREFIX:PATH=destination
- name: build
run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
- name: test
run: |
cd build &&
ctest --output-on-failure -C ${{matrix.cfg}}

View File

@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.14)
project(fast_float VERSION 8.3.2 LANGUAGES CXX) project(fast_float VERSION 8.3.4 LANGUAGES CXX)
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat") set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD}) set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
option(FASTFLOAT_TEST "Enable tests" OFF) option(FASTFLOAT_TEST "Enable tests" OFF)

View File

@ -9,3 +9,4 @@ Jan Pharago
Maya Warrier Maya Warrier
Taha Khokhar Taha Khokhar
Anders Dalvander Anders Dalvander
Elle Solomina

View File

@ -2,7 +2,7 @@
module( module(
name = "fast_float", name = "fast_float",
version = "8.3.2", version = "8.3.4",
compatibility_level = 8, compatibility_level = 8,
) )

137
README.md
View File

@ -1,10 +1,124 @@
## fast_float number parsing library: 4x faster than strtod ## fast_float HE edition number parsing library.
[![Ubuntu 22.04](https://github.com/irainman/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/ubuntu22.yml)
[![Ubuntu 22.04 clang](https://github.com/irainman/fast_float/actions/workflows/ubuntu22-clang.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/ubuntu22-clang.yml)
[![Ubuntu 24.04](https://github.com/irainman/fast_float/actions/workflows/ubuntu24.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/ubuntu24.yml)
[![Ubuntu 24.04 C++20](https://github.com/irainman/fast_float/actions/workflows/ubuntu24-cxx20.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/ubuntu24-cxx20.yml)
[![vs17](https://github.com/irainman/fast_float/actions/workflows/vs17-ci.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/vs17-ci.yml)
[![vs17 C++20](https://github.com/irainman/fast_float/actions/workflows/vs17-cxx20.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/vs17-cxx20.yml)
[![vs17 clang](https://github.com/irainman/fast_float/actions/workflows/vs17-clang-ci.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/vs17-clang-ci.yml)
[![vs18](https://github.com/irainman/fast_float/actions/workflows/vs18-ci.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/vs18-ci.yml)
[![vs18 C++23](https://github.com/irainman/fast_float/actions/workflows/vs18-cxx23.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/vs18-cxx23.yml)
[![vs18 clang](https://github.com/irainman/fast_float/actions/workflows/vs18-clang-ci.yml/badge.svg)](https://github.com/irainman/fast_float/actions/workflows/vs18-clang-ci.yml)
[![CodeFactor](https://www.codefactor.io/repository/github/irainman/fast_float/badge)](https://www.codefactor.io/repository/github/irainman/fast_float)
[![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml) ## This is a fork of [fast_float](https://github.com/fastfloat/fast_float) made by HedgehogInTheCPP with some additional options, code refactoring and big cleanup to maximize performance, reduce size and improve mainteinability, please give a star to this repo ^^
*Note: This library is for C++ users. C programmers should consider [ffc.h](https://github.com/kolemannix/ffc.h). It is a high-performance port of fast_float to C.* ### 🚀 Performance and Code Size Improvements
* Optimized parsing logic for use as an **internal lightweight parser** (for example, inside other libraries or in compilers).
* Reduced binary size by conditionally excluding unneeded parsing features.
* Improved the `parsed_number_string_t` layout and increased `constexpr`/`consteval` propagation to enable compile-time optimizations.
* Reduced register and cache pressure and reduce branching especially in parsing hot paths.
* Improved performance in both 64-bit and 32-bit builds for all supported types.
* Added more optimized x86 specific code that uses up to SSE4.2 instructions in algorithms and significantly improve speed parsing especially for numbers with many digits.
---
### ⚙️ New Configuration Macros
Introduced new optional macros to minimize overhead when certain parsing features are not required:
* **`FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN`**
Restricts parsing to **positive C-style numbers only** — no sign characters, no `INF`, `INFINITY`, or `NaN` and no additional options like skip white spaces or support for Fortran/JSON/JS.
* **`FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED`**
Assumes that only the **IEEE 754 “round-to-nearest”** rounding mode is needed, removing crutches and support code for other modes.
* **`FASTFLOAT_ISNOT_CHECKED_BOUNDS`**
Disables bounds checking for input ranges that are assumed to be valid.
This option is very usefull if you use library as **internal lightweight parser** — this reduces branching and improves performance.
* **`FASTFLOAT_ASSUME`**
Provides a portable abstraction for the compiler’s `[[assume]]` intrinsic.
* **`FASTFLOAT_HAS_BYTESWAP`**
Automatically uses std::byteswap if available to reduce code size and speed up.
* **`FASTFLOAT_HAS_BIT_CAST`**
Automatically uses std::bit_cast if available to reduce code size and speed up.
* **`FASTFLOAT_X86_SIMD`**
Automatically uses reworking SSE parsing algorithms to improve performance on x86 machines. And hardware level also can be set manually by user.
* **`FASTFLOAT_USE_SIMD`**
Can be set to 0 for completely disable manually optimized SIMD paths and use compiler + linker to give the best possible result automatically
---
### 🧩 Remove Deprecated Macros
* Removed obsolete macroses **`FASTFLOAT_ALLOWS_LEADING_PLUS`** and **`FASTFLOAT_SKIP_WHITE_SPACE`**, which are now superseded by the stricter limited-mode options above.
---
### 🧠 Internal Refactoring and Quality Improvements
* Simplified internal parsing logic with compile-time branching controlled by the new macros.
* Enhanced `constexpr` coverage across the codebase for greater compile-time evaluation.
* Updated benchmarks and tests to validate new restricted configurations.
* Fixed minor compiler warnings and addressed **PVS-Studio** static analysis feedback.
* Properly use FASTFLOAT_SIMD_DISABLE_WARNINGS and FASTFLOAT_SIMD_RESTORE_WARNINGS only for instructions that allow unaligned loads.
* Many small cleanups and fixes.
* Added many comments about functionality and realization details.
---
### 📘 Documentation and Build
* Updated **README.md** to describe all new configuration macros and their intended use cases.
* Adjusted build scripts and benchmark configurations for the new compile-time flags.
---
✅ **Result:**
Smaller, [faster by default](https://github.com/fastfloat/fast_float/pull/307#issuecomment-4352796857), and more configurable builds — now library can be used for **internal numeric parsers** or **embedded environments** — while maintaining full compatibility, functionality, and performance improvements in the default configuration.
🔥 **Motivation:**
There is a really common use case in mathematical and other abstract syntax tree (AST)-like parsers that already processes
the sign and all other symbols before any number by itself. In this case you can use fastfloat to only parse positive numbers
in all supported formats with macros `FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN`, which significantly reduce the code size
and improve performance. You also can use macros `FASTFLOAT_ISNOT_CHECKED_BOUNDS` if your code already checks bounds;
it's very likely because all parsers need to check the first character by itself before parsing. Additionally, you can use
macros `FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED` if you only need `FE_TONEAREST` rounding mode in the parsing; this option
also improves performance a bit and reduces code size. In the high-performance example, I also use the [fmt library](https://github.com/fmtlib/fmt), which also
supports all C++ standards since C++11. I also recommend using `string_view` everywhere if it's possible; it's available
since C++17, and if you want maximum performance, use the latest compiler with the latest C++ with maximum optimization (I use C++23 and some from C++26):
```
-O3 -DNDEBUG + LTO
```
```C++
#define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
#define FASTFLOAT_ISNOT_CHECKED_BOUNDS
#define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
#include "fast_float/fast_float.h"
#include "fmt/core.h"
#include <string_view>
int main() {
std::string_view input = "23.14069263277926900572";
double result;
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
if ((answer.ec != std::errc()) || ((result != 23.14069263277927 /*properly rounded value */)))
{
fmt::print(stderr, "parsing failure!\n the number {}.", result);
return 1;
}
fmt::print("parsed the number {}.", result);
return 0;
}
```
The fast_float library provides fast header-only implementations for the C++ The fast_float library provides fast header-only implementations for the C++
from_chars functions for `float` and `double` types as well as integer types. from_chars functions for `float` and `double` types as well as integer types.
@ -52,7 +166,8 @@ parsed value. In case of error, the returned `ec` contains a representative
error, otherwise the default (`std::errc()`) value is stored. error, otherwise the default (`std::errc()`) value is stored.
The implementation does not throw and does not allocate memory (e.g., with `new` The implementation does not throw and does not allocate memory (e.g., with `new`
or `malloc`). or `malloc`) and can be usable in the kernel, embeded and other scenarious that
relays on such behavior.
It will parse infinity and nan values. It will parse infinity and nan values.
@ -586,8 +701,8 @@ sufficiently recent version of CMake (3.11 or better at least):
```cmake ```cmake
FetchContent_Declare( FetchContent_Declare(
fast_float fast_float
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git GIT_REPOSITORY https://github.com/irainman/fast_float.git
GIT_TAG tags/v8.3.2 GIT_TAG tags/v8.3.4
GIT_SHALLOW TRUE) GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float) FetchContent_MakeAvailable(fast_float)
@ -602,8 +717,8 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
```cmake ```cmake
CPMAddPackage( CPMAddPackage(
NAME fast_float NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float" GITHUB_REPOSITORY "irainman/fast_float"
GIT_TAG v8.3.2) GIT_TAG v8.3.4)
``` ```
## Using as single header ## Using as single header
@ -615,7 +730,7 @@ if desired as described in the command line help.
You may directly download automatically generated single-header files: You may directly download automatically generated single-header files:
<https://github.com/fastfloat/fast_float/releases/download/v8.3.2/fast_float.h> <https://github.com/irainman/fast_float/releases/download/v8.3.4/fast_float.h>
## Benchmarking ## Benchmarking
@ -669,6 +784,10 @@ long digits.
The library includes code adapted from Google Wuffs (written by Nigel Tao) which The library includes code adapted from Google Wuffs (written by Nigel Tao) which
was originally published under the Apache 2.0 license. was originally published under the Apache 2.0 license.
## SAST Tools
[PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code.
## License ## License
<sup> <sup>

View File

@ -1,3 +1,8 @@
// #define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// #define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
// #define FASTFLOAT_ISNOT_CHECKED_BOUNDS
#include "counters/bench.h" #include "counters/bench.h"
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <charconv> #include <charconv>
@ -29,8 +34,7 @@ void pretty_print(size_t volume, size_t bytes, std::string name,
printf("\n"); printf("\n");
} }
fastfloat_really_inline const char *seek_ip_end(const char *p, fastfloat_inline const char *seek_ip_end(const char *p, const char *pend) {
const char *pend) {
const char *current = p; const char *current = p;
size_t count = 0; size_t count = 0;
for (; current != pend; ++current) { for (; current != pend; ++current) {
@ -55,7 +59,7 @@ fastfloat_really_inline const char *seek_ip_end(const char *p,
enum class parse_method { standard, fast_float }; enum class parse_method { standard, fast_float };
template <parse_method use_standard> template <parse_method use_standard>
fastfloat_really_inline std::pair<bool, uint32_t> fastfloat_inline std::pair<bool, uint32_t>
simple_parse_ip_line(const char *p, const char *pend) { simple_parse_ip_line(const char *p, const char *pend) {
const char *current = p; const char *current = p;
uint32_t ip = 0; uint32_t ip = 0;
@ -179,4 +183,4 @@ int main() {
})); }));
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -1,247 +1,267 @@
#if defined(__linux__) || (__APPLE__ && __aarch64__)
#define USING_COUNTERS #define FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
#endif #define FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
#include "counters/event_counter.h" #define FASTFLOAT_ISNOT_CHECKED_BOUNDS
#include <algorithm>
#include "fast_float/fast_float.h" #if defined(__linux__) || (__APPLE__ && __aarch64__)
#include <chrono> #define USING_COUNTERS
#include <climits> #endif
#include <cmath> #include "counters/event_counter.h"
#include <cstdint> #include <algorithm>
#include <cstdio> #include <climits>
#include <cstdlib> #include <cmath>
#include <cstring> #include <cstdint>
#include <ctype.h> #include <cstdio>
#include <fstream> #include <cstdlib>
#include <iomanip> #include <cstring>
#include <iostream> #include <ctype.h>
#include <random> #include <fstream>
#include <sstream> #include <iomanip>
#include <stdio.h> #include <iostream>
#include <string> #include <random>
#include <vector> #include <sstream>
#include <locale.h> #include <stdio.h>
#include <string>
template <typename CharT> #include <locale.h>
double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
double answer = 0; #include "fast_float/fast_float.h"
double x = 0;
for (auto &st : s) { template <typename CharT, typename Value>
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x); Value findmax_fastfloat(std::vector<std::basic_string<CharT>> &s) {
if (p == st.data()) { Value answer = 0;
throw std::runtime_error("bug in findmax_fastfloat"); Value x = 0;
} for (auto &st : s) {
answer = answer > x ? answer : x; auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
}
return answer; if (p == st.data()) {
} throw std::runtime_error("bug in findmax_fastfloat");
}
template <typename CharT> answer = answer > x ? answer : x;
double findmax_fastfloat32(std::vector<std::basic_string<CharT>> &s) { }
float answer = 0; return answer;
float x = 0; }
for (auto &st : s) {
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x); #ifdef USING_COUNTERS
if (p == st.data()) {
throw std::runtime_error("bug in findmax_fastfloat"); counters::event_collector collector{};
}
answer = answer > x ? answer : x; template <class T, class CharT>
} std::vector<counters::event_count>
return answer; time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
} uint32_t repeat) {
std::vector<counters::event_count> aggregate;
counters::event_collector collector{}; bool printed_bug = false;
for (uint32_t i = 0; i != repeat; ++i) {
#ifdef USING_COUNTERS collector.start();
template <class T, class CharT> auto const ts = function(lines);
std::vector<counters::event_count> aggregate.push_back(collector.end());
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
size_t repeat) { if (ts == 0 && !printed_bug) {
std::vector<counters::event_count> aggregate; printf("bug\n");
bool printed_bug = false; printed_bug = true;
for (size_t i = 0; i < repeat; i++) { }
collector.start(); }
double ts = function(lines); return aggregate;
if (ts == 0 && !printed_bug) { }
printf("bug\n");
printed_bug = true; void pretty_print(uint64_t volume, size_t number_of_floats, std::string name,
} std::vector<counters::event_count> events) {
aggregate.push_back(collector.end()); double volumeMB = volume / (1024. * 1024.);
} double average_ns{0};
return aggregate; double min_ns{DBL_MAX};
} double cycles_min{DBL_MAX};
double instructions_min{DBL_MAX};
void pretty_print(double volume, size_t number_of_floats, std::string name, double cycles_avg{0};
std::vector<counters::event_count> events) { double instructions_avg{0};
double volumeMB = volume / (1024. * 1024.); double branches_min{0};
double average_ns{0}; double branches_avg{0};
double min_ns{DBL_MAX}; double branch_misses_min{0};
double cycles_min{DBL_MAX}; double branch_misses_avg{0};
double instructions_min{DBL_MAX}; for (counters::event_count e : events) {
double cycles_avg{0}; double ns = e.elapsed_ns();
double instructions_avg{0}; average_ns += ns;
double branches_min{0}; min_ns = min_ns < ns ? min_ns : ns;
double branches_avg{0};
double branch_misses_min{0}; double cycles = e.cycles();
double branch_misses_avg{0}; cycles_avg += cycles;
for (counters::event_count e : events) { cycles_min = cycles_min < cycles ? cycles_min : cycles;
double ns = e.elapsed_ns();
average_ns += ns; double instructions = e.instructions();
min_ns = min_ns < ns ? min_ns : ns; instructions_avg += instructions;
instructions_min =
double cycles = e.cycles(); instructions_min < instructions ? instructions_min : instructions;
cycles_avg += cycles;
cycles_min = cycles_min < cycles ? cycles_min : cycles; double branches = e.branches();
branches_avg += branches;
double instructions = e.instructions(); branches_min = branches_min < branches ? branches_min : branches;
instructions_avg += instructions;
instructions_min = double branch_misses = e.branch_misses();
instructions_min < instructions ? instructions_min : instructions; branch_misses_avg += branch_misses;
branch_misses_min =
double branches = e.branches(); branch_misses_min < branch_misses ? branch_misses_min : branch_misses;
branches_avg += branches; }
branches_min = branches_min < branches ? branches_min : branches; cycles_avg /= events.size();
instructions_avg /= events.size();
double branch_misses = e.branch_misses(); average_ns /= events.size();
branch_misses_avg += branch_misses; branches_avg /= events.size();
branch_misses_min = branch_misses_avg /= events.size();
branch_misses_min < branch_misses ? branch_misses_min : branch_misses; printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
} volumeMB * 1000000000 / min_ns,
cycles_avg /= events.size(); (average_ns - min_ns) * 100.0 / average_ns);
instructions_avg /= events.size(); printf("%8.2f Mfloat/s ", number_of_floats * 1000 / min_ns);
average_ns /= events.size(); if (instructions_min > 0) {
branches_avg /= events.size(); printf(" %8.2f i/B %8.2f i/f (+/- %.1f %%) ", instructions_min / volume,
branch_misses_avg /= events.size(); instructions_min / number_of_floats,
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(), (instructions_avg - instructions_min) * 100.0 / instructions_avg);
volumeMB * 1000000000 / min_ns,
(average_ns - min_ns) * 100.0 / average_ns); printf(" %8.2f c/B %8.2f c/f (+/- %.1f %%) ", cycles_min / volume,
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / min_ns); cycles_min / number_of_floats,
if (instructions_min > 0) { (cycles_avg - cycles_min) * 100.0 / cycles_avg);
printf(" %8.2f i/B %8.2f i/f (+/- %.1f %%) ", instructions_min / volume, printf(" %8.2f i/c ", instructions_min / cycles_min);
instructions_min / number_of_floats, printf(" %8.2f b/f ", branches_avg / number_of_floats);
(instructions_avg - instructions_min) * 100.0 / instructions_avg); printf(" %8.2f bm/f ", branch_misses_avg / number_of_floats);
printf(" %8.2f GHz ", cycles_min / min_ns);
printf(" %8.2f c/B %8.2f c/f (+/- %.1f %%) ", cycles_min / volume, }
cycles_min / number_of_floats, printf("\n");
(cycles_avg - cycles_min) * 100.0 / cycles_avg); }
printf(" %8.2f i/c ", instructions_min / cycles_min); #else
printf(" %8.2f b/f ", branches_avg / number_of_floats); template <class T, class CharT>
printf(" %8.2f bm/f ", branch_misses_avg / number_of_floats); std::pair<double, double>
printf(" %8.2f GHz ", cycles_min / min_ns); time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
} size_t repeat) {
printf("\n"); std::chrono::high_resolution_clock::time_point t1, t2;
} double average = 0;
#else double min_value = DBL_MAX;
template <class T, class CharT> bool printed_bug = false;
std::pair<double, double> for (size_t i = 0; i != repeat; ++i) {
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function, t1 = std::chrono::high_resolution_clock::now();
size_t repeat) { auto const ts = function(lines);
std::chrono::high_resolution_clock::time_point t1, t2; t2 = std::chrono::high_resolution_clock::now();
double average = 0;
double min_value = DBL_MAX; double const dif = static_cast<double>(
bool printed_bug = false; std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count());
for (size_t i = 0; i < repeat; i++) { average += dif;
t1 = std::chrono::high_resolution_clock::now(); min_value = min_value < dif ? min_value : dif;
double ts = function(lines);
if (ts == 0 && !printed_bug) { if (ts == 0 && !printed_bug) {
printf("bug\n"); printf("bug\n");
printed_bug = true; printed_bug = true;
} }
t2 = std::chrono::high_resolution_clock::now(); }
double dif = average /= repeat;
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count(); return std::make_pair(min_value, average);
average += dif; }
min_value = min_value < dif ? min_value : dif;
} void pretty_print(uint64_t volume, size_t number_of_floats,
average /= repeat; std::string const &name, std::pair<double, double> result) {
return std::make_pair(min_value, average); double volumeMB = volume / (1024. * 1024.);
} printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
volumeMB * 1000000000 / result.first,
void pretty_print(double volume, size_t number_of_floats, std::string name, (result.second - result.first) * 100.0 / result.second);
std::pair<double, double> result) { printf("%8.2f Mfloat/s ", number_of_floats * 1000 / result.first);
double volumeMB = volume / (1024. * 1024.); printf(" %8.2f ns/f \n", double(result.first) / number_of_floats);
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(), }
volumeMB * 1000000000 / result.first, #endif
(result.second - result.first) * 100.0 / result.second);
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / result.first); // this is okay, all chars are ASCII
printf(" %8.2f ns/f \n", double(result.first) / number_of_floats); inline std::u16string widen(std::string const &line) {
} std::u16string u16line;
#endif u16line.resize(line.size());
for (uint32_t i = 0; i != line.size(); ++i) {
// this is okay, all chars are ASCII u16line[i] = char16_t(line[i]);
inline std::u16string widen(std::string line) { }
std::u16string u16line; return u16line;
u16line.resize(line.size()); }
for (size_t i = 0; i < line.size(); ++i) {
u16line[i] = char16_t(line[i]); std::vector<std::u16string> widen(const std::vector<std::string> &lines) {
} std::vector<std::u16string> u16lines;
return u16line; u16lines.reserve(lines.size());
} for (auto const &line : lines) {
u16lines.emplace_back(widen(line));
std::vector<std::u16string> widen(const std::vector<std::string> &lines) { }
std::vector<std::u16string> u16lines; return u16lines;
u16lines.reserve(lines.size()); }
for (auto const &line : lines) {
u16lines.push_back(widen(line)); void process(std::vector<std::string> &lines, size_t volume) {
} size_t constexpr repeat = 1000;
return u16lines; double volumeMB = volume / (1024. * 1024.);
} std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)",
void process(std::vector<std::string> &lines, size_t volume) { time_it_ns(lines, findmax_fastfloat<char, double>, repeat));
size_t repeat = 1000; pretty_print(volume, lines.size(), "fastfloat (32)",
double volumeMB = volume / (1024. * 1024.); time_it_ns(lines, findmax_fastfloat<char, float>, repeat));
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)", std::vector<std::u16string> lines16 = widen(lines);
time_it_ns(lines, findmax_fastfloat64<char>, repeat)); volume = 2 * volume;
pretty_print(volume, lines.size(), "fastfloat (32)", volumeMB = volume / (1024. * 1024.);
time_it_ns(lines, findmax_fastfloat32<char>, repeat)); std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
pretty_print(
std::vector<std::u16string> lines16 = widen(lines); volume, lines.size(), "fastfloat (64)",
volume = 2 * volume; time_it_ns(lines16, findmax_fastfloat<char16_t, double>, repeat));
volumeMB = volume / (1024. * 1024.); pretty_print(volume, lines.size(), "fastfloat (32)",
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl; time_it_ns(lines16, findmax_fastfloat<char16_t, float>, repeat));
pretty_print(volume, lines.size(), "fastfloat (64)", }
time_it_ns(lines16, findmax_fastfloat64<char16_t>, repeat));
pretty_print(volume, lines.size(), "fastfloat (32)", void fileload(std::string filename) {
time_it_ns(lines16, findmax_fastfloat32<char16_t>, repeat)); std::ifstream inputfile(filename);
} if (!inputfile) {
std::cerr << "can't open " << filename << std::endl;
void fileload(std::string filename) { return;
std::ifstream inputfile(filename); }
if (!inputfile) { std::cout << "#### " << std::endl;
std::cerr << "can't open " << filename << std::endl; std::cout << "# reading " << filename << std::endl;
return; std::cout << "#### " << std::endl;
} std::string line;
std::cout << "#### " << std::endl; std::vector<std::string> lines;
std::cout << "# reading " << filename << std::endl; lines.reserve(120000); // let us reserve plenty of memory.
std::cout << "#### " << std::endl; size_t volume = 0;
std::string line; while (getline(inputfile, line)) {
std::vector<std::string> lines; #ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
lines.reserve(10000); // let us reserve plenty of memory. if (line[0] == '-') {
size_t volume = 0; line.erase(0, 1);
while (getline(inputfile, line)) { }
volume += line.size(); #endif
lines.push_back(line); volume += line.size();
} lines.emplace_back(line);
std::cout << "# read " << lines.size() << " lines " << std::endl; }
process(lines, volume); std::cout << "# read " << lines.size() << " lines " << std::endl;
} process(lines, volume);
}
int main(int argc, char **argv) {
if (collector.has_events()) { int main(int argc, char **argv) {
std::cout << "# Using hardware counters" << std::endl; #ifdef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
} else { std::cout << "# FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN is enabled"
#if defined(__linux__) || (__APPLE__ && __aarch64__) << std::endl;
std::cout << "# Hardware counters not available, try to run in privileged " #endif
"mode (e.g., sudo)." #ifdef FASTFLOAT_TABLE_HACK_CHAR_DIGIT_LUT_DISABLED
<< std::endl; std::cout << "# FASTFLOAT_TABLE_HACK_CHAR_DIGIT_LUT_DISABLED is enabled"
#endif << std::endl;
} #endif
if (argc > 1) { #ifdef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
fileload(argv[1]); std::cout << "# FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED is enabled"
return EXIT_SUCCESS; << std::endl;
} #endif
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt"); #ifdef FASTFLOAT_ISNOT_CHECKED_BOUNDS
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt"); std::cout << "# FASTFLOAT_ISNOT_CHECKED_BOUNDS is enabled" << std::endl;
return EXIT_SUCCESS; #endif
} #ifdef USING_COUNTERS
if (collector.has_events()) {
std::cout << "# Using hardware counters" << std::endl;
} else {
#if defined(__linux__) || (__APPLE__ && __aarch64__)
std::cout << "# Hardware counters not available, try to run in privileged "
"mode (e.g., sudo)."
<< std::endl;
#endif
}
#endif
if (argc > 1) {
fileload(argv[1]);
return EXIT_SUCCESS;
}
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada_short.txt");
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
return EXIT_SUCCESS;
}

182
benchmarks/event_counter.h Normal file
View File

@ -0,0 +1,182 @@
#ifndef __EVENT_COUNTER_H
#define __EVENT_COUNTER_H
#include <cctype>
#ifndef _MSC_VER
#include <dirent.h>
#endif
#include <cinttypes>
#include <cstring>
#include <chrono>
#include <array>
#include "linux-perf-events.h"
#ifdef __linux__
#include <libgen.h>
#endif
#if (defined(__APPLE__) && __APPLE__) && (defined(__aarch64__) && __aarch64__)
#include "apple_arm_events.h"
#endif
struct event_count {
// The types of counters (so we can read the getter more easily)
enum event_counter_types {
CPU_CYCLES = 0,
INSTRUCTIONS = 1,
BRANCHES = 2,
MISSED_BRANCHES = 3,
event_counter_types_size = 4
};
std::chrono::duration<double> elapsed;
std::array<unsigned long long, event_counter_types_size> event_counts;
event_count() : elapsed(0), event_counts{0, 0, 0, 0} {}
event_count(const std::chrono::duration<double> &_elapsed,
const std::array<unsigned long long, event_counter_types_size>
&_event_counts)
: elapsed(_elapsed), event_counts(_event_counts) {}
event_count(const event_count &other)
: elapsed(other.elapsed), event_counts(other.event_counts) {}
double elapsed_sec() const {
return std::chrono::duration<double>(elapsed).count();
}
double elapsed_ns() const {
return std::chrono::duration<double, std::nano>(elapsed).count();
}
double cycles() const {
return static_cast<double>(event_counts[CPU_CYCLES]);
}
double instructions() const {
return static_cast<double>(event_counts[INSTRUCTIONS]);
}
double branches() const {
return static_cast<double>(event_counts[BRANCHES]);
}
double missed_branches() const {
return static_cast<double>(event_counts[MISSED_BRANCHES]);
}
event_count &operator=(const event_count &other) {
this->elapsed = other.elapsed;
this->event_counts = other.event_counts;
return *this;
}
event_count operator+(const event_count &other) const {
return event_count(elapsed + other.elapsed,
{
event_counts[0] + other.event_counts[0],
event_counts[1] + other.event_counts[1],
event_counts[2] + other.event_counts[2],
event_counts[3] + other.event_counts[3],
});
}
void operator+=(const event_count &other) { *this = *this + other; }
};
struct event_aggregate {
bool has_events = false;
int iterations = 0;
event_count total{};
event_count best{};
event_count worst{};
event_aggregate() = default;
void operator<<(const event_count &other) {
if (iterations == 0 || other.elapsed < best.elapsed) {
best = other;
}
if (iterations == 0 || other.elapsed > worst.elapsed) {
worst = other;
}
iterations++;
total += other;
}
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
double cycles() const { return total.cycles() / iterations; }
double instructions() const { return total.instructions() / iterations; }
double branches() const { return total.branches() / iterations; }
double missed_branches() const {
return total.missed_branches() / iterations;
}
};
struct event_collector {
event_count count{};
std::chrono::time_point<std::chrono::steady_clock> start_clock{};
#if defined(__linux__)
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
event_collector()
: linux_events(std::array<unsigned long long,
4 /*event_counter_types_size*/>{
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions
PERF_COUNT_HW_BRANCH_MISSES}) {}
bool has_events() { return linux_events.is_working(); }
#elif __APPLE__ && __aarch64__
performance_counters diff;
event_collector() : diff(0) { setup_performance_counters(); }
bool has_events() { return setup_performance_counters(); }
#else
event_collector() = default;
bool has_events() { return false; }
#endif
inline void start() {
#if defined(__linux)
linux_events.start();
#elif __APPLE__ && __aarch64__
if (has_events()) {
diff = get_counters();
}
#endif
start_clock = std::chrono::steady_clock::now();
}
inline event_count &end() {
const auto end_clock = std::chrono::steady_clock::now();
#if defined(__linux)
linux_events.end(count.event_counts);
#elif __APPLE__ && __aarch64__
if (has_events()) {
performance_counters end = get_counters();
diff = end - diff;
}
count.event_counts[0] = diff.cycles;
count.event_counts[1] = diff.instructions;
count.event_counts[2] = diff.branches;
count.event_counts[3] = diff.missed_branches;
#endif
count.elapsed = end_clock - start_clock;
return count;
}
};
#endif

View File

@ -0,0 +1,105 @@
#pragma once
#ifdef __linux__
#include <asm/unistd.h> // for __NR_perf_event_open
#include <linux/perf_event.h> // for perf event constants
#include <sys/ioctl.h> // for ioctl
#include <unistd.h> // for syscall
#include <cerrno> // for errno
#include <cstring> // for memset
#include <stdexcept>
#include <array>
#include <vector>
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
int fd;
bool working;
perf_event_attr attribs{};
size_t num_events{};
std::vector<uint64_t> temp_result_vec{};
std::vector<uint64_t> ids{};
public:
explicit LinuxEvents(std::array<unsigned long long, 4> config_vec)
: fd(0), working(true) {
memset(&attribs, 0, sizeof(attribs));
attribs.type = TYPE;
attribs.size = sizeof(attribs);
attribs.disabled = 1;
attribs.exclude_kernel = 1;
attribs.exclude_hv = 1;
attribs.sample_period = 0;
attribs.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
const int pid = 0; // the current process
const int cpu = -1; // all CPUs
const unsigned long flags = 0;
int group = -1; // no group
num_events = config_vec.size();
ids.resize(config_vec.size());
uint32_t i = 0;
for (auto config : config_vec) {
attribs.config = config;
int _fd = static_cast<int>(
syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
if (_fd == -1) {
report_error("perf_event_open");
}
ioctl(_fd, PERF_EVENT_IOC_ID, &ids[i++]);
if (group == -1) {
group = _fd;
fd = _fd;
}
}
temp_result_vec.resize(num_events * 2 + 1);
}
~LinuxEvents() {
if (fd != -1) {
close(fd);
}
}
inline void start() {
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_RESET)");
}
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_ENABLE)");
}
}
}
inline void end(std::array<unsigned long long, 4> &results) {
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
}
if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) {
report_error("read");
}
}
// our actual results are in slots 1,3,5, ... of this structure
for (uint32_t i = 1; i < temp_result_vec.size(); i += 2) {
results[i / 2] = temp_result_vec[i];
}
for (uint32_t i = 2; i < temp_result_vec.size(); i += 2) {
if (ids[i / 2 - 1] != temp_result_vec[i]) {
report_error("event mismatch");
}
}
}
bool is_working() { return working; }
private:
void report_error(const std::string &) { working = false; }
};
#endif

View File

@ -87,7 +87,7 @@
if (cached && cached.tag) { if (cached && cached.tag) {
applyVersion(cached.tag, cached.url); applyVersion(cached.tag, cached.url);
} else { } else {
fetch("https://api.github.com/repos/fastfloat/fast_float/releases/latest", { fetch("https://api.github.com/repos/irainman/fast_float/releases/latest", {
headers: { Accept: "application/vnd.github+json" }, headers: { Accept: "application/vnd.github+json" },
}) })
.then(function (r) { return r.ok ? r.json() : null; }) .then(function (r) { return r.ok ? r.json() : null; })

View File

@ -127,7 +127,7 @@ pre:hover .copy-btn { opacity: 1; }
.skip { .skip {
position: absolute; left: -9999px; position: absolute; left: -9999px;
background: var(--accent); color: white; background: var(--accent); color: white;
padding: 0.6rem 1rem; border-radius: 0 0 6px 0; padding: 0.6rem 1rem; border-radius: 0 0 6px;
} }
.skip:focus { left: 0; top: 0; } .skip:focus { left: 0; top: 0; }

View File

@ -10,7 +10,7 @@
<meta property="og:title" content="fast_float — parse floats at 1 GB/s" /> <meta property="og:title" content="fast_float — parse floats at 1 GB/s" />
<meta property="og:description" content="A header-only C++ library for fast and exact floating-point and integer parsing." /> <meta property="og:description" content="A header-only C++ library for fast and exact floating-point and integer parsing." />
<meta property="og:type" content="website" /> <meta property="og:type" content="website" />
<meta property="og:url" content="https://fastfloat.github.io/fast_float/" /> <meta property="og:url" content="https://irainman.github.io/fast_float/" />
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<link rel="icon" type="image/svg+xml" href="assets/logo.svg" /> <link rel="icon" type="image/svg+xml" href="assets/logo.svg" />
@ -34,7 +34,7 @@
<a href="#install">Install</a> <a href="#install">Install</a>
</nav> </nav>
<div class="nav-actions"> <div class="nav-actions">
<a class="ghost-btn" href="https://github.com/fastfloat/fast_float" aria-label="GitHub repository"> <a class="ghost-btn" href="https://github.com/irainman/fast_float" aria-label="GitHub repository">
<svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-2c-3.2.7-3.88-1.37-3.88-1.37-.52-1.33-1.27-1.69-1.27-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.03 1.76 2.7 1.25 3.36.96.1-.74.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.78 0c2.2-1.49 3.17-1.18 3.17-1.18.62 1.59.23 2.76.11 3.05.74.81 1.18 1.84 1.18 3.1 0 4.43-2.69 5.4-5.25 5.69.41.36.78 1.07.78 2.16v3.2c0 .31.21.67.8.56A11.5 11.5 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z"/></svg> <svg width="18" height="18" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 .5C5.65.5.5 5.65.5 12c0 5.08 3.29 9.39 7.86 10.91.58.1.79-.25.79-.56v-2c-3.2.7-3.88-1.37-3.88-1.37-.52-1.33-1.27-1.69-1.27-1.69-1.04-.71.08-.7.08-.7 1.15.08 1.76 1.18 1.76 1.18 1.03 1.76 2.7 1.25 3.36.96.1-.74.4-1.25.73-1.54-2.55-.29-5.24-1.28-5.24-5.7 0-1.26.45-2.29 1.18-3.1-.12-.29-.51-1.46.11-3.05 0 0 .97-.31 3.18 1.18a11 11 0 0 1 5.78 0c2.2-1.49 3.17-1.18 3.17-1.18.62 1.59.23 2.76.11 3.05.74.81 1.18 1.84 1.18 3.1 0 4.43-2.69 5.4-5.25 5.69.41.36.78 1.07.78 2.16v3.2c0 .31.21.67.8.56A11.5 11.5 0 0 0 23.5 12C23.5 5.65 18.35.5 12 .5z"/></svg>
<span>GitHub</span> <span>GitHub</span>
</a> </a>
@ -59,8 +59,8 @@
</p> </p>
<div class="cta-row"> <div class="cta-row">
<a class="btn primary" href="#quickstart">Get started</a> <a class="btn primary" href="#quickstart">Get started</a>
<a class="btn" href="https://github.com/fastfloat/fast_float">View on GitHub</a> <a class="btn" href="https://github.com/irainman/fast_float">View on GitHub</a>
<a class="btn ghost" href="https://github.com/fastfloat/fast_float/releases/latest" id="download-latest"> <a class="btn ghost" href="https://github.com/irainman/fast_float/releases/latest" id="download-latest">
Download <code class="version-tag" data-version>v{{VERSION}}</code> Download <code class="version-tag" data-version>v{{VERSION}}</code>
</a> </a>
</div> </div>
@ -68,9 +68,9 @@
<span class="badge" id="release-badge"> <span class="badge" id="release-badge">
<span class="dot"></span> Latest release: <code data-version>v{{VERSION}}</code> <span class="dot"></span> Latest release: <code data-version>v{{VERSION}}</code>
</span> </span>
<a class="meta-link" href="https://github.com/fastfloat/fast_float/blob/main/LICENSE-APACHE">Apache 2.0</a> <a class="meta-link" href="https://github.com/irainman/fast_float/blob/main/LICENSE-APACHE">Apache 2.0</a>
<a class="meta-link" href="https://github.com/fastfloat/fast_float/blob/main/LICENSE-MIT">MIT</a> <a class="meta-link" href="https://github.com/irainman/fast_float/blob/main/LICENSE-MIT">MIT</a>
<a class="meta-link" href="https://github.com/fastfloat/fast_float/blob/main/LICENSE-BOOST">Boost</a> <a class="meta-link" href="https://github.com/irainman/fast_float/blob/main/LICENSE-BOOST">Boost</a>
</div> </div>
</div> </div>
</section> </section>
@ -220,7 +220,7 @@ constexpr double pi = parse("3.1415"); // computed at compile time</code></pre>
<h3>CMake <code>FetchContent</code></h3> <h3>CMake <code>FetchContent</code></h3>
<pre data-lang="cmake"><code>FetchContent_Declare( <pre data-lang="cmake"><code>FetchContent_Declare(
fast_float fast_float
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git GIT_REPOSITORY https://github.com/irainman/fast_float.git
GIT_TAG tags/<span data-version>v{{VERSION}}</span> GIT_TAG tags/<span data-version>v{{VERSION}}</span>
GIT_SHALLOW TRUE) GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float) FetchContent_MakeAvailable(fast_float)
@ -231,14 +231,14 @@ target_link_libraries(myprogram PUBLIC fast_float)</code></pre>
<h3>CPM</h3> <h3>CPM</h3>
<pre data-lang="cmake"><code>CPMAddPackage( <pre data-lang="cmake"><code>CPMAddPackage(
NAME fast_float NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float" GITHUB_REPOSITORY "irainman/fast_float"
GIT_TAG <span data-version>v{{VERSION}}</span>)</code></pre> GIT_TAG <span data-version>v{{VERSION}}</span>)</code></pre>
</article> </article>
<article class="card"> <article class="card">
<h3>Single header</h3> <h3>Single header</h3>
<p>Download a pre-amalgamated header — no build system required.</p> <p>Download a pre-amalgamated header — no build system required.</p>
<pre data-lang="bash"><code>curl -LO https://github.com/fastfloat/fast_float/releases/download/<span data-version>v{{VERSION}}</span>/fast_float.h</code></pre> <pre data-lang="bash"><code>curl -LO https://github.com/irainman/fast_float/releases/download/<span data-version>v{{VERSION}}</span>/fast_float.h</code></pre>
</article> </article>
<article class="card"> <article class="card">
@ -311,10 +311,10 @@ target_link_libraries(myprogram PUBLIC fast_float)</code></pre>
<div> <div>
<h4>Project</h4> <h4>Project</h4>
<ul> <ul>
<li><a href="https://github.com/fastfloat/fast_float">GitHub repository</a></li> <li><a href="https://github.com/irainman/fast_float">GitHub repository</a></li>
<li><a href="https://github.com/fastfloat/fast_float/releases">Releases</a></li> <li><a href="https://github.com/irainman/fast_float/releases">Releases</a></li>
<li><a href="https://github.com/fastfloat/fast_float/issues">Issue tracker</a></li> <li><a href="https://github.com/irainman/fast_float/issues">Issue tracker</a></li>
<li><a href="https://github.com/fastfloat/fast_float/blob/main/SECURITY.md">Security policy</a></li> <li><a href="https://github.com/irainman/fast_float/blob/main/SECURITY.md">Security policy</a></li>
</ul> </ul>
</div> </div>
<div> <div>
@ -322,13 +322,13 @@ target_link_libraries(myprogram PUBLIC fast_float)</code></pre>
<ul> <ul>
<li><a href="https://arxiv.org/abs/2101.11408">Number Parsing at a Gigabyte per Second</a></li> <li><a href="https://arxiv.org/abs/2101.11408">Number Parsing at a Gigabyte per Second</a></li>
<li><a href="https://www.youtube.com/watch?v=AVXgvlMeIm4">Go Systems 2020 talk</a></li> <li><a href="https://www.youtube.com/watch?v=AVXgvlMeIm4">Go Systems 2020 talk</a></li>
<li><a href="https://github.com/fastfloat/fast_float/blob/main/README.md">README</a></li> <li><a href="https://github.com/irainman/fast_float/blob/main/README.md">README</a></li>
</ul> </ul>
</div> </div>
</div> </div>
<div class="container subfoot"> <div class="container subfoot">
<span>Current release <code data-version>v{{VERSION}}</code></span> <span>Current release <code data-version>v{{VERSION}}</code></span>
<span>Maintained by the <a href="https://github.com/fastfloat">fast_float</a> contributors.</span> <span>This is a fork and it currently maintained only by the HedgehogInTheCPP.</span>
</div> </div>
</footer> </footer>

File diff suppressed because it is too large Load Diff

View File

@ -19,11 +19,11 @@ namespace fast_float {
#if defined(FASTFLOAT_64BIT) && !defined(__sparc) #if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB 1 #define FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb; typedef uint64_t limb;
constexpr size_t limb_bits = 64; constexpr limb_t limb_bits = 64;
#else #else
#define FASTFLOAT_32BIT_LIMB #define FASTFLOAT_32BIT_LIMB
typedef uint32_t limb; typedef uint32_t limb;
constexpr size_t limb_bits = 32; constexpr limb_t limb_bits = 32;
#endif #endif
typedef span<limb> limb_span; typedef span<limb> limb_span;
@ -32,59 +32,58 @@ typedef span<limb> limb_span;
// of bits required to store the largest bigint, which is // of bits required to store the largest bigint, which is
// `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or
// ~3600 bits, so we round to 4000. // ~3600 bits, so we round to 4000.
constexpr size_t bigint_bits = 4000; typedef uint_fast16_t bigint_bits_t;
constexpr size_t bigint_limbs = bigint_bits / limb_bits; constexpr bigint_bits_t bigint_bits = 4000;
constexpr limb_t bigint_limbs = bigint_bits / limb_bits;
// vector-like type that is allocated on the stack. the entire // vector-like type that is allocated on the stack. the entire
// buffer is pre-allocated, and only the length changes. // buffer is pre-allocated, and only the length changes.
template <uint16_t size> struct stackvec { template <limb_t size> struct stackvec {
limb data[size]; FASTFLOAT_NO_UNIQUE_ADDRESS limb data[size];
// we never need more than 150 limbs // we never need more than 150 limbs
uint16_t length{0}; FASTFLOAT_NO_UNIQUE_ADDRESS limb_t length{0};
stackvec() = default; FASTFLOAT_CONSTEXPR20 stackvec() noexcept = default;
stackvec(stackvec const &) = delete; stackvec(stackvec const &) = delete;
stackvec &operator=(stackvec const &) = delete; stackvec &operator=(stackvec const &) = delete;
stackvec(stackvec &&) = delete; stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete; stackvec &operator=(stackvec &&other) = delete;
// create stack vector from existing limb span. // create stack vector from existing limb span.
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) { FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) noexcept {
FASTFLOAT_ASSERT(try_extend(s)); FASTFLOAT_ASSERT(try_extend(s));
} }
FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept { FASTFLOAT_CONSTEXPR14 limb &operator[](limb_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &operator[](limb_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
// index from the end of the container // index from the end of the container
FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb &rindex(limb_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1; auto rindex = length - index - 1;
return data[rindex]; return data[rindex];
} }
// set the length, without bounds checking. // set the length, without bounds checking.
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept { FASTFLOAT_CONSTEXPR14 void set_len(limb_t len) noexcept { length = len; }
length = uint16_t(len);
}
constexpr size_t len() const noexcept { return length; } constexpr limb_t len() const noexcept { return length; }
constexpr bool is_empty() const noexcept { return length == 0; } constexpr bool is_empty() const noexcept { return length == 0; }
constexpr size_t capacity() const noexcept { return size; } constexpr limb_t capacity() const noexcept { return size; }
// append item to vector, without bounds checking // append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept { FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value; data[length] = value;
length++; ++length;
} }
// append item to vector, returning if item was added // append item to vector, returning if item was added
@ -101,7 +100,7 @@ template <uint16_t size> struct stackvec {
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb *ptr = data + length; limb *ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr); std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len()); set_len(len() + static_cast<limb_t>(s.len()));
} }
// try to add items to the vector, returning if items were added // try to add items to the vector, returning if items were added
@ -118,20 +117,18 @@ template <uint16_t size> struct stackvec {
// if the new size is longer than the vector, assign value to each // if the new size is longer than the vector, assign value to each
// appended item. // appended item.
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
void resize_unchecked(size_t new_len, limb value) noexcept { void resize_unchecked(limb_t new_len, limb value) noexcept {
if (new_len > len()) { if (new_len > len()) {
size_t count = new_len - len(); auto const count = new_len - len();
limb *first = data + len(); limb *first = data + len();
limb *last = first + count; limb *last = first + count;
::std::fill(first, last, value); ::std::fill(first, last, value);
set_len(new_len);
} else {
set_len(new_len);
} }
set_len(new_len);
} }
// try to resize the vector, returning if the vector was resized. // try to resize the vector, returning if the vector was resized.
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept { FASTFLOAT_CONSTEXPR20 bool try_resize(limb_t new_len, limb value) noexcept {
if (new_len > capacity()) { if (new_len > capacity()) {
return false; return false;
} else { } else {
@ -143,12 +140,12 @@ template <uint16_t size> struct stackvec {
// check if any limbs are non-zero after the given index. // check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index // this needs to be done in reverse order, since the index
// is relative to the most significant limbs. // is relative to the most significant limbs.
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 bool nonzero(limb_t index) const noexcept {
while (index < len()) { while (index < len()) {
if (rindex(index) != 0) { if (rindex(index) != 0) {
return true; return true;
} }
index++; ++index;
} }
return false; return false;
} }
@ -156,50 +153,50 @@ template <uint16_t size> struct stackvec {
// normalize the big integer, so most-significant zero limbs are removed. // normalize the big integer, so most-significant zero limbs are removed.
FASTFLOAT_CONSTEXPR14 void normalize() noexcept { FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) { while (len() > 0 && rindex(0) == 0) {
length--; --length;
} }
} }
}; };
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t fastfloat_inline FASTFLOAT_CONSTEXPR14 uint64_t
empty_hi64(bool &truncated) noexcept { empty_hi64(bool &truncated) noexcept {
truncated = false; truncated = false;
return 0; return 0;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t fastfloat_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_hi64(uint64_t r0, bool &truncated) noexcept { uint64_hi64(uint64_t r0, bool &truncated) noexcept {
truncated = false; truncated = false;
int shl = leading_zeroes(r0); auto const shl = leading_zeroes(r0);
return r0 << shl; return r0 << shl;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t fastfloat_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept { uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept {
int shl = leading_zeroes(r0); auto const shl = leading_zeroes(r0);
if (shl == 0) { if (shl == 0) {
truncated = r1 != 0; truncated = r1 != 0;
return r0; return r0;
} else { } else {
int shr = 64 - shl; limb_t shr = 64 - shl;
truncated = (r1 << shl) != 0; truncated = (r1 << shl) != 0;
return (r0 << shl) | (r1 >> shr); return (r0 << shl) | (r1 >> shr);
} }
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t fastfloat_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint32_hi64(uint32_t r0, bool &truncated) noexcept { uint32_hi64(uint32_t r0, bool &truncated) noexcept {
return uint64_hi64(r0, truncated); return uint64_hi64(r0, truncated);
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t fastfloat_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept { uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept {
uint64_t x0 = r0; uint64_t x0 = r0;
uint64_t x1 = r1; uint64_t x1 = r1;
return uint64_hi64((x0 << 32) | x1, truncated); return uint64_hi64((x0 << 32) | x1, truncated);
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t fastfloat_inline FASTFLOAT_CONSTEXPR20 uint64_t
uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept { uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
uint64_t x0 = r0; uint64_t x0 = r0;
uint64_t x1 = r1; uint64_t x1 = r1;
@ -211,17 +208,15 @@ uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept {
// we want an efficient operation. for msvc, where // we want an efficient operation. for msvc, where
// we don't have built-in intrinsics, this is still // we don't have built-in intrinsics, this is still
// pretty fast. // pretty fast.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb fastfloat_inline FASTFLOAT_CONSTEXPR20 limb
scalar_add(limb x, limb y, bool &overflow) noexcept { scalar_add(limb x, limb y, bool &overflow) noexcept {
limb z; limb z;
// gcc and clang // gcc and clang
#if defined(__has_builtin) #if FASTFLOAT_HAS_BUILTIN(__builtin_add_overflow)
#if __has_builtin(__builtin_add_overflow) if (!is_constant_evaluated()) {
if (!cpp20_and_in_constexpr()) {
overflow = __builtin_add_overflow(x, y, &z); overflow = __builtin_add_overflow(x, y, &z);
return z; return z;
} }
#endif
#endif #endif
// generic, this still optimizes correctly on MSVC. // generic, this still optimizes correctly on MSVC.
@ -231,8 +226,8 @@ scalar_add(limb x, limb y, bool &overflow) noexcept {
} }
// multiply two small integers, getting both the high and low bits. // multiply two small integers, getting both the high and low bits.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb fastfloat_inline FASTFLOAT_CONSTEXPR20 limb scalar_mul(limb x, limb y,
scalar_mul(limb x, limb y, limb &carry) noexcept { limb &carry) noexcept {
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
#if defined(__SIZEOF_INT128__) #if defined(__SIZEOF_INT128__)
// GCC and clang both define it as an extension. // GCC and clang both define it as an extension.
@ -258,16 +253,14 @@ scalar_mul(limb x, limb y, limb &carry) noexcept {
// add scalar value to bigint starting from offset. // add scalar value to bigint starting from offset.
// used in grade school multiplication // used in grade school multiplication
template <uint16_t size> template <limb_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y, fastfloat_inline FASTFLOAT_CONSTEXPR20 bool
size_t start) noexcept { small_add_from(stackvec<size> &vec, limb carry, limb_t start) noexcept {
size_t index = start;
limb carry = y;
bool overflow; bool overflow;
while (carry != 0 && index < vec.len()) { while (carry != 0 && start < vec.len()) {
vec[index] = scalar_add(vec[index], carry, overflow); vec[start] = scalar_add(vec[start], carry, overflow);
carry = limb(overflow); carry = limb(overflow);
index += 1; ++start;
} }
if (carry != 0) { if (carry != 0) {
FASTFLOAT_TRY(vec.try_push(carry)); FASTFLOAT_TRY(vec.try_push(carry));
@ -276,18 +269,18 @@ inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec<size> &vec, limb y,
} }
// add scalar value to bigint. // add scalar value to bigint.
template <uint16_t size> template <limb_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_inline FASTFLOAT_CONSTEXPR20 bool small_add(stackvec<size> &vec,
small_add(stackvec<size> &vec, limb y) noexcept { limb y) noexcept {
return small_add_from(vec, y, 0); return small_add_from(vec, y, 0);
} }
// multiply bigint by scalar value. // multiply bigint by scalar value.
template <uint16_t size> template <limb_t size>
inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec, fastfloat_inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
limb y) noexcept { limb y) noexcept {
limb carry = 0; limb carry = 0;
for (size_t index = 0; index < vec.len(); index++) { for (limb_t index = 0; index != vec.len(); ++index) {
vec[index] = scalar_mul(vec[index], y, carry); vec[index] = scalar_mul(vec[index], y, carry);
} }
if (carry != 0) { if (carry != 0) {
@ -298,19 +291,20 @@ inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec<size> &vec,
// add bigint to bigint starting from index. // add bigint to bigint starting from index.
// used in grade school multiplication // used in grade school multiplication
template <uint16_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y, fastfloat_inline FASTFLOAT_CONSTEXPR20 bool
size_t start) noexcept { large_add_from(stackvec<size> &x, limb_span y, limb_t start) noexcept {
// the effective x buffer is from `xstart..x.len()`, so exit early // the effective x buffer is from `xstart..x.len()`, so exit early
// if we can't get that current range. // if we can't get that current range.
if (x.len() < start || y.len() > x.len() - start) { if (x.len() < start ||
FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); y.len() > static_cast<uint_fast16_t>(x.len() - start)) {
FASTFLOAT_TRY(x.try_resize(static_cast<limb_t>(y.len()) + start, 0));
} }
bool carry = false; bool carry = false;
for (size_t index = 0; index < y.len(); index++) { for (limb_t index = 0; index != y.len(); ++index) {
limb xi = x[index + start]; limb xi = x[index + start];
limb yi = y[index]; limb const yi = y[index];
bool c1 = false; bool c1 = false;
bool c2 = false; bool c2 = false;
xi = scalar_add(xi, yi, c1); xi = scalar_add(xi, yi, c1);
@ -323,21 +317,22 @@ FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec<size> &x, limb_span y,
// handle overflow // handle overflow
if (carry) { if (carry) {
FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); FASTFLOAT_TRY(small_add_from(x, 1, static_cast<limb_t>(y.len()) + start));
} }
return true; return true;
} }
// add bigint to bigint. // add bigint to bigint.
template <uint16_t size> template <limb_t size>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_inline FASTFLOAT_CONSTEXPR20 bool
large_add_from(stackvec<size> &x, limb_span y) noexcept { large_add_from(stackvec<size> &x, limb_span y) noexcept {
return large_add_from(x, y, 0); return large_add_from(x, y, 0);
} }
// grade-school multiplication algorithm // grade-school multiplication algorithm
template <uint16_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept { fastfloat_inline FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x,
limb_span y) noexcept {
limb_span xs = limb_span(x.data, x.len()); limb_span xs = limb_span(x.data, x.len());
stackvec<size> z(xs); stackvec<size> z(xs);
limb_span zs = limb_span(z.data, z.len()); limb_span zs = limb_span(z.data, z.len());
@ -345,8 +340,8 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
if (y.len() != 0) { if (y.len() != 0) {
limb y0 = y[0]; limb y0 = y[0];
FASTFLOAT_TRY(small_mul(x, y0)); FASTFLOAT_TRY(small_mul(x, y0));
for (size_t index = 1; index < y.len(); index++) { for (limb_t index = 1; index != y.len(); ++index) {
limb yi = y[index]; limb const yi = y[index];
stackvec<size> zi; stackvec<size> zi;
if (yi != 0) { if (yi != 0) {
// re-use the same buffer throughout // re-use the same buffer throughout
@ -364,8 +359,9 @@ FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec<size> &x, limb_span y) noexcept {
} }
// grade-school multiplication algorithm // grade-school multiplication algorithm
template <uint16_t size> template <limb_t size>
FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept { fastfloat_inline FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x,
limb_span y) noexcept {
if (y.len() == 1) { if (y.len() == 1) {
FASTFLOAT_TRY(small_mul(x, y[0])); FASTFLOAT_TRY(small_mul(x, y[0]));
} else { } else {
@ -375,7 +371,7 @@ FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec<size> &x, limb_span y) noexcept {
} }
template <typename = void> struct pow5_tables { template <typename = void> struct pow5_tables {
static constexpr uint32_t large_step = 135; static constexpr limb_t large_step = 135;
static constexpr uint64_t small_power_of_5[] = { static constexpr uint64_t small_power_of_5[] = {
1UL, 1UL,
5UL, 5UL,
@ -419,7 +415,7 @@ template <typename = void> struct pow5_tables {
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <typename T> constexpr uint32_t pow5_tables<T>::large_step; template <typename T> constexpr limb_t pow5_tables<T>::large_step;
template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[]; template <typename T> constexpr uint64_t pow5_tables<T>::small_power_of_5[];
@ -435,14 +431,14 @@ struct bigint : pow5_tables<> {
// storage of the limbs, in little-endian order. // storage of the limbs, in little-endian order.
stackvec<bigint_limbs> vec; stackvec<bigint_limbs> vec;
FASTFLOAT_CONSTEXPR20 bigint() : vec() {} FASTFLOAT_CONSTEXPR20 bigint() noexcept : vec() {}
bigint(bigint const &) = delete; bigint(bigint const &) = delete;
bigint &operator=(bigint const &) = delete; bigint &operator=(bigint const &) = delete;
bigint(bigint &&) = delete; bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete; bigint &operator=(bigint &&other) = delete;
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() { FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) noexcept : vec() {
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
vec.push_unchecked(value); vec.push_unchecked(value);
#else #else
@ -493,9 +489,9 @@ struct bigint : pow5_tables<> {
} else if (vec.len() < other.vec.len()) { } else if (vec.len() < other.vec.len()) {
return -1; return -1;
} else { } else {
for (size_t index = vec.len(); index > 0; index--) { for (limb_t index = vec.len(); index != 0; --index) {
limb xi = vec[index - 1]; limb const xi = vec[index - 1];
limb yi = other.vec[index - 1]; limb const yi = other.vec[index - 1];
if (xi > yi) { if (xi > yi) {
return 1; return 1;
} else if (xi < yi) { } else if (xi < yi) {
@ -508,7 +504,7 @@ struct bigint : pow5_tables<> {
// shift left each limb n bits, carrying over to the new limb // shift left each limb n bits, carrying over to the new limb
// returns true if we were able to shift all the digits. // returns true if we were able to shift all the digits.
FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl_bits(limb_t n) noexcept {
// Internally, for each item, we shift left by n, and add the previous // Internally, for each item, we shift left by n, and add the previous
// right shifted limb-bits. // right shifted limb-bits.
// For example, we transform (for u8) shifted left 2, to: // For example, we transform (for u8) shifted left 2, to:
@ -517,10 +513,10 @@ struct bigint : pow5_tables<> {
FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n != 0);
FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8);
size_t shl = n; limb_t const shl = n;
size_t shr = limb_bits - shl; limb_t const shr = limb_bits - shl;
limb prev = 0; limb prev = 0;
for (size_t index = 0; index < vec.len(); index++) { for (limb_t index = 0; index != vec.len(); ++index) {
limb xi = vec[index]; limb xi = vec[index];
vec[index] = (xi << shl) | (prev >> shr); vec[index] = (xi << shl) | (prev >> shr);
prev = xi; prev = xi;
@ -534,9 +530,10 @@ struct bigint : pow5_tables<> {
} }
// move the limbs left by `n` limbs. // move the limbs left by `n` limbs.
FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl_limbs(limb_t n) noexcept {
FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n != 0);
if (n + vec.len() > vec.capacity()) { if (n + vec.len() > vec.capacity()) {
// we can't shift more than the capacity of the vector.
return false; return false;
} else if (!vec.is_empty()) { } else if (!vec.is_empty()) {
// move limbs // move limbs
@ -555,9 +552,9 @@ struct bigint : pow5_tables<> {
} }
// move the limbs left by `n` bits. // move the limbs left by `n` bits.
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept { FASTFLOAT_CONSTEXPR20 bool shl(bigint_bits_t n) noexcept {
size_t rem = n % limb_bits; auto const rem = static_cast<limb_t>(n % limb_bits);
size_t div = n / limb_bits; auto const div = static_cast<limb_t>(n / limb_bits);
if (rem != 0) { if (rem != 0) {
FASTFLOAT_TRY(shl_bits(rem)); FASTFLOAT_TRY(shl_bits(rem));
} }
@ -568,8 +565,9 @@ struct bigint : pow5_tables<> {
} }
// get the number of leading zeros in the bigint. // get the number of leading zeros in the bigint.
FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept { FASTFLOAT_CONSTEXPR20 bigint_bits_t ctlz() const noexcept {
if (vec.is_empty()) { if (vec.is_empty()) {
// empty vector, no bits, no zeros.
return 0; return 0;
} else { } else {
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
@ -583,9 +581,9 @@ struct bigint : pow5_tables<> {
} }
// get the number of bits in the bigint. // get the number of bits in the bigint.
FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept { FASTFLOAT_CONSTEXPR20 bigint_bits_t bit_length() const noexcept {
int lz = ctlz(); auto lz = ctlz();
return int(limb_bits * vec.len()) - lz; return limb_bits * vec.len() - lz;
} }
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); } FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); }
@ -593,41 +591,51 @@ struct bigint : pow5_tables<> {
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); } FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); }
// multiply as if by 2 raised to a power. // multiply as if by 2 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); } FASTFLOAT_CONSTEXPR20 bool pow2(am_pow_t const exp) noexcept {
FASTFLOAT_ASSERT(exp >= 0);
return shl(static_cast<fast_float::bigint_bits_t>(exp));
}
// multiply as if by 5 raised to a power. // multiply as if by 5 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow5(am_pow_t exp) noexcept {
FASTFLOAT_ASSERT(exp >= 0);
// multiply by a power of 5 // multiply by a power of 5
size_t large_length = sizeof(large_power_of_5) / sizeof(limb); limb_t const large_length = sizeof(large_power_of_5) / sizeof(limb);
limb_span large = limb_span(large_power_of_5, large_length); limb_span const large = limb_span(large_power_of_5, large_length);
while (exp >= large_step) { while (exp >= large_step) {
FASTFLOAT_TRY(large_mul(vec, large)); FASTFLOAT_TRY(large_mul(vec, large));
exp -= large_step; exp -= large_step;
} }
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
uint32_t small_step = 27; limb_t constexpr small_step = 27;
limb max_native = 7450580596923828125UL; limb constexpr max_native = 7450580596923828125UL;
#else #else
uint32_t small_step = 13; limb_t constexpr small_step = 13;
limb max_native = 1220703125U; limb constexpr max_native = 1220703125U;
#endif #endif
while (exp >= small_step) { while (exp >= small_step) {
FASTFLOAT_TRY(small_mul(vec, max_native)); FASTFLOAT_TRY(small_mul(vec, max_native));
exp -= small_step; exp -= small_step;
} }
if (exp != 0) { if (exp != 0) {
#if defined(__clang__)
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
// This is similar to https://github.com/llvm/llvm-project/issues/47746, // This is similar to
// except the workaround described there don't work here // https://github.com/llvm/llvm-project/issues/47746, except
// the workaround described there don't work here
FASTFLOAT_TRY(small_mul(vec, limb((static_cast<void>(small_power_of_5[0]), FASTFLOAT_TRY(small_mul(vec, limb((static_cast<void>(small_power_of_5[0]),
small_power_of_5[exp])))); small_power_of_5[exp]))));
#else
FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp])));
#endif
} }
return true; return true;
} }
// multiply as if by 10 raised to a power. // multiply as if by 10 raised to a power.
FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept { FASTFLOAT_CONSTEXPR20 bool pow10(am_pow_t exp) noexcept {
FASTFLOAT_ASSERT(exp >= 0);
FASTFLOAT_TRY(pow5(exp)); FASTFLOAT_TRY(pow5(exp));
return pow2(exp); return pow2(exp);
} }

View File

@ -1,53 +1,135 @@
#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H #ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H #define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
#ifdef __has_include #ifdef _MSVC_LANG
#if __has_include(<version>) #define FASTFLOAT_CPLUSPLUS _MSVC_LANG
#include <version> #else
#endif #define FASTFLOAT_CPLUSPLUS __cplusplus
#endif #endif
// Testing for https://wg21.link/N3652, adopted in C++14 // Detect __has_*.
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 #ifdef __has_feature
#define FASTFLOAT_HAS_FEATURE(x) __has_feature(x)
#else
#define FASTFLOAT_HAS_FEATURE(x) 0
#endif
#ifdef __has_include
#define FASTFLOAT_HAS_INCLUDE(x) __has_include(x)
#else
#define FASTFLOAT_HAS_INCLUDE(x) 0
#endif
#ifdef __has_builtin
#define FASTFLOAT_HAS_BUILTIN(x) __has_builtin(x)
#else
#define FASTFLOAT_HAS_BUILTIN(x) 0
#endif
#ifdef __has_cpp_attribute
#define FASTFLOAT_HAS_CPP_ATTRIBUTE(x) __has_cpp_attribute(x)
#else
#define FASTFLOAT_HAS_CPP_ATTRIBUTE(x) 0
#endif
#if FASTFLOAT_HAS_INCLUDE(<version>)
#include <version>
#endif
// C++14 constexpr
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304L
#define FASTFLOAT_CONSTEXPR14 constexpr
#elif FASTFLOAT_CPLUSPLUS >= 201402L
#define FASTFLOAT_CONSTEXPR14 constexpr
#elif defined(_MSC_VER) && _MSC_VER >= 1910 && _MSVC_LANG >= 201402L
#define FASTFLOAT_CONSTEXPR14 constexpr #define FASTFLOAT_CONSTEXPR14 constexpr
#else #else
#define FASTFLOAT_CONSTEXPR14 #define FASTFLOAT_CONSTEXPR14
#endif #endif
// C++14 variable templates
#if defined(__cpp_variable_templates) && __cpp_variable_templates >= 201304L
#define FASTFLOAT_HAS_VARIABLE_TEMPLATES 1
#elif FASTFLOAT_CPLUSPLUS >= 201402L
#define FASTFLOAT_HAS_VARIABLE_TEMPLATES 1
#else
#define FASTFLOAT_HAS_VARIABLE_TEMPLATES 0
#endif
// C++17 if constexpr
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
#define FASTFLOAT_CONSTEXPR17 constexpr
#elif defined(__cpp_constexpr) && __cpp_constexpr >= 201603L
#define FASTFLOAT_CONSTEXPR17 constexpr
#elif FASTFLOAT_CPLUSPLUS >= 201703L
#define FASTFLOAT_CONSTEXPR17 constexpr
#else
#define FASTFLOAT_CONSTEXPR17
#endif
// C++17 inline variables
#if defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L
#define FASTFLOAT_INLINE_VARIABLE inline constexpr
#elif __cplusplus >= 201703L
#define FASTFLOAT_INLINE_VARIABLE inline constexpr
#elif defined(_MSC_VER) && _MSC_VER >= 1912 && _MSVC_LANG >= 201703L
#define FASTFLOAT_INLINE_VARIABLE inline constexpr
#else
#define FASTFLOAT_INLINE_VARIABLE static constexpr
#endif
// Before C++17 constexpr variables may need an out-of-class definition.
#if FASTFLOAT_CPLUSPLUS >= 201703L
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0
#else
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1
#endif
// C++20 std::bit_cast
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
#define FASTFLOAT_HAS_BIT_CAST 1 #define FASTFLOAT_HAS_BIT_CAST 1
#else #else
#define FASTFLOAT_HAS_BIT_CAST 0 #define FASTFLOAT_HAS_BIT_CAST 0
#endif #endif
// C++20 constexpr library support, including std::is_constant_evaluated(),
// std::bit_cast(), std::copy and std::fill, and constexpr algorithms. Therefore
// all required library feature macros must be present.
#if defined(__cpp_lib_is_constant_evaluated) && \ #if defined(__cpp_lib_is_constant_evaluated) && \
__cpp_lib_is_constant_evaluated >= 201811L __cpp_lib_is_constant_evaluated >= 201811L && defined(__cpp_consteval) && \
__cpp_consteval >= 201811L && FASTFLOAT_HAS_BIT_CAST
#define FASTFLOAT_CONSTEVAL consteval
#define FASTFLOAT_CONSTEXPR20 constexpr
#define FASTFLOAT_IS_CONSTEXPR 1
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1 #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
#else #else
#define FASTFLOAT_CONSTEVAL constexpr
#define FASTFLOAT_CONSTEXPR20
#define FASTFLOAT_IS_CONSTEXPR 0
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
#endif #endif
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L // C++20 std::byteswap
#define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x) #if defined(__cpp_lib_byteswap) && __cpp_lib_byteswap >= 202110L
#define FASTFLOAT_HAS_BYTESWAP 1
#else #else
#define FASTFLOAT_IF_CONSTEXPR17(x) if (x) #define FASTFLOAT_HAS_BYTESWAP 0
#endif #endif
// Testing for relevant C++20 constexpr library features #ifdef FASTFLOAT_ASSUME
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \ // Use the provided definition.
defined(__cpp_lib_constexpr_algorithms) && \ #elif FASTFLOAT_HAS_CPP_ATTRIBUTE(assume) && !defined(__GNUC__)
__cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/ #define FASTFLOAT_ASSUME(expr) [[assume(expr)]]
#define FASTFLOAT_CONSTEXPR20 constexpr
#define FASTFLOAT_IS_CONSTEXPR 1
#else #else
#define FASTFLOAT_CONSTEXPR20 #define FASTFLOAT_ASSUME(expr)
#define FASTFLOAT_IS_CONSTEXPR 0
#endif #endif
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) #ifdef FASTFLOAT_NO_UNIQUE_ADDRESS
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0 // Use the provided definition.
#elif FASTFLOAT_HAS_CPP_ATTRIBUTE(no_unique_address)
#define FASTFLOAT_NO_UNIQUE_ADDRESS [[no_unique_address]]
// VS2019 v16.10 and later except clang-cl (https://reviews.llvm.org/D110485).
#elif defined(_MSC_VER) && _MSC_VER >= 1929 && !defined(__clang__)
#define FASTFLOAT_NO_UNIQUE_ADDRESS [[msvc::no_unique_address]]
#else #else
#define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1 #define FASTFLOAT_NO_UNIQUE_ADDRESS
#endif #endif
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H #endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H

View File

@ -17,30 +17,32 @@ namespace fast_float {
// most significant bits and the low part corresponding to the least significant // most significant bits and the low part corresponding to the least significant
// bits. // bits.
// //
template <int bit_precision> template <am_bits_t bit_precision>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 fastfloat_inline FASTFLOAT_CONSTEXPR20 value128
compute_product_approximation(int64_t q, uint64_t w) { compute_product_approximation(am_pow_t q, am_mant_t w) noexcept {
int const index = 2 * int(q - powers::smallest_power_of_five); am_pow_t const index = 2 * (q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact // For small values of q, e.g., q in [0,27], the answer is always exact
// because The line value128 firstproduct = full_multiplication(w, // because The line value128 firstproduct = full_multiplication(w,
// power_of_five_128[index]); gives the exact answer. // power_of_five_128[index]); gives the exact answer.
value128 firstproduct = value128 firstproduct =
full_multiplication(w, powers::power_of_five_128[index]); full_multiplication(w, powers::power_of_five_128[index]);
static_assert((bit_precision >= 0) && (bit_precision <= 64), static_assert((bit_precision >= 0) && (bit_precision <= 64),
" precision should be in (0,64]"); " precision should be in [0,64]");
constexpr uint64_t precision_mask = constexpr uint64_t precision_mask =
(bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision)
: uint64_t(0xFFFFFFFFFFFFFFFF); : uint64_t(0xFFFFFFFFFFFFFFFF);
if ((firstproduct.high & precision_mask) == if ((firstproduct.high & precision_mask) ==
precision_mask) { // could further guard with (lower + w < lower) precision_mask) { // could further guard with (lower + w < lower)
// regarding the second product, we only need secondproduct.high, but our // regarding the second product, we only need secondproduct.high, but our
// expectation is that the compiler will optimize this extra work away if // expectation is that the compiler will optimize this extra work away if
// needed. // needed.
value128 secondproduct = value128 const secondproduct =
full_multiplication(w, powers::power_of_five_128[index + 1]); full_multiplication(w, powers::power_of_five_128[index + 1]);
firstproduct.low += secondproduct.high; firstproduct.low += secondproduct.high;
if (secondproduct.high > firstproduct.low) { if (secondproduct.high > firstproduct.low) {
firstproduct.high++; ++firstproduct.high;
} }
} }
return firstproduct; return firstproduct;
@ -68,7 +70,7 @@ namespace detail {
* non-negative without changing the result: the bias is a multiple of 2^16 * non-negative without changing the result: the bias is a multiple of 2^16
* and is removed again after the shift. * and is removed again after the shift.
*/ */
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { constexpr fastfloat_inline am_pow_t power(am_pow_t const q) noexcept {
return (((152170 + 65536) * q + (2048 * 65536)) >> 16) - 2048 + 63; return (((152170 + 65536) * q + (2048 * 65536)) >> 16) - 2048 + 63;
} }
} // namespace detail } // namespace detail
@ -76,25 +78,25 @@ constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept {
// create an adjusted mantissa, biased by the invalid power2 // create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q. // for significant digits already multiplied by 10 ** q.
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa fastfloat_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa
compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { compute_error_scaled(am_pow_t q, am_mant_t w, limb_t lz) noexcept {
int hilz = int(w >> 63) ^ 1; auto const hilz = static_cast<am_bits_t>((w >> 63) ^ 1);
adjusted_mantissa answer; adjusted_mantissa answer;
answer.mantissa = w << hilz; answer.mantissa = w << hilz;
int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); constexpr am_pow_t bias =
answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + binary::mantissa_explicit_bits() - binary::minimum_exponent();
invalid_am_bias); answer.power2 = detail::power(q) + bias - hilz - lz - 62 + invalid_am_bias;
return answer; return answer;
} }
// w * 10 ** q, without rounding the representation up. // w * 10 ** q, without rounding the representation up.
// the power2 in the exponent will be adjusted by invalid_am_bias. // the power2 in the exponent will be adjusted by invalid_am_bias.
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
compute_error(int64_t q, uint64_t w) noexcept { compute_error(am_pow_t q, am_mant_t w) noexcept {
int lz = leading_zeroes(w); auto const lz = leading_zeroes(w);
w <<= lz; w <<= lz;
value128 product = value128 const product =
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
return compute_error_scaled<binary>(q, product.high, lz); return compute_error_scaled<binary>(q, product.high, lz);
} }
@ -105,26 +107,27 @@ compute_error(int64_t q, uint64_t w) noexcept {
// cases, we return an adjusted_mantissa with a negative power of 2: the caller // cases, we return an adjusted_mantissa with a negative power of 2: the caller
// should recompute in such cases. // should recompute in such cases.
template <typename binary> template <typename binary>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
compute_float(int64_t q, uint64_t w) noexcept { compute_float(am_pow_t q, am_mant_t w) noexcept {
adjusted_mantissa answer; adjusted_mantissa answer;
if ((w == 0) || (q < binary::smallest_power_of_ten())) { if ((w == 0) || (q < binary::smallest_power_of_ten())) {
// result should be zero
answer.power2 = 0; answer.power2 = 0;
answer.mantissa = 0; answer.mantissa = 0;
// result should be zero
return answer; return answer;
} }
if (q > binary::largest_power_of_ten()) { if (q > binary::largest_power_of_ten()) {
// we want to get infinity: // result should be infinity
answer.power2 = binary::infinite_power(); answer.power2 = binary::infinite_power();
answer.mantissa = 0; answer.mantissa = 0;
return answer; return answer;
} }
// At this point in time q is in [powers::smallest_power_of_five, // At this point in time q is in [powers::smallest_power_of_five,
// powers::largest_power_of_five]. // powers::largest_power_of_five].
// We want the most significant bit of i to be 1. Shift if needed. // We want the most significant bit of i to be 1. Shift if needed.
int lz = leading_zeroes(w); auto const lz = leading_zeroes(w);
w <<= lz; w <<= lz;
// The required precision is binary::mantissa_explicit_bits() + 3 because // The required precision is binary::mantissa_explicit_bits() + 3 because
@ -133,7 +136,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// 3. We might lose a bit due to the "upperbit" routine (result too small, // 3. We might lose a bit due to the "upperbit" routine (result too small,
// requiring a shift) // requiring a shift)
value128 product = value128 const product =
compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w); compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
// The computed 'product' is always sufficient. // The computed 'product' is always sufficient.
// Mathematical proof: // Mathematical proof:
@ -144,25 +147,29 @@ compute_float(int64_t q, uint64_t w) noexcept {
// branchless approach: value128 product = compute_product(q, w); but in // branchless approach: value128 product = compute_product(q, w); but in
// practice, we can win big with the compute_product_approximation if its // practice, we can win big with the compute_product_approximation if its
// additional branch is easily predicted. Which is best is data specific. // additional branch is easily predicted. Which is best is data specific.
int upperbit = int(product.high >> 63); auto const upperbit = static_cast<am_bits_t>(product.high >> 63);
int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3; auto const shift = static_cast<am_bits_t>(
upperbit + 64 - binary::mantissa_explicit_bits() - 3);
// Shift right the mantissa to the correct position
answer.mantissa = product.high >> shift; answer.mantissa = product.high >> shift;
answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - answer.power2 = detail::power(q) + upperbit - lz - binary::minimum_exponent();
binary::minimum_exponent());
if (answer.power2 <= 0) { // we have a subnormal? // Now, we need to round the mantissa correctly.
if (answer.power2 <= 0) {
// We have a subnormal or very small number.
// Here have that answer.power2 <= 0 so -answer.power2 >= 0 // Here have that answer.power2 <= 0 so -answer.power2 >= 0
if (-answer.power2 + 1 >= auto const subnormal_shift = static_cast<am_bits_t>(-answer.power2 + 1);
64) { // if we have more than 64 bits below the minimum exponent, you if (subnormal_shift >= 64) {
// have a zero for sure. // We have more than 64 bits below the minimum exponent
// result should be zero
answer.power2 = 0; answer.power2 = 0;
answer.mantissa = 0; answer.mantissa = 0;
// result should be zero
return answer; return answer;
} }
// next line is safe because -answer.power2 + 1 < 64 // We have a subnormal number. We need to shift the mantissa to the right
int const subnormal_shift = -answer.power2 + 1;
answer.mantissa >>= subnormal_shift; answer.mantissa >>= subnormal_shift;
// A subnormal result can also fall exactly between two floats. With at // A subnormal result can also fall exactly between two floats. With at
// most 19 digits this never happens for float and double, but it does for // most 19 digits this never happens for float and double, but it does for
@ -174,7 +181,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
(q <= binary::max_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
((answer.mantissa & 3) == 1)) { ((answer.mantissa & 3) == 1)) {
if (((answer.mantissa << subnormal_shift) << shift) == product.high) { if (((answer.mantissa << subnormal_shift) << shift) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up answer.mantissa &= ~am_mant_t(1); // flip it so that we do not round up
} }
} }
answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa += (answer.mantissa & 1); // round up
@ -187,14 +194,14 @@ compute_float(int64_t q, uint64_t w) noexcept {
// subnormal, but we can only know this after rounding. // subnormal, but we can only know this after rounding.
// So we only declare a subnormal if we are smaller than the threshold. // So we only declare a subnormal if we are smaller than the threshold.
answer.power2 = answer.power2 =
(answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) (answer.mantissa < (am_mant_t(1) << binary::mantissa_explicit_bits()))
? 0 ? 0
: 1; : 1;
return answer; return answer;
} }
// usually, we round *up*, but if we fall right in between and and we have an // Usually, we round *up*, but if we fall right in between and we have an even
// even basis, we need to round down // basis, we need to round down.
// We are only concerned with the cases where 5**q fits in single 64-bit word. // We are only concerned with the cases where 5**q fits in single 64-bit word.
if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) &&
(q <= binary::max_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) &&
@ -205,10 +212,11 @@ compute_float(int64_t q, uint64_t w) noexcept {
// ... we dropped out only zeroes. But if this happened, then we can go // ... we dropped out only zeroes. But if this happened, then we can go
// back!!! // back!!!
if ((answer.mantissa << shift) == product.high) { if ((answer.mantissa << shift) == product.high) {
answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up answer.mantissa &= ~am_mant_t(1); // flip it so that we do not round up
} }
} }
// Normal rounding
answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1; answer.mantissa >>= 1;
// Both fix-ups below are rare. They are marked unlikely so that clang keeps // Both fix-ups below are rare. They are marked unlikely so that clang keeps
@ -220,16 +228,15 @@ compute_float(int64_t q, uint64_t w) noexcept {
#pragma clang diagnostic ignored "-Wc++20-extensions" #pragma clang diagnostic ignored "-Wc++20-extensions"
#endif #endif
#endif #endif
if fastfloat_clang_unlikely (answer.mantissa >= if fastfloat_unlikely (answer.mantissa >=
(uint64_t(2) (am_mant_t(2) << binary::mantissa_explicit_bits())) {
<< binary::mantissa_explicit_bits())) { answer.mantissa = (am_mant_t(1) << binary::mantissa_explicit_bits());
answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); ++answer.power2; // undo previous addition
answer.power2++; // undo previous addition
} }
answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); answer.mantissa &= ~(am_mant_t(1) << binary::mantissa_explicit_bits());
if fastfloat_clang_unlikely (answer.power2 >= binary::infinite_power()) { if fastfloat_unlikely (answer.power2 >= binary::infinite_power()) {
// infinity // result should be infinity
answer.power2 = binary::infinite_power(); answer.power2 = binary::infinite_power();
answer.mantissa = 0; answer.mantissa = 0;
} }

View File

@ -37,8 +37,8 @@ constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
// this algorithm is not even close to optimized, but it has no practical // this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need // effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast. // to slow down performance for faster algorithms, and this is still fast.
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t fastfloat_inline FASTFLOAT_CONSTEXPR14 am_pow_t
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept { scientific_exponent(am_mant_t mantissa, am_pow_t exponent) noexcept {
while (mantissa >= 10000) { while (mantissa >= 10000) {
mantissa /= 10000; mantissa /= 10000;
exponent += 4; exponent += 4;
@ -56,30 +56,27 @@ scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
// this converts a native floating-point number to an extended-precision float. // this converts a native floating-point number to an extended-precision float.
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
to_extended(T value) noexcept { to_extended(T const value) noexcept {
using equiv_uint = equiv_uint_t<T>; using equiv_uint = equiv_uint_t<T>;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am; adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - constexpr am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent(); binary_format<T>::minimum_exponent();
equiv_uint bits;
#if FASTFLOAT_HAS_BIT_CAST auto const bits = bit_cast<equiv_uint>(value);
bits = std::bit_cast<equiv_uint>(value);
#else
::memcpy(&bits, &value, sizeof(T));
#endif
if ((bits & exponent_mask) == 0) { if ((bits & exponent_mask) == 0) {
// denormal // denormal
am.power2 = 1 - bias; am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask; am.mantissa = bits & mantissa_mask;
} else { } else {
// normal // normal
am.power2 = int32_t((bits & exponent_mask) >> am.power2 = static_cast<am_pow_t>(
binary_format<T>::mantissa_explicit_bits()); (bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias; am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
} }
@ -91,8 +88,8 @@ to_extended(T value) noexcept {
// we are given a native float that represents b, so we need to adjust it // we are given a native float that represents b, so we need to adjust it
// halfway between b and b+u. // halfway between b and b+u.
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
to_extended_halfway(T value) noexcept { to_extended_halfway(T const value) noexcept {
adjusted_mantissa am = to_extended(value); adjusted_mantissa am = to_extended(value);
am.mantissa <<= 1; am.mantissa <<= 1;
am.mantissa += 1; am.mantissa += 1;
@ -102,16 +99,17 @@ to_extended_halfway(T value) noexcept {
// round an extended-precision float to the nearest machine float. // round an extended-precision float to the nearest machine float.
template <typename T, typename callback> template <typename T, typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am, fastfloat_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
callback cb) noexcept { callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; constexpr am_pow_t mantissa_shift =
64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) { if (-am.power2 >= mantissa_shift) {
// have a denormal float // have a denormal float
int32_t shift = -am.power2 + 1; am_pow_t shift = -am.power2 + 1;
cb(am, (shift < 64 ? shift : 64)); cb(am, (shift < 64 ? shift : 64));
// check for round-up: if rounding-nearest carried us to the hidden bit. // check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < am.power2 = (am.mantissa <
(uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits()))
? 0 ? 0
: 1; : 1;
return; return;
@ -122,13 +120,13 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
// check for carry // check for carry
if (am.mantissa >= if (am.mantissa >=
(uint64_t(2) << binary_format<T>::mantissa_explicit_bits())) { (am_mant_t(2) << binary_format<T>::mantissa_explicit_bits())) {
am.mantissa = (uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); am.mantissa = (am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
am.power2++; ++am.power2;
} }
// check for infinite: we could have carried to an infinite power // check for infinite: we could have carried to an infinite power
am.mantissa &= ~(uint64_t(1) << binary_format<T>::mantissa_explicit_bits()); am.mantissa &= ~(am_mant_t(1) << binary_format<T>::mantissa_explicit_bits());
if (am.power2 >= binary_format<T>::infinite_power()) { if (am.power2 >= binary_format<T>::infinite_power()) {
am.power2 = binary_format<T>::infinite_power(); am.power2 = binary_format<T>::infinite_power();
am.mantissa = 0; am.mantissa = 0;
@ -136,12 +134,13 @@ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am,
} }
template <typename callback> template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_inline FASTFLOAT_CONSTEXPR14 void
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift, round_nearest_tie_even(adjusted_mantissa &am, am_pow_t shift,
callback cb) noexcept { callback cb) noexcept {
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1; am_mant_t const mask = (shift == 64) ? std::numeric_limits<am_mant_t>::max()
uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1); : (am_mant_t(1) << shift) - 1;
uint64_t truncated_bits = am.mantissa & mask; am_mant_t const halfway = (shift == 0) ? 0 : am_mant_t(1) << (shift - 1);
am_mant_t truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway; bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway; bool is_halfway = truncated_bits == halfway;
@ -154,11 +153,11 @@ round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
am.power2 += shift; am.power2 += shift;
bool is_odd = (am.mantissa & 1) == 1; bool is_odd = (am.mantissa & 1) == 1;
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); am.mantissa += am_mant_t(cb(is_odd, is_halfway, is_above));
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_inline FASTFLOAT_CONSTEXPR14 void
round_down(adjusted_mantissa &am, int32_t shift) noexcept { round_down(adjusted_mantissa &am, am_pow_t shift) noexcept {
if (shift == 64) { if (shift == 64) {
am.mantissa = 0; am.mantissa = 0;
} else { } else {
@ -168,39 +167,41 @@ round_down(adjusted_mantissa &am, int32_t shift) noexcept {
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_inline FASTFLOAT_CONSTEXPR20 void
skip_zeros(UC const *&first, UC const *last) noexcept { skip_zeros(UC const *&first, UC const *last) noexcept {
uint64_t val; if (!is_constant_evaluated()) {
while (!cpp20_and_in_constexpr() && while (std::distance(first, last) >= int_cmp_len<UC>()) {
std::distance(first, last) >= int_cmp_len<UC>()) { uint64_t val;
::memcpy(&val, first, sizeof(uint64_t)); std::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) { if (val != int_cmp_zeros<UC>()) {
break; break;
}
first += int_cmp_len<UC>();
} }
first += int_cmp_len<UC>();
} }
while (first != last) { while (first != last) {
if (*first != UC('0')) { if (*first != UC('0')) {
break; break;
} }
first++; ++first;
} }
} }
// determine if any non-zero digits were truncated. // determine if any non-zero digits were truncated.
// all characters must be valid digits. // all characters must be valid digits.
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_inline FASTFLOAT_CONSTEXPR20 bool
is_truncated(UC const *first, UC const *last) noexcept { is_truncated(UC const *first, UC const *last) noexcept {
// do 8-bit optimizations, can just compare to 8 literal 0s. // do 8-bit optimizations, can just compare to 8 literal 0s.
uint64_t val; if (!is_constant_evaluated()) {
while (!cpp20_and_in_constexpr() && while (std::distance(first, last) >= int_cmp_len<UC>()) {
std::distance(first, last) >= int_cmp_len<UC>()) { uint64_t val;
::memcpy(&val, first, sizeof(uint64_t)); std::memcpy(&val, first, sizeof(uint64_t));
if (val != int_cmp_zeros<UC>()) { if (val != int_cmp_zeros<UC>()) {
return true; return true;
}
first += int_cmp_len<UC>();
} }
first += int_cmp_len<UC>();
} }
while (first != last) { while (first != last) {
if (*first != UC('0')) { if (*first != UC('0')) {
@ -212,60 +213,82 @@ is_truncated(UC const *first, UC const *last) noexcept {
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_inline FASTFLOAT_CONSTEXPR20 bool
is_truncated(span<UC const> s) noexcept { is_truncated(span<UC const> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len()); return is_truncated(s.ptr, s.ptr + s.len());
} }
#if defined(FASTFLOAT_64BIT_LIMB) && defined(FASTFLOAT_X86_SIMD) && \
FASTFLOAT_X86_SIMD >= 31
fastfloat_inline FASTFLOAT_CONSTEXPR20 void
parse_sixteen_digits(char const *&p, limb &value, am_digits &counter,
am_digits &count) noexcept {
value = parse_16_digits(p);
p += 16;
counter += 16;
count += 16;
}
#endif
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_inline FASTFLOAT_CONSTEXPR20 void
parse_eight_digits(UC const *&p, limb &value, size_t &counter, parse_eight_digits(UC const *&p, limb &value, am_digits &counter,
size_t &count) noexcept { am_digits &count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p); value = value * 100000000 + parse_8_digits(p);
p += 8; p += 8;
counter += 8; counter += 8;
count += 8; count += 8;
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_inline FASTFLOAT_CONSTEXPR14 void
parse_one_digit(UC const *&p, limb &value, size_t &counter, parse_four_digits(UC const *&p, limb &value, am_digits &counter,
size_t &count) noexcept { am_digits &count) noexcept {
value = value * 10 + limb(*p - UC('0')); value = value * 10000 + parse_4_digits(read_chars_to_unsigned<uint32_t>(p));
p++; p += 4;
counter++; counter += 4;
count++; count += 4;
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void template <typename UC>
add_native(bigint &big, limb power, limb value) noexcept { fastfloat_inline FASTFLOAT_CONSTEXPR14 void
parse_one_digit(UC const *&p, limb &value, am_digits &counter,
am_digits &count) noexcept {
value = value * 10 + limb(*p - UC('0'));
++p;
++counter;
++count;
}
fastfloat_inline FASTFLOAT_CONSTEXPR20 void add_native(bigint &big, limb power,
limb value) noexcept {
big.mul(power); big.mul(power);
big.add(value); big.add(value);
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_inline FASTFLOAT_CONSTEXPR20 void
round_up_bigint(bigint &big, size_t &count) noexcept { round_up_bigint(bigint &big, am_digits &count) noexcept {
// need to round-up the digits, but need to avoid rounding // need to round-up the digits, but need to avoid rounding
// ....9999 to ...10000, which could cause a false halfway point. // ....9999 to ...10000, which could cause a false halfway point.
add_native(big, 10, 1); add_native(big, 10, 1);
count++; ++count;
} }
// parse the significant digits into a big integer // parse the significant digits into a big integer
template <typename UC> template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 void inline FASTFLOAT_CONSTEXPR20 am_digits
parse_mantissa(bigint &result, parsed_number_string_t<UC> &num, parse_mantissa(bigint &result, const parsed_number_string_t<UC> &num) noexcept {
size_t max_digits, size_t &digits) noexcept {
// try to minimize the number of big integer and scalar multiplication. // try to minimize the number of big integer and scalar multiplication.
// therefore, try to parse 8 digits at a time, and multiply by the largest // therefore, try to parse 8 digits at a time, and multiply by the largest
// scalar value (9 or 19 digits) for each step. // scalar value (9 or 19 digits) for each step.
size_t counter = 0; constexpr am_digits max_digits = binary_format<T>::max_digits();
digits = 0; am_digits counter = 0;
am_digits digits = 0;
limb value = 0; limb value = 0;
#ifdef FASTFLOAT_64BIT_LIMB #ifdef FASTFLOAT_64BIT_LIMB
size_t step = 19; constexpr am_digits step = 19;
#else #else
size_t step = 9; constexpr am_digits step = 9;
#endif #endif
// process all integer digits. // process all integer digits.
@ -274,26 +297,42 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
skip_zeros(p, pend); skip_zeros(p, pend);
// process all digits, in increments of step per loop // process all digits, in increments of step per loop
while (p != pend) { while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && #if defined(FASTFLOAT_64BIT_LIMB) && defined(FASTFLOAT_X86_SIMD) && \
(max_digits - digits >= 8)) { FASTFLOAT_X86_SIMD >= 31
if FASTFLOAT_CONSTEXPR17 (sizeof(UC) == 1) {
if (!is_constant_evaluated() && std::distance(p, pend) >= 16 &&
step - counter >= 16 && max_digits - digits >= 16) {
parse_sixteen_digits(p, value, counter, digits);
}
}
#endif
while (std::distance(p, pend) >= 8 && step - counter >= 8 &&
max_digits - digits >= 8) {
parse_eight_digits(p, value, counter, digits); parse_eight_digits(p, value, counter, digits);
} }
if (std::distance(p, pend) >= 4 && step - counter >= 4 &&
max_digits - digits >= 4) {
parse_four_digits(p, value, counter, digits);
}
while (counter < step && p != pend && digits < max_digits) { while (counter < step && p != pend && digits < max_digits) {
parse_one_digit(p, value, counter, digits); parse_one_digit(p, value, counter, digits);
} }
if (digits == max_digits) { if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits // add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value); add_native(result, static_cast<limb>(powers_of_ten_uint64[counter]),
value);
bool truncated = is_truncated(p, pend); bool truncated = is_truncated(p, pend);
if (num.fraction.ptr != nullptr) { // A nonzero integer suffix already determines the rounding direction.
truncated |= is_truncated(num.fraction); if (!truncated && num.fraction.ptr != nullptr) {
truncated = is_truncated(num.fraction);
} }
if (truncated) { if (truncated) {
round_up_bigint(result, digits); round_up_bigint(result, digits);
} }
return; return digits;
} else { } else {
add_native(result, limb(powers_of_ten_uint64[counter]), value); add_native(result, static_cast<limb>(powers_of_ten_uint64[counter]),
value);
counter = 0; counter = 0;
value = 0; value = 0;
} }
@ -308,8 +347,17 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
} }
// process all digits, in increments of step per loop // process all digits, in increments of step per loop
while (p != pend) { while (p != pend) {
while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && #if defined(FASTFLOAT_64BIT_LIMB) && defined(FASTFLOAT_X86_SIMD) && \
(max_digits - digits >= 8)) { FASTFLOAT_X86_SIMD >= 31
if FASTFLOAT_CONSTEXPR17 (sizeof(UC) == 1) {
if (!is_constant_evaluated() && std::distance(p, pend) >= 16 &&
step - counter >= 16 && max_digits - digits >= 16) {
parse_sixteen_digits(p, value, counter, digits);
}
}
#endif
while (std::distance(p, pend) >= 8 && step - counter >= 8 &&
max_digits - digits >= 8) {
parse_eight_digits(p, value, counter, digits); parse_eight_digits(p, value, counter, digits);
} }
while (counter < step && p != pend && digits < max_digits) { while (counter < step && p != pend && digits < max_digits) {
@ -317,33 +365,37 @@ parse_mantissa(bigint &result, parsed_number_string_t<UC> &num,
} }
if (digits == max_digits) { if (digits == max_digits) {
// add the temporary value, then check if we've truncated any digits // add the temporary value, then check if we've truncated any digits
add_native(result, limb(powers_of_ten_uint64[counter]), value); add_native(result, static_cast<limb>(powers_of_ten_uint64[counter]),
value);
bool truncated = is_truncated(p, pend); bool truncated = is_truncated(p, pend);
if (truncated) { if (truncated) {
round_up_bigint(result, digits); round_up_bigint(result, digits);
} }
return; return digits;
} else { } else {
add_native(result, limb(powers_of_ten_uint64[counter]), value); add_native(result, static_cast<limb>(powers_of_ten_uint64[counter]),
value);
counter = 0; counter = 0;
value = 0; value = 0;
} }
} }
} }
return digits;
} }
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(
positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept { bigint &bigmant, adjusted_mantissa am, am_pow_t const exponent) noexcept {
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); FASTFLOAT_ASSERT(bigmant.pow10(exponent));
adjusted_mantissa answer;
bool truncated; bool truncated;
answer.mantissa = bigmant.hi64(truncated); am.mantissa = bigmant.hi64(truncated);
int bias = binary_format<T>::mantissa_explicit_bits() - constexpr am_pow_t bias = binary_format<T>::mantissa_explicit_bits() -
binary_format<T>::minimum_exponent(); binary_format<T>::minimum_exponent();
answer.power2 = bigmant.bit_length() - 64 + bias; am.power2 =
static_cast<fast_float::am_pow_t>(bigmant.bit_length() - 64 + bias);
round<T>(answer, [truncated](adjusted_mantissa &a, int32_t shift) { round<T>(am, [truncated](adjusted_mantissa &a, am_pow_t shift) {
round_nearest_tie_even( round_nearest_tie_even(
a, shift, a, shift,
[truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool {
@ -352,7 +404,7 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
}); });
}); });
return answer; return am;
} }
// the scaling here is quite simple: we have, for the real digits `m * 10^e`, // the scaling here is quite simple: we have, for the real digits `m * 10^e`,
@ -361,39 +413,40 @@ positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept {
// we then need to scale by `2^(f- e)`, and then the two significant digits // we then need to scale by `2^(f- e)`, and then the two significant digits
// are of the same magnitude. // are of the same magnitude.
template <typename T> template <typename T>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( fastfloat_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept { negative_digit_comp(bigint &real_digits, adjusted_mantissa am,
bigint &real_digits = bigmant; am_pow_t const real_exp) noexcept {
int32_t real_exp = exponent;
// get the value of `b`, rounded down, and get a bigint representation of b+h // get the value of `b`, rounded down, and get a bigint representation of b+h
adjusted_mantissa am_b = am; adjusted_mantissa am_b = am;
// gcc7 buf: use a lambda to remove the noexcept qualifier bug with // gcc7 bug: use a lambda to remove the noexcept qualifier bug with
// -Wnoexcept-type. // -Wnoexcept-type.
round<T>(am_b, round<T>(am_b,
[](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); }); [](adjusted_mantissa &a, am_pow_t shift) { round_down(a, shift); });
T b; T b;
to_float(false, am_b, b); to_float(
adjusted_mantissa theor = to_extended_halfway(b); #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
am_b, b);
adjusted_mantissa const theor = to_extended_halfway(b);
bigint theor_digits(theor.mantissa); bigint theor_digits(theor.mantissa);
int32_t theor_exp = theor.power2; am_pow_t const theor_exp = theor.power2;
// scale real digits and theor digits to be same power. // scale real digits and theor digits to be same power.
int32_t pow2_exp = theor_exp - real_exp; auto const pow2_exp = theor_exp - real_exp;
uint32_t pow5_exp = uint32_t(-real_exp); auto const pow5_exp = -real_exp;
if (pow5_exp != 0) { if (pow5_exp != 0) {
FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp));
} }
if (pow2_exp > 0) { if (pow2_exp > 0) {
FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); FASTFLOAT_ASSERT(theor_digits.pow2(pow2_exp));
} else if (pow2_exp < 0) { } else if (pow2_exp < 0) {
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); FASTFLOAT_ASSERT(real_digits.pow2(-pow2_exp));
} }
// compare digits, and use it to direct rounding // compare digits, and use it to direct rounding
int ord = real_digits.compare(theor_digits); auto const ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am; round<T>(am, [ord](adjusted_mantissa &a, am_pow_t shift) {
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
round_nearest_tie_even( round_nearest_tie_even(
a, shift, [ord](bool is_odd, bool _, bool __) -> bool { a, shift, [ord](bool is_odd, bool _, bool __) -> bool {
static_cast<void>(_); // not needed, since we've done our comparison static_cast<void>(_); // not needed, since we've done our comparison
@ -408,7 +461,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
}); });
}); });
return answer; return am;
} }
// parse the significant digits as a big integer to unambiguously round // parse the significant digits as a big integer to unambiguously round
@ -425,21 +478,18 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
// the actual digits. we then compare the big integer representations // the actual digits. we then compare the big integer representations
// of both, and use that to direct rounding. // of both, and use that to direct rounding.
template <typename T, typename UC> template <typename T, typename UC>
inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_noinline FASTFLOAT_CONSTEXPR20 adjusted_mantissa digit_comp(
digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept { parsed_number_string_t<UC> const &num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias // remove the invalid exponent bias
am.power2 -= invalid_am_bias; am.power2 -= invalid_am_bias;
int32_t sci_exp = am_pow_t const sci_exp = scientific_exponent(num.mantissa, num.exponent);
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0;
bigint bigmant; bigint bigmant;
parse_mantissa(bigmant, num, max_digits, digits); am_digits const digits = parse_mantissa<T, UC>(bigmant, num);
// can't underflow, since digits is at most max_digits. // can't underflow, since digits is at most max_digits.
int32_t exponent = sci_exp + 1 - int32_t(digits); am_pow_t const exponent = sci_exp + 1 - static_cast<am_pow_t>(digits);
if (exponent >= 0) { if (exponent >= 0) {
return positive_digit_comp<T>(bigmant, exponent); return positive_digit_comp<T>(bigmant, am, exponent);
} else { } else {
return negative_digit_comp<T>(bigmant, am, exponent); return negative_digit_comp<T>(bigmant, am, exponent);
} }

View File

@ -34,7 +34,7 @@ template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)> typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, from_chars(UC const *first, UC const *last, T &value,
chars_format fmt = chars_format::general) noexcept; chars_format const fmt = chars_format::general) noexcept;
/** /**
* Like from_chars, but accepts an `options` argument to govern number parsing. * Like from_chars, but accepts an `options` argument to govern number parsing.
@ -43,7 +43,7 @@ from_chars(UC const *first, UC const *last, T &value,
template <typename T, typename UC = char> template <typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(UC const *first, UC const *last, T &value, from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept; parse_options_t<UC> const options) noexcept;
/** /**
* This function multiplies an integer number by a power of 10 and returns * This function multiplies an integer number by a power of 10 and returns
@ -59,9 +59,12 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
* `new` or `malloc`). * `new` or `malloc`).
*/ */
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept; integer_times_pow10(uint64_t const mantissa,
int const decimal_exponent) noexcept;
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept; integer_times_pow10(int64_t const mantissa,
int const decimal_exponent) noexcept;
/** /**
* This function is a template overload of `integer_times_pow10()` * This function is a template overload of `integer_times_pow10()`
@ -71,11 +74,14 @@ integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept; integer_times_pow10(uint64_t const mantissa,
int const decimal_exponent) noexcept;
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept; integer_times_pow10(int64_t const mantissa,
int const decimal_exponent) noexcept;
/** /**
* from_chars for integer types. * from_chars for integer types.
@ -83,7 +89,8 @@ FASTFLOAT_CONSTEXPR20
template <typename T, typename UC = char, template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)> typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept; from_chars(UC const *first, UC const *last, T &value,
int const base = 10) noexcept;
} // namespace fast_float } // namespace fast_float

View File

@ -1,8 +1,6 @@
#ifndef FASTFLOAT_FAST_TABLE_H #ifndef FASTFLOAT_FAST_TABLE_H
#define FASTFLOAT_FAST_TABLE_H #define FASTFLOAT_FAST_TABLE_H
#include <cstdint>
namespace fast_float { namespace fast_float {
/** /**
@ -30,15 +28,14 @@ namespace fast_float {
* of 5 greater than 308. * of 5 greater than 308.
*/ */
template <class unused = void> struct powers_template { template <class unused = void> struct powers_template {
constexpr static am_pow_t smallest_power_of_five =
constexpr static int smallest_power_of_five =
binary_format<double>::smallest_power_of_ten(); binary_format<double>::smallest_power_of_ten();
constexpr static int largest_power_of_five = constexpr static am_pow_t largest_power_of_five =
binary_format<double>::largest_power_of_ten(); binary_format<double>::largest_power_of_ten();
constexpr static int number_of_entries = constexpr static am_pow_t number_of_entries =
2 * (largest_power_of_five - smallest_power_of_five + 1); 2 * (largest_power_of_five - smallest_power_of_five + 1);
// Powers of five from 5^-342 all the way to 5^308 rounded toward one. // Powers of five from 5^-342 all the way to 5^308 rounded toward one.
constexpr static uint64_t power_of_five_128[number_of_entries] = { constexpr static am_mant_t power_of_five_128[number_of_entries] = {
0xeef453d6923bd65a, 0x113faa2906a13b3f, 0xeef453d6923bd65a, 0x113faa2906a13b3f,
0x9558b4661b6565f8, 0x4ac7ca59a424c507, 0x9558b4661b6565f8, 0x4ac7ca59a424c507,
0xbaaee17fa23ebf76, 0x5d79bcf00d2df649, 0xbaaee17fa23ebf76, 0x5d79bcf00d2df649,
@ -696,7 +693,7 @@ template <class unused = void> struct powers_template {
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <class unused> template <class unused>
constexpr uint64_t constexpr am_mant_t
powers_template<unused>::power_of_five_128[number_of_entries]; powers_template<unused>::power_of_five_128[number_of_entries];
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@
namespace fast_float { namespace fast_float {
namespace detail { namespace detail {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
/** /**
* Special case +inf, -inf, nan, infinity, -infinity. * Special case +inf, -inf, nan, infinity, -infinity.
* The case comparisons could be made much faster given that we know that the * The case comparisons could be made much faster given that we know that the
@ -22,18 +23,20 @@ namespace detail {
template <typename T, typename UC> template <typename T, typename UC>
from_chars_result_t<UC> from_chars_result_t<UC>
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
T &value, chars_format fmt) noexcept { T &value,
from_chars_result_t<UC> answer{}; const chars_format fmt) noexcept {
from_chars_result_t<UC> answer;
answer.ptr = first; answer.ptr = first;
answer.ec = std::errc(); // be optimistic
// assume first < last, so dereference without checks; FASTFLOAT_ASSUME(first < last); // so dereference without checks
bool const minusSign = (*first == UC('-')); bool const minusSign = (*first == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
if ((*first == UC('-')) || if (minusSign || ((chars_format_t(fmt & chars_format::allow_leading_plus)) &&
(uint64_t(fmt & chars_format::allow_leading_plus) && (*first == UC('+')))) {
(*first == UC('+')))) {
++first; ++first;
} }
if (last - first >= 3) { if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) { if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3); answer.ptr = (first += 3);
@ -42,7 +45,7 @@ from_chars_result_t<UC>
// Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7,
// C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan).
if (first != last && *first == UC('(')) { if (first != last && *first == UC('(')) {
for (UC const *ptr = first + 1; ptr != last; ++ptr) { for (auto const *ptr = first + 1; ptr != last; ++ptr) {
if (*ptr == UC(')')) { if (*ptr == UC(')')) {
answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) answer.ptr = ptr + 1; // valid nan(n-char-seq-opt)
break; break;
@ -52,6 +55,7 @@ from_chars_result_t<UC>
break; // forbidden char, not nan(n-char-seq-opt) break; // forbidden char, not nan(n-char-seq-opt)
} }
} }
answer.ec = std::errc();
return answer; return answer;
} }
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) { if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
@ -63,19 +67,22 @@ from_chars_result_t<UC>
} }
value = minusSign ? -std::numeric_limits<T>::infinity() value = minusSign ? -std::numeric_limits<T>::infinity()
: std::numeric_limits<T>::infinity(); : std::numeric_limits<T>::infinity();
answer.ec = std::errc();
return answer; return answer;
} }
} }
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
return answer; return answer;
} }
#endif
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
/** /**
* Returns true if the floating-pointing rounding mode is to 'nearest'. * Returns true if the floating-pointing rounding mode is to 'nearest'.
* It is the default on most system. This function is meant to be inexpensive. * It is the default on most system. This function is meant to be inexpensive.
* Credit : @mwalcott3 * Credit : @mwalcott3
*/ */
fastfloat_really_inline bool rounds_to_nearest() noexcept { fastfloat_inline bool rounds_to_nearest() noexcept {
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/ // https://lemire.me/blog/2020/06/26/gcc-not-nearest/
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return false; return false;
@ -96,7 +103,7 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
// asm). The value does not need to be std::numeric_limits<float>::min(), any // asm). The value does not need to be std::numeric_limits<float>::min(), any
// small value so that 1 + x should round to 1 would do (after accounting for // small value so that 1 + x should round to 1 would do (after accounting for
// excess precision, as in 387 instructions). // excess precision, as in 387 instructions).
static float volatile fmin = (std::numeric_limits<float>::min)(); static float volatile fmin = std::numeric_limits<float>::min();
float fmini = fmin; // we copy it so that it gets loaded at most once. float fmini = fmin; // we copy it so that it gets loaded at most once.
// //
// Explanation: // Explanation:
@ -134,15 +141,15 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif
} }
#endif
} // namespace detail } // namespace detail
template <typename T> struct from_chars_caller { template <typename T> struct from_chars_caller {
template <typename UC> template <typename UC>
fastfloat_clang_really_inline fastfloat_inline FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> call(UC const *first, UC const *last, T &value,
call(UC const *first, UC const *last, T &value, parse_options_t<UC> const options) noexcept {
parse_options_t<UC> options) noexcept {
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
}; };
@ -150,19 +157,13 @@ template <typename T> struct from_chars_caller {
#ifdef __STDCPP_FLOAT32_T__ #ifdef __STDCPP_FLOAT32_T__
template <> struct from_chars_caller<std::float32_t> { template <> struct from_chars_caller<std::float32_t> {
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, std::float32_t &value, call(UC const *first, UC const *last, std::float32_t &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
// if std::float32_t is defined, and we are in C++23 mode; macro set for // if std::float32_t is defined, and we are in C++23 mode; macro set for
// float32; set value to float due to equivalence between float and // float32; set value to float due to equivalence between float and
// float32_t // float32_t
float val = 0.0f; return from_chars_advanced(first, last, value, options);
auto ret = from_chars_advanced(first, last, val, options);
// If no characters match the pattern, value must be left unmodified.
if (ret.ec != std::errc::invalid_argument) {
value = val;
}
return ret;
} }
}; };
#endif #endif
@ -170,53 +171,46 @@ template <> struct from_chars_caller<std::float32_t> {
#ifdef __STDCPP_FLOAT64_T__ #ifdef __STDCPP_FLOAT64_T__
template <> struct from_chars_caller<std::float64_t> { template <> struct from_chars_caller<std::float64_t> {
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, std::float64_t &value, call(UC const *first, UC const *last, std::float64_t &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
// if std::float64_t is defined, and we are in C++23 mode; macro set for // if std::float64_t is defined, and we are in C++23 mode; macro set for
// float64; set value as double due to equivalence between double and // float64; set value as double due to equivalence between double and
// float64_t // float64_t
double val = 0.0; return from_chars_advanced(first, last, value, options);
auto ret = from_chars_advanced(first, last, val, options);
// If no characters match the pattern, value must be left unmodified.
if (ret.ec != std::errc::invalid_argument) {
value = val;
}
return ret;
} }
}; };
#endif #endif
#ifdef __clang__
// Parser instantiated for a format fixed at compile time, so that every test // Parser instantiated for a format fixed at compile time, so that every test
// on the format folds away inside it. GCC gets the same effect by inlining the // on the format folds away inside it. GCC gets the same effect by inlining the
// whole parser into each caller, where the format is a constant; clang keeps // whole parser into each caller, where the format is a constant; clang keeps
// it out of line and would otherwise re-test each format flag per conversion. // it out of line and would otherwise re-test each format flag per conversion.
template <typename T, typename UC, chars_format Fmt> template <typename T, typename UC, chars_format Fmt>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_fixed_format(UC const *first, UC const *last, T &value) noexcept { from_chars_fixed_format(UC const *first, UC const *last, T &value) noexcept {
return from_chars_caller<T>::call(first, last, value, return from_chars_caller<T>::call(first, last, value,
parse_options_t<UC>(Fmt)); parse_options_t<UC>(Fmt));
} }
#endif
template <typename T, typename UC, typename> template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, from_chars(UC const *first, UC const *last, T &value,
chars_format fmt /*= chars_format::general*/) noexcept { chars_format const fmt /*= chars_format::general*/) noexcept {
#ifdef __clang__
if (fmt == chars_format::general) { if (fmt == chars_format::general) {
return from_chars_fixed_format<T, UC, chars_format::general>(first, last, return from_chars_fixed_format<T, UC, chars_format::general>(first, last,
value); value);
} }
#endif
return from_chars_caller<T>::call(first, last, value, return from_chars_caller<T>::call(first, last, value,
parse_options_t<UC>(fmt)); parse_options_t<UC>(fmt));
} }
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_inline FASTFLOAT_CONSTEXPR20 bool
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative, clinger_fast_path_impl(am_mant_t const mantissa, am_pow_t const exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bool const is_negative,
#endif
T &value) noexcept { T &value) noexcept {
// The implementation of the Clinger's fast path is convoluted because // The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode // we want round-to-nearest in all cases, irrespective of the rounding mode
@ -239,10 +233,14 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
// We could check it first (before the previous branch), but // We could check it first (before the previous branch), but
// there might be performance advantages at having the check // there might be performance advantages at having the check
// be last. // be last.
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { if (!is_constant_evaluated()
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
&& detail::rounds_to_nearest()
#endif
) {
// We have that fegetround() == FE_TONEAREST. // We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path. // Next is Clinger's fast path.
value = T(mantissa); value = static_cast<T>(mantissa);
if (exponent < 0) { if (exponent < 0) {
value = value / binary_format<T>::exact_power_of_ten(-exponent); value = value / binary_format<T>::exact_power_of_ten(-exponent);
} else { } else {
@ -250,14 +248,17 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
// Only std::float16_t can overflow here (e.g., "656e2"); let the // Only std::float16_t can overflow here (e.g., "656e2"); let the
// slow path report result_out_of_range. // slow path report result_out_of_range.
if (binary_format<T>::fast_path_can_overflow() && if (binary_format<T>::fast_path_can_overflow() &&
value > (std::numeric_limits<T>::max)()) { value > std::numeric_limits<T>::max()) {
return false; return false;
} }
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (is_negative) { if (is_negative) {
value = -value; value = -value;
} }
#endif
return true; return true;
#ifndef FASTFLOAT_ONLY_ROUNDS_TO_NEAREST_SUPPORTED
} else { } else {
// We do not have that fegetround() == FE_TONEAREST. // We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
@ -267,16 +268,24 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
#if defined(__clang__) || defined(FASTFLOAT_32BIT) #if defined(__clang__) || defined(FASTFLOAT_32BIT)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if (mantissa == 0) { if (mantissa == 0) {
value = is_negative ? T(-0.) : T(0.); value =
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative ? static_cast<T>(-0.) :
#endif
static_cast<T>(0.);
return true; return true;
} }
#endif #endif
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent); value = static_cast<T>(mantissa) *
binary_format<T>::exact_power_of_ten(exponent);
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (is_negative) { if (is_negative) {
value = -value; value = -value;
} }
#endif
return true; return true;
} }
#endif
} }
} }
return false; return false;
@ -288,10 +297,10 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
* parsing options or other parsing custom function implemented by user. * parsing options or other parsing custom function implemented by user.
*/ */
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept { from_chars_advanced(parsed_number_string_t<UC> const &pns, T &value) noexcept {
static_assert(is_supported_float_type<T>::value, static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported"); "this type of floating-point type isn't supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
@ -300,8 +309,11 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch; answer.ptr = pns.lastmatch;
if (!pns.too_many_digits && if (!pns.too_many_digits && clinger_fast_path_impl(pns.mantissa, pns.exponent,
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value)) #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
value))
return answer; return answer;
adjusted_mantissa am = adjusted_mantissa am =
@ -314,10 +326,14 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
// If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa) // If we called compute_float<binary_format<T>>(pns.exponent, pns.mantissa)
// and we have an invalid power (am.power2 < 0), then we need to go the long // and we have an invalid power (am.power2 < 0), then we need to go the long
// way around again. This is very uncommon. // way around again. This is very uncommon.
if (am.power2 < 0) { if fastfloat_unlikely (am.power2 < 0) {
am = digit_comp<T>(pns, am); am = digit_comp<T>(pns, am);
} }
to_float(pns.negative, am, value); to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
am, value);
// Test for over/underflow. // Test for over/underflow.
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) ||
am.power2 == binary_format<T>::infinite_power()) { am.power2 == binary_format<T>::infinite_power()) {
@ -334,27 +350,36 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
// and the am.power2<0 digit_comp recompute, so both slow branches collapse to // and the am.power2<0 digit_comp recompute, so both slow branches collapse to
// one helper call. // one helper call.
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> static fastfloat_noinline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
parse_number_slow_path(UC const *first, UC const *last, T &value, parse_number_slow_path(UC const *first, UC const *last, T &value,
parse_options_t<UC> options, bool bjf) noexcept { parse_options_t<UC> const options
parsed_number_string_t<UC> pns = #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
,
bool const bjf
#endif
) noexcept {
parsed_number_string_t<UC> const pns =
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bjf ? parse_number_string_impl<true, false, UC>(first, last, options, bjf ? parse_number_string_impl<true, false, UC>(first, last, options,
true) true)
: parse_number_string_impl<false, false, UC>(first, last, options, :
true); #endif
parse_number_string_impl<false, false, UC>(first, last, options,
true);
return from_chars_advanced(pns, value); return from_chars_advanced(pns, value);
} }
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// Cold: the whole chars_format::javascript conversion. Kept out of line and out // Cold: the whole chars_format::javascript conversion. Kept out of line and out
// of from_chars_float_advanced so the common frame holds neither the javascript // of from_chars_float_advanced so the common frame holds neither the javascript
// parser body nor a call that would force the parsed number string onto the // parser body nor a call that would force the parsed number string onto the
// stack. Mirrors the main path, only with the javascript parser. // stack. Mirrors the main path, only with the javascript parser.
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_never_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_noinline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_float_javascript(UC const *first, UC const *last, T &value, from_chars_float_javascript(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> options) noexcept {
chars_format const fmt = detail::adjust_for_feature_macros(options.format); if (chars_format_t(options.format & chars_format::skip_white_space)) {
if (uint64_t(fmt & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(*first)) {
first++; first++;
} }
@ -367,64 +392,91 @@ from_chars_float_javascript(UC const *first, UC const *last, T &value,
} }
parsed_number_string_t<UC> pns = parsed_number_string_t<UC> pns =
parse_number_string_javascript<UC>(first, last, options, true); parse_number_string_javascript<UC>(first, last, options, true);
if (!pns.valid) { if (pns.invalid) {
if (uint64_t(fmt & chars_format::no_infnan)) { if (chars_format_t(options.format & chars_format::no_infnan)) {
return answer; return answer;
} }
return detail::parse_infnan(first, last, value, fmt); return detail::parse_infnan(first, last, value, options.format);
} }
return from_chars_advanced(pns, value); return from_chars_advanced(pns, value);
} }
#endif
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_float_advanced(UC const *first, UC const *last, T &value, from_chars_float_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
static_assert(is_supported_float_type<T>::value, static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported"); "this type of floating-point type isn't supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format); #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
// Leave for the javascript conversion before anything else is live, so this // Leave for the javascript conversion before anything else is live, so this
// compiles to a tail call and the common path keeps its registers. The cold // compiles to a tail call and the common path keeps its registers. The cold
// function repeats the leading-whitespace and empty-input handling below. // function repeats the leading-whitespace and empty-input handling below.
if fastfloat_unlikely (uint64_t(fmt & detail::basic_javascript_fmt)) { if fastfloat_unlikely (chars_format_t(options.format &
detail::javascript_fmt)) {
return from_chars_float_javascript<T, UC>(first, last, value, options); return from_chars_float_javascript<T, UC>(first, last, value, options);
} }
#endif
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
if (uint64_t(fmt & chars_format::skip_white_space)) { #ifdef FASTFLOAT_ISNOT_CHECKED_BOUNDS
while ((first != last) && fast_float::is_space(*first)) { // We are in parser code with external loop that checks bounds.
first++; FASTFLOAT_ASSUME(first < last);
#endif
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (chars_format_t(options.format & chars_format::skip_white_space)) {
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
while (first != last && fast_float::is_space(*first)) {
++first;
} }
#else
do {
if (fast_float::is_space(*first)) {
++first;
} else {
break;
}
} while (first != last)
#endif
} }
#endif
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
if (first == last) { if (first == last) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} }
bool const bjf = uint64_t(fmt & detail::basic_json_fmt) != 0; #endif
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bool const bjf = chars_format_t(options.format & detail::json_fmt) != 0;
#endif
// Fast path: parse WITHOUT materializing the integer/fraction spans (read // Fast path: parse WITHOUT materializing the integer/fraction spans (read
// only by the rare slow paths). Skipping their stores keeps the fat // only by the rare slow paths). Skipping their stores keeps the fat
// parsed_number_string_t off the hot path. store_spans is a runtime argument, // parsed_number_string_t off the hot path. store_spans is a runtime argument,
// so this reuses the single parse_number_string_impl instantiation. // so this reuses the single parse_number_string_impl instantiation.
parsed_number_string_t<UC> pns = parsed_number_string_t<UC> const pns =
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
bjf ? parse_number_string_impl<true, false, UC>(first, last, options, bjf ? parse_number_string_impl<true, false, UC>(first, last, options,
false) false)
: parse_number_string_impl<false, false, UC>(first, last, options, :
false); #endif
if (!pns.valid) { parse_number_string_impl<false, false, UC>(first, last, options,
if (uint64_t(fmt & chars_format::no_infnan)) { false);
if (pns.invalid) {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (chars_format_t(options.format & chars_format::no_infnan)) {
#endif
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
} else { } else {
return detail::parse_infnan(first, last, value, fmt); return detail::parse_infnan(first, last, value, options.format);
} }
#endif
} }
// Slow path A (rare): > 19 significant digits. The no-span parse left the // Slow path A (rare): > 19 significant digits. The no-span parse left the
@ -442,12 +494,21 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
#endif #endif
#endif #endif
if fastfloat_unlikely (pns.too_many_digits) { if fastfloat_unlikely (pns.too_many_digits) {
return parse_number_slow_path<T, UC>(first, last, value, options, bjf); return parse_number_slow_path<T, UC>(first, last, value, options
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
,
bjf
#endif
);
} }
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch; answer.ptr = pns.lastmatch;
if (clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value)) { if (clinger_fast_path_impl(pns.mantissa, pns.exponent,
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
value)) {
return answer; return answer;
} }
@ -458,16 +519,24 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
// dead-effect since it already failed here; the cold re-parse + digit_comp // dead-effect since it already failed here; the cold re-parse + digit_comp
// via from_chars_advanced reproduces this branch). // via from_chars_advanced reproduces this branch).
if fastfloat_unlikely (am.power2 < 0) { if fastfloat_unlikely (am.power2 < 0) {
return parse_number_slow_path<T, UC>(first, last, value, options, bjf); return parse_number_slow_path<T, UC>(first, last, value, options
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
,
bjf
#endif
);
} }
to_float(pns.negative, am, value); to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
pns.negative,
#endif
am, value);
// Test for over/underflow. Marked unlikely so that clang keeps it as a // Test for over/underflow. Marked unlikely so that clang keeps it as a
// branch instead of folding it into a chain of conditional moves that // branch instead of folding it into a chain of conditional moves that
// every conversion pays for. // every conversion pays for.
if fastfloat_clang_unlikely ((pns.mantissa != 0 && am.mantissa == 0 && if fastfloat_unlikely ((pns.mantissa != 0 && am.mantissa == 0 &&
am.power2 == 0) || am.power2 == 0) ||
am.power2 == am.power2 == binary_format<T>::infinite_power()) {
binary_format<T>::infinite_power()) {
answer.ec = std::errc::result_out_of_range; answer.ec = std::errc::result_out_of_range;
} }
#ifdef __clang__ #ifdef __clang__
@ -478,57 +547,82 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
template <typename T, typename UC, typename> template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base) noexcept { from_chars(UC const *first, UC const *last, T &value, int const base) noexcept {
static_assert(is_supported_integer_type<T>::value, static_assert(is_supported_integer_type<T>::value,
"only integer types are supported"); "only integer types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
parse_options_t<UC> options; parse_options_t<UC> const options(chars_format::general, static_cast<UC>('.'),
options.base = base; static_cast<base_t>(base));
return from_chars_advanced(first, last, value, options); return from_chars_advanced(first, last, value, options);
} }
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(uint64_t const mantissa,
int const decimal_exponent) noexcept {
T value; T value;
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value)) if (clinger_fast_path_impl(mantissa, static_cast<am_pow_t>(decimal_exponent),
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
value))
return value; return value;
adjusted_mantissa am = adjusted_mantissa am = compute_float<binary_format<T>>(
compute_float<binary_format<T>>(decimal_exponent, mantissa); static_cast<am_pow_t>(decimal_exponent), mantissa);
to_float(false, am, value); to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
false,
#endif
am, value);
return value; return value;
} }
template <typename T> template <typename T>
FASTFLOAT_CONSTEXPR20 FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(int64_t const mantissa,
const bool is_negative = mantissa < 0; int const decimal_exponent) noexcept {
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
const auto is_negative = mantissa < 0;
// Negate as unsigned: -mantissa overflows for the most negative int64_t. // Negate as unsigned: -mantissa overflows for the most negative int64_t.
const uint64_t m = is_negative ? uint64_t(0) - static_cast<uint64_t>(mantissa) const auto m = is_negative ? am_mant_t(0) - static_cast<am_mant_t>(mantissa)
: static_cast<uint64_t>(mantissa); : static_cast<am_mant_t>(mantissa);
#else
FASTFLOAT_ASSUME(mantissa >= 0);
const auto m = static_cast<am_mant_t>(mantissa);
#endif
T value; T value;
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value)) if (clinger_fast_path_impl(m, static_cast<am_pow_t>(decimal_exponent),
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative,
#endif
value))
return value; return value;
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m); adjusted_mantissa const am = compute_float<binary_format<T>>(
to_float(is_negative, am, value); static_cast<am_pow_t>(decimal_exponent), m);
to_float(
#ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
is_negative,
#endif
am, value);
return value; return value;
} }
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(uint64_t const mantissa,
int const decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent); return integer_times_pow10<double>(mantissa, decimal_exponent);
} }
FASTFLOAT_CONSTEXPR20 inline double FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept { integer_times_pow10(int64_t const mantissa,
int const decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent); return integer_times_pow10<double>(mantissa, decimal_exponent);
} }
@ -540,7 +634,8 @@ FASTFLOAT_CONSTEXPR20
std::is_integral<Int>::value && std::is_integral<Int>::value &&
!std::is_signed<Int>::value, !std::is_signed<Int>::value,
T>::type T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int const mantissa,
int const decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa), return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
decimal_exponent); decimal_exponent);
} }
@ -551,7 +646,8 @@ FASTFLOAT_CONSTEXPR20
std::is_integral<Int>::value && std::is_integral<Int>::value &&
std::is_signed<Int>::value, std::is_signed<Int>::value,
T>::type T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int const mantissa,
int const decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<int64_t>(mantissa), return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
decimal_exponent); decimal_exponent);
} }
@ -559,37 +655,44 @@ FASTFLOAT_CONSTEXPR20
template <typename Int> template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if< FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int const mantissa, int const decimal_exponent) noexcept {
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent); return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
} }
template <typename Int> template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if< FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept { integer_times_pow10(Int const mantissa, int const decimal_exponent) noexcept {
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent); return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
} }
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_int_advanced(UC const *first, UC const *last, T &value, from_chars_int_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
static_assert(is_supported_integer_type<T>::value, static_assert(is_supported_integer_type<T>::value,
"only integer types are supported"); "only integer types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format); #ifdef FASTFLOAT_ISNOT_CHECKED_BOUNDS
int const base = options.base; // We are in parser code with external loop that checks bounds.
FASTFLOAT_ASSUME(first < last);
from_chars_result_t<UC> answer; #endif
if (uint64_t(fmt & chars_format::skip_white_space)) { #ifndef FASTFLOAT_ONLY_POSITIVE_C_NUMBER_WO_INF_NAN
if (chars_format_t(options.format & chars_format::skip_white_space)) {
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(*first)) {
first++; ++first;
} }
} }
if (first == last || base < 2 || base > 36) { #else
#endif
if (
#ifndef FASTFLOAT_ISNOT_CHECKED_BOUNDS
first == last ||
#endif
options.base < 2 || options.base > 36) {
from_chars_result_t<UC> answer;
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
@ -604,26 +707,26 @@ template <size_t TypeIx> struct from_chars_advanced_caller {
template <> struct from_chars_advanced_caller<1> { template <> struct from_chars_advanced_caller<1> {
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value, call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
return from_chars_float_advanced(first, last, value, options); return from_chars_float_advanced(first, last, value, options);
} }
}; };
template <> struct from_chars_advanced_caller<2> { template <> struct from_chars_advanced_caller<2> {
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value, call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
return from_chars_int_advanced(first, last, value, options); return from_chars_int_advanced(first, last, value, options);
} }
}; };
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(UC const *first, UC const *last, T &value, from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> const options) noexcept {
return from_chars_advanced_caller< return from_chars_advanced_caller<
size_t(is_supported_float_type<T>::value) + size_t(is_supported_float_type<T>::value) +
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value, 2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,

View File

@ -161,8 +161,8 @@ readmefile = f"{maindir}{os.sep}README.md"
for line in fileinput.input(readmefile, inplace=1, backup=".bak"): for line in fileinput.input(readmefile, inplace=1, backup=".bak"):
line = re.sub( line = re.sub(
r"https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h", r"https://github.com/irainman/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h",
f"https://github.com/fastfloat/fast_float/releases/download/v{newversionstring}/fast_float.h", f"https://github.com/irainman/fast_float/releases/download/v{newversionstring}/fast_float.h",
line.rstrip(), line.rstrip(),
) )
line = re.sub( line = re.sub(

View File

@ -21,10 +21,6 @@
#endif // #ifndef FASTFLOAT_CONSTEXPR_TESTS #endif // #ifndef FASTFLOAT_CONSTEXPR_TESTS
#endif // FASTFLOAT_IS_CONSTEXPR #endif // FASTFLOAT_IS_CONSTEXPR
#if FASTFLOAT_HAS_BIT_CAST
#include <bit>
#endif
#ifndef SUPPLEMENTAL_TEST_DATA_DIR #ifndef SUPPLEMENTAL_TEST_DATA_DIR
#define SUPPLEMENTAL_TEST_DATA_DIR "data/" #define SUPPLEMENTAL_TEST_DATA_DIR "data/"
#endif #endif
@ -69,7 +65,7 @@ template <typename T> std::string fHexAndDec(T v) {
return ss.str(); return ss.str();
} }
char const *round_name(int d) { constexpr std::string_view const round_name(int d) {
switch (d) { switch (d) {
case FE_UPWARD: case FE_UPWARD:
return "FE_UPWARD"; return "FE_UPWARD";
@ -975,7 +971,7 @@ constexpr void check_basic_test_result(stringtype str, result_type result,
auto copysign = [](double x, double y) -> double { auto copysign = [](double x, double y) -> double {
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
if (fast_float::cpp20_and_in_constexpr()) { if (fast_float::is_constant_evaluated()) {
using equiv_int = std::make_signed_t<fast_float::equiv_uint_t<double>>; using equiv_int = std::make_signed_t<fast_float::equiv_uint_t<double>>;
auto const i = std::bit_cast<equiv_int>(y); auto const i = std::bit_cast<equiv_int>(y);
if (i < 0) { if (i < 0) {
@ -2364,7 +2360,7 @@ TEST_CASE("integer_times_pow10") {
for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) { for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) {
fesetround(mode); fesetround(mode);
INFO("fesetround(): " << std::string{round_name(mode)}); INFO("fesetround(): " << round_name(mode));
struct Guard { struct Guard {
~Guard() { fesetround(FE_TONEAREST); } ~Guard() { fesetround(FE_TONEAREST); }

View File

@ -13,6 +13,8 @@
#include <string> #include <string>
#include <system_error> #include <system_error>
#ifdef __STDCPP_FLOAT16_T__
namespace { namespace {
// Decimal digit strings, most significant digit first, no leading zeros. // Decimal digit strings, most significant digit first, no leading zeros.
@ -149,7 +151,10 @@ template <typename T> Failure sweep() {
} // namespace } // namespace
#endif
int main() { int main() {
#ifdef __STDCPP_FLOAT16_T__
Failure f16 = sweep<std::float16_t>(); Failure f16 = sweep<std::float16_t>();
std::printf("float16: %ld strings, %ld failures\n", f16.checked, f16.count); std::printf("float16: %ld strings, %ld failures\n", f16.checked, f16.count);
Failure bf16 = sweep<std::bfloat16_t>(); Failure bf16 = sweep<std::bfloat16_t>();
@ -158,6 +163,9 @@ int main() {
if (f16.count != 0 || bf16.count != 0) { if (f16.count != 0 || bf16.count != 0) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
#else
std::printf("float16x type unsupported, so this test isn't applied\n");
#endif
std::printf("all ok\n"); std::printf("all ok\n");
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -37,8 +37,7 @@ void uint32_to_ipv4_string(uint32_t ip, char *buffer) {
*ptr = '\0'; *ptr = '\0';
} }
fastfloat_really_inline uint32_t ipv4_string_to_uint32(const char *str, uint32_t ipv4_string_to_uint32(const char *str, const char *end) {
const char *end) {
uint32_t ip = 0; uint32_t ip = 0;
const char *current = str; const char *current = str;
@ -90,4 +89,4 @@ int main() {
std::cerr << "IPv4 conversion test failed!" << std::endl; std::cerr << "IPv4 conversion test failed!" << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }

View File

@ -114,7 +114,7 @@ int main() {
{"nan", {fast_float::parse_error::no_digits_in_mantissa, 0}}, {"nan", {fast_float::parse_error::no_digits_in_mantissa, 0}},
}; };
for (std::size_t i = 0; i < accept.size(); ++i) { for (std::size_t i = 0; i != accept.size(); ++i) {
auto const &s = accept[i].input; auto const &s = accept[i].input;
auto const &expected = accept[i].expected; auto const &expected = accept[i].expected;
double result; double result;
@ -138,7 +138,7 @@ int main() {
} }
} }
for (std::size_t i = 0; i < reject.size(); ++i) { for (std::size_t i = 0; i != reject.size(); ++i) {
auto const &s = reject[i].input; auto const &s = reject[i].input;
double result; double result;
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result,
@ -150,13 +150,13 @@ int main() {
} }
} }
for (std::size_t i = 0; i < reject.size(); ++i) { for (std::size_t i = 0; i != reject.size(); ++i) {
auto const &f = reject[i].input; auto const &f = reject[i].input;
auto const &expected_reason = reject[i].reason; auto const &expected_reason = reject[i].reason;
auto answer = fast_float::parse_number_string<false>( auto answer = fast_float::parse_number_string<false>(
f.data(), f.data() + f.size(), f.data(), f.data() + f.size(),
fast_float::parse_options(fast_float::chars_format::javascript)); fast_float::parse_options(fast_float::chars_format::javascript));
if (answer.valid) { if (!answer.invalid) {
std::cerr << "javascript parse accepted invalid javascript " << f std::cerr << "javascript parse accepted invalid javascript " << f
<< std::endl; << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
@ -176,7 +176,7 @@ int main() {
} }
// Sloppy mode accepts everything strict mode accepts... // Sloppy mode accepts everything strict mode accepts...
for (std::size_t i = 0; i < accept.size(); ++i) { for (std::size_t i = 0; i != accept.size(); ++i) {
auto const &s = accept[i].input; auto const &s = accept[i].input;
auto const &expected = accept[i].expected; auto const &expected = accept[i].expected;
double result; double result;
@ -200,7 +200,7 @@ int main() {
{"09e+1", {90., ""}}, {"08.5e1", {85., ""}}, {"0009", {9., ""}}, {"09e+1", {90., ""}}, {"08.5e1", {85., ""}}, {"0009", {9., ""}},
{"08n", {8., "n"}}, {"08n", {8., "n"}},
}; };
for (std::size_t i = 0; i < accept_sloppy.size(); ++i) { for (std::size_t i = 0; i != accept_sloppy.size(); ++i) {
auto const &s = accept_sloppy[i].input; auto const &s = accept_sloppy[i].input;
auto const &expected = accept_sloppy[i].expected; auto const &expected = accept_sloppy[i].expected;
double result; double result;
@ -228,7 +228,7 @@ int main() {
auto strict = fast_float::parse_number_string<false>( auto strict = fast_float::parse_number_string<false>(
s.data(), s.data() + s.size(), s.data(), s.data() + s.size(),
fast_float::parse_options(fast_float::chars_format::javascript)); fast_float::parse_options(fast_float::chars_format::javascript));
if (strict.valid || if (!strict.invalid ||
strict.error != strict.error !=
fast_float::parse_error::leading_zeros_in_integer_part) { fast_float::parse_error::leading_zeros_in_integer_part) {
std::cerr << "javascript fmt should have rejected " << s std::cerr << "javascript fmt should have rejected " << s
@ -255,7 +255,7 @@ int main() {
{"+1", {fast_float::parse_error::no_digits_in_mantissa, 0}}, {"+1", {fast_float::parse_error::no_digits_in_mantissa, 0}},
{"inf", {fast_float::parse_error::no_digits_in_mantissa, 0}}, {"inf", {fast_float::parse_error::no_digits_in_mantissa, 0}},
}; };
for (std::size_t i = 0; i < reject_sloppy.size(); ++i) { for (std::size_t i = 0; i != reject_sloppy.size(); ++i) {
auto const &f = reject_sloppy[i].input; auto const &f = reject_sloppy[i].input;
auto const &expected_reason = reject_sloppy[i].reason; auto const &expected_reason = reject_sloppy[i].reason;
double result; double result;
@ -270,7 +270,7 @@ int main() {
auto parsed = fast_float::parse_number_string<false>( auto parsed = fast_float::parse_number_string<false>(
f.data(), f.data() + f.size(), f.data(), f.data() + f.size(),
fast_float::parse_options(fast_float::chars_format::javascript_sloppy)); fast_float::parse_options(fast_float::chars_format::javascript_sloppy));
if (parsed.valid) { if (!parsed.invalid) {
std::cerr << "javascript_sloppy parse accepted invalid javascript " << f std::cerr << "javascript_sloppy parse accepted invalid javascript " << f
<< std::endl; << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
@ -297,7 +297,7 @@ int main() {
fast_float::parse_options const options{ fast_float::parse_options const options{
fast_float::chars_format::javascript | fast_float::chars_format::javascript |
fast_float::chars_format::allow_leading_plus}; fast_float::chars_format::allow_leading_plus};
for (std::size_t i = 0; i < plus.size(); ++i) { for (std::size_t i = 0; i != plus.size(); ++i) {
auto const &s = plus[i]; auto const &s = plus[i];
double result; double result;
auto answer = fast_float::from_chars_advanced( auto answer = fast_float::from_chars_advanced(

View File

@ -76,6 +76,7 @@ int main() {
{"0.02", {0.02, ""}}, {"0.02", {0.02, ""}},
{"0.002", {0.002, ""}}, {"0.002", {0.002, ""}},
{"1e+0000", {1., ""}}, {"1e+0000", {1., ""}},
{"1e-0000", {1., ""}},
{"0e-2", {0., ""}}, {"0e-2", {0., ""}},
{"1e", {1., "e"}}, {"1e", {1., "e"}},
{"1e+", {1., "e+"}}, {"1e+", {1., "e+"}},
@ -136,7 +137,7 @@ int main() {
fast_float::parse_options( fast_float::parse_options(
fast_float::chars_format::json | fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus)); // should be ignored fast_float::chars_format::allow_leading_plus)); // should be ignored
if (answer.valid) { if (!answer.invalid) {
std::cerr << "json parse accepted invalid json " << f << std::endl; std::cerr << "json parse accepted invalid json " << f << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
@ -167,4 +168,4 @@ int main() {
#endif #endif
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -51,7 +51,6 @@ template <typename T> bool test() {
} }
int main() { int main() {
std::cout << "32 bits checks" << std::endl; std::cout << "32 bits checks" << std::endl;
Assert(test<float>()); Assert(test<float>());

View File

@ -54,7 +54,8 @@ float cygwin_strtof_l(char const *start, char **end) {
class RandomEngine { class RandomEngine {
public: public:
RandomEngine() = delete; RandomEngine() = delete;
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) {}
uint64_t next() { uint64_t next() {
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
@ -198,7 +199,7 @@ bool tester(uint64_t seed, size_t volume) {
char buffer[4096]; // large buffer (can't overflow) char buffer[4096]; // large buffer (can't overflow)
RandomEngine rand(seed); RandomEngine rand(seed);
for (size_t i = 0; i < volume; i++) { for (size_t i = 0; i < volume; i++) {
if ((i % 100000) == 0) { if ((i % 1000000) == 0) {
std::cout << "."; std::cout << ".";
std::cout.flush(); std::cout.flush();
} }
@ -256,10 +257,12 @@ bool tester(uint64_t seed, size_t volume) {
} }
int main() { int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun) defined(sun) || defined(__sun)
std::cout << "Warning: msys/cygwin or solaris detected." << std::endl; std::cout << "Warning: msys/cygwin or solaris detected. This particular test "
"is likely to generate false failures due to our reliance on "
"the underlying runtime library."
<< std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
#else #else
if (tester(1234344, 100000000)) { if (tester(1234344, 100000000)) {

View File

@ -8,9 +8,9 @@
#include <vector> #include <vector>
struct test_data { struct test_data {
std::string input; const std::string input;
bool expected_success; const bool expected_success;
double expected_result; const double expected_result;
}; };
bool eddelbuettel() { bool eddelbuettel() {
@ -51,10 +51,10 @@ bool eddelbuettel() {
{"-+inf", false, 0.0}, {"-+inf", false, 0.0},
{"-+nan", false, 0.0}, {"-+nan", false, 0.0},
}; };
for (size_t i = 0; i < test_datas.size(); i++) { for (const auto &i : test_datas) {
auto const &input = test_datas[i].input; auto const &input = i.input;
auto const expected_success = test_datas[i].expected_success; auto const expected_success = i.expected_success;
auto const expected_result = test_datas[i].expected_result; auto const expected_result = i.expected_result;
double result; double result;
// answer contains a error code and a pointer to the end of the // answer contains a error code and a pointer to the end of the
// parsed region (on success). // parsed region (on success).

View File

@ -54,7 +54,8 @@ float cygwin_strtof_l(char const *start, char **end) {
class RandomEngine { class RandomEngine {
public: public:
RandomEngine() = delete; RandomEngine() = delete;
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed) {}
uint64_t next() { uint64_t next() {
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
@ -253,9 +254,9 @@ bool tester(uint64_t seed, size_t volume) {
int main() { int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun) defined(sun) || defined(__sun)
std::cout << "Warning: msys/cygwin detected. This particular test is likely " std::cout << "Warning: msys/cygwin or solaris detected. This particular test "
"to generate false failures due to our reliance on the " "is likely to generate false failures due to our reliance on "
"underlying runtime library." "the underlying runtime library."
<< std::endl; << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
#else #else
@ -263,6 +264,7 @@ int main() {
std::cout << "All tests ok." << std::endl; std::cout << "All tests ok." << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
std::cout << "Failure." << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
#endif #endif