mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-07 01:06:48 +08:00
Merge branch 'main' of https://github.com/Pharago/fast_float into other_chars
This commit is contained in:
commit
927eb9bcd2
56
.github/workflows/alpine.yml
vendored
56
.github/workflows/alpine.yml
vendored
@ -1,27 +1,47 @@
|
|||||||
name: Alpine Linux
|
name: Alpine Linux
|
||||||
'on':
|
on:
|
||||||
- push
|
- push
|
||||||
- pull_request
|
- pull_request
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ubuntu-build:
|
build:
|
||||||
|
name: Build on Alpine ${{ matrix.arch }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
arch:
|
||||||
|
- x86_64
|
||||||
|
- x86
|
||||||
|
- aarch64
|
||||||
|
- armv7
|
||||||
|
- ppc64le
|
||||||
|
- riscv64
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- name: Checkout repository
|
||||||
- name: start docker
|
uses: actions/checkout@v1
|
||||||
|
|
||||||
|
- name: Install latest Alpine Linux for ${{ matrix.arch }}
|
||||||
|
uses: jirutka/setup-alpine@v1
|
||||||
|
with:
|
||||||
|
arch: ${{ matrix.arch }}
|
||||||
|
branch: ${{ matrix.arch == 'riscv64' && 'edge' || 'latest-stable' }}
|
||||||
|
packages: >
|
||||||
|
build-base
|
||||||
|
cmake
|
||||||
|
g++
|
||||||
|
linux-headers
|
||||||
|
git
|
||||||
|
bash
|
||||||
|
build-base
|
||||||
|
- name: Prepare
|
||||||
run: |
|
run: |
|
||||||
docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest
|
cmake -DFASTFLOAT_TEST=ON -B build
|
||||||
echo 'docker exec alpine "$@";' > ./alpine.sh
|
shell: alpine.sh {0}
|
||||||
chmod +x ./alpine.sh
|
- name: Build
|
||||||
- name: install packages
|
|
||||||
run: |
|
run: |
|
||||||
./alpine.sh apk update
|
cmake --build build
|
||||||
./alpine.sh apk add build-base cmake g++ linux-headers git bash
|
shell: alpine.sh {0}
|
||||||
- name: cmake
|
- name: Test
|
||||||
run: |
|
run: |
|
||||||
./alpine.sh cmake -DFASTFLOAT_TEST=ON -B build_for_alpine
|
ctest --test-dir build -R basictest
|
||||||
- name: build
|
shell: alpine.sh {0}
|
||||||
run: |
|
|
||||||
./alpine.sh cmake --build build_for_alpine
|
|
||||||
- name: test
|
|
||||||
run: |
|
|
||||||
./alpine.sh bash -c "cd build_for_alpine && ctest -R basictest"
|
|
||||||
|
|||||||
12
.github/workflows/amalgamate-ubuntu20.yml
vendored
12
.github/workflows/amalgamate-ubuntu20.yml
vendored
@ -1,20 +1,12 @@
|
|||||||
name: Amalgamate Ubuntu 20.04 CI (GCC 9, 8)
|
name: Amalgamate Ubuntu 20.04 CI (GCC 9)
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ubuntu-build:
|
ubuntu-build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Legacy/x86 compilers cause CI failures.
|
|
||||||
#- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: }
|
|
||||||
- {cxx: , arch: } # default=gcc9
|
|
||||||
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Compile with amalgamation
|
- name: Compile with amalgamation
|
||||||
run: |
|
run: |
|
||||||
mkdir build &&
|
mkdir build &&
|
||||||
|
|||||||
2
.github/workflows/msys2-clang.yml
vendored
2
.github/workflows/msys2-clang.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
|||||||
CMAKE_GENERATOR: Ninja
|
CMAKE_GENERATOR: Ninja
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: msys2/setup-msys2@v2
|
- uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
update: true
|
update: true
|
||||||
|
|||||||
2
.github/workflows/msys2.yml
vendored
2
.github/workflows/msys2.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
|||||||
CMAKE_GENERATOR: Ninja
|
CMAKE_GENERATOR: Ninja
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- uses: msys2/setup-msys2@v2
|
- uses: msys2/setup-msys2@v2
|
||||||
with:
|
with:
|
||||||
update: true
|
update: true
|
||||||
|
|||||||
30
.github/workflows/s390x.yml
vendored
Normal file
30
.github/workflows/s390x.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: Ubuntu s390x (GCC 11)
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: uraimo/run-on-arch-action@v2
|
||||||
|
name: Test
|
||||||
|
id: runcmd
|
||||||
|
with:
|
||||||
|
arch: s390x
|
||||||
|
githubToken: ${{ github.token }}
|
||||||
|
distro: ubuntu_latest
|
||||||
|
install: |
|
||||||
|
apt-get update -q -y
|
||||||
|
apt-get install -y cmake make g++
|
||||||
|
run: |
|
||||||
|
cmake -DCMAKE_BUILD_TYPE=Release -B build
|
||||||
|
cmake --build build -j=2
|
||||||
|
ctest --output-on-failure --test-dir build
|
||||||
|
|
||||||
13
.github/workflows/ubuntu18.yml
vendored
13
.github/workflows/ubuntu18.yml
vendored
@ -1,21 +1,12 @@
|
|||||||
name: Ubuntu 18.04 CI (GCC 7, 6, 5)
|
name: Ubuntu 18.04 CI (GCC 7)
|
||||||
|
|
||||||
on: [push, pull_request]
|
on: [push, pull_request]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
ubuntu-build:
|
ubuntu-build:
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Legacy/x86 compilers cause CI failures.
|
|
||||||
#- {cxx: -DCMAKE_CXX_COMPILER=g++-5, arch: }
|
|
||||||
#- {cxx: -DCMAKE_CXX_COMPILER=g++-6, arch: }
|
|
||||||
- {cxx: , arch: } # default=gcc7
|
|
||||||
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc7
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Setup cmake
|
- name: Setup cmake
|
||||||
uses: jwlawson/actions-setup-cmake@v1.4
|
uses: jwlawson/actions-setup-cmake@v1.4
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/ubuntu20-cxx20.yml
vendored
2
.github/workflows/ubuntu20-cxx20.yml
vendored
@ -8,7 +8,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Use cmake
|
- name: Use cmake
|
||||||
run: |
|
run: |
|
||||||
mkdir build &&
|
mkdir build &&
|
||||||
|
|||||||
16
.github/workflows/ubuntu20-fastmath.yml
vendored
Normal file
16
.github/workflows/ubuntu20-fastmath.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
name: Ubuntu 20.04 CI (GCC 9, fast-math)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DCMAKE_CXX_FLAGS="-ffast-math" -DFASTFLOAT_TEST=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest --output-on-failure
|
||||||
10
.github/workflows/ubuntu20.yml
vendored
10
.github/workflows/ubuntu20.yml
vendored
@ -5,16 +5,8 @@ on: [push, pull_request]
|
|||||||
jobs:
|
jobs:
|
||||||
ubuntu-build:
|
ubuntu-build:
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
# Legacy/x86 compilers cause CI failures.
|
|
||||||
#- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: }
|
|
||||||
- {cxx: , arch: } # default=gcc9
|
|
||||||
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v3
|
||||||
- name: Use cmake
|
- name: Use cmake
|
||||||
run: |
|
run: |
|
||||||
mkdir build &&
|
mkdir build &&
|
||||||
|
|||||||
25
.github/workflows/ubuntu22-clang.yml
vendored
Normal file
25
.github/workflows/ubuntu22-clang.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
name: Ubuntu 22.04 CI (clang 14)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Install clang++-14
|
||||||
|
run: sudo apt-get install -y clang++-14
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
CXX=clang++-14 cmake -DFASTFLOAT_TEST=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest --output-on-failure
|
||||||
|
- name: Use cmake CXX20
|
||||||
|
run: |
|
||||||
|
mkdir build20 &&
|
||||||
|
cd build20 &&
|
||||||
|
CXX=clang++-14 cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest --output-on-failure
|
||||||
23
.github/workflows/ubuntu22-gcc12.yml
vendored
Normal file
23
.github/workflows/ubuntu22-gcc12.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
name: Ubuntu 22.04 CI (GCC 12)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
CXX=g++-12 CXXFLAGS=-Werror cmake -DFASTFLOAT_TEST=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest --output-on-failure
|
||||||
|
- name: Use cmake CXX20
|
||||||
|
run: |
|
||||||
|
mkdir build20 &&
|
||||||
|
cd build20 &&
|
||||||
|
CXX=g++-12 CXXFLAGS=-Werror cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest --output-on-failure
|
||||||
16
.github/workflows/ubuntu22.yml
vendored
Normal file
16
.github/workflows/ubuntu22.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
name: Ubuntu 22.04 CI (GCC 11)
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ubuntu-build:
|
||||||
|
runs-on: ubuntu-22.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Use cmake
|
||||||
|
run: |
|
||||||
|
mkdir build &&
|
||||||
|
cd build &&
|
||||||
|
cmake -DFASTFLOAT_TEST=ON .. &&
|
||||||
|
cmake --build . &&
|
||||||
|
ctest --output-on-failure
|
||||||
29
.github/workflows/vs15-ci.yml
vendored
29
.github/workflows/vs15-ci.yml
vendored
@ -1,29 +0,0 @@
|
|||||||
name: VS15-CI
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ci:
|
|
||||||
if: >-
|
|
||||||
! contains(toJSON(github.event.commits.*.message), '[skip ci]') &&
|
|
||||||
! contains(toJSON(github.event.commits.*.message), '[skip github]')
|
|
||||||
name: windows-vs15
|
|
||||||
runs-on: windows-2016
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- {gen: Visual Studio 15 2017, arch: Win32}
|
|
||||||
- {gen: Visual Studio 15 2017, arch: x64}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Configure
|
|
||||||
run: |
|
|
||||||
mkdir build
|
|
||||||
cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON ..
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --verbose --config Release --parallel
|
|
||||||
- name: 'Run CTest'
|
|
||||||
run: |
|
|
||||||
cd build
|
|
||||||
ctest -C Release --output-on-failure -R basictest
|
|
||||||
21
.github/workflows/vs16-arm-ci.yml
vendored
21
.github/workflows/vs16-arm-ci.yml
vendored
@ -1,21 +0,0 @@
|
|||||||
name: VS16-ARM-CI
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ci:
|
|
||||||
name: windows-vs16
|
|
||||||
runs-on: windows-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- {arch: ARM}
|
|
||||||
- {arch: ARM64}
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Use cmake
|
|
||||||
run: |
|
|
||||||
cmake -A ${{ matrix.arch }} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON -B build &&
|
|
||||||
cmake --build build --verbose
|
|
||||||
29
.github/workflows/vs16-ci.yml
vendored
29
.github/workflows/vs16-ci.yml
vendored
@ -1,29 +0,0 @@
|
|||||||
name: VS16-CI
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ci:
|
|
||||||
name: windows-vs16
|
|
||||||
runs-on: windows-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- {gen: Visual Studio 16 2019, arch: Win32}
|
|
||||||
- {gen: Visual Studio 16 2019, arch: x64}
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Use cmake
|
|
||||||
run: |
|
|
||||||
mkdir build &&
|
|
||||||
cd build &&
|
|
||||||
cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
|
||||||
cmake --build . --verbose &&
|
|
||||||
ctest --output-on-failure &&
|
|
||||||
cmake --install . &&
|
|
||||||
cd ../tests/installation_tests/find &&
|
|
||||||
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . --verbose
|
|
||||||
cd ../../issue72_installation &&
|
|
||||||
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . --verbose
|
|
||||||
27
.github/workflows/vs16-clang-ci.yml
vendored
27
.github/workflows/vs16-clang-ci.yml
vendored
@ -1,27 +0,0 @@
|
|||||||
name: VS16-CLANG-CI
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ci:
|
|
||||||
name: windows-vs16
|
|
||||||
runs-on: windows-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- {gen: Visual Studio 16 2019, arch: Win32}
|
|
||||||
- {gen: Visual Studio 16 2019, arch: x64}
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Configure
|
|
||||||
run: |
|
|
||||||
mkdir build
|
|
||||||
cd build && cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON ..
|
|
||||||
- name: Build
|
|
||||||
run: cmake --build build --config Release --parallel
|
|
||||||
- name: Run basic tests
|
|
||||||
run: |
|
|
||||||
cd build
|
|
||||||
ctest -C Release --output-on-failure -R basictest
|
|
||||||
24
.github/workflows/vs16-cxx20.yml
vendored
24
.github/workflows/vs16-cxx20.yml
vendored
@ -1,24 +0,0 @@
|
|||||||
name: VS16-CI C++20
|
|
||||||
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
ci:
|
|
||||||
name: windows-vs16
|
|
||||||
runs-on: windows-latest
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- {gen: Visual Studio 16 2019, arch: Win32}
|
|
||||||
- {gen: Visual Studio 16 2019, arch: x64}
|
|
||||||
steps:
|
|
||||||
- name: checkout
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
- name: Use cmake
|
|
||||||
run: |
|
|
||||||
mkdir build &&
|
|
||||||
cd build &&
|
|
||||||
cmake ${{matrix.cxx}} ${{matrix.arch}} -DCMAKE_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
|
||||||
cmake --build . --verbose &&
|
|
||||||
ctest --output-on-failure
|
|
||||||
30
.github/workflows/vs17-arm-ci.yml
vendored
Normal file
30
.github/workflows/vs17-arm-ci.yml
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
name: VS17-ARM-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {gen: Visual Studio 17 2022, arch: ARM, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: ARM, cfg: Debug}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -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}}
|
||||||
43
.github/workflows/vs17-ci.yml
vendored
Normal file
43
.github/workflows/vs17-ci.yml
vendored
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
name: VS17-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: configure
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -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
|
||||||
28
.github/workflows/vs17-clang-ci.yml
vendored
Normal file
28
.github/workflows/vs17-clang-ci.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: VS17-CLANG-CI
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: Configure
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -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
|
||||||
33
.github/workflows/vs17-cxx20.yml
vendored
Normal file
33
.github/workflows/vs17-cxx20.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
name: VS17-CI C++20
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ci:
|
||||||
|
name: vs17/${{matrix.arch}}/${{matrix.cfg}}
|
||||||
|
runs-on: windows-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Release}
|
||||||
|
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||||
|
steps:
|
||||||
|
- name: checkout
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
- name: configure
|
||||||
|
run: >-
|
||||||
|
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}
|
||||||
|
-DCMAKE_CXX_STANDARD=20
|
||||||
|
-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}}
|
||||||
@ -2,8 +2,6 @@ cmake_minimum_required(VERSION 3.9)
|
|||||||
|
|
||||||
project(fast_float VERSION 3.10.1 LANGUAGES CXX)
|
project(fast_float VERSION 3.10.1 LANGUAGES CXX)
|
||||||
option(FASTFLOAT_TEST "Enable tests" OFF)
|
option(FASTFLOAT_TEST "Enable tests" OFF)
|
||||||
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used")
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
if(FASTFLOAT_TEST)
|
if(FASTFLOAT_TEST)
|
||||||
enable_testing()
|
enable_testing()
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
@ -22,14 +20,21 @@ if (NOT CMAKE_BUILD_TYPE)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
option(FASTFLOAT_INSTALL "Enable install" ON)
|
||||||
|
|
||||||
|
if(FASTFLOAT_INSTALL)
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_library(fast_float INTERFACE)
|
add_library(fast_float INTERFACE)
|
||||||
|
add_library(FastFloat::fast_float ALIAS fast_float)
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
fast_float
|
fast_float
|
||||||
INTERFACE
|
INTERFACE
|
||||||
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
|
||||||
$<INSTALL_INTERFACE:include>
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
|
||||||
)
|
)
|
||||||
|
target_compile_features(fast_float INTERFACE cxx_std_11)
|
||||||
if(FASTFLOAT_SANITIZE)
|
if(FASTFLOAT_SANITIZE)
|
||||||
target_compile_options(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
|
target_compile_options(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
|
||||||
target_link_libraries(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
|
target_link_libraries(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
|
||||||
@ -42,24 +47,30 @@ if(MSVC_VERSION GREATER 1910)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if(FASTFLOAT_INSTALL)
|
||||||
include(CMakePackageConfigHelpers)
|
include(CMakePackageConfigHelpers)
|
||||||
|
|
||||||
set(FASTFLOAT_VERSION_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfigVersion.cmake")
|
set(FASTFLOAT_VERSION_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfigVersion.cmake")
|
||||||
set(FASTFLOAT_PROJECT_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfig.cmake")
|
set(FASTFLOAT_PROJECT_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfig.cmake")
|
||||||
set(FASTFLOAT_INSTALL_DIR "share/FastFloat")
|
set(FASTFLOAT_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake/FastFloat")
|
||||||
|
|
||||||
|
if(${CMAKE_VERSION} VERSION_LESS "3.14")
|
||||||
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
|
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
|
||||||
|
else()
|
||||||
|
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT)
|
||||||
|
endif()
|
||||||
configure_package_config_file("cmake/config.cmake.in"
|
configure_package_config_file("cmake/config.cmake.in"
|
||||||
"${FASTFLOAT_PROJECT_CONFIG}"
|
"${FASTFLOAT_PROJECT_CONFIG}"
|
||||||
INSTALL_DESTINATION "${FASTFLOAT_INSTALL_DIR}")
|
INSTALL_DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
|
||||||
|
|
||||||
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/fast_float" DESTINATION "include")
|
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/fast_float" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
install(FILES "${FASTFLOAT_PROJECT_CONFIG}" "${FASTFLOAT_VERSION_CONFIG}" DESTINATION "${FASTFLOAT_INSTALL_DIR}")
|
install(FILES "${FASTFLOAT_PROJECT_CONFIG}" "${FASTFLOAT_VERSION_CONFIG}" DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
|
||||||
install(EXPORT ${PROJECT_NAME}-targets NAMESPACE FastFloat:: DESTINATION "${FASTFLOAT_INSTALL_DIR}")
|
install(EXPORT ${PROJECT_NAME}-targets NAMESPACE FastFloat:: DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
|
||||||
|
|
||||||
install(TARGETS fast_float
|
install(TARGETS fast_float
|
||||||
EXPORT ${PROJECT_NAME}-targets
|
EXPORT ${PROJECT_NAME}-targets
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
ARCHIVE DESTINATION lib
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
LIBRARY DESTINATION lib
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
)
|
)
|
||||||
|
endif()
|
||||||
|
|||||||
@ -4,3 +4,5 @@ Marcin Wojdyr
|
|||||||
Neal Richardson
|
Neal Richardson
|
||||||
Tim Paine
|
Tim Paine
|
||||||
Fabio Pellacini
|
Fabio Pellacini
|
||||||
|
Lénárd Szolnoki
|
||||||
|
Jan Pharago
|
||||||
@ -175,18 +175,7 @@
|
|||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
Copyright 2021 The fast_float authors
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright 2020 The fast_float authors
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|||||||
@ -1,3 +1,7 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 The fast_float authors
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any
|
Permission is hereby granted, free of charge, to any
|
||||||
person obtaining a copy of this software and associated
|
person obtaining a copy of this software and associated
|
||||||
documentation files (the "Software"), to deal in the
|
documentation files (the "Software"), to deal in the
|
||||||
|
|||||||
89
README.md
89
README.md
@ -1,11 +1,8 @@
|
|||||||
## fast_float number parsing library: 4x faster than strtod
|
|
||||||
|
|
||||||
/badge.svg)
|
## fast_float number parsing library: 4x faster than strtod
|
||||||
/badge.svg)
|
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fast_float)
|
||||||

|
[](https://github.com/fastfloat/fast_float/actions/workflows/vs17-ci.yml)
|
||||||

|
[](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
|
||||||

|
|
||||||
[](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml)
|
|
||||||
|
|
||||||
The fast_float library provides fast header-only implementations for the C++ from_chars
|
The fast_float library provides fast header-only implementations for the C++ from_chars
|
||||||
functions for `float` and `double` types. These functions convert ASCII strings representing
|
functions for `float` and `double` types. These functions convert ASCII strings representing
|
||||||
@ -73,11 +70,32 @@ The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/char
|
|||||||
Furthermore, we have the following restrictions:
|
Furthermore, we have the following restrictions:
|
||||||
* We only support `float` and `double` types at this time.
|
* We only support `float` and `double` types at this time.
|
||||||
* We only support the decimal format: we do not support hexadecimal strings.
|
* We only support the decimal format: we do not support hexadecimal strings.
|
||||||
* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value.
|
* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`.
|
||||||
|
|
||||||
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
|
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
|
||||||
|
|
||||||
|
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
|
||||||
|
|
||||||
|
## C++20: compile-time evaluation (constexpr)
|
||||||
|
|
||||||
|
In C++20, you may use `fast_float::from_chars` to parse strings
|
||||||
|
at compile-time, as in the following example:
|
||||||
|
|
||||||
|
```C++
|
||||||
|
// consteval forces compile-time evaluation of the function in C++20.
|
||||||
|
consteval double parse(std::string_view input) {
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) { return -1.0; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should compile to a function which
|
||||||
|
// merely returns 3.1415.
|
||||||
|
constexpr double constexptest() {
|
||||||
|
return parse("3.1415 input");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
## Using commas as decimal separator
|
## Using commas as decimal separator
|
||||||
|
|
||||||
@ -104,23 +122,62 @@ int main() {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can parse delimited numbers:
|
||||||
|
```C++
|
||||||
|
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) {
|
||||||
|
// check error
|
||||||
|
}
|
||||||
|
// we have result == 234532.3426362.
|
||||||
|
if(answer.ptr[0] != ',') {
|
||||||
|
// unexpected delimiter
|
||||||
|
}
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) {
|
||||||
|
// check error
|
||||||
|
}
|
||||||
|
// we have result == 7869234.9823.
|
||||||
|
if(answer.ptr[0] != ',') {
|
||||||
|
// unexpected delimiter
|
||||||
|
}
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) {
|
||||||
|
// check error
|
||||||
|
}
|
||||||
|
// we have result == 324562.645.
|
||||||
|
```
|
||||||
|
|
||||||
## Reference
|
|
||||||
|
|
||||||
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021.
|
## Relation With Other Work
|
||||||
|
|
||||||
|
The fast_float library is part of:
|
||||||
|
|
||||||
|
- GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
|
||||||
|
- [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
|
||||||
|
|
||||||
|
|
||||||
|
The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
|
||||||
|
|
||||||
|
There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS).
|
||||||
|
|
||||||
|
|
||||||
|
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
|
||||||
|
|
||||||
|
## References
|
||||||
|
|
||||||
|
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021.
|
||||||
|
- Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience (to appear)
|
||||||
|
|
||||||
## Other programming languages
|
## Other programming languages
|
||||||
|
|
||||||
- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
|
- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
|
||||||
- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`.
|
- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`.
|
||||||
- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`.
|
- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core).
|
||||||
- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
|
- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
|
||||||
|
|
||||||
|
|
||||||
## Relation With Other Work
|
|
||||||
|
|
||||||
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
|
|
||||||
|
|
||||||
## Users
|
## Users
|
||||||
|
|
||||||
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet).
|
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet).
|
||||||
@ -189,7 +246,7 @@ 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/v1.1.2/fast_float.h
|
https://github.com/fastfloat/fast_float/releases/download/v3.4.0/fast_float.h
|
||||||
|
|
||||||
## Credit
|
## Credit
|
||||||
|
|
||||||
|
|||||||
8
fuzz/build.sh
Normal file
8
fuzz/build.sh
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
$CXX $CFLAGS $CXXFLAGS \
|
||||||
|
-I $SRC/fast_float/include \
|
||||||
|
-c $SRC/fast_float/fuzz/from_chars.cc -o from_chars.o
|
||||||
|
|
||||||
|
$CXX $CFLAGS $CXXFLAGS $LIB_FUZZING_ENGINE from_chars.o \
|
||||||
|
-o $OUT/from_chars
|
||||||
34
fuzz/from_chars.cc
Normal file
34
fuzz/from_chars.cc
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <fuzzer/FuzzedDataProvider.h>
|
||||||
|
#include <string>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) {
|
||||||
|
using fast_float::chars_format;
|
||||||
|
switch (fdp.ConsumeIntegralInRange<int>(0,3)) {
|
||||||
|
case 0:
|
||||||
|
return chars_format::scientific;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
return chars_format::fixed;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
return chars_format::fixed;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return chars_format::general;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||||
|
FuzzedDataProvider fdp(data, size);
|
||||||
|
fast_float::chars_format format = arbitrary_format(fdp);
|
||||||
|
double result_d = 0.0;
|
||||||
|
std::string input_d = fdp.ConsumeRandomLengthString(128);
|
||||||
|
auto answer =
|
||||||
|
fast_float::from_chars(input_d.data(), input_d.data() + input_d.size(), result_d, format);
|
||||||
|
std::string input_f = fdp.ConsumeRandomLengthString(128);
|
||||||
|
double result_f = 0.0;
|
||||||
|
answer =
|
||||||
|
fast_float::from_chars(input_f.data(), input_f.data() + input_f.size(), result_f, format);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@ -12,9 +12,12 @@ namespace fast_float {
|
|||||||
|
|
||||||
// Next function can be micro-optimized, but compilers are entirely
|
// Next function can be micro-optimized, but compilers are entirely
|
||||||
// able to optimize it well.
|
// able to optimize it well.
|
||||||
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
|
template <typename UC>
|
||||||
|
fastfloat_really_inline constexpr bool is_integer(UC c) noexcept {
|
||||||
|
return !(c > UC('9') || c < UC('0'));
|
||||||
|
}
|
||||||
|
|
||||||
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
|
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
|
||||||
return (val & 0xFF00000000000000) >> 56
|
return (val & 0xFF00000000000000) >> 56
|
||||||
| (val & 0x00FF000000000000) >> 40
|
| (val & 0x00FF000000000000) >> 40
|
||||||
| (val & 0x0000FF0000000000) >> 24
|
| (val & 0x0000FF0000000000) >> 24
|
||||||
@ -24,8 +27,17 @@ fastfloat_really_inline uint64_t byteswap(uint64_t val) {
|
|||||||
| (val & 0x000000000000FF00) << 40
|
| (val & 0x000000000000FF00) << 40
|
||||||
| (val & 0x00000000000000FF) << 56;
|
| (val & 0x00000000000000FF) << 56;
|
||||||
}
|
}
|
||||||
|
template <typename UC>
|
||||||
fastfloat_really_inline uint64_t read_u64(const char *chars) {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
uint64_t read_u64(UC const * chars) {
|
||||||
|
if (cpp20_and_in_constexpr() || sizeof(UC) > 1) {
|
||||||
|
uint64_t val{};
|
||||||
|
for(int i = 0; i < 8; ++i) {
|
||||||
|
val |= uint64_t(char(*chars)) << (i * 8);
|
||||||
|
++chars;
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
::memcpy(&val, chars, sizeof(uint64_t));
|
::memcpy(&val, chars, sizeof(uint64_t));
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
||||||
@ -35,7 +47,16 @@ fastfloat_really_inline uint64_t read_u64(const char *chars) {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
void write_u64(uint8_t *chars, uint64_t val) {
|
||||||
|
if (cpp20_and_in_constexpr()) {
|
||||||
|
for(int i = 0; i < 8; ++i) {
|
||||||
|
*chars = uint8_t(val);
|
||||||
|
val >>= 8;
|
||||||
|
++chars;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
||||||
// Need to read as-if the number was in little-endian order.
|
// Need to read as-if the number was in little-endian order.
|
||||||
val = byteswap(val);
|
val = byteswap(val);
|
||||||
@ -44,7 +65,8 @@ fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// credit @aqrit
|
// credit @aqrit
|
||||||
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
|
uint32_t parse_eight_digits_unrolled(uint64_t val) {
|
||||||
const uint64_t mask = 0x000000FF000000FF;
|
const uint64_t mask = 0x000000FF000000FF;
|
||||||
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
||||||
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
||||||
@ -53,47 +75,55 @@ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
|
|||||||
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
|
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
|
||||||
return uint32_t(val);
|
return uint32_t(val);
|
||||||
}
|
}
|
||||||
|
template <typename UC>
|
||||||
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
uint32_t parse_eight_digits_unrolled(UC const * chars) noexcept {
|
||||||
return parse_eight_digits_unrolled(read_u64(chars));
|
return parse_eight_digits_unrolled(read_u64(chars));
|
||||||
}
|
}
|
||||||
|
|
||||||
// credit @aqrit
|
// credit @aqrit
|
||||||
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
||||||
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
|
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
|
||||||
0x8080808080808080));
|
0x8080808080808080));
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline bool is_made_of_eight_digits_fast(const char *chars) noexcept {
|
template <typename UC>
|
||||||
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
bool is_made_of_eight_digits_fast(UC const * chars) noexcept {
|
||||||
return is_made_of_eight_digits_fast(read_u64(chars));
|
return is_made_of_eight_digits_fast(read_u64(chars));
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef span<const char> byte_span;
|
template <typename UC>
|
||||||
|
struct parsed_number_string_t {
|
||||||
struct parsed_number_string {
|
|
||||||
int64_t exponent{0};
|
int64_t exponent{0};
|
||||||
uint64_t mantissa{0};
|
uint64_t mantissa{0};
|
||||||
const char *lastmatch{nullptr};
|
UC const * lastmatch{nullptr};
|
||||||
bool negative{false};
|
bool negative{false};
|
||||||
bool valid{false};
|
bool valid{false};
|
||||||
bool too_many_digits{false};
|
bool too_many_digits{false};
|
||||||
// contains the range of the significant digits
|
// contains the range of the significant digits
|
||||||
byte_span integer{}; // non-nullable
|
span<UC> integer{}; // non-nullable
|
||||||
byte_span fraction{}; // nullable
|
span<UC> fraction{}; // nullable
|
||||||
};
|
};
|
||||||
|
using byte_span = span<char>;
|
||||||
|
using parsed_number_string = parsed_number_string_t<char>;
|
||||||
// Assuming that you use no more than 19 digits, this will
|
// Assuming that you use no more than 19 digits, this will
|
||||||
// parse an ASCII string.
|
// parse an ASCII string.
|
||||||
fastfloat_really_inline
|
template <typename UC>
|
||||||
parsed_number_string parse_number_string(const char *p, const char *pend, parse_options options) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
const chars_format fmt = options.format;
|
parsed_number_string_t<UC> parse_number_string(UC const *p, UC const * pend, parse_options_t<UC> options) noexcept {
|
||||||
const char decimal_point = options.decimal_point;
|
chars_format const fmt = options.format;
|
||||||
|
UC const decimal_point = options.decimal_point;
|
||||||
|
|
||||||
parsed_number_string answer;
|
parsed_number_string_t<UC> answer;
|
||||||
answer.valid = false;
|
answer.valid = false;
|
||||||
answer.too_many_digits = false;
|
answer.too_many_digits = false;
|
||||||
answer.negative = (*p == '-');
|
answer.negative = (*p == UC('-'));
|
||||||
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||||
|
if ((*p == UC('-')) || (*p == UC('+'))) {
|
||||||
|
#else
|
||||||
|
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||||
|
#endif
|
||||||
++p;
|
++p;
|
||||||
if (p == pend) {
|
if (p == pend) {
|
||||||
return answer;
|
return answer;
|
||||||
@ -102,28 +132,24 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const char *const start_digits = p;
|
UC const * const start_digits = p;
|
||||||
|
|
||||||
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
||||||
|
|
||||||
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
|
|
||||||
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
|
|
||||||
p += 8;
|
|
||||||
}
|
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
// a multiplication by 10 is cheaper than an arbitrary integer
|
// a multiplication by 10 is cheaper than an arbitrary integer
|
||||||
// multiplication
|
// multiplication
|
||||||
i = 10 * i +
|
i = 10 * i +
|
||||||
uint64_t(*p - '0'); // might overflow, we will handle the overflow later
|
uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
const char *const end_of_integer_part = p;
|
UC const * const end_of_integer_part = p;
|
||||||
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
||||||
answer.integer = byte_span(start_digits, size_t(digit_count));
|
answer.integer = span<UC>(start_digits, size_t(digit_count));
|
||||||
int64_t exponent = 0;
|
int64_t exponent = 0;
|
||||||
if ((p != pend) && (*p == decimal_point)) {
|
if ((p != pend) && (*p == decimal_point)) {
|
||||||
++p;
|
++p;
|
||||||
const char* before = p;
|
UC const * before = p;
|
||||||
// can occur at most twice without overflowing, but let it occur more, since
|
// can occur at most twice without overflowing, but let it occur more, since
|
||||||
// for integers with many digits, digit parsing is the primary bottleneck.
|
// for integers with many digits, digit parsing is the primary bottleneck.
|
||||||
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
|
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
|
||||||
@ -131,12 +157,12 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
p += 8;
|
p += 8;
|
||||||
}
|
}
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
uint8_t digit = uint8_t(*p - '0');
|
uint8_t digit = uint8_t(*p - UC('0'));
|
||||||
++p;
|
++p;
|
||||||
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
|
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
|
||||||
}
|
}
|
||||||
exponent = before - p;
|
exponent = before - p;
|
||||||
answer.fraction = byte_span(before, size_t(p - before));
|
answer.fraction = span<UC>(before, size_t(p - before));
|
||||||
digit_count -= exponent;
|
digit_count -= exponent;
|
||||||
}
|
}
|
||||||
// we must have encountered at least one integer!
|
// we must have encountered at least one integer!
|
||||||
@ -144,14 +170,14 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
int64_t exp_number = 0; // explicit exponential part
|
int64_t exp_number = 0; // explicit exponential part
|
||||||
if ((fmt & chars_format::scientific) && (p != pend) && (('e' == *p) || ('E' == *p))) {
|
if ((fmt & chars_format::scientific) && (p != pend) && ((UC('e') == *p) || (UC('E') == *p))) {
|
||||||
const char * location_of_e = p;
|
UC const * location_of_e = p;
|
||||||
++p;
|
++p;
|
||||||
bool neg_exp = false;
|
bool neg_exp = false;
|
||||||
if ((p != pend) && ('-' == *p)) {
|
if ((p != pend) && (UC('-') == *p)) {
|
||||||
neg_exp = true;
|
neg_exp = true;
|
||||||
++p;
|
++p;
|
||||||
} else if ((p != pend) && ('+' == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
} else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1)
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
if ((p == pend) || !is_integer(*p)) {
|
if ((p == pend) || !is_integer(*p)) {
|
||||||
@ -163,7 +189,7 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
p = location_of_e;
|
p = location_of_e;
|
||||||
} else {
|
} else {
|
||||||
while ((p != pend) && is_integer(*p)) {
|
while ((p != pend) && is_integer(*p)) {
|
||||||
uint8_t digit = uint8_t(*p - '0');
|
uint8_t digit = uint8_t(*p - UC('0'));
|
||||||
if (exp_number < 0x10000000) {
|
if (exp_number < 0x10000000) {
|
||||||
exp_number = 10 * exp_number + digit;
|
exp_number = 10 * exp_number + digit;
|
||||||
}
|
}
|
||||||
@ -189,9 +215,9 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
// We have to handle the case where we have 0.0000somenumber.
|
// We have to handle the case where we have 0.0000somenumber.
|
||||||
// We need to be mindful of the case where we only have zeroes...
|
// We need to be mindful of the case where we only have zeroes...
|
||||||
// E.g., 0.000000000...000.
|
// E.g., 0.000000000...000.
|
||||||
const char *start = start_digits;
|
UC const * start = start_digits;
|
||||||
while ((start != pend) && (*start == '0' || *start == decimal_point)) {
|
while ((start != pend) && (*start == UC('0') || *start == decimal_point)) {
|
||||||
if(*start == '0') { digit_count --; }
|
if(*start == UC('0')) { digit_count --; }
|
||||||
start++;
|
start++;
|
||||||
}
|
}
|
||||||
if (digit_count > 19) {
|
if (digit_count > 19) {
|
||||||
@ -201,19 +227,19 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
// pre-tokenized spans from above.
|
// pre-tokenized spans from above.
|
||||||
i = 0;
|
i = 0;
|
||||||
p = answer.integer.ptr;
|
p = answer.integer.ptr;
|
||||||
const char* int_end = p + answer.integer.len();
|
UC const * int_end = p + answer.integer.len();
|
||||||
const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
|
const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
|
||||||
while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
|
while((i < minimal_nineteen_digit_integer) && (p != int_end)) {
|
||||||
i = i * 10 + uint64_t(*p - '0');
|
i = i * 10 + uint64_t(*p - UC('0'));
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
|
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
|
||||||
exponent = end_of_integer_part - p + exp_number;
|
exponent = end_of_integer_part - p + exp_number;
|
||||||
} else { // We have a value with a fractional component.
|
} else { // We have a value with a fractional component.
|
||||||
p = answer.fraction.ptr;
|
p = answer.fraction.ptr;
|
||||||
const char* frac_end = p + answer.fraction.len();
|
UC const * frac_end = p + answer.fraction.len();
|
||||||
while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
|
while((i < minimal_nineteen_digit_integer) && (p != frac_end)) {
|
||||||
i = i * 10 + uint64_t(*p - '0');
|
i = i * 10 + uint64_t(*p - UC('0'));
|
||||||
++p;
|
++p;
|
||||||
}
|
}
|
||||||
exponent = answer.fraction.ptr - p + exp_number;
|
exponent = answer.fraction.ptr - p + exp_number;
|
||||||
|
|||||||
@ -17,7 +17,7 @@ namespace fast_float {
|
|||||||
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
|
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
|
||||||
// doing `8 * sizeof(limb)`.
|
// doing `8 * sizeof(limb)`.
|
||||||
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
|
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
|
||||||
#define FASTFLOAT_64BIT_LIMB
|
#define FASTFLOAT_64BIT_LIMB 1
|
||||||
typedef uint64_t limb;
|
typedef uint64_t limb;
|
||||||
constexpr size_t limb_bits = 64;
|
constexpr size_t limb_bits = 64;
|
||||||
#else
|
#else
|
||||||
@ -50,27 +50,27 @@ struct stackvec {
|
|||||||
stackvec &operator=(stackvec &&other) = delete;
|
stackvec &operator=(stackvec &&other) = delete;
|
||||||
|
|
||||||
// create stack vector from existing limb span.
|
// create stack vector from existing limb span.
|
||||||
stackvec(limb_span s) {
|
FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) {
|
||||||
FASTFLOAT_ASSERT(try_extend(s));
|
FASTFLOAT_ASSERT(try_extend(s));
|
||||||
}
|
}
|
||||||
|
|
||||||
limb& operator[](size_t index) noexcept {
|
FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||||
return data[index];
|
return data[index];
|
||||||
}
|
}
|
||||||
const limb& operator[](size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 const limb& operator[](size_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
|
||||||
const limb& rindex(size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||||
size_t rindex = length - index - 1;
|
size_t rindex = length - index - 1;
|
||||||
return data[rindex];
|
return data[rindex];
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the length, without bounds checking.
|
// set the length, without bounds checking.
|
||||||
void set_len(size_t len) noexcept {
|
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
|
||||||
length = uint16_t(len);
|
length = uint16_t(len);
|
||||||
}
|
}
|
||||||
constexpr size_t len() const noexcept {
|
constexpr size_t len() const noexcept {
|
||||||
@ -83,12 +83,12 @@ struct stackvec {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
// append item to vector, without bounds checking
|
// append item to vector, without bounds checking
|
||||||
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
|
||||||
bool try_push(limb value) noexcept {
|
FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
|
||||||
if (len() < capacity()) {
|
if (len() < capacity()) {
|
||||||
push_unchecked(value);
|
push_unchecked(value);
|
||||||
return true;
|
return true;
|
||||||
@ -97,13 +97,13 @@ struct stackvec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// add items to the vector, from a span, without bounds checking
|
// add items to the vector, from a span, without bounds checking
|
||||||
void extend_unchecked(limb_span s) noexcept {
|
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
|
||||||
limb* ptr = data + length;
|
limb* ptr = data + length;
|
||||||
::memcpy((void*)ptr, (const void*)s.ptr, sizeof(limb) * s.len());
|
std::copy_n(s.ptr, s.len(), ptr);
|
||||||
set_len(len() + s.len());
|
set_len(len() + 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
|
||||||
bool try_extend(limb_span s) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
|
||||||
if (len() + s.len() <= capacity()) {
|
if (len() + s.len() <= capacity()) {
|
||||||
extend_unchecked(s);
|
extend_unchecked(s);
|
||||||
return true;
|
return true;
|
||||||
@ -114,6 +114,7 @@ struct stackvec {
|
|||||||
// resize the vector, without bounds checking
|
// resize the vector, without bounds checking
|
||||||
// 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
|
||||||
void resize_unchecked(size_t new_len, limb value) noexcept {
|
void resize_unchecked(size_t new_len, limb value) noexcept {
|
||||||
if (new_len > len()) {
|
if (new_len > len()) {
|
||||||
size_t count = new_len - len();
|
size_t count = new_len - len();
|
||||||
@ -126,7 +127,7 @@ struct stackvec {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// try to resize the vector, returning if the vector was resized.
|
// try to resize the vector, returning if the vector was resized.
|
||||||
bool try_resize(size_t new_len, limb value) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
|
||||||
if (new_len > capacity()) {
|
if (new_len > capacity()) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
@ -137,7 +138,7 @@ 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.
|
||||||
bool nonzero(size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
|
||||||
while (index < len()) {
|
while (index < len()) {
|
||||||
if (rindex(index) != 0) {
|
if (rindex(index) != 0) {
|
||||||
return true;
|
return true;
|
||||||
@ -147,27 +148,27 @@ struct stackvec {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// normalize the big integer, so most-significant zero limbs are removed.
|
// normalize the big integer, so most-significant zero limbs are removed.
|
||||||
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_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
uint64_t empty_hi64(bool& truncated) noexcept {
|
uint64_t empty_hi64(bool& truncated) noexcept {
|
||||||
truncated = false;
|
truncated = false;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
|
uint64_t uint64_hi64(uint64_t r0, bool& truncated) noexcept {
|
||||||
truncated = false;
|
truncated = false;
|
||||||
int shl = leading_zeroes(r0);
|
int shl = leading_zeroes(r0);
|
||||||
return r0 << shl;
|
return r0 << shl;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
|
uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
|
||||||
int shl = leading_zeroes(r0);
|
int shl = leading_zeroes(r0);
|
||||||
if (shl == 0) {
|
if (shl == 0) {
|
||||||
@ -180,19 +181,19 @@ uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool& truncated) noexcept {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
|
uint64_t uint32_hi64(uint32_t r0, bool& truncated) noexcept {
|
||||||
return uint64_hi64(r0, truncated);
|
return uint64_hi64(r0, truncated);
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool& truncated) noexcept {
|
uint64_t 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_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noexcept {
|
uint64_t 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;
|
||||||
@ -204,15 +205,16 @@ uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool& truncated) noe
|
|||||||
// 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_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
limb scalar_add(limb x, limb y, bool& overflow) noexcept {
|
limb scalar_add(limb x, limb y, bool& overflow) noexcept {
|
||||||
limb z;
|
limb z;
|
||||||
|
|
||||||
// gcc and clang
|
// gcc and clang
|
||||||
#if defined(__has_builtin)
|
#if defined(__has_builtin)
|
||||||
#if __has_builtin(__builtin_add_overflow)
|
#if __has_builtin(__builtin_add_overflow)
|
||||||
|
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
|
#endif
|
||||||
|
|
||||||
@ -223,7 +225,7 @@ limb 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_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
limb scalar_mul(limb x, limb y, limb& carry) noexcept {
|
limb scalar_mul(limb x, limb y, limb& carry) noexcept {
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
#if defined(__SIZEOF_INT128__)
|
#if defined(__SIZEOF_INT128__)
|
||||||
@ -251,7 +253,8 @@ limb 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 <uint16_t size>
|
||||||
inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
|
bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
|
||||||
size_t index = start;
|
size_t index = start;
|
||||||
limb carry = y;
|
limb carry = y;
|
||||||
bool overflow;
|
bool overflow;
|
||||||
@ -268,13 +271,15 @@ inline bool small_add_from(stackvec<size>& vec, limb y, size_t start) noexcept {
|
|||||||
|
|
||||||
// add scalar value to bigint.
|
// add scalar value to bigint.
|
||||||
template <uint16_t size>
|
template <uint16_t size>
|
||||||
fastfloat_really_inline bool small_add(stackvec<size>& vec, limb y) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
bool small_add(stackvec<size>& vec, 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 <uint16_t size>
|
||||||
inline bool small_mul(stackvec<size>& vec, limb y) noexcept {
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
|
bool small_mul(stackvec<size>& vec, limb y) noexcept {
|
||||||
limb carry = 0;
|
limb carry = 0;
|
||||||
for (size_t index = 0; index < vec.len(); index++) {
|
for (size_t index = 0; index < vec.len(); index++) {
|
||||||
vec[index] = scalar_mul(vec[index], y, carry);
|
vec[index] = scalar_mul(vec[index], y, carry);
|
||||||
@ -288,6 +293,7 @@ inline bool small_mul(stackvec<size>& vec, limb y) noexcept {
|
|||||||
// 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 <uint16_t size>
|
||||||
|
FASTFLOAT_CONSTEXPR20
|
||||||
bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
|
bool large_add_from(stackvec<size>& x, limb_span y, size_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.
|
||||||
@ -318,12 +324,14 @@ bool large_add_from(stackvec<size>& x, limb_span y, size_t start) noexcept {
|
|||||||
|
|
||||||
// add bigint to bigint.
|
// add bigint to bigint.
|
||||||
template <uint16_t size>
|
template <uint16_t size>
|
||||||
fastfloat_really_inline bool large_add_from(stackvec<size>& x, limb_span y) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
bool 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 <uint16_t size>
|
||||||
|
FASTFLOAT_CONSTEXPR20
|
||||||
bool long_mul(stackvec<size>& x, limb_span y) noexcept {
|
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);
|
||||||
@ -352,6 +360,7 @@ bool long_mul(stackvec<size>& x, limb_span y) noexcept {
|
|||||||
|
|
||||||
// grade-school multiplication algorithm
|
// grade-school multiplication algorithm
|
||||||
template <uint16_t size>
|
template <uint16_t size>
|
||||||
|
FASTFLOAT_CONSTEXPR20
|
||||||
bool large_mul(stackvec<size>& x, limb_span y) noexcept {
|
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]));
|
||||||
@ -361,21 +370,52 @@ bool large_mul(stackvec<size>& x, limb_span y) noexcept {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename = void>
|
||||||
|
struct pow5_tables {
|
||||||
|
static constexpr uint32_t large_step = 135;
|
||||||
|
static constexpr uint64_t small_power_of_5[] = {
|
||||||
|
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
|
||||||
|
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
|
||||||
|
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
|
||||||
|
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
|
||||||
|
2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
|
||||||
|
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
|
||||||
|
};
|
||||||
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
|
constexpr static limb large_power_of_5[] = {
|
||||||
|
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
|
||||||
|
10482974169319127550UL, 198276706040285095UL};
|
||||||
|
#else
|
||||||
|
constexpr static limb large_power_of_5[] = {
|
||||||
|
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
|
||||||
|
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr uint32_t pow5_tables<T>::large_step;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr uint64_t pow5_tables<T>::small_power_of_5[];
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr limb pow5_tables<T>::large_power_of_5[];
|
||||||
|
|
||||||
// big integer type. implements a small subset of big integer
|
// big integer type. implements a small subset of big integer
|
||||||
// arithmetic, using simple algorithms since asymptotically
|
// arithmetic, using simple algorithms since asymptotically
|
||||||
// faster algorithms are slower for a small number of limbs.
|
// faster algorithms are slower for a small number of limbs.
|
||||||
// all operations assume the big-integer is normalized.
|
// all operations assume the big-integer is normalized.
|
||||||
struct bigint {
|
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;
|
||||||
|
|
||||||
bigint(): vec() {}
|
FASTFLOAT_CONSTEXPR20 bigint(): vec() {}
|
||||||
bigint(const bigint &) = delete;
|
bigint(const bigint &) = delete;
|
||||||
bigint &operator=(const bigint &) = delete;
|
bigint &operator=(const bigint &) = delete;
|
||||||
bigint(bigint &&) = delete;
|
bigint(bigint &&) = delete;
|
||||||
bigint &operator=(bigint &&other) = delete;
|
bigint &operator=(bigint &&other) = delete;
|
||||||
|
|
||||||
bigint(uint64_t value): vec() {
|
FASTFLOAT_CONSTEXPR20 bigint(uint64_t value): vec() {
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
vec.push_unchecked(value);
|
vec.push_unchecked(value);
|
||||||
#else
|
#else
|
||||||
@ -387,7 +427,7 @@ struct bigint {
|
|||||||
|
|
||||||
// get the high 64 bits from the vector, and if bits were truncated.
|
// get the high 64 bits from the vector, and if bits were truncated.
|
||||||
// this is to get the significant digits for the float.
|
// this is to get the significant digits for the float.
|
||||||
uint64_t hi64(bool& truncated) const noexcept {
|
FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool& truncated) const noexcept {
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
if (vec.len() == 0) {
|
if (vec.len() == 0) {
|
||||||
return empty_hi64(truncated);
|
return empty_hi64(truncated);
|
||||||
@ -419,7 +459,7 @@ struct bigint {
|
|||||||
// positive, this is larger, otherwise they are equal.
|
// positive, this is larger, otherwise they are equal.
|
||||||
// the limbs are stored in little-endian order, so we
|
// the limbs are stored in little-endian order, so we
|
||||||
// must compare the limbs in ever order.
|
// must compare the limbs in ever order.
|
||||||
int compare(const bigint& other) const noexcept {
|
FASTFLOAT_CONSTEXPR20 int compare(const bigint& other) const noexcept {
|
||||||
if (vec.len() > other.vec.len()) {
|
if (vec.len() > other.vec.len()) {
|
||||||
return 1;
|
return 1;
|
||||||
} else if (vec.len() < other.vec.len()) {
|
} else if (vec.len() < other.vec.len()) {
|
||||||
@ -440,7 +480,7 @@ struct bigint {
|
|||||||
|
|
||||||
// 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.
|
||||||
bool shl_bits(size_t n) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool shl_bits(size_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:
|
||||||
@ -466,7 +506,7 @@ struct bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// move the limbs left by `n` limbs.
|
// move the limbs left by `n` limbs.
|
||||||
bool shl_limbs(size_t n) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_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()) {
|
||||||
return false;
|
return false;
|
||||||
@ -474,7 +514,7 @@ struct bigint {
|
|||||||
// move limbs
|
// move limbs
|
||||||
limb* dst = vec.data + n;
|
limb* dst = vec.data + n;
|
||||||
const limb* src = vec.data;
|
const limb* src = vec.data;
|
||||||
::memmove(dst, src, sizeof(limb) * vec.len());
|
std::copy_backward(src, src + vec.len(), dst + vec.len());
|
||||||
// fill in empty limbs
|
// fill in empty limbs
|
||||||
limb* first = vec.data;
|
limb* first = vec.data;
|
||||||
limb* last = first + n;
|
limb* last = first + n;
|
||||||
@ -487,7 +527,7 @@ struct bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// move the limbs left by `n` bits.
|
// move the limbs left by `n` bits.
|
||||||
bool shl(size_t n) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept {
|
||||||
size_t rem = n % limb_bits;
|
size_t rem = n % limb_bits;
|
||||||
size_t div = n / limb_bits;
|
size_t div = n / limb_bits;
|
||||||
if (rem != 0) {
|
if (rem != 0) {
|
||||||
@ -500,7 +540,7 @@ struct bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the number of leading zeros in the bigint.
|
// get the number of leading zeros in the bigint.
|
||||||
int ctlz() const noexcept {
|
FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept {
|
||||||
if (vec.is_empty()) {
|
if (vec.is_empty()) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
@ -515,45 +555,27 @@ struct bigint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get the number of bits in the bigint.
|
// get the number of bits in the bigint.
|
||||||
int bit_length() const noexcept {
|
FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept {
|
||||||
int lz = ctlz();
|
int lz = ctlz();
|
||||||
return int(limb_bits * vec.len()) - lz;
|
return int(limb_bits * vec.len()) - lz;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mul(limb y) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept {
|
||||||
return small_mul(vec, y);
|
return small_mul(vec, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool add(limb y) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept {
|
||||||
return small_add(vec, y);
|
return small_add(vec, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiply as if by 2 raised to a power.
|
// multiply as if by 2 raised to a power.
|
||||||
bool pow2(uint32_t exp) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept {
|
||||||
return shl(exp);
|
return shl(exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiply as if by 5 raised to a power.
|
// multiply as if by 5 raised to a power.
|
||||||
bool pow5(uint32_t exp) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept {
|
||||||
// multiply by a power of 5
|
// multiply by a power of 5
|
||||||
static constexpr uint32_t large_step = 135;
|
|
||||||
static constexpr uint64_t small_power_of_5[] = {
|
|
||||||
1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL,
|
|
||||||
1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL,
|
|
||||||
6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL,
|
|
||||||
3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL,
|
|
||||||
2384185791015625UL, 11920928955078125UL, 59604644775390625UL,
|
|
||||||
298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL,
|
|
||||||
};
|
|
||||||
#ifdef FASTFLOAT_64BIT_LIMB
|
|
||||||
constexpr static limb large_power_of_5[] = {
|
|
||||||
1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL,
|
|
||||||
10482974169319127550UL, 198276706040285095UL};
|
|
||||||
#else
|
|
||||||
constexpr static limb large_power_of_5[] = {
|
|
||||||
4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U,
|
|
||||||
1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U};
|
|
||||||
#endif
|
|
||||||
size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
|
size_t large_length = sizeof(large_power_of_5) / sizeof(limb);
|
||||||
limb_span large = limb_span(large_power_of_5, large_length);
|
limb_span large = limb_span(large_power_of_5, large_length);
|
||||||
while (exp >= large_step) {
|
while (exp >= large_step) {
|
||||||
@ -572,14 +594,19 @@ struct bigint {
|
|||||||
exp -= small_step;
|
exp -= small_step;
|
||||||
}
|
}
|
||||||
if (exp != 0) {
|
if (exp != 0) {
|
||||||
FASTFLOAT_TRY(small_mul(vec, limb(small_power_of_5[exp])));
|
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||||
|
// This is similar to https://github.com/llvm/llvm-project/issues/47746,
|
||||||
|
// except the workaround described there don't work here
|
||||||
|
FASTFLOAT_TRY(
|
||||||
|
small_mul(vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiply as if by 10 raised to a power.
|
// multiply as if by 10 raised to a power.
|
||||||
bool pow10(uint32_t exp) noexcept {
|
FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept {
|
||||||
FASTFLOAT_TRY(pow5(exp));
|
FASTFLOAT_TRY(pow5(exp));
|
||||||
return pow2(exp);
|
return pow2(exp);
|
||||||
}
|
}
|
||||||
|
|||||||
40
include/fast_float/constexpr_feature_detect.h
Normal file
40
include/fast_float/constexpr_feature_detect.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
|
||||||
|
#define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
|
||||||
|
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<version>)
|
||||||
|
#include <version>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Testing for https://wg21.link/N3652, adopted in C++14
|
||||||
|
#if __cpp_constexpr >= 201304
|
||||||
|
#define FASTFLOAT_CONSTEXPR14 constexpr
|
||||||
|
#else
|
||||||
|
#define FASTFLOAT_CONSTEXPR14
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L
|
||||||
|
#define FASTFLOAT_HAS_BIT_CAST 1
|
||||||
|
#else
|
||||||
|
#define FASTFLOAT_HAS_BIT_CAST 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__cpp_lib_is_constant_evaluated) && __cpp_lib_is_constant_evaluated >= 201811L
|
||||||
|
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1
|
||||||
|
#else
|
||||||
|
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Testing for relevant C++20 constexpr library features
|
||||||
|
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED \
|
||||||
|
&& FASTFLOAT_HAS_BIT_CAST \
|
||||||
|
&& __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
|
||||||
|
#define FASTFLOAT_CONSTEXPR20 constexpr
|
||||||
|
#define FASTFLOAT_IS_CONSTEXPR 1
|
||||||
|
#else
|
||||||
|
#define FASTFLOAT_CONSTEXPR20
|
||||||
|
#define FASTFLOAT_IS_CONSTEXPR 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H
|
||||||
@ -17,7 +17,7 @@ namespace fast_float {
|
|||||||
// low part corresponding to the least significant bits.
|
// low part corresponding to the least significant bits.
|
||||||
//
|
//
|
||||||
template <int bit_precision>
|
template <int bit_precision>
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
value128 compute_product_approximation(int64_t q, uint64_t w) {
|
value128 compute_product_approximation(int64_t q, uint64_t w) {
|
||||||
const int index = 2 * int(q - powers::smallest_power_of_five);
|
const int index = 2 * int(q - powers::smallest_power_of_five);
|
||||||
// For small values of q, e.g., q in [0,27], the answer is always exact because
|
// For small values of q, e.g., q in [0,27], the answer is always exact because
|
||||||
@ -63,7 +63,7 @@ namespace detail {
|
|||||||
// 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_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
|
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
|
||||||
int hilz = int(w >> 63) ^ 1;
|
int hilz = int(w >> 63) ^ 1;
|
||||||
adjusted_mantissa answer;
|
adjusted_mantissa answer;
|
||||||
@ -76,7 +76,7 @@ adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept
|
|||||||
// 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_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
|
adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
|
||||||
int lz = leading_zeroes(w);
|
int lz = leading_zeroes(w);
|
||||||
w <<= lz;
|
w <<= lz;
|
||||||
@ -90,7 +90,7 @@ adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept {
|
|||||||
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
|
// return an adjusted_mantissa with a negative power of 2: the caller should recompute
|
||||||
// in such cases.
|
// in such cases.
|
||||||
template <typename binary>
|
template <typename binary>
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
|
adjusted_mantissa compute_float(int64_t q, uint64_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())) {
|
||||||
@ -117,16 +117,11 @@ adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept {
|
|||||||
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
|
// 3. We might lose a bit due to the "upperbit" routine (result too small, requiring a shift)
|
||||||
|
|
||||||
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
value128 product = compute_product_approximation<binary::mantissa_explicit_bits() + 3>(q, w);
|
||||||
if(product.low == 0xFFFFFFFFFFFFFFFF) { // could guard it further
|
// The computed 'product' is always sufficient.
|
||||||
// In some very rare cases, this could happen, in which case we might need a more accurate
|
// Mathematical proof:
|
||||||
// computation that what we can provide cheaply. This is very, very unlikely.
|
// Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear)
|
||||||
//
|
// See script/mushtak_lemire.py
|
||||||
const bool inside_safe_exponent = (q >= -27) && (q <= 55); // always good because 5**q <2**128 when q>=0,
|
|
||||||
// and otherwise, for q<0, we have 5**-q<2**64 and the 128-bit reciprocal allows for exact computation.
|
|
||||||
if(!inside_safe_exponent) {
|
|
||||||
return compute_error_scaled<binary>(q, product.high, lz);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
|
// The "compute_product_approximation" function can be slightly slower than a branchless approach:
|
||||||
// value128 product = compute_product(q, w);
|
// value128 product = compute_product(q, w);
|
||||||
// but in practice, we can win big with the compute_product_approximation if its additional branch
|
// but in practice, we can win big with the compute_product_approximation if its additional branch
|
||||||
|
|||||||
@ -23,7 +23,9 @@ constexpr static uint64_t powers_of_ten_uint64[] = {
|
|||||||
// 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 int32_t scientific_exponent(parsed_number_string& num) noexcept {
|
template <typename UC>
|
||||||
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
|
int32_t scientific_exponent(parsed_number_string_t<UC> & num) noexcept {
|
||||||
uint64_t mantissa = num.mantissa;
|
uint64_t mantissa = num.mantissa;
|
||||||
int32_t exponent = int32_t(num.exponent);
|
int32_t exponent = int32_t(num.exponent);
|
||||||
while (mantissa >= 10000) {
|
while (mantissa >= 10000) {
|
||||||
@ -43,15 +45,21 @@ fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) n
|
|||||||
|
|
||||||
// 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 adjusted_mantissa to_extended(T value) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
adjusted_mantissa to_extended(T value) noexcept {
|
||||||
|
using equiv_uint = typename binary_format<T>::equiv_uint;
|
||||||
|
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
|
||||||
|
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_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() - binary_format<T>::minimum_exponent();
|
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
|
||||||
if (std::is_same<T, float>::value) {
|
equiv_uint bits;
|
||||||
constexpr uint32_t exponent_mask = 0x7F800000;
|
#if FASTFLOAT_HAS_BIT_CAST
|
||||||
constexpr uint32_t mantissa_mask = 0x007FFFFF;
|
bits = std::bit_cast<equiv_uint>(value);
|
||||||
constexpr uint64_t hidden_bit_mask = 0x00800000;
|
#else
|
||||||
uint32_t bits;
|
|
||||||
::memcpy(&bits, &value, sizeof(T));
|
::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;
|
||||||
@ -62,23 +70,6 @@ fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
|
|||||||
am.power2 -= bias;
|
am.power2 -= bias;
|
||||||
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
constexpr uint64_t exponent_mask = 0x7FF0000000000000;
|
|
||||||
constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF;
|
|
||||||
constexpr uint64_t hidden_bit_mask = 0x0010000000000000;
|
|
||||||
uint64_t bits;
|
|
||||||
::memcpy(&bits, &value, sizeof(T));
|
|
||||||
if ((bits & exponent_mask) == 0) {
|
|
||||||
// denormal
|
|
||||||
am.power2 = 1 - bias;
|
|
||||||
am.mantissa = bits & mantissa_mask;
|
|
||||||
} else {
|
|
||||||
// normal
|
|
||||||
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
|
|
||||||
am.power2 -= bias;
|
|
||||||
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return am;
|
return am;
|
||||||
}
|
}
|
||||||
@ -87,7 +78,8 @@ fastfloat_really_inline adjusted_mantissa 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 adjusted_mantissa to_extended_halfway(T value) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
adjusted_mantissa to_extended_halfway(T 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;
|
||||||
@ -97,12 +89,13 @@ fastfloat_really_inline adjusted_mantissa 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 void round(adjusted_mantissa& am, callback cb) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
|
void round(adjusted_mantissa& am, callback cb) noexcept {
|
||||||
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
|
int32_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;
|
int32_t shift = -am.power2 + 1;
|
||||||
cb(am, std::min(shift, 64));
|
cb(am, std::min<int32_t>(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 < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
|
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
|
||||||
return;
|
return;
|
||||||
@ -126,23 +119,19 @@ fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename callback>
|
template <typename callback>
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
|
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
|
||||||
uint64_t mask;
|
const uint64_t mask
|
||||||
uint64_t halfway;
|
= (shift == 64)
|
||||||
if (shift == 64) {
|
? UINT64_MAX
|
||||||
mask = UINT64_MAX;
|
: (uint64_t(1) << shift) - 1;
|
||||||
} else {
|
const uint64_t halfway
|
||||||
mask = (uint64_t(1) << shift) - 1;
|
= (shift == 0)
|
||||||
}
|
? 0
|
||||||
if (shift == 0) {
|
: uint64_t(1) << (shift - 1);
|
||||||
halfway = 0;
|
|
||||||
} else {
|
|
||||||
halfway = uint64_t(1) << (shift - 1);
|
|
||||||
}
|
|
||||||
uint64_t truncated_bits = am.mantissa & mask;
|
uint64_t truncated_bits = am.mantissa & mask;
|
||||||
uint64_t is_above = truncated_bits > halfway;
|
bool is_above = truncated_bits > halfway;
|
||||||
uint64_t is_halfway = truncated_bits == halfway;
|
bool is_halfway = truncated_bits == halfway;
|
||||||
|
|
||||||
// shift digits into position
|
// shift digits into position
|
||||||
if (shift == 64) {
|
if (shift == 64) {
|
||||||
@ -156,7 +145,8 @@ void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) n
|
|||||||
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
|
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
|
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
|
||||||
if (shift == 64) {
|
if (shift == 64) {
|
||||||
am.mantissa = 0;
|
am.mantissa = 0;
|
||||||
} else {
|
} else {
|
||||||
@ -164,18 +154,19 @@ fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) no
|
|||||||
}
|
}
|
||||||
am.power2 += shift;
|
am.power2 += shift;
|
||||||
}
|
}
|
||||||
|
template <typename UC>
|
||||||
fastfloat_really_inline void skip_zeros(const char*& first, const char* last) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
void skip_zeros(UC const * & first, UC const * last) noexcept {
|
||||||
uint64_t val;
|
uint64_t val;
|
||||||
while (std::distance(first, last) >= 8) {
|
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||||
::memcpy(&val, first, sizeof(uint64_t));
|
::memcpy(&val, first, sizeof(uint64_t));
|
||||||
if (val != 0x3030303030303030) {
|
if (val != int_cmp_zeros<UC>()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
first += 8;
|
first += int_cmp_len<UC>();
|
||||||
}
|
}
|
||||||
while (first != last) {
|
while (first != last) {
|
||||||
if (*first != '0') {
|
if (*first != UC('0')) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
first++;
|
first++;
|
||||||
@ -184,52 +175,58 @@ fastfloat_really_inline void skip_zeros(const char*& first, const char* last) no
|
|||||||
|
|
||||||
// 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.
|
||||||
fastfloat_really_inline bool is_truncated(const char* first, const char* last) noexcept {
|
template <typename UC>
|
||||||
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
bool 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;
|
uint64_t val;
|
||||||
while (std::distance(first, last) >= 8) {
|
while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len<UC>()) {
|
||||||
::memcpy(&val, first, sizeof(uint64_t));
|
::memcpy(&val, first, sizeof(uint64_t));
|
||||||
if (val != 0x3030303030303030) {
|
if (val != int_cmp_zeros<UC>()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
first += 8;
|
first += int_cmp_len<UC>();
|
||||||
}
|
}
|
||||||
while (first != last) {
|
while (first != last) {
|
||||||
if (*first != '0') {
|
if (*first != UC('0')) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
first++;
|
++first;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
template <typename UC>
|
||||||
fastfloat_really_inline bool is_truncated(byte_span s) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
bool is_truncated(span<UC> s) noexcept {
|
||||||
return is_truncated(s.ptr, s.ptr + s.len());
|
return is_truncated(s.ptr, s.ptr + s.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
template <typename UC>
|
||||||
void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
void parse_eight_digits(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept {
|
||||||
value = value * 100000000 + parse_eight_digits_unrolled(p);
|
value = value * 100000000 + parse_eight_digits_unrolled(p);
|
||||||
p += 8;
|
p += 8;
|
||||||
counter += 8;
|
counter += 8;
|
||||||
count += 8;
|
count += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
template <typename UC>
|
||||||
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
value = value * 10 + limb(*p - '0');
|
void parse_one_digit(UC const *& p, limb& value, size_t& counter, size_t& count) noexcept {
|
||||||
|
value = value * 10 + limb(*p - UC('0'));
|
||||||
p++;
|
p++;
|
||||||
counter++;
|
counter++;
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
void add_native(bigint& big, limb power, limb value) noexcept {
|
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 void round_up_bigint(bigint& big, size_t& count) noexcept {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
void round_up_bigint(bigint& big, size_t& 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);
|
||||||
@ -237,7 +234,9 @@ fastfloat_really_inline void round_up_bigint(bigint& big, size_t& count) noexcep
|
|||||||
}
|
}
|
||||||
|
|
||||||
// parse the significant digits into a big integer
|
// parse the significant digits into a big integer
|
||||||
inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max_digits, size_t& digits) noexcept {
|
template <typename UC>
|
||||||
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
|
void parse_mantissa(bigint& result, parsed_number_string_t<UC>& num, 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.
|
||||||
@ -251,8 +250,8 @@ inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// process all integer digits.
|
// process all integer digits.
|
||||||
const char* p = num.integer.ptr;
|
UC const * p = num.integer.ptr;
|
||||||
const char* pend = p + num.integer.len();
|
UC const * pend = p + num.integer.len();
|
||||||
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) {
|
||||||
@ -317,7 +316,8 @@ inline void parse_mantissa(bigint& result, parsed_number_string& num, size_t max
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
|
adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent) noexcept {
|
||||||
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
|
FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent)));
|
||||||
adjusted_mantissa answer;
|
adjusted_mantissa answer;
|
||||||
bool truncated;
|
bool truncated;
|
||||||
@ -340,7 +340,8 @@ inline adjusted_mantissa positive_digit_comp(bigint& bigmant, int32_t exponent)
|
|||||||
// 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 adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
|
adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa am, int32_t exponent) noexcept {
|
||||||
bigint& real_digits = bigmant;
|
bigint& real_digits = bigmant;
|
||||||
int32_t real_exp = exponent;
|
int32_t real_exp = exponent;
|
||||||
|
|
||||||
@ -399,8 +400,9 @@ inline adjusted_mantissa negative_digit_comp(bigint& bigmant, adjusted_mantissa
|
|||||||
// `b` as a big-integer type, scaled to the same binary exponent as
|
// `b` as a big-integer type, scaled to the same binary exponent as
|
||||||
// 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>
|
template <typename T, typename UC>
|
||||||
inline adjusted_mantissa digit_comp(parsed_number_string& num, adjusted_mantissa am) noexcept {
|
inline FASTFLOAT_CONSTEXPR20
|
||||||
|
adjusted_mantissa digit_comp(parsed_number_string_t<UC>& 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;
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "constexpr_feature_detect.h"
|
||||||
|
|
||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
enum chars_format {
|
enum chars_format {
|
||||||
scientific = 1<<0,
|
scientific = 1<<0,
|
||||||
@ -11,22 +13,25 @@ enum chars_format {
|
|||||||
general = fixed | scientific
|
general = fixed | scientific
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename UC>
|
||||||
struct from_chars_result {
|
struct from_chars_result_t {
|
||||||
const char *ptr;
|
UC const * ptr;
|
||||||
std::errc ec;
|
std::errc ec;
|
||||||
};
|
};
|
||||||
|
using from_chars_result = from_chars_result_t<char>;
|
||||||
|
|
||||||
struct parse_options {
|
template <typename UC>
|
||||||
constexpr explicit parse_options(chars_format fmt = chars_format::general,
|
struct parse_options_t {
|
||||||
char dot = '.')
|
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
|
||||||
|
UC dot = UC('.'))
|
||||||
: format(fmt), decimal_point(dot) {}
|
: format(fmt), decimal_point(dot) {}
|
||||||
|
|
||||||
/** Which number formats are accepted */
|
/** Which number formats are accepted */
|
||||||
chars_format format;
|
chars_format format;
|
||||||
/** The character used as decimal point */
|
/** The character used as decimal point */
|
||||||
char decimal_point;
|
UC decimal_point;
|
||||||
};
|
};
|
||||||
|
using parse_options = parse_options_t<char>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
|
* This function parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
|
||||||
@ -44,20 +49,22 @@ struct parse_options {
|
|||||||
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
|
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
|
||||||
* the type `fast_float::chars_format`. It is a bitset value: we check whether
|
* the type `fast_float::chars_format`. It is a bitset value: we check whether
|
||||||
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
|
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
|
||||||
* to determine whether we allowe the fixed point and scientific notation respectively.
|
* to determine whether we allow the fixed point and scientific notation respectively.
|
||||||
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
|
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T, typename UC = char>
|
||||||
from_chars_result from_chars(const char *first, const char *last,
|
FASTFLOAT_CONSTEXPR20
|
||||||
|
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
|
||||||
T &value, chars_format fmt = chars_format::general) noexcept;
|
T &value, chars_format 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.
|
||||||
*/
|
*/
|
||||||
template<typename T>
|
template<typename T, typename UC = char>
|
||||||
from_chars_result from_chars_advanced(const char *first, const char *last,
|
FASTFLOAT_CONSTEXPR20
|
||||||
T &value, parse_options options) noexcept;
|
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||||
|
T &value, parse_options_t<UC> options) noexcept;
|
||||||
|
|
||||||
}
|
} // namespace fast_float
|
||||||
#include "parse_number.h"
|
#include "parse_number.h"
|
||||||
#endif // FASTFLOAT_FAST_FLOAT_H
|
#endif // FASTFLOAT_FAST_FLOAT_H
|
||||||
|
|||||||
@ -17,11 +17,11 @@ namespace fast_float {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The smallest non-zero float (binary64) is 2^−1074.
|
* The smallest non-zero float (binary64) is 2^-1074.
|
||||||
* We take as input numbers of the form w x 10^q where w < 2^64.
|
* We take as input numbers of the form w x 10^q where w < 2^64.
|
||||||
* We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
|
* We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076.
|
||||||
* However, we have that
|
* However, we have that
|
||||||
* (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074.
|
* (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074.
|
||||||
* Thus it is possible for a number of the form w * 10^-342 where
|
* Thus it is possible for a number of the form w * 10^-342 where
|
||||||
* w is a 64-bit value to be a non-zero floating-point number.
|
* w is a 64-bit value to be a non-zero floating-point number.
|
||||||
*********
|
*********
|
||||||
@ -36,11 +36,7 @@ constexpr static int smallest_power_of_five = binary_format<double>::smallest_po
|
|||||||
constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
|
constexpr static int largest_power_of_five = binary_format<double>::largest_power_of_ten();
|
||||||
constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1);
|
constexpr static int number_of_entries = 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.
|
||||||
static const uint64_t power_of_five_128[number_of_entries];
|
constexpr static uint64_t power_of_five_128[number_of_entries] = {
|
||||||
};
|
|
||||||
|
|
||||||
template <class unused>
|
|
||||||
const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
|
|
||||||
0xeef453d6923bd65a,0x113faa2906a13b3f,
|
0xeef453d6923bd65a,0x113faa2906a13b3f,
|
||||||
0x9558b4661b6565f8,0x4ac7ca59a424c507,
|
0x9558b4661b6565f8,0x4ac7ca59a424c507,
|
||||||
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
|
0xbaaee17fa23ebf76,0x5d79bcf00d2df649,
|
||||||
@ -692,8 +688,13 @@ const uint64_t powers_template<unused>::power_of_five_128[number_of_entries] = {
|
|||||||
0xb6472e511c81471d,0xe0133fe4adf8e952,
|
0xb6472e511c81471d,0xe0133fe4adf8e952,
|
||||||
0xe3d8f9e563a198e5,0x58180fddd97723a6,
|
0xe3d8f9e563a198e5,0x58180fddd97723a6,
|
||||||
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
|
0x8e679c2f5e44ff8f,0x570f09eaa7ea7648,};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class unused>
|
||||||
|
constexpr uint64_t powers_template<unused>::power_of_five_128[number_of_entries];
|
||||||
|
|
||||||
using powers = powers_template<>;
|
using powers = powers_template<>;
|
||||||
|
|
||||||
}
|
} // namespace fast_float
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -5,18 +5,22 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#if FASTFLOAT_HAS_BIT_CAST
|
||||||
|
#include <bit>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|
||||||
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|
||||||
|| defined(__MINGW64__) \
|
|| defined(__MINGW64__) \
|
||||||
|| defined(__s390x__) \
|
|| defined(__s390x__) \
|
||||||
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
|
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) )
|
||||||
|| defined(__EMSCRIPTEN__))
|
#define FASTFLOAT_64BIT 1
|
||||||
#define FASTFLOAT_64BIT
|
|
||||||
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|
||||||
|| defined(__arm__) || defined(_M_ARM) \
|
|| defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \
|
||||||
|| defined(__MINGW32__))
|
|| defined(__MINGW32__) || defined(__EMSCRIPTEN__))
|
||||||
#define FASTFLOAT_32BIT
|
#define FASTFLOAT_32BIT 1
|
||||||
#else
|
#else
|
||||||
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
|
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
|
||||||
// We can never tell the register width, but the SIZE_MAX is a good approximation.
|
// We can never tell the register width, but the SIZE_MAX is a good approximation.
|
||||||
@ -24,9 +28,9 @@
|
|||||||
#if SIZE_MAX == 0xffff
|
#if SIZE_MAX == 0xffff
|
||||||
#error Unknown platform (16-bit, unsupported)
|
#error Unknown platform (16-bit, unsupported)
|
||||||
#elif SIZE_MAX == 0xffffffff
|
#elif SIZE_MAX == 0xffffffff
|
||||||
#define FASTFLOAT_32BIT
|
#define FASTFLOAT_32BIT 1
|
||||||
#elif SIZE_MAX == 0xffffffffffffffff
|
#elif SIZE_MAX == 0xffffffffffffffff
|
||||||
#define FASTFLOAT_64BIT
|
#define FASTFLOAT_64BIT 1
|
||||||
#else
|
#else
|
||||||
#error Unknown platform (not 32-bit, not 64-bit?)
|
#error Unknown platform (not 32-bit, not 64-bit?)
|
||||||
#endif
|
#endif
|
||||||
@ -40,7 +44,9 @@
|
|||||||
#define FASTFLOAT_VISUAL_STUDIO 1
|
#define FASTFLOAT_VISUAL_STUDIO 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
|
||||||
|
#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||||
|
#elif defined _WIN32
|
||||||
#define FASTFLOAT_IS_BIG_ENDIAN 0
|
#define FASTFLOAT_IS_BIG_ENDIAN 0
|
||||||
#else
|
#else
|
||||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||||
@ -48,7 +54,11 @@
|
|||||||
#elif defined(sun) || defined(__sun)
|
#elif defined(sun) || defined(__sun)
|
||||||
#include <sys/byteorder.h>
|
#include <sys/byteorder.h>
|
||||||
#else
|
#else
|
||||||
|
#ifdef __has_include
|
||||||
|
#if __has_include(<endian.h>)
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
|
#endif //__has_include(<endian.h>)
|
||||||
|
#endif //__has_include
|
||||||
#endif
|
#endif
|
||||||
#
|
#
|
||||||
#ifndef __BYTE_ORDER__
|
#ifndef __BYTE_ORDER__
|
||||||
@ -75,12 +85,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FASTFLOAT_ASSERT
|
#ifndef FASTFLOAT_ASSERT
|
||||||
#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); }
|
#define FASTFLOAT_ASSERT(x) { ((void)(x)); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FASTFLOAT_DEBUG_ASSERT
|
#ifndef FASTFLOAT_DEBUG_ASSERT
|
||||||
#include <cassert>
|
#define FASTFLOAT_DEBUG_ASSERT(x) { ((void)(x)); }
|
||||||
#define FASTFLOAT_DEBUG_ASSERT(x) assert(x)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// rust style `try!()` macro, or `?` operator
|
// rust style `try!()` macro, or `?` operator
|
||||||
@ -88,12 +97,21 @@
|
|||||||
|
|
||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
|
fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
|
||||||
|
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED
|
||||||
|
return std::is_constant_evaluated();
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Compares two ASCII strings in a case insensitive manner.
|
// Compares two ASCII strings in a case insensitive manner.
|
||||||
inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
|
template <typename UC>
|
||||||
size_t length) {
|
inline FASTFLOAT_CONSTEXPR14 bool
|
||||||
|
fastfloat_strncasecmp(UC const * input1, UC const * input2, size_t length) {
|
||||||
char running_diff{0};
|
char running_diff{0};
|
||||||
for (size_t i = 0; i < length; i++) {
|
for (size_t i = 0; i < length; ++i) {
|
||||||
running_diff |= (input1[i] ^ input2[i]);
|
running_diff |= (char(input1[i]) ^ char(input2[i]));
|
||||||
}
|
}
|
||||||
return (running_diff == 0) || (running_diff == 32);
|
return (running_diff == 0) || (running_diff == 32);
|
||||||
}
|
}
|
||||||
@ -107,14 +125,14 @@ template <typename T>
|
|||||||
struct span {
|
struct span {
|
||||||
const T* ptr;
|
const T* ptr;
|
||||||
size_t length;
|
size_t length;
|
||||||
span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
||||||
span() : ptr(nullptr), length(0) {}
|
constexpr span() : ptr(nullptr), length(0) {}
|
||||||
|
|
||||||
constexpr size_t len() const noexcept {
|
constexpr size_t len() const noexcept {
|
||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
const T& operator[](size_t index) const noexcept {
|
FASTFLOAT_CONSTEXPR14 const T& operator[](size_t index) const noexcept {
|
||||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||||
return ptr[index];
|
return ptr[index];
|
||||||
}
|
}
|
||||||
@ -123,13 +141,31 @@ struct span {
|
|||||||
struct value128 {
|
struct value128 {
|
||||||
uint64_t low;
|
uint64_t low;
|
||||||
uint64_t high;
|
uint64_t high;
|
||||||
value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
|
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
|
||||||
value128() : low(0), high(0) {}
|
constexpr value128() : low(0), high(0) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Helper C++11 constexpr generic implementation of leading_zeroes */
|
||||||
|
fastfloat_really_inline constexpr
|
||||||
|
int leading_zeroes_generic(uint64_t input_num, int last_bit = 0) {
|
||||||
|
return (
|
||||||
|
((input_num & uint64_t(0xffffffff00000000)) && (input_num >>= 32, last_bit |= 32)),
|
||||||
|
((input_num & uint64_t( 0xffff0000)) && (input_num >>= 16, last_bit |= 16)),
|
||||||
|
((input_num & uint64_t( 0xff00)) && (input_num >>= 8, last_bit |= 8)),
|
||||||
|
((input_num & uint64_t( 0xf0)) && (input_num >>= 4, last_bit |= 4)),
|
||||||
|
((input_num & uint64_t( 0xc)) && (input_num >>= 2, last_bit |= 2)),
|
||||||
|
((input_num & uint64_t( 0x2)) && (input_num >>= 1, last_bit |= 1)),
|
||||||
|
63 - last_bit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/* result might be undefined when input_num is zero */
|
/* result might be undefined when input_num is zero */
|
||||||
fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
int leading_zeroes(uint64_t input_num) {
|
||||||
assert(input_num > 0);
|
assert(input_num > 0);
|
||||||
|
if (cpp20_and_in_constexpr()) {
|
||||||
|
return leading_zeroes_generic(input_num);
|
||||||
|
}
|
||||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||||
#if defined(_M_X64) || defined(_M_ARM64)
|
#if defined(_M_X64) || defined(_M_ARM64)
|
||||||
unsigned long leading_zero = 0;
|
unsigned long leading_zero = 0;
|
||||||
@ -138,31 +174,20 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
|
|||||||
_BitScanReverse64(&leading_zero, input_num);
|
_BitScanReverse64(&leading_zero, input_num);
|
||||||
return (int)(63 - leading_zero);
|
return (int)(63 - leading_zero);
|
||||||
#else
|
#else
|
||||||
int last_bit = 0;
|
return leading_zeroes_generic(input_num);
|
||||||
if(input_num & uint64_t(0xffffffff00000000)) input_num >>= 32, last_bit |= 32;
|
|
||||||
if(input_num & uint64_t( 0xffff0000)) input_num >>= 16, last_bit |= 16;
|
|
||||||
if(input_num & uint64_t( 0xff00)) input_num >>= 8, last_bit |= 8;
|
|
||||||
if(input_num & uint64_t( 0xf0)) input_num >>= 4, last_bit |= 4;
|
|
||||||
if(input_num & uint64_t( 0xc)) input_num >>= 2, last_bit |= 2;
|
|
||||||
if(input_num & uint64_t( 0x2)) input_num >>= 1, last_bit |= 1;
|
|
||||||
return 63 - last_bit;
|
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
return __builtin_clzll(input_num);
|
return __builtin_clzll(input_num);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FASTFLOAT_32BIT
|
|
||||||
|
|
||||||
// slow emulation routine for 32-bit
|
// slow emulation routine for 32-bit
|
||||||
fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
|
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
|
||||||
return x * (uint64_t)y;
|
return x * (uint64_t)y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// slow emulation routine for 32-bit
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
#if !defined(__MINGW64__)
|
uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
||||||
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
|
|
||||||
uint64_t *hi) {
|
|
||||||
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
|
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
|
||||||
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
|
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
|
||||||
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
|
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
|
||||||
@ -172,17 +197,32 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
|
|||||||
(adbc_carry << 32) + !!(lo < bd);
|
(adbc_carry << 32) + !!(lo < bd);
|
||||||
return lo;
|
return lo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FASTFLOAT_32BIT
|
||||||
|
|
||||||
|
// slow emulation routine for 32-bit
|
||||||
|
#if !defined(__MINGW64__)
|
||||||
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
|
uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) {
|
||||||
|
return umul128_generic(ab, cd, hi);
|
||||||
|
}
|
||||||
#endif // !__MINGW64__
|
#endif // !__MINGW64__
|
||||||
|
|
||||||
#endif // FASTFLOAT_32BIT
|
#endif // FASTFLOAT_32BIT
|
||||||
|
|
||||||
|
|
||||||
// compute 64-bit a*b
|
// compute 64-bit a*b
|
||||||
fastfloat_really_inline value128 full_multiplication(uint64_t a,
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
uint64_t b) {
|
value128 full_multiplication(uint64_t a, uint64_t b) {
|
||||||
|
if (cpp20_and_in_constexpr()) {
|
||||||
value128 answer;
|
value128 answer;
|
||||||
#ifdef _M_ARM64
|
answer.low = umul128_generic(a, b, &answer.high);
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
value128 answer;
|
||||||
|
#if defined(_M_ARM64) && !defined(__MINGW32__)
|
||||||
// ARM64 has native support for 64-bit multiplications, no need to emulate
|
// ARM64 has native support for 64-bit multiplications, no need to emulate
|
||||||
|
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
|
||||||
answer.high = __umulh(a, b);
|
answer.high = __umulh(a, b);
|
||||||
answer.low = a * b;
|
answer.low = a * b;
|
||||||
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
|
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
|
||||||
@ -192,7 +232,7 @@ fastfloat_really_inline value128 full_multiplication(uint64_t a,
|
|||||||
answer.low = uint64_t(r);
|
answer.low = uint64_t(r);
|
||||||
answer.high = uint64_t(r >> 64);
|
answer.high = uint64_t(r >> 64);
|
||||||
#else
|
#else
|
||||||
#error Not implemented
|
answer.low = umul128_generic(a, b, &answer.high);
|
||||||
#endif
|
#endif
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
@ -201,10 +241,10 @@ struct adjusted_mantissa {
|
|||||||
uint64_t mantissa{0};
|
uint64_t mantissa{0};
|
||||||
int32_t power2{0}; // a negative value indicates an invalid result
|
int32_t power2{0}; // a negative value indicates an invalid result
|
||||||
adjusted_mantissa() = default;
|
adjusted_mantissa() = default;
|
||||||
bool operator==(const adjusted_mantissa &o) const {
|
constexpr bool operator==(const adjusted_mantissa &o) const {
|
||||||
return mantissa == o.mantissa && power2 == o.power2;
|
return mantissa == o.mantissa && power2 == o.power2;
|
||||||
}
|
}
|
||||||
bool operator!=(const adjusted_mantissa &o) const {
|
constexpr bool operator!=(const adjusted_mantissa &o) const {
|
||||||
return mantissa != o.mantissa || power2 != o.power2;
|
return mantissa != o.mantissa || power2 != o.power2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -215,25 +255,91 @@ constexpr static int32_t invalid_am_bias = -0x8000;
|
|||||||
constexpr static double powers_of_ten_double[] = {
|
constexpr static double powers_of_ten_double[] = {
|
||||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
|
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
|
||||||
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
|
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
|
||||||
constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5,
|
constexpr static float powers_of_ten_float[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
|
||||||
1e6, 1e7, 1e8, 1e9, 1e10};
|
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
|
||||||
|
// used for max_mantissa_double and max_mantissa_float
|
||||||
|
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
|
||||||
|
// Largest integer value v so that (5**index * v) <= 1<<53.
|
||||||
|
// 0x10000000000000 == 1 << 53
|
||||||
|
constexpr static uint64_t max_mantissa_double[] = {
|
||||||
|
0x10000000000000,
|
||||||
|
0x10000000000000 / 5,
|
||||||
|
0x10000000000000 / (5 * 5),
|
||||||
|
0x10000000000000 / (5 * 5 * 5),
|
||||||
|
0x10000000000000 / (5 * 5 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555),
|
||||||
|
0x10000000000000 / (constant_55555 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * 5 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||||
|
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)};
|
||||||
|
// Largest integer value v so that (5**index * v) <= 1<<24.
|
||||||
|
// 0x1000000 == 1<<24
|
||||||
|
constexpr static uint64_t max_mantissa_float[] = {
|
||||||
|
0x1000000,
|
||||||
|
0x1000000 / 5,
|
||||||
|
0x1000000 / (5 * 5),
|
||||||
|
0x1000000 / (5 * 5 * 5),
|
||||||
|
0x1000000 / (5 * 5 * 5 * 5),
|
||||||
|
0x1000000 / (constant_55555),
|
||||||
|
0x1000000 / (constant_55555 * 5),
|
||||||
|
0x1000000 / (constant_55555 * 5 * 5),
|
||||||
|
0x1000000 / (constant_55555 * 5 * 5 * 5),
|
||||||
|
0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
|
||||||
|
0x1000000 / (constant_55555 * constant_55555),
|
||||||
|
0x1000000 / (constant_55555 * constant_55555 * 5)};
|
||||||
|
|
||||||
template <typename T> struct binary_format {
|
template <typename T> struct binary_format {
|
||||||
|
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
|
||||||
|
|
||||||
static inline constexpr int mantissa_explicit_bits();
|
static inline constexpr int mantissa_explicit_bits();
|
||||||
static inline constexpr int minimum_exponent();
|
static inline constexpr int minimum_exponent();
|
||||||
static inline constexpr int infinite_power();
|
static inline constexpr int infinite_power();
|
||||||
static inline constexpr int sign_index();
|
static inline constexpr int sign_index();
|
||||||
static inline constexpr int min_exponent_fast_path();
|
static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||||
static inline constexpr int max_exponent_fast_path();
|
static inline constexpr int max_exponent_fast_path();
|
||||||
static inline constexpr int max_exponent_round_to_even();
|
static inline constexpr int max_exponent_round_to_even();
|
||||||
static inline constexpr int min_exponent_round_to_even();
|
static inline constexpr int min_exponent_round_to_even();
|
||||||
static inline constexpr uint64_t max_mantissa_fast_path();
|
static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
|
||||||
|
static inline constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||||
static inline constexpr int largest_power_of_ten();
|
static inline constexpr int largest_power_of_ten();
|
||||||
static inline constexpr int smallest_power_of_ten();
|
static inline constexpr int smallest_power_of_ten();
|
||||||
static inline constexpr T exact_power_of_ten(int64_t power);
|
static inline constexpr T exact_power_of_ten(int64_t power);
|
||||||
static inline constexpr size_t max_digits();
|
static inline constexpr size_t max_digits();
|
||||||
|
static inline constexpr equiv_uint exponent_mask();
|
||||||
|
static inline constexpr equiv_uint mantissa_mask();
|
||||||
|
static inline constexpr equiv_uint hidden_bit_mask();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
||||||
|
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
return -22;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
||||||
|
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||||
|
return 0;
|
||||||
|
#else
|
||||||
|
return -10;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
|
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
|
||||||
return 52;
|
return 52;
|
||||||
}
|
}
|
||||||
@ -274,34 +380,30 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
|
|||||||
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
|
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
|
||||||
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
|
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
|
||||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return -22;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
|
||||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
|
||||||
return 0;
|
|
||||||
#else
|
|
||||||
return -10;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
|
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
|
||||||
return 22;
|
return 22;
|
||||||
}
|
}
|
||||||
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
|
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
|
||||||
return 10;
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
|
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
|
||||||
return uint64_t(2) << mantissa_explicit_bits();
|
return uint64_t(2) << mantissa_explicit_bits();
|
||||||
}
|
}
|
||||||
|
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path(int64_t power) {
|
||||||
|
// caller is responsible to ensure that
|
||||||
|
// power >= 0 && power <= 22
|
||||||
|
//
|
||||||
|
return max_mantissa_double[power];
|
||||||
|
}
|
||||||
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
||||||
return uint64_t(2) << mantissa_explicit_bits();
|
return uint64_t(2) << mantissa_explicit_bits();
|
||||||
}
|
}
|
||||||
|
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path(int64_t power) {
|
||||||
|
// caller is responsible to ensure that
|
||||||
|
// power >= 0 && power <= 10
|
||||||
|
//
|
||||||
|
return max_mantissa_float[power];
|
||||||
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
|
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
|
||||||
@ -339,24 +441,131 @@ template <> inline constexpr size_t binary_format<float>::max_digits() {
|
|||||||
return 114;
|
return 114;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <> inline constexpr binary_format<float>::equiv_uint
|
||||||
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
|
binary_format<float>::exponent_mask() {
|
||||||
uint64_t word = am.mantissa;
|
return 0x7F800000;
|
||||||
word |= uint64_t(am.power2) << binary_format<T>::mantissa_explicit_bits();
|
|
||||||
word = negative
|
|
||||||
? word | (uint64_t(1) << binary_format<T>::sign_index()) : word;
|
|
||||||
#if FASTFLOAT_IS_BIG_ENDIAN == 1
|
|
||||||
if (std::is_same<T, float>::value) {
|
|
||||||
::memcpy(&value, (char *)&word + 4, sizeof(T)); // extract value at offset 4-7 if float on big-endian
|
|
||||||
} else {
|
|
||||||
::memcpy(&value, &word, sizeof(T));
|
|
||||||
}
|
}
|
||||||
|
template <> inline constexpr binary_format<double>::equiv_uint
|
||||||
|
binary_format<double>::exponent_mask() {
|
||||||
|
return 0x7FF0000000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline constexpr binary_format<float>::equiv_uint
|
||||||
|
binary_format<float>::mantissa_mask() {
|
||||||
|
return 0x007FFFFF;
|
||||||
|
}
|
||||||
|
template <> inline constexpr binary_format<double>::equiv_uint
|
||||||
|
binary_format<double>::mantissa_mask() {
|
||||||
|
return 0x000FFFFFFFFFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline constexpr binary_format<float>::equiv_uint
|
||||||
|
binary_format<float>::hidden_bit_mask() {
|
||||||
|
return 0x00800000;
|
||||||
|
}
|
||||||
|
template <> inline constexpr binary_format<double>::equiv_uint
|
||||||
|
binary_format<double>::hidden_bit_mask() {
|
||||||
|
return 0x0010000000000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR20
|
||||||
|
void to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||||
|
using uint = typename binary_format<T>::equiv_uint;
|
||||||
|
uint word = (uint)am.mantissa;
|
||||||
|
word |= uint(am.power2) << binary_format<T>::mantissa_explicit_bits();
|
||||||
|
word |= uint(negative) << binary_format<T>::sign_index();
|
||||||
|
#if FASTFLOAT_HAS_BIT_CAST
|
||||||
|
value = std::bit_cast<T>(word);
|
||||||
#else
|
#else
|
||||||
// For little-endian systems:
|
|
||||||
::memcpy(&value, &word, sizeof(T));
|
::memcpy(&value, &word, sizeof(T));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||||
|
template <typename = void>
|
||||||
|
struct space_lut {
|
||||||
|
static constexpr bool value[] = {
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool space_lut<T>::value[];
|
||||||
|
|
||||||
|
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr uint64_t int_cmp_zeros()
|
||||||
|
{
|
||||||
|
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size");
|
||||||
|
return (sizeof(UC) == 1) ? 0x3030303030303030 : (sizeof(UC) == 2) ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | uint64_t(UC('0')) << 16 | UC('0')) : (uint64_t(UC('0')) << 32 | UC('0'));
|
||||||
|
}
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr int int_cmp_len()
|
||||||
|
{
|
||||||
|
return sizeof(uint64_t) / sizeof(UC);
|
||||||
|
}
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr UC const * str_const_nan()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char const * str_const_nan<char>()
|
||||||
|
{
|
||||||
|
return "nan";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr wchar_t const * str_const_nan<wchar_t>()
|
||||||
|
{
|
||||||
|
return L"nan";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char16_t const * str_const_nan<char16_t>()
|
||||||
|
{
|
||||||
|
return u"nan";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char32_t const * str_const_nan<char32_t>()
|
||||||
|
{
|
||||||
|
return U"nan";
|
||||||
|
}
|
||||||
|
template<typename UC>
|
||||||
|
static constexpr UC const * str_const_inf()
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char const * str_const_inf<char>()
|
||||||
|
{
|
||||||
|
return "infinity";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr wchar_t const * str_const_inf<wchar_t>()
|
||||||
|
{
|
||||||
|
return L"infinity";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char16_t const * str_const_inf<char16_t>()
|
||||||
|
{
|
||||||
|
return u"infinity";
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
constexpr char32_t const * str_const_inf<char32_t>()
|
||||||
|
{
|
||||||
|
return U"infinity";
|
||||||
|
}
|
||||||
} // namespace fast_float
|
} // namespace fast_float
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -19,35 +19,41 @@ namespace detail {
|
|||||||
* 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
|
||||||
* strings a null-free and fixed.
|
* strings a null-free and fixed.
|
||||||
**/
|
**/
|
||||||
template <typename T>
|
template <typename T, typename UC>
|
||||||
from_chars_result parse_infnan(const char *first, const char *last, T &value) noexcept {
|
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14
|
||||||
from_chars_result answer;
|
parse_infnan(UC const * first, UC const * last, T &value) noexcept {
|
||||||
|
from_chars_result_t<UC> answer{};
|
||||||
answer.ptr = first;
|
answer.ptr = first;
|
||||||
answer.ec = std::errc(); // be optimistic
|
answer.ec = std::errc(); // be optimistic
|
||||||
bool minusSign = false;
|
bool minusSign = false;
|
||||||
if (*first == '-') { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
|
if (*first == UC('-')) { // assume first < last, so dereference without checks; C++17 20.19.3.(7.1) explicitly forbids '+' here
|
||||||
minusSign = true;
|
minusSign = true;
|
||||||
++first;
|
++first;
|
||||||
}
|
}
|
||||||
|
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||||
|
if (*first == UC('+')) {
|
||||||
|
++first;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (last - first >= 3) {
|
if (last - first >= 3) {
|
||||||
if (fastfloat_strncasecmp(first, "nan", 3)) {
|
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
|
||||||
answer.ptr = (first += 3);
|
answer.ptr = (first += 3);
|
||||||
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
|
value = minusSign ? -std::numeric_limits<T>::quiet_NaN() : std::numeric_limits<T>::quiet_NaN();
|
||||||
// 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).
|
// 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).
|
||||||
if(first != last && *first == '(') {
|
if(first != last && *first == UC('(')) {
|
||||||
for(const char* ptr = first + 1; ptr != last; ++ptr) {
|
for(UC const * ptr = first + 1; ptr != last; ++ptr) {
|
||||||
if (*ptr == ')') {
|
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;
|
||||||
}
|
}
|
||||||
else if(!(('a' <= *ptr && *ptr <= 'z') || ('A' <= *ptr && *ptr <= 'Z') || ('0' <= *ptr && *ptr <= '9') || *ptr == '_'))
|
else if(!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_')))
|
||||||
break; // forbidden char, not nan(n-char-seq-opt)
|
break; // forbidden char, not nan(n-char-seq-opt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
if (fastfloat_strncasecmp(first, "inf", 3)) {
|
if (fastfloat_strncasecmp(first, str_const_inf<UC>(), 3)) {
|
||||||
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, "inity", 5)) {
|
if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf<UC>() + 3, 5)) {
|
||||||
answer.ptr = first + 8;
|
answer.ptr = first + 8;
|
||||||
} else {
|
} else {
|
||||||
answer.ptr = first + 3;
|
answer.ptr = first + 3;
|
||||||
@ -60,41 +66,147 @@ from_chars_result parse_infnan(const char *first, const char *last, T &value) n
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace detail
|
/**
|
||||||
|
* Returns true if the floating-pointing rounding mode is to 'nearest'.
|
||||||
template<typename T>
|
* It is the default on most system. This function is meant to be inexpensive.
|
||||||
from_chars_result from_chars(const char *first, const char *last,
|
* Credit : @mwalcott3
|
||||||
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
|
*/
|
||||||
return from_chars_advanced(first, last, value, parse_options{fmt});
|
fastfloat_really_inline bool rounds_to_nearest() noexcept {
|
||||||
|
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/
|
||||||
|
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
// See
|
||||||
|
// A fast function to check your floating-point rounding mode
|
||||||
|
// https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
|
||||||
|
//
|
||||||
|
// This function is meant to be equivalent to :
|
||||||
|
// prior: #include <cfenv>
|
||||||
|
// return fegetround() == FE_TONEAREST;
|
||||||
|
// However, it is expected to be much faster than the fegetround()
|
||||||
|
// function call.
|
||||||
|
//
|
||||||
|
// The volatile keywoard prevents the compiler from computing the function
|
||||||
|
// at compile-time.
|
||||||
|
// There might be other ways to prevent compile-time optimizations (e.g., 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 excess
|
||||||
|
// precision, as in 387 instructions).
|
||||||
|
static volatile float fmin = std::numeric_limits<float>::min();
|
||||||
|
float fmini = fmin; // we copy it so that it gets loaded at most once.
|
||||||
|
//
|
||||||
|
// Explanation:
|
||||||
|
// Only when fegetround() == FE_TONEAREST do we have that
|
||||||
|
// fmin + 1.0f == 1.0f - fmin.
|
||||||
|
//
|
||||||
|
// FE_UPWARD:
|
||||||
|
// fmin + 1.0f > 1
|
||||||
|
// 1.0f - fmin == 1
|
||||||
|
//
|
||||||
|
// FE_DOWNWARD or FE_TOWARDZERO:
|
||||||
|
// fmin + 1.0f == 1
|
||||||
|
// 1.0f - fmin < 1
|
||||||
|
//
|
||||||
|
// Note: This may fail to be accurate if fast-math has been
|
||||||
|
// enabled, as rounding conventions may not apply.
|
||||||
|
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||||
|
# pragma warning(push)
|
||||||
|
// todo: is there a VS warning?
|
||||||
|
// see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
|
||||||
|
#elif defined(__clang__)
|
||||||
|
# pragma clang diagnostic push
|
||||||
|
# pragma clang diagnostic ignored "-Wfloat-equal"
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic push
|
||||||
|
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||||
|
#endif
|
||||||
|
return (fmini + 1.0f == 1.0f - fmini);
|
||||||
|
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||||
|
# pragma warning(pop)
|
||||||
|
#elif defined(__clang__)
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
} // namespace detail
|
||||||
from_chars_result from_chars_advanced(const char *first, const char *last,
|
|
||||||
T &value, parse_options options) noexcept {
|
template<typename T, typename UC>
|
||||||
|
FASTFLOAT_CONSTEXPR20
|
||||||
|
from_chars_result_t<UC> from_chars(UC const * first, UC const * last,
|
||||||
|
T &value, chars_format fmt /*= chars_format::general*/) noexcept {
|
||||||
|
return from_chars_advanced(first, last, value, parse_options_t<UC>{fmt});
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T, typename UC>
|
||||||
|
FASTFLOAT_CONSTEXPR20
|
||||||
|
from_chars_result_t<UC> from_chars_advanced(UC const * first, UC const * last,
|
||||||
|
T &value, parse_options_t<UC> options) noexcept {
|
||||||
|
|
||||||
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
|
static_assert (std::is_same<T, double>::value || std::is_same<T, float>::value, "only float and double are supported");
|
||||||
|
static_assert (std::is_same<UC, char>::value ||
|
||||||
|
std::is_same<UC, wchar_t>::value ||
|
||||||
|
std::is_same<UC, char16_t>::value ||
|
||||||
|
std::is_same<UC, char32_t>::value , "only char, wchar_t, char16_t and char32_t are supported");
|
||||||
|
|
||||||
|
from_chars_result_t<UC> answer;
|
||||||
from_chars_result answer;
|
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||||
|
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
|
||||||
|
first++;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
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;
|
||||||
}
|
}
|
||||||
parsed_number_string pns = parse_number_string(first, last, options);
|
parsed_number_string_t<UC> pns = parse_number_string<UC>(first, last, options);
|
||||||
if (!pns.valid) {
|
if (!pns.valid) {
|
||||||
return detail::parse_infnan(first, last, value);
|
return detail::parse_infnan(first, last, value);
|
||||||
}
|
}
|
||||||
answer.ec = std::errc(); // be optimistic
|
answer.ec = std::errc(); // be optimistic
|
||||||
answer.ptr = pns.lastmatch;
|
answer.ptr = pns.lastmatch;
|
||||||
|
// The implementation of the Clinger's fast path is convoluted because
|
||||||
|
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
||||||
|
// selected on the thread.
|
||||||
|
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
|
||||||
|
// true.
|
||||||
|
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
|
||||||
|
// Unfortunately, the conventional Clinger's fast path is only possible
|
||||||
|
// when the system rounds to the nearest float.
|
||||||
|
//
|
||||||
|
// We expect the next branch to almost always be selected.
|
||||||
|
// We could check it first (before the previous branch), but
|
||||||
|
// there might be performance advantages at having the check
|
||||||
|
// be last.
|
||||||
|
if(!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
|
||||||
|
// We have that fegetround() == FE_TONEAREST.
|
||||||
// Next is Clinger's fast path.
|
// Next is Clinger's fast path.
|
||||||
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) {
|
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
|
||||||
value = T(pns.mantissa);
|
value = T(pns.mantissa);
|
||||||
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
|
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
|
||||||
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
|
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
|
||||||
if (pns.negative) { value = -value; }
|
if (pns.negative) { value = -value; }
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// We do not have that fegetround() == FE_TONEAREST.
|
||||||
|
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
|
||||||
|
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
|
||||||
|
#if defined(__clang__)
|
||||||
|
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
|
||||||
|
if(pns.mantissa == 0) {
|
||||||
|
value = pns.negative ? -0. : 0.;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||||
|
if (pns.negative) { value = -value; }
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
|
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
|
||||||
if(pns.too_many_digits && am.power2 >= 0) {
|
if(pns.too_many_digits && am.power2 >= 0) {
|
||||||
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
|
if(am != compute_float<binary_format<T>>(pns.exponent, pns.mantissa + 1)) {
|
||||||
@ -105,6 +217,10 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
|
|||||||
// then we need to go the long way around again. This is very uncommon.
|
// then we need to go the long way around again. This is very uncommon.
|
||||||
if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
|
if(am.power2 < 0) { am = digit_comp<T>(pns, am); }
|
||||||
to_float(pns.negative, am, value);
|
to_float(pns.negative, am, value);
|
||||||
|
// Test for over/underflow.
|
||||||
|
if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format<T>::infinite_power()) {
|
||||||
|
answer.ec = std::errc::result_out_of_range;
|
||||||
|
}
|
||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,46 +3,79 @@ processed_files = { }
|
|||||||
|
|
||||||
# authors
|
# authors
|
||||||
for filename in ['AUTHORS', 'CONTRIBUTORS']:
|
for filename in ['AUTHORS', 'CONTRIBUTORS']:
|
||||||
with open(filename) as f:
|
with open(filename, encoding='utf8') as f:
|
||||||
text = ''
|
text = ''
|
||||||
for line in f:
|
for line in f:
|
||||||
if filename == 'AUTHORS':
|
if filename == 'AUTHORS':
|
||||||
text += '// fast_float by ' + line
|
text += '// fast_float by ' + line
|
||||||
if filename == 'CONTRIBUTORS':
|
if filename == 'CONTRIBUTORS':
|
||||||
text += '// with contributions from ' + line
|
text += '// with contributions from ' + line
|
||||||
processed_files[filename] = text
|
processed_files[filename] = text + '//\n'
|
||||||
|
|
||||||
# licenses
|
# licenses
|
||||||
for filename in ['LICENSE-MIT', 'LICENSE-APACHE']:
|
for filename in ['LICENSE-MIT', 'LICENSE-APACHE']:
|
||||||
with open(filename) as f:
|
lines = []
|
||||||
|
with open(filename, encoding='utf8') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
# Retrieve subset required for inclusion in source
|
||||||
|
if filename == 'LICENSE-APACHE':
|
||||||
|
lines = [
|
||||||
|
' Copyright 2021 The fast_float authors\n',
|
||||||
|
*lines[179:-1]
|
||||||
|
]
|
||||||
|
|
||||||
text = ''
|
text = ''
|
||||||
for line in f:
|
for line in lines:
|
||||||
text += '// ' + line
|
text += '// ' + line.strip() + '\n'
|
||||||
processed_files[filename] = text
|
processed_files[filename] = text
|
||||||
|
|
||||||
# code
|
# code
|
||||||
for filename in [ 'fast_float.h', 'float_common.h', 'ascii_number.h',
|
for filename in [ 'constexpr_feature_detect.h', 'fast_float.h', 'float_common.h', 'ascii_number.h',
|
||||||
'fast_table.h', 'decimal_to_binary.h', 'bigint.h',
|
'fast_table.h', 'decimal_to_binary.h', 'bigint.h',
|
||||||
'ascii_number.h', 'digit_comparison.h', 'parse_number.h']:
|
'ascii_number.h', 'digit_comparison.h', 'parse_number.h']:
|
||||||
with open('include/fast_float/' + filename) as f:
|
with open('include/fast_float/' + filename, encoding='utf8') as f:
|
||||||
text = ''
|
text = ''
|
||||||
for line in f:
|
for line in f:
|
||||||
if line.startswith('#include "'): continue
|
if line.startswith('#include "'): continue
|
||||||
text += line
|
text += line
|
||||||
processed_files[filename] = text
|
processed_files[filename] = '\n' + text
|
||||||
|
|
||||||
# command line
|
# command line
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(description='Amalgamate fast_float.')
|
parser = argparse.ArgumentParser(description='Amalgamate fast_float.')
|
||||||
parser.add_argument('--license', default='MIT', help='choose license')
|
parser.add_argument('--license', default='DUAL', choices=['DUAL', 'MIT', 'APACHE'], help='choose license')
|
||||||
parser.add_argument('--output', default='', help='output file (stdout if none')
|
parser.add_argument('--output', default='', help='output file (stdout if none')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
text = '\n\n'.join([
|
def license_content(license_arg):
|
||||||
|
result = []
|
||||||
|
|
||||||
|
if license_arg == 'DUAL':
|
||||||
|
result += [
|
||||||
|
'// Licensed under the Apache License, Version 2.0, or the\n',
|
||||||
|
'// MIT License at your option. This file may not be copied,\n',
|
||||||
|
'// modified, or distributed except according to those terms.\n',
|
||||||
|
'//\n'
|
||||||
|
]
|
||||||
|
|
||||||
|
if license_arg in ('DUAL', 'MIT'):
|
||||||
|
result.append('// MIT License Notice\n//\n')
|
||||||
|
result.append(processed_files['LICENSE-MIT'])
|
||||||
|
result.append('//\n')
|
||||||
|
if license_arg in ('DUAL', 'APACHE'):
|
||||||
|
result.append('// Apache License (Version 2.0) Notice\n//\n')
|
||||||
|
result.append(processed_files['LICENSE-APACHE'])
|
||||||
|
result.append('//\n')
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
text = ''.join([
|
||||||
processed_files['AUTHORS'], processed_files['CONTRIBUTORS'],
|
processed_files['AUTHORS'], processed_files['CONTRIBUTORS'],
|
||||||
processed_files['LICENSE-' + args.license],
|
*license_content(args.license),
|
||||||
|
processed_files['constexpr_feature_detect.h'],
|
||||||
processed_files['fast_float.h'], processed_files['float_common.h'],
|
processed_files['fast_float.h'], processed_files['float_common.h'],
|
||||||
processed_files['ascii_number.h'], processed_files['fast_table.h'],
|
processed_files['ascii_number.h'], processed_files['fast_table.h'],
|
||||||
processed_files['decimal_to_binary.h'], processed_files['bigint.h'],
|
processed_files['decimal_to_binary.h'], processed_files['bigint.h'],
|
||||||
@ -50,7 +83,7 @@ text = '\n\n'.join([
|
|||||||
processed_files['parse_number.h']])
|
processed_files['parse_number.h']])
|
||||||
|
|
||||||
if args.output:
|
if args.output:
|
||||||
with open(args.output, 'wt') as f:
|
with open(args.output, 'wt', encoding='utf8') as f:
|
||||||
f.write(text)
|
f.write(text)
|
||||||
else:
|
else:
|
||||||
print(text)
|
print(text)
|
||||||
|
|||||||
75
script/mushtak_lemire.py
Normal file
75
script/mushtak_lemire.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#
|
||||||
|
# Reference :
|
||||||
|
# Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear)
|
||||||
|
#
|
||||||
|
|
||||||
|
all_tqs = []
|
||||||
|
|
||||||
|
# Generates all possible values of T[q]
|
||||||
|
# Appendix B of Number parsing at a gigabyte per second.
|
||||||
|
# Software: Practice and Experience 2021;51(8):1700–1727.
|
||||||
|
for q in range(-342, -27):
|
||||||
|
power5 = 5**-q
|
||||||
|
z = 0
|
||||||
|
while (1 << z) < power5:
|
||||||
|
z += 1
|
||||||
|
b = 2 * z + 2 * 64
|
||||||
|
c = 2**b // power5 + 1
|
||||||
|
while c >= (1 << 128):
|
||||||
|
c //= 2
|
||||||
|
all_tqs.append(c)
|
||||||
|
for q in range(-27, 0):
|
||||||
|
power5 = 5**-q
|
||||||
|
z = 0
|
||||||
|
while (1 << z) < power5:
|
||||||
|
z += 1
|
||||||
|
b = z + 127
|
||||||
|
c = 2**b // power5 + 1
|
||||||
|
all_tqs.append(c)
|
||||||
|
for q in range(0, 308 + 1):
|
||||||
|
power5 = 5**q
|
||||||
|
while power5 < (1 << 127):
|
||||||
|
power5 *= 2
|
||||||
|
while power5 >= (1 << 128):
|
||||||
|
power5 //= 2
|
||||||
|
all_tqs.append(power5)
|
||||||
|
|
||||||
|
# Returns the continued fraction of numer/denom as a list [a0; a1, a2, ..., an]
|
||||||
|
def continued_fraction(numer, denom):
|
||||||
|
# (look at page numbers in top-left, not PDF page numbers)
|
||||||
|
cf = []
|
||||||
|
while denom != 0:
|
||||||
|
quot, rem = divmod(numer, denom)
|
||||||
|
cf.append(quot)
|
||||||
|
numer, denom = denom, rem
|
||||||
|
return cf
|
||||||
|
|
||||||
|
# Given a continued fraction [a0; a1, a2, ..., an], returns
|
||||||
|
# all the convergents of that continued fraction
|
||||||
|
# as pairs of the form (numer, denom), where numer/denom is
|
||||||
|
# a convergent of the continued fraction in simple form.
|
||||||
|
def convergents(cf):
|
||||||
|
p_n_minus_2 = 0
|
||||||
|
q_n_minus_2 = 1
|
||||||
|
p_n_minus_1 = 1
|
||||||
|
q_n_minus_1 = 0
|
||||||
|
convergents = []
|
||||||
|
for a_n in cf:
|
||||||
|
p_n = a_n * p_n_minus_1 + p_n_minus_2
|
||||||
|
q_n = a_n * q_n_minus_1 + q_n_minus_2
|
||||||
|
convergents.append((p_n, q_n))
|
||||||
|
p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = p_n_minus_1, q_n_minus_1, p_n, q_n
|
||||||
|
return convergents
|
||||||
|
|
||||||
|
|
||||||
|
# Enumerate through all the convergents of T[q] / 2^137 with denominators < 2^64
|
||||||
|
found_solution = False
|
||||||
|
for j, tq in enumerate(all_tqs):
|
||||||
|
for _, w in convergents(continued_fraction(tq, 2**137)):
|
||||||
|
if w >= 2**64:
|
||||||
|
break
|
||||||
|
if (tq*w) % 2**137 > 2**137 - 2**64:
|
||||||
|
print(f"SOLUTION: q={j-342} T[q]={tq} w={w}")
|
||||||
|
found_solution = True
|
||||||
|
if not found_solution:
|
||||||
|
print("No solutions!")
|
||||||
@ -38,7 +38,11 @@ add_library(supplemental-data INTERFACE)
|
|||||||
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
|
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
|
||||||
function(fast_float_add_cpp_test TEST_NAME)
|
function(fast_float_add_cpp_test TEST_NAME)
|
||||||
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
|
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
|
||||||
add_test(${TEST_NAME} ${TEST_NAME})
|
if(CMAKE_CROSSCOMPILING)
|
||||||
|
set(ccemulator ${CMAKE_CROSSCOMPILING_EMULATOR})
|
||||||
|
endif()
|
||||||
|
add_test(NAME ${TEST_NAME}
|
||||||
|
COMMAND ${ccemulator} $<TARGET_FILE:${TEST_NAME}>)
|
||||||
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||||
target_compile_options(${TEST_NAME} PUBLIC /EHsc)
|
target_compile_options(${TEST_NAME} PUBLIC /EHsc)
|
||||||
endif()
|
endif()
|
||||||
@ -52,10 +56,18 @@ function(fast_float_add_cpp_test TEST_NAME)
|
|||||||
endif()
|
endif()
|
||||||
endfunction(fast_float_add_cpp_test)
|
endfunction(fast_float_add_cpp_test)
|
||||||
|
|
||||||
|
fast_float_add_cpp_test(rcppfastfloat_test)
|
||||||
fast_float_add_cpp_test(example_test)
|
fast_float_add_cpp_test(example_test)
|
||||||
fast_float_add_cpp_test(example_comma_test)
|
fast_float_add_cpp_test(example_comma_test)
|
||||||
fast_float_add_cpp_test(basictest)
|
fast_float_add_cpp_test(basictest)
|
||||||
|
option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF)
|
||||||
|
if (FASTFLOAT_CONSTEXPR_TESTS)
|
||||||
|
target_compile_features(basictest PRIVATE cxx_std_20)
|
||||||
|
target_compile_definitions(basictest PRIVATE FASTFLOAT_CONSTEXPR_TESTS)
|
||||||
|
else()
|
||||||
|
target_compile_features(basictest PRIVATE cxx_std_17)
|
||||||
|
endif()
|
||||||
|
|
||||||
fast_float_add_cpp_test(long_test)
|
fast_float_add_cpp_test(long_test)
|
||||||
fast_float_add_cpp_test(powersoffive_hardround)
|
fast_float_add_cpp_test(powersoffive_hardround)
|
||||||
fast_float_add_cpp_test(string_test)
|
fast_float_add_cpp_test(string_test)
|
||||||
@ -77,3 +89,4 @@ if (FASTFLOAT_EXHAUSTIVE)
|
|||||||
endif(FASTFLOAT_EXHAUSTIVE)
|
endif(FASTFLOAT_EXHAUSTIVE)
|
||||||
|
|
||||||
add_subdirectory(build_tests)
|
add_subdirectory(build_tests)
|
||||||
|
add_subdirectory(bloat_analysis)
|
||||||
|
|||||||
@ -10,6 +10,18 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cfenv>
|
||||||
|
|
||||||
|
#if FASTFLOAT_IS_CONSTEXPR
|
||||||
|
#ifndef FASTFLOAT_CONSTEXPR_TESTS
|
||||||
|
#define FASTFLOAT_CONSTEXPR_TESTS 1
|
||||||
|
#endif // #ifndef FASTFLOAT_CONSTEXPR_TESTS
|
||||||
|
#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/"
|
||||||
@ -42,6 +54,198 @@
|
|||||||
#define FASTFLOAT_ODDPLATFORM 1
|
#define FASTFLOAT_ODDPLATFORM 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"
|
||||||
|
#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")"
|
||||||
|
|
||||||
|
|
||||||
|
const char * round_name(int d) {
|
||||||
|
switch(d) {
|
||||||
|
case FE_UPWARD:
|
||||||
|
return "FE_UPWARD";
|
||||||
|
case FE_DOWNWARD:
|
||||||
|
return "FE_DOWNWARD";
|
||||||
|
case FE_TOWARDZERO:
|
||||||
|
return "FE_TOWARDZERO";
|
||||||
|
case FE_TONEAREST:
|
||||||
|
return "FE_TONEAREST";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define FASTFLOAT_STR(x) #x
|
||||||
|
#define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))
|
||||||
|
|
||||||
|
TEST_CASE("system_info") {
|
||||||
|
std::cout << "system info:" << std::endl;
|
||||||
|
#ifdef FASTFLOAT_CONSTEXPR_TESTS
|
||||||
|
SHOW_DEFINE(FASTFLOAT_CONSTEXPR_TESTS);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
SHOW_DEFINE(_MSC_VER);
|
||||||
|
#endif
|
||||||
|
#ifdef FASTFLOAT_64BIT_LIMB
|
||||||
|
SHOW_DEFINE(FASTFLOAT_64BIT_LIMB);
|
||||||
|
#endif
|
||||||
|
#ifdef __clang__
|
||||||
|
SHOW_DEFINE(__clang__);
|
||||||
|
#endif
|
||||||
|
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||||
|
SHOW_DEFINE(FASTFLOAT_VISUAL_STUDIO);
|
||||||
|
#endif
|
||||||
|
#ifdef FASTFLOAT_IS_BIG_ENDIAN
|
||||||
|
#if FASTFLOAT_IS_BIG_ENDIAN
|
||||||
|
printf("big endian\n");
|
||||||
|
#else
|
||||||
|
printf("little endian\n");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifdef FASTFLOAT_32BIT
|
||||||
|
SHOW_DEFINE(FASTFLOAT_32BIT);
|
||||||
|
#endif
|
||||||
|
#ifdef FASTFLOAT_64BIT
|
||||||
|
SHOW_DEFINE(FASTFLOAT_64BIT);
|
||||||
|
#endif
|
||||||
|
#ifdef FLT_EVAL_METHOD
|
||||||
|
SHOW_DEFINE(FLT_EVAL_METHOD);
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
SHOW_DEFINE(_WIN32);
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN64
|
||||||
|
SHOW_DEFINE(_WIN64);
|
||||||
|
#endif
|
||||||
|
std::cout << "fegetround() = " << round_name(fegetround()) << std::endl;
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TEST_CASE("rounds_to_nearest") {
|
||||||
|
//
|
||||||
|
// If this function fails, we may be left in a non-standard rounding state.
|
||||||
|
//
|
||||||
|
static volatile float fmin = std::numeric_limits<float>::min();
|
||||||
|
fesetround(FE_UPWARD);
|
||||||
|
std::cout << "FE_UPWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||||
|
CHECK(fegetround() == FE_UPWARD);
|
||||||
|
CHECK(fast_float::detail::rounds_to_nearest() == false);
|
||||||
|
|
||||||
|
fesetround(FE_DOWNWARD);
|
||||||
|
std::cout << "FE_DOWNWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||||
|
CHECK(fegetround() == FE_DOWNWARD);
|
||||||
|
CHECK(fast_float::detail::rounds_to_nearest() == false);
|
||||||
|
|
||||||
|
fesetround(FE_TOWARDZERO);
|
||||||
|
std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||||
|
CHECK(fegetround() == FE_TOWARDZERO);
|
||||||
|
CHECK(fast_float::detail::rounds_to_nearest() == false);
|
||||||
|
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
|
std::cout << "FE_TONEAREST: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||||
|
CHECK(fegetround() == FE_TONEAREST);
|
||||||
|
#if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0)
|
||||||
|
CHECK(fast_float::detail::rounds_to_nearest() == true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("parse_zero") {
|
||||||
|
//
|
||||||
|
// If this function fails, we may be left in a non-standard rounding state.
|
||||||
|
//
|
||||||
|
const char * zero = "0";
|
||||||
|
uint64_t float64_parsed;
|
||||||
|
double f = 0;
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
CHECK(float64_parsed == 0);
|
||||||
|
|
||||||
|
fesetround(FE_UPWARD);
|
||||||
|
auto r1 = fast_float::from_chars(zero, zero + 1, f);
|
||||||
|
CHECK(r1.ec == std::errc());
|
||||||
|
std::cout << "FE_UPWARD parsed zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0);
|
||||||
|
|
||||||
|
fesetround(FE_TOWARDZERO);
|
||||||
|
auto r2 = fast_float::from_chars(zero, zero + 1, f);
|
||||||
|
CHECK(r2.ec == std::errc());
|
||||||
|
std::cout << "FE_TOWARDZERO parsed zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0);
|
||||||
|
|
||||||
|
fesetround(FE_DOWNWARD);
|
||||||
|
auto r3 = fast_float::from_chars(zero, zero + 1, f);
|
||||||
|
CHECK(r3.ec == std::errc());
|
||||||
|
std::cout << "FE_DOWNWARD parsed zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0);
|
||||||
|
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
|
auto r4 = fast_float::from_chars(zero, zero + 1, f);
|
||||||
|
CHECK(r4.ec == std::errc());
|
||||||
|
std::cout << "FE_TONEAREST parsed zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("parse_negative_zero") {
|
||||||
|
//
|
||||||
|
// If this function fails, we may be left in a non-standard rounding state.
|
||||||
|
//
|
||||||
|
const char * negative_zero = "-0";
|
||||||
|
uint64_t float64_parsed;
|
||||||
|
double f = -0.;
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
CHECK(float64_parsed == 0x8000'0000'0000'0000);
|
||||||
|
|
||||||
|
fesetround(FE_UPWARD);
|
||||||
|
auto r1 = fast_float::from_chars(negative_zero, negative_zero + 2, f);
|
||||||
|
CHECK(r1.ec == std::errc());
|
||||||
|
std::cout << "FE_UPWARD parsed negative zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0x8000'0000'0000'0000);
|
||||||
|
|
||||||
|
fesetround(FE_TOWARDZERO);
|
||||||
|
auto r2 = fast_float::from_chars(negative_zero, negative_zero + 2, f);
|
||||||
|
CHECK(r2.ec == std::errc());
|
||||||
|
std::cout << "FE_TOWARDZERO parsed negative zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0x8000'0000'0000'0000);
|
||||||
|
|
||||||
|
fesetround(FE_DOWNWARD);
|
||||||
|
auto r3 = fast_float::from_chars(negative_zero, negative_zero + 2, f);
|
||||||
|
CHECK(r3.ec == std::errc());
|
||||||
|
std::cout << "FE_DOWNWARD parsed negative zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0x8000'0000'0000'0000);
|
||||||
|
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
|
auto r4 = fast_float::from_chars(negative_zero, negative_zero + 2, f);
|
||||||
|
CHECK(r4.ec == std::errc());
|
||||||
|
std::cout << "FE_TONEAREST parsed negative zero as " << iHexAndDec(f) << std::endl;
|
||||||
|
CHECK(f == 0);
|
||||||
|
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||||
|
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||||
|
CHECK(float64_parsed == 0x8000'0000'0000'0000);
|
||||||
|
}
|
||||||
|
|
||||||
// C++ 17 because it is otherwise annoying to browse all files in a directory.
|
// C++ 17 because it is otherwise annoying to browse all files in a directory.
|
||||||
// We also only run these tests on little endian systems.
|
// We also only run these tests on little endian systems.
|
||||||
#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM)
|
#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM)
|
||||||
@ -50,9 +254,16 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
|
|
||||||
// return true on succcess
|
|
||||||
|
|
||||||
|
// return true on success
|
||||||
bool check_file(std::string file_name) {
|
bool check_file(std::string file_name) {
|
||||||
std::cout << "Checking " << file_name << std::endl;
|
std::cout << "Checking " << file_name << std::endl;
|
||||||
|
// We check all rounding directions, for each file.
|
||||||
|
std::vector<int> directions = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST};
|
||||||
|
for (int d : directions) {
|
||||||
|
std::cout << "fesetround to " << round_name(d) << std::endl;
|
||||||
|
fesetround(d);
|
||||||
size_t number{0};
|
size_t number{0};
|
||||||
std::fstream newfile(file_name, std::ios::in);
|
std::fstream newfile(file_name, std::ios::in);
|
||||||
if (newfile.is_open()) {
|
if (newfile.is_open()) {
|
||||||
@ -75,11 +286,11 @@ bool check_file(std::string file_name) {
|
|||||||
// Parse as 32-bit float
|
// Parse as 32-bit float
|
||||||
float parsed_32;
|
float parsed_32;
|
||||||
auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32);
|
auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32);
|
||||||
if(fast_float_r32.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
|
if(fast_float_r32.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) {std::cerr << "32-bit fast_float parsing failure for: " + str + "\n"; return false; }
|
||||||
// Parse as 64-bit float
|
// Parse as 64-bit float
|
||||||
double parsed_64;
|
double parsed_64;
|
||||||
auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64);
|
auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64);
|
||||||
if(fast_float_r64.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
|
if(fast_float_r64.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) { std::cerr << "64-bit fast_float parsing failure: " + str + "\n"; return false; }
|
||||||
// Convert the floats to unsigned ints.
|
// Convert the floats to unsigned ints.
|
||||||
uint32_t float32_parsed;
|
uint32_t float32_parsed;
|
||||||
uint64_t float64_parsed;
|
uint64_t float64_parsed;
|
||||||
@ -88,10 +299,18 @@ bool check_file(std::string file_name) {
|
|||||||
// Compare with expected results
|
// Compare with expected results
|
||||||
if (float32_parsed != float32) {
|
if (float32_parsed != float32) {
|
||||||
std::cout << "bad 32 " << str << std::endl;
|
std::cout << "bad 32 " << str << std::endl;
|
||||||
|
std::cout << "parsed as " << iHexAndDec(parsed_32) << std::endl;
|
||||||
|
std::cout << "as raw uint32_t, parsed = " << float32_parsed << ", expected = " << float32 << std::endl;
|
||||||
|
std::cout << "fesetround: " << round_name(d) << std::endl;
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (float64_parsed != float64) {
|
if (float64_parsed != float64) {
|
||||||
std::cout << "bad 64 " << str << std::endl;
|
std::cout << "bad 64 " << str << std::endl;
|
||||||
|
std::cout << "parsed as " << iHexAndDec(parsed_64) << std::endl;
|
||||||
|
std::cout << "as raw uint64_t, parsed = " << float64_parsed << ", expected = " << float64 << std::endl;
|
||||||
|
std::cout << "fesetround: " << round_name(d) << std::endl;
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
number++;
|
number++;
|
||||||
@ -101,8 +320,11 @@ bool check_file(std::string file_name) {
|
|||||||
newfile.close(); // close the file object
|
newfile.close(); // close the file object
|
||||||
} else {
|
} else {
|
||||||
std::cout << "Could not read " << file_name << std::endl;
|
std::cout << "Could not read " << file_name << std::endl;
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,9 +347,6 @@ TEST_CASE("leading_zeroes") {
|
|||||||
CHECK(fast_float::leading_zeroes(bit << 63) == 0);
|
CHECK(fast_float::leading_zeroes(bit << 63) == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"
|
|
||||||
#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")"
|
|
||||||
|
|
||||||
void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) {
|
void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) {
|
||||||
fast_float::value128 v;
|
fast_float::value128 v;
|
||||||
v = fast_float::full_multiplication(lhs, rhs);
|
v = fast_float::full_multiplication(lhs, rhs);
|
||||||
@ -224,6 +443,33 @@ TEST_CASE("decimal_point_parsing") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("issue19") {
|
||||||
|
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
|
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 234532.3426362\n");
|
||||||
|
CHECK_MESSAGE(answer.ptr == input.data() + 14,
|
||||||
|
"Parsed the number " << result
|
||||||
|
<< " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters");
|
||||||
|
CHECK_MESSAGE(result == 234532.3426362, "We want to parse234532.3426362\n");
|
||||||
|
CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n");
|
||||||
|
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 7869234.9823\n");
|
||||||
|
CHECK_MESSAGE(answer.ptr == input.data() + 27,
|
||||||
|
"Parsed the number " << result
|
||||||
|
<< " and stopped at the wrong character " << (answer.ptr - input.data()));
|
||||||
|
CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n");
|
||||||
|
CHECK_MESSAGE(result == 7869234.9823, "We want to parse up 7869234.9823\n");
|
||||||
|
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 324562.645\n");
|
||||||
|
CHECK_MESSAGE(answer.ptr == input.data() + 38,
|
||||||
|
"Parsed the number " << result
|
||||||
|
<< " and stopped at the wrong character " << (answer.ptr - input.data()));
|
||||||
|
CHECK_MESSAGE(result == 324562.645, "We want to parse up 7869234.9823\n");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("issue19") {
|
TEST_CASE("issue19") {
|
||||||
const std::string input = "3.14e";
|
const std::string input = "3.14e";
|
||||||
double result;
|
double result;
|
||||||
@ -263,6 +509,9 @@ TEST_CASE("test_fixed_only") {
|
|||||||
|
|
||||||
|
|
||||||
static const double testing_power_of_ten[] = {
|
static const double testing_power_of_ten[] = {
|
||||||
|
1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316, 1e-315,
|
||||||
|
1e-314, 1e-313, 1e-312, 1e-311, 1e-310, 1e-309, 1e-308,
|
||||||
|
|
||||||
1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299,
|
1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299,
|
||||||
1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290,
|
1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290,
|
||||||
1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281,
|
1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281,
|
||||||
@ -346,8 +595,9 @@ TEST_CASE("powers_of_ten") {
|
|||||||
REQUIRE(n < sizeof(buf)); // if false, fails the test and exits
|
REQUIRE(n < sizeof(buf)); // if false, fails the test and exits
|
||||||
double actual;
|
double actual;
|
||||||
auto result = fast_float::from_chars(buf, buf + 1000, actual);
|
auto result = fast_float::from_chars(buf, buf + 1000, actual);
|
||||||
CHECK_MESSAGE(result.ec == std::errc(), " I could not parse " << buf);
|
double expected = ((i >= -323) ? testing_power_of_ten[i + 323] : std::pow(10, i));
|
||||||
double expected = ((i >= -307) ? testing_power_of_ten[i + 307] : std::pow(10, i));
|
auto expected_ec = (i < -323 || i > 308) ? std::errc::result_out_of_range : std::errc();
|
||||||
|
CHECK_MESSAGE(result.ec == expected_ec, " I could not parse " << buf);
|
||||||
CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual);
|
CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -388,53 +638,173 @@ std::string append_zeros(std::string str, size_t number_of_zeros) {
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
namespace {
|
||||||
void check_basic_test_result(const std::string& str, fast_float::from_chars_result result,
|
|
||||||
T actual, T expected) {
|
enum class Diag { runtime, comptime };
|
||||||
INFO("str=" << str << "\n"
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
template <Diag diag, class T, typename result_type, typename stringtype>
|
||||||
|
constexpr void check_basic_test_result(stringtype str,
|
||||||
|
result_type result,
|
||||||
|
T actual, T expected, std::errc expected_ec) {
|
||||||
|
if constexpr (diag == Diag::runtime) {
|
||||||
|
INFO(
|
||||||
|
"str=" << str << "\n"
|
||||||
<< " expected=" << fHexAndDec(expected) << "\n"
|
<< " expected=" << fHexAndDec(expected) << "\n"
|
||||||
<< " ..actual=" << fHexAndDec(actual) << "\n"
|
<< " ..actual=" << fHexAndDec(actual) << "\n"
|
||||||
<< " expected mantissa=" << iHexAndDec(get_mantissa(expected)) << "\n"
|
<< " expected mantissa=" << iHexAndDec(get_mantissa(expected))
|
||||||
|
<< "\n"
|
||||||
<< " ..actual mantissa=" << iHexAndDec(get_mantissa(actual)));
|
<< " ..actual mantissa=" << iHexAndDec(get_mantissa(actual)));
|
||||||
CHECK_EQ(result.ec, std::errc());
|
|
||||||
CHECK_EQ(result.ptr, str.data() + str.size());
|
|
||||||
CHECK_EQ(copysign(1, actual), copysign(1, expected));
|
|
||||||
CHECK_EQ(std::isnan(actual), std::isnan(expected));
|
|
||||||
CHECK_EQ(actual, expected);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
struct ComptimeDiag {
|
||||||
void basic_test(std::string str, T expected) {
|
// Purposely not constexpr
|
||||||
|
static void error_not_equal() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define FASTFLOAT_CHECK_EQ(...) \
|
||||||
|
if constexpr (diag == Diag::runtime) { \
|
||||||
|
CHECK_EQ(__VA_ARGS__); \
|
||||||
|
} else { \
|
||||||
|
if ([](const auto &lhs, const auto &rhs) { \
|
||||||
|
return lhs != rhs; \
|
||||||
|
}(__VA_ARGS__)) { \
|
||||||
|
ComptimeDiag::error_not_equal(); \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
auto copysign = [](double x, double y) -> double {
|
||||||
|
#if FASTFLOAT_HAS_BIT_CAST
|
||||||
|
if (fast_float::cpp20_and_in_constexpr()) {
|
||||||
|
using equiv_int = std::make_signed_t<
|
||||||
|
typename fast_float::binary_format<double>::equiv_uint>;
|
||||||
|
const auto i = std::bit_cast<equiv_int>(y);
|
||||||
|
if (i < 0) {
|
||||||
|
return -x;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return std::copysign(x, y);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto isnan = [](double x) -> bool {
|
||||||
|
return x != x;
|
||||||
|
};
|
||||||
|
|
||||||
|
FASTFLOAT_CHECK_EQ(result.ec, expected_ec);
|
||||||
|
FASTFLOAT_CHECK_EQ(result.ptr, str.data() + str.size());
|
||||||
|
FASTFLOAT_CHECK_EQ(copysign(1, actual), copysign(1, expected));
|
||||||
|
FASTFLOAT_CHECK_EQ(isnan(actual), isnan(expected));
|
||||||
|
FASTFLOAT_CHECK_EQ(actual, expected);
|
||||||
|
|
||||||
|
#undef FASTFLOAT_CHECK_EQ
|
||||||
|
}
|
||||||
|
|
||||||
|
// We give plenty of memory: 2048 characters.
|
||||||
|
const size_t global_string_capacity = 2048;
|
||||||
|
std::u16string u16(global_string_capacity, '\0');
|
||||||
|
std::u32string u32(global_string_capacity, '\0');
|
||||||
|
|
||||||
|
template<Diag diag, class T>
|
||||||
|
constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec = std::errc()) {
|
||||||
T actual;
|
T actual;
|
||||||
auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual);
|
auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual);
|
||||||
check_basic_test_result(str, result, actual, expected);
|
check_basic_test_result<diag>(str, result, actual, expected, expected_ec);
|
||||||
|
if(str.size() > global_string_capacity) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
for (size_t i = 0; i < str.size(); i++) {
|
||||||
void basic_test(std::string str, T expected, fast_float::parse_options options) {
|
u16[i] = char16_t(str[i]);
|
||||||
|
}
|
||||||
|
auto result16 = fast_float::from_chars(u16.data(), u16.data() + str.size(), actual);
|
||||||
|
check_basic_test_result<diag>(std::u16string_view(u16.data(), str.size()), result16, actual, expected, expected_ec);
|
||||||
|
|
||||||
|
|
||||||
|
for (size_t i = 0; i < str.size(); i++) {
|
||||||
|
u32[i] = char32_t(str[i]);
|
||||||
|
}
|
||||||
|
auto result32 = fast_float::from_chars(u32.data(), u32.data() + str.size(), actual);
|
||||||
|
check_basic_test_result<diag>(std::u32string_view(u32.data(), str.size()), result32, actual, expected, expected_ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<Diag diag, class T>
|
||||||
|
constexpr void basic_test(std::string_view str, T expected, fast_float::parse_options options) {
|
||||||
T actual;
|
T actual;
|
||||||
auto result = fast_float::from_chars_advanced(str.data(), str.data() + str.size(), actual, options);
|
auto result = fast_float::from_chars_advanced(str.data(), str.data() + str.size(), actual, options);
|
||||||
check_basic_test_result(str, result, actual, expected);
|
check_basic_test_result<diag>(str, result, actual, expected, std::errc());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<Diag diag, class T>
|
||||||
|
constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec, fast_float::parse_options options) {
|
||||||
|
T actual;
|
||||||
|
auto result = fast_float::from_chars_advanced(str.data(), str.data() + str.size(), actual, options);
|
||||||
|
check_basic_test_result<diag>(str, result, actual, expected, expected_ec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void basic_test(float val) {
|
void basic_test(float val) {
|
||||||
{
|
{
|
||||||
std::string long_vals = to_long_string(val);
|
std::string long_vals = to_long_string(val);
|
||||||
INFO("long vals: " << long_vals);
|
INFO("long vals: " << long_vals);
|
||||||
basic_test<float>(long_vals, val);
|
basic_test<Diag::runtime, float>(long_vals, val);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
std::string vals = to_string(val);
|
std::string vals = to_string(val);
|
||||||
INFO("vals: " << vals);
|
INFO("vals: " << vals);
|
||||||
basic_test<float>(vals, val);
|
basic_test<Diag::runtime, float>(vals, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define verify(lhs, rhs) { INFO(lhs); basic_test(lhs, rhs); }
|
#define verify_runtime(...) \
|
||||||
|
do { \
|
||||||
|
basic_test<Diag::runtime>(__VA_ARGS__); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define verify_comptime(...) \
|
||||||
|
do { \
|
||||||
|
constexpr int verify_comptime_var = \
|
||||||
|
(basic_test<Diag::comptime>(__VA_ARGS__), 0); \
|
||||||
|
(void)verify_comptime_var; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define verify_options_runtime(...) \
|
||||||
|
do { \
|
||||||
|
basic_test<Diag::runtime>(__VA_ARGS__, options); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define verify_options_comptime(...) \
|
||||||
|
do { \
|
||||||
|
constexpr int verify_options_comptime_var = \
|
||||||
|
(basic_test<Diag::comptime>(__VA_ARGS__, options), 0); \
|
||||||
|
(void)verify_options_comptime_var; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#if defined(FASTFLOAT_CONSTEXPR_TESTS)
|
||||||
|
#if !FASTFLOAT_IS_CONSTEXPR
|
||||||
|
#error "from_chars must be constexpr for constexpr tests"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define verify(...) \
|
||||||
|
do { \
|
||||||
|
verify_runtime(__VA_ARGS__); \
|
||||||
|
verify_comptime(__VA_ARGS__); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define verify_options(...) \
|
||||||
|
do { \
|
||||||
|
verify_options_runtime(__VA_ARGS__); \
|
||||||
|
verify_options_comptime(__VA_ARGS__); \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define verify verify_runtime
|
||||||
|
#define verify_options verify_options_runtime
|
||||||
|
#endif
|
||||||
|
|
||||||
#define verify32(val) { INFO(#val); basic_test(val); }
|
#define verify32(val) { INFO(#val); basic_test(val); }
|
||||||
|
|
||||||
#define verify_options(lhs, rhs) { INFO(lhs); basic_test(lhs, rhs, options); }
|
|
||||||
|
|
||||||
TEST_CASE("64bit.inf") {
|
TEST_CASE("64bit.inf") {
|
||||||
verify("INF", std::numeric_limits<double>::infinity());
|
verify("INF", std::numeric_limits<double>::infinity());
|
||||||
verify("-INF", -std::numeric_limits<double>::infinity());
|
verify("-INF", -std::numeric_limits<double>::infinity());
|
||||||
@ -444,20 +814,23 @@ TEST_CASE("64bit.inf") {
|
|||||||
verify("-infinity", -std::numeric_limits<double>::infinity());
|
verify("-infinity", -std::numeric_limits<double>::infinity());
|
||||||
verify("inf", std::numeric_limits<double>::infinity());
|
verify("inf", std::numeric_limits<double>::infinity());
|
||||||
verify("-inf", -std::numeric_limits<double>::infinity());
|
verify("-inf", -std::numeric_limits<double>::infinity());
|
||||||
verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits<double>::infinity());
|
verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits<double>::infinity());
|
verify("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("1.8e308", std::numeric_limits<double>::infinity());
|
verify("1.8e308", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits<double>::infinity());
|
verify("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("2e30000000000000000", std::numeric_limits<double>::infinity());
|
verify("2e30000000000000000", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("2e3000", std::numeric_limits<double>::infinity());
|
verify("2e3000", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("1.9e308", std::numeric_limits<double>::infinity());
|
verify("1.9e308", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("64bit.general") {
|
TEST_CASE("64bit.general") {
|
||||||
|
verify("22250738585072012e-324",0x1p-1022); /* limit between normal and subnormal*/
|
||||||
|
verify("-22250738585072012e-324",-0x1p-1022); /* limit between normal and subnormal*/
|
||||||
|
verify("-1e-999", -0.0, std::errc::result_out_of_range);
|
||||||
verify("-2.2222222222223e-322",-0x1.68p-1069);
|
verify("-2.2222222222223e-322",-0x1.68p-1069);
|
||||||
verify("9007199254740993.0", 0x1p+53);
|
verify("9007199254740993.0", 0x1p+53);
|
||||||
verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328);
|
verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328);
|
||||||
verify(append_zeros("9007199254740993.0",1000), 0x1p+53);
|
verify_runtime(append_zeros("9007199254740993.0",1000), 0x1p+53);
|
||||||
verify("10000000000000000000", 0x1.158e460913dp+63);
|
verify("10000000000000000000", 0x1.158e460913dp+63);
|
||||||
verify("10000000000000000000000000000001000000000000", 0x1.cb2d6f618c879p+142);
|
verify("10000000000000000000000000000001000000000000", 0x1.cb2d6f618c879p+142);
|
||||||
verify("10000000000000000000000000000000000000000001", 0x1.cb2d6f618c879p+142);
|
verify("10000000000000000000000000000000000000000001", 0x1.cb2d6f618c879p+142);
|
||||||
@ -509,26 +882,29 @@ TEST_CASE("64bit.general") {
|
|||||||
verify("45035996.273704985", 45035996.273704985);
|
verify("45035996.273704985", 45035996.273704985);
|
||||||
verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375);
|
verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375);
|
||||||
verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375);
|
verify("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375);
|
||||||
verify("1438456663141390273526118207642235581183227845246331231162636653790368152091394196930365828634687637948157940776599182791387527135353034738357134110310609455693900824193549772792016543182680519740580354365467985440183598701312257624545562331397018329928613196125590274187720073914818062530830316533158098624984118889298281371812288789537310599037529113415438738954894752124724983067241108764488346454376699018673078404751121414804937224240805993123816932326223683090770561597570457793932985826162604255884529134126396282202126526253389383421806727954588525596114379801269094096329805054803089299736996870951258573010877404407451953846698609198213926882692078557033228265259305481198526059813164469187586693257335779522020407645498684263339921905227556616698129967412891282231685504660671277927198290009824680186319750978665734576683784255802269708917361719466043175201158849097881370477111850171579869056016061666173029059588433776015644439705050377554277696143928278093453792803846252715966016733222646442382892123940052441346822429721593884378212558701004356924243030059517489346646577724622498919752597382095222500311124181823512251071356181769376577651390028297796156208815375089159128394945710515861334486267101797497111125909272505194792870889617179758703442608016143343262159998149700606597792535574457560429226974273443630323818747730771316763398572110874959981923732463076884528677392654150010269822239401993427482376513231389212353583573566376915572650916866553612366187378959554983566712767093372906030188976220169058025354973622211666504549316958271880975697143546564469806791358707318873075708383345004090151974068325838177531266954177406661392229801349994695941509935655355652985723782153570084089560139142231.738475042362596875449154552392299548947138162081694168675340677843807613129780449323363759027012972466987370921816813162658754726545121090545507240267000456594786540949605260722461937870630634874991729398208026467698131898691830012167897399682179601734569071423681e-733", std::numeric_limits<double>::infinity());
|
verify("1438456663141390273526118207642235581183227845246331231162636653790368152091394196930365828634687637948157940776599182791387527135353034738357134110310609455693900824193549772792016543182680519740580354365467985440183598701312257624545562331397018329928613196125590274187720073914818062530830316533158098624984118889298281371812288789537310599037529113415438738954894752124724983067241108764488346454376699018673078404751121414804937224240805993123816932326223683090770561597570457793932985826162604255884529134126396282202126526253389383421806727954588525596114379801269094096329805054803089299736996870951258573010877404407451953846698609198213926882692078557033228265259305481198526059813164469187586693257335779522020407645498684263339921905227556616698129967412891282231685504660671277927198290009824680186319750978665734576683784255802269708917361719466043175201158849097881370477111850171579869056016061666173029059588433776015644439705050377554277696143928278093453792803846252715966016733222646442382892123940052441346822429721593884378212558701004356924243030059517489346646577724622498919752597382095222500311124181823512251071356181769376577651390028297796156208815375089159128394945710515861334486267101797497111125909272505194792870889617179758703442608016143343262159998149700606597792535574457560429226974273443630323818747730771316763398572110874959981923732463076884528677392654150010269822239401993427482376513231389212353583573566376915572650916866553612366187378959554983566712767093372906030188976220169058025354973622211666504549316958271880975697143546564469806791358707318873075708383345004090151974068325838177531266954177406661392229801349994695941509935655355652985723782153570084089560139142231.738475042362596875449154552392299548947138162081694168675340677843807613129780449323363759027012972466987370921816813162658754726545121090545507240267000456594786540949605260722461937870630634874991729398208026467698131898691830012167897399682179601734569071423681e-733", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("-2240084132271013504.131248280843119943687942846658579428", -0x1.f1660a65b00bfp+60);
|
verify("-2240084132271013504.131248280843119943687942846658579428", -0x1.f1660a65b00bfp+60);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("64bit.decimal_point") {
|
TEST_CASE("64bit.decimal_point") {
|
||||||
fast_float::parse_options options{};
|
constexpr auto options = []{
|
||||||
options.decimal_point = ',';
|
fast_float::parse_options ret{};
|
||||||
|
ret.decimal_point = ',';
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
// infinities
|
// infinities
|
||||||
verify_options("1,8e308", std::numeric_limits<double>::infinity());
|
verify_options("1,8e308", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify_options("1,832312213213213232132132143451234453123412321321312e308", std::numeric_limits<double>::infinity());
|
verify_options("1,832312213213213232132132143451234453123412321321312e308", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify_options("2e30000000000000000", std::numeric_limits<double>::infinity());
|
verify_options("2e30000000000000000", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify_options("2e3000", std::numeric_limits<double>::infinity());
|
verify_options("2e3000", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
verify_options("1,9e308", std::numeric_limits<double>::infinity());
|
verify_options("1,9e308", std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||||
|
|
||||||
// finites
|
// finites
|
||||||
verify_options("-2,2222222222223e-322",-0x1.68p-1069);
|
verify_options("-2,2222222222223e-322",-0x1.68p-1069);
|
||||||
verify_options("9007199254740993,0", 0x1p+53);
|
verify_options("9007199254740993,0", 0x1p+53);
|
||||||
verify_options("860228122,6654514319E+90", 0x1.92bb20990715fp+328);
|
verify_options("860228122,6654514319E+90", 0x1.92bb20990715fp+328);
|
||||||
verify_options(append_zeros("9007199254740993,0",1000), 0x1p+53);
|
verify_options_runtime(append_zeros("9007199254740993,0",1000), 0x1p+53);
|
||||||
verify_options("1,1920928955078125e-07", 1.1920928955078125e-07);
|
verify_options("1,1920928955078125e-07", 1.1920928955078125e-07);
|
||||||
verify_options("9355950000000000000,00000000000000000000000000000000001844674407370955161600000184467440737095516161844674407370955161407370955161618446744073709551616000184467440737095516166000001844674407370955161618446744073709551614073709551616184467440737095516160001844674407370955161601844674407370955674451616184467440737095516140737095516161844674407370955161600018446744073709551616018446744073709551611616000184467440737095001844674407370955161600184467440737095516160018446744073709551168164467440737095516160001844073709551616018446744073709551616184467440737095516160001844674407536910751601611616000184467440737095001844674407370955161600184467440737095516160018446744073709551616184467440737095516160001844955161618446744073709551616000184467440753691075160018446744073709",0x1.03ae05e8fca1cp+63);
|
verify_options("9355950000000000000,00000000000000000000000000000000001844674407370955161600000184467440737095516161844674407370955161407370955161618446744073709551616000184467440737095516166000001844674407370955161618446744073709551614073709551616184467440737095516160001844674407370955161601844674407370955674451616184467440737095516140737095516161844674407370955161600018446744073709551616018446744073709551611616000184467440737095001844674407370955161600184467440737095516160018446744073709551168164467440737095516160001844073709551616018446744073709551616184467440737095516160001844674407536910751601611616000184467440737095001844674407370955161600184467440737095516160018446744073709551616184467440737095516160001844955161618446744073709551616000184467440753691075160018446744073709",0x1.03ae05e8fca1cp+63);
|
||||||
verify_options("2,22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022);
|
verify_options("2,22507385850720212418870147920222032907240528279439037814303133837435107319244194686754406432563881851382188218502438069999947733013005649884107791928741341929297200970481951993067993290969042784064731682041565926728632933630474670123316852983422152744517260835859654566319282835244787787799894310779783833699159288594555213714181128458251145584319223079897504395086859412457230891738946169368372321191373658977977723286698840356390251044443035457396733706583981055420456693824658413747607155981176573877626747665912387199931904006317334709003012790188175203447190250028061277777916798391090578584006464715943810511489154282775041174682194133952466682503431306181587829379004205392375072083366693241580002758391118854188641513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022);
|
||||||
@ -582,24 +958,25 @@ TEST_CASE("32bit.inf") {
|
|||||||
verify("-infinity", -std::numeric_limits<float>::infinity());
|
verify("-infinity", -std::numeric_limits<float>::infinity());
|
||||||
verify("inf", std::numeric_limits<float>::infinity());
|
verify("inf", std::numeric_limits<float>::infinity());
|
||||||
verify("-inf", -std::numeric_limits<float>::infinity());
|
verify("-inf", -std::numeric_limits<float>::infinity());
|
||||||
verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits<float>::infinity());
|
verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits<float>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("2e3000", std::numeric_limits<float>::infinity());
|
verify("2e3000", std::numeric_limits<float>::infinity(), std::errc::result_out_of_range);
|
||||||
verify("3.5028234666e38", std::numeric_limits<float>::infinity());
|
verify("3.5028234666e38", std::numeric_limits<float>::infinity(), std::errc::result_out_of_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("32bit.general") {
|
TEST_CASE("32bit.general") {
|
||||||
|
verify("-1e-999", -0.0f, std::errc::result_out_of_range);
|
||||||
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f);
|
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f);
|
||||||
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f);
|
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f);
|
||||||
verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f);
|
verify_runtime(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f);
|
||||||
verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656), 0x1.2ced3p+0f);
|
verify_runtime(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656), 0x1.2ced3p+0f);
|
||||||
verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000), 0x1.2ced3p+0f);
|
verify_runtime(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000), 0x1.2ced3p+0f);
|
||||||
std::string test_string;
|
std::string test_string;
|
||||||
test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655) + std::string("e-38");
|
test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655) + std::string("e-38");
|
||||||
verify(test_string, 0x1.fffff8p-127f);
|
verify_runtime(test_string, 0x1.fffff8p-127f);
|
||||||
test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656) + std::string("e-38");
|
test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656) + std::string("e-38");
|
||||||
verify(test_string, 0x1.fffff8p-127f);
|
verify_runtime(test_string, 0x1.fffff8p-127f);
|
||||||
test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000) + std::string("e-38");
|
test_string = append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000) + std::string("e-38");
|
||||||
verify(test_string, 0x1.fffff8p-127f);
|
verify_runtime(test_string, 0x1.fffff8p-127f);
|
||||||
verify32(1.00000006e+09f);
|
verify32(1.00000006e+09f);
|
||||||
verify32(1.4012984643e-45f);
|
verify32(1.4012984643e-45f);
|
||||||
verify32(1.1754942107e-38f);
|
verify32(1.1754942107e-38f);
|
||||||
@ -653,7 +1030,7 @@ TEST_CASE("32bit.general") {
|
|||||||
verify("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f);
|
verify("3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f);
|
||||||
verify("2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f);
|
verify("2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f);
|
||||||
verify("1", 1.f);
|
verify("1", 1.f);
|
||||||
verify("7.0060e-46", 0.f);
|
verify("7.0060e-46", 0.f, std::errc::result_out_of_range);
|
||||||
verify("3.4028234664e38", 0x1.fffffep+127f);
|
verify("3.4028234664e38", 0x1.fffffep+127f);
|
||||||
verify("3.4028234665e38", 0x1.fffffep+127f);
|
verify("3.4028234665e38", 0x1.fffffep+127f);
|
||||||
verify("3.4028234666e38", 0x1.fffffep+127f);
|
verify("3.4028234666e38", 0x1.fffffep+127f);
|
||||||
@ -664,25 +1041,28 @@ TEST_CASE("32bit.general") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("32bit.decimal_point") {
|
TEST_CASE("32bit.decimal_point") {
|
||||||
fast_float::parse_options options{};
|
constexpr auto options = [] {
|
||||||
options.decimal_point = ',';
|
fast_float::parse_options ret{};
|
||||||
|
ret.decimal_point = ',';
|
||||||
|
return ret;
|
||||||
|
}();
|
||||||
|
|
||||||
// infinity
|
// infinity
|
||||||
verify_options("3,5028234666e38", std::numeric_limits<float>::infinity());
|
verify_options("3,5028234666e38", std::numeric_limits<float>::infinity(), std::errc::result_out_of_range);
|
||||||
|
|
||||||
// finites
|
// finites
|
||||||
verify_options("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f);
|
verify_options("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f);
|
||||||
verify_options("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f);
|
verify_options("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f);
|
||||||
verify_options(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f);
|
verify_options_runtime(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f);
|
||||||
verify_options(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656), 0x1.2ced3p+0f);
|
verify_options_runtime(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656), 0x1.2ced3p+0f);
|
||||||
verify_options(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000), 0x1.2ced3p+0f);
|
verify_options_runtime(append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000), 0x1.2ced3p+0f);
|
||||||
std::string test_string;
|
std::string test_string;
|
||||||
test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655) + std::string("e-38");
|
test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655) + std::string("e-38");
|
||||||
verify_options(test_string, 0x1.fffff8p-127f);
|
verify_options_runtime(test_string, 0x1.fffff8p-127f);
|
||||||
test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656) + std::string("e-38");
|
test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",656) + std::string("e-38");
|
||||||
verify_options(test_string, 0x1.fffff8p-127f);
|
verify_options_runtime(test_string, 0x1.fffff8p-127f);
|
||||||
test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000) + std::string("e-38");
|
test_string = append_zeros("1,1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",1000) + std::string("e-38");
|
||||||
verify_options(test_string, 0x1.fffff8p-127f);
|
verify_options_runtime(test_string, 0x1.fffff8p-127f);
|
||||||
verify_options("1,1754943508e-38", 1.1754943508e-38f);
|
verify_options("1,1754943508e-38", 1.1754943508e-38f);
|
||||||
verify_options("30219,0830078125", 30219.0830078125f);
|
verify_options("30219,0830078125", 30219.0830078125f);
|
||||||
verify_options("1,1754947011469036e-38", 0x1.000006p-126f);
|
verify_options("1,1754947011469036e-38", 0x1.000006p-126f);
|
||||||
@ -693,7 +1073,7 @@ TEST_CASE("32bit.decimal_point") {
|
|||||||
verify_options("3,1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f);
|
verify_options("3,1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f);
|
||||||
verify_options("2,3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f);
|
verify_options("2,3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f);
|
||||||
verify_options("1", 1.f);
|
verify_options("1", 1.f);
|
||||||
verify_options("7,0060e-46", 0.f);
|
verify_options("7,0060e-46", 0.f, std::errc::result_out_of_range);
|
||||||
verify_options("3,4028234664e38", 0x1.fffffep+127f);
|
verify_options("3,4028234664e38", 0x1.fffffep+127f);
|
||||||
verify_options("3,4028234665e38", 0x1.fffffep+127f);
|
verify_options("3,4028234665e38", 0x1.fffffep+127f);
|
||||||
verify_options("3,4028234666e38", 0x1.fffffep+127f);
|
verify_options("3,4028234666e38", 0x1.fffffep+127f);
|
||||||
|
|||||||
5
tests/bloat_analysis/CMakeLists.txt
Normal file
5
tests/bloat_analysis/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
add_executable(bloaty main.cpp a1.cpp a2.cpp a3.cpp a4.cpp a4.cpp a5.cpp a6.cpp a7.cpp a8.cpp a9.cpp a10.cpp)
|
||||||
|
target_link_libraries(bloaty PUBLIC fast_float)
|
||||||
|
|
||||||
|
add_executable(bloatyref main_ref.cpp)
|
||||||
|
target_link_libraries(bloatyref PUBLIC fast_float)
|
||||||
9
tests/bloat_analysis/a1.cpp
Normal file
9
tests/bloat_analysis/a1.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get1(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 1; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a10.cpp
Normal file
9
tests/bloat_analysis/a10.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get10(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 10; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a2.cpp
Normal file
9
tests/bloat_analysis/a2.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get2(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 2; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a3.cpp
Normal file
9
tests/bloat_analysis/a3.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get3(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 3; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a4.cpp
Normal file
9
tests/bloat_analysis/a4.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get4(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 4; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a5.cpp
Normal file
9
tests/bloat_analysis/a5.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get5(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 5; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a6.cpp
Normal file
9
tests/bloat_analysis/a6.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get6(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 6; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a7.cpp
Normal file
9
tests/bloat_analysis/a7.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get7(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 7; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a8.cpp
Normal file
9
tests/bloat_analysis/a8.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get8(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 8; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
9
tests/bloat_analysis/a9.cpp
Normal file
9
tests/bloat_analysis/a9.cpp
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
|
||||||
|
double get9(const char* input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 9; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
19
tests/bloat_analysis/main.cpp
Normal file
19
tests/bloat_analysis/main.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
|
||||||
|
double get1(const char* input);
|
||||||
|
double get2(const char* input);
|
||||||
|
double get3(const char* input);
|
||||||
|
double get4(const char* input);
|
||||||
|
double get5(const char* input);
|
||||||
|
double get6(const char* input);
|
||||||
|
double get7(const char* input);
|
||||||
|
double get8(const char* input);
|
||||||
|
double get9(const char* input);
|
||||||
|
double get10(const char* input);
|
||||||
|
|
||||||
|
int main(int arg, char** argv) {
|
||||||
|
double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) + get5(argv[0])
|
||||||
|
+ get6(argv[0]) + get7(argv[0]) + get8(argv[0]) + get9(argv[0]) + get10(argv[0]);
|
||||||
|
|
||||||
|
return int(x);
|
||||||
|
}
|
||||||
13
tests/bloat_analysis/main_ref.cpp
Normal file
13
tests/bloat_analysis/main_ref.cpp
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
|
||||||
|
double get(const char * input) {
|
||||||
|
double result_value;
|
||||||
|
auto result = fast_float::from_chars(input, input + strlen(input), result_value);
|
||||||
|
if (result.ec != std::errc()) { return 10; }
|
||||||
|
return result_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int arg, char** argv) {
|
||||||
|
double x = get(argv[0]);
|
||||||
|
return int(x);
|
||||||
|
}
|
||||||
@ -4,11 +4,77 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
|
||||||
|
|
||||||
|
bool many() {
|
||||||
|
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) { return false; }
|
||||||
|
if(result != 234532.3426362) { return false; }
|
||||||
|
if(answer.ptr[0] != ',') { return false; }
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) { return false; }
|
||||||
|
if(result != 7869234.9823) { return false; }
|
||||||
|
if(answer.ptr[0] != ',') { return false; }
|
||||||
|
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) { return false; }
|
||||||
|
if(result != 324562.645) { return false; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void many_loop() {
|
||||||
|
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||||
|
double result;
|
||||||
|
const char* pointer = input.data();
|
||||||
|
const char* end_pointer = input.data() + input.size();
|
||||||
|
|
||||||
|
while(pointer < end_pointer) {
|
||||||
|
auto answer = fast_float::from_chars(pointer, end_pointer, result);
|
||||||
|
if(answer.ec != std::errc()) {
|
||||||
|
std::cerr << "error while parsing" << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
std::cout << "parsed: " << result << std::endl;
|
||||||
|
pointer = answer.ptr;
|
||||||
|
if ((answer.ptr < end_pointer) && (*pointer == ',')) {
|
||||||
|
pointer++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if FASTFLOAT_IS_CONSTEXPR
|
||||||
|
// consteval forces compile-time evaluation of the function in C++20.
|
||||||
|
consteval double parse(std::string_view input) {
|
||||||
|
double result;
|
||||||
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
|
if(answer.ec != std::errc()) { return -1.0; }
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function should compile to a function which
|
||||||
|
// merely returns 3.1415.
|
||||||
|
constexpr double constexptest() {
|
||||||
|
return parse("3.1415 input");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
const std::string input = "3.1416 xyz ";
|
const std::string input = "3.1416 xyz ";
|
||||||
double result;
|
double result;
|
||||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||||
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||||
std::cout << "parsed the number " << result << std::endl;
|
std::cout << "parsed the number " << result << std::endl;
|
||||||
|
|
||||||
|
if(!many()) {
|
||||||
|
printf("Bug\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
many_loop();
|
||||||
|
#if FASTFLOAT_IS_CONSTEXPR
|
||||||
|
if constexpr(constexptest() != 3.1415) {
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,7 +30,10 @@ void allvalues() {
|
|||||||
const char *string_end = to_string(v, buffer);
|
const char *string_end = to_string(v, buffer);
|
||||||
float result_value;
|
float result_value;
|
||||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||||
if (result.ec != std::errc()) {
|
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||||
|
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||||
|
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||||
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,7 +21,7 @@ bool basic_test_64bit(std::string vals, double val) {
|
|||||||
double result_value;
|
double result_value;
|
||||||
auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(),
|
auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(),
|
||||||
result_value);
|
result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cerr << " I could not parse " << vals << std::endl;
|
std::cerr << " I could not parse " << vals << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -90,7 +90,10 @@ bool allvalues() {
|
|||||||
|
|
||||||
float result_value;
|
float result_value;
|
||||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||||
if (result.ec != std::errc()) {
|
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||||
|
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||||
|
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||||
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -132,7 +135,7 @@ bool allvalues() {
|
|||||||
|
|
||||||
inline void Assert(bool Assertion) {
|
inline void Assert(bool Assertion) {
|
||||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||||
if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; }
|
if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; }
|
||||||
#else
|
#else
|
||||||
if (!Assertion) { throw std::runtime_error("bug"); }
|
if (!Assertion) { throw std::runtime_error("bug"); }
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
cmake_minimum_required(VERSION 3.15)
|
cmake_minimum_required(VERSION 3.15)
|
||||||
|
|
||||||
project(test_simdjson_install VERSION 0.1.0 LANGUAGES CXX)
|
project(test_install VERSION 0.1.0 LANGUAGES CXX)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|||||||
@ -29,7 +29,10 @@ void allvalues() {
|
|||||||
const char *string_end = to_string(v, buffer);
|
const char *string_end = to_string(v, buffer);
|
||||||
float result_value;
|
float result_value;
|
||||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||||
if (result.ec != std::errc()) {
|
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||||
|
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||||
|
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||||
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,7 +28,10 @@ void all_32bit_values() {
|
|||||||
const char *string_end = to_string(v, buffer);
|
const char *string_end = to_string(v, buffer);
|
||||||
double result_value;
|
double result_value;
|
||||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||||
if (result.ec != std::errc()) {
|
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||||
|
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||||
|
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||||
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -56,12 +56,16 @@ void random_values(size_t N) {
|
|||||||
const char *string_end = to_string(v, buffer);
|
const char *string_end = to_string(v, buffer);
|
||||||
double result_value;
|
double result_value;
|
||||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||||
if (result.ec != std::errc()) {
|
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||||
|
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||||
|
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||||
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||||
errors++;
|
errors++;
|
||||||
if (errors > 10) {
|
if (errors > 10) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (std::isnan(v)) {
|
if (std::isnan(v)) {
|
||||||
if (!std::isnan(result_value)) {
|
if (!std::isnan(result_value)) {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ bool test() {
|
|||||||
while((begin < end) && (std::isspace(*begin))) { begin++; }
|
while((begin < end) && (std::isspace(*begin))) { begin++; }
|
||||||
auto result = fast_float::from_chars(begin, end,
|
auto result = fast_float::from_chars(begin, end,
|
||||||
result_value);
|
result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
printf("parsing %.*s\n", int(end - begin), begin);
|
printf("parsing %.*s\n", int(end - begin), begin);
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -105,7 +105,7 @@ bool tester() {
|
|||||||
double result_value;
|
double result_value;
|
||||||
auto result =
|
auto result =
|
||||||
fast_float::from_chars(to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value);
|
fast_float::from_chars(to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cout << to_be_parsed << std::endl;
|
std::cout << to_be_parsed << std::endl;
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -59,12 +59,16 @@ void random_values(size_t N) {
|
|||||||
const char *string_end = to_string(v, buffer);
|
const char *string_end = to_string(v, buffer);
|
||||||
double result_value;
|
double result_value;
|
||||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||||
if (result.ec != std::errc()) {
|
// Starting with version 4.0 for fast_float, we return result_out_of_range if the
|
||||||
|
// value is either too small (too close to zero) or too large (effectively infinity).
|
||||||
|
// So std::errc::result_out_of_range is normal for well-formed input strings.
|
||||||
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
std::cerr << "parsing error ? " << buffer << std::endl;
|
std::cerr << "parsing error ? " << buffer << std::endl;
|
||||||
errors++;
|
errors++;
|
||||||
if (errors > 10) {
|
if (errors > 10) {
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (std::isnan(v)) {
|
if (std::isnan(v)) {
|
||||||
if (!std::isnan(result_value)) {
|
if (!std::isnan(result_value)) {
|
||||||
|
|||||||
@ -101,7 +101,12 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
|
|||||||
if (i == size_t(location_of_decimal_separator)) {
|
if (i == size_t(location_of_decimal_separator)) {
|
||||||
buffer[pos++] = '.';
|
buffer[pos++] = '.';
|
||||||
}
|
}
|
||||||
buffer[pos++] = char(rand.next_digit() + '0');
|
buffer[pos] = char(rand.next_digit() + '0');
|
||||||
|
// We can have a leading zero only if location_of_decimal_separator = 1.
|
||||||
|
while(i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') {
|
||||||
|
buffer[pos] = char(rand.next_digit() + '0');
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
if (rand.next_bool()) {
|
if (rand.next_bool()) {
|
||||||
if (rand.next_bool()) {
|
if (rand.next_bool()) {
|
||||||
@ -178,7 +183,7 @@ bool tester(uint64_t seed, size_t volume) {
|
|||||||
double result_value;
|
double result_value;
|
||||||
auto result =
|
auto result =
|
||||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
printf("parsing %.*s\n", int(length), buffer);
|
printf("parsing %.*s\n", int(length), buffer);
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -201,7 +206,7 @@ bool tester(uint64_t seed, size_t volume) {
|
|||||||
float result_value;
|
float result_value;
|
||||||
auto result =
|
auto result =
|
||||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
printf("parsing %.*s\n", int(length), buffer);
|
printf("parsing %.*s\n", int(length), buffer);
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
123
tests/rcppfastfloat_test.cpp
Normal file
123
tests/rcppfastfloat_test.cpp
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
/**
|
||||||
|
* See https://github.com/eddelbuettel/rcppfastfloat/issues/4
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FASTFLOAT_ALLOWS_LEADING_PLUS 1
|
||||||
|
#define FASTFLOAT_SKIP_WHITE_SPACE 1 // important !
|
||||||
|
#include "fast_float/fast_float.h"
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
bool eddelbuettel() {
|
||||||
|
std::vector<std::string> inputs = {"infinity",
|
||||||
|
" \r\n\t\f\v3.16227766016838 \r\n\t\f\v",
|
||||||
|
" \r\n\t\f\v3 \r\n\t\f\v",
|
||||||
|
" 1970-01-01",
|
||||||
|
"-NaN",
|
||||||
|
"-inf",
|
||||||
|
" \r\n\t\f\v2.82842712474619 \r\n\t\f\v",
|
||||||
|
"nan",
|
||||||
|
" \r\n\t\f\v2.44948974278318 \r\n\t\f\v",
|
||||||
|
"Inf",
|
||||||
|
" \r\n\t\f\v2 \r\n\t\f\v",
|
||||||
|
"-infinity",
|
||||||
|
" \r\n\t\f\v0 \r\n\t\f\v",
|
||||||
|
" \r\n\t\f\v1.73205080756888 \r\n\t\f\v",
|
||||||
|
" \r\n\t\f\v1 \r\n\t\f\v",
|
||||||
|
" \r\n\t\f\v1.4142135623731 \r\n\t\f\v",
|
||||||
|
" \r\n\t\f\v2.23606797749979 \r\n\t\f\v",
|
||||||
|
"1970-01-02 ",
|
||||||
|
" \r\n\t\f\v2.64575131106459 \r\n\t\f\v",
|
||||||
|
"inf",
|
||||||
|
"-nan",
|
||||||
|
"NaN",
|
||||||
|
"",
|
||||||
|
"-Inf",
|
||||||
|
"+2.2"};
|
||||||
|
std::vector<std::pair<bool, double>> expected_results = {
|
||||||
|
{true, std::numeric_limits<double>::infinity()},
|
||||||
|
{true, 3.16227766016838},
|
||||||
|
{true, 3},
|
||||||
|
{false, -1},
|
||||||
|
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||||
|
{true, -std::numeric_limits<double>::infinity()},
|
||||||
|
{true, 2.82842712474619},
|
||||||
|
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||||
|
{true, 2.44948974278318},
|
||||||
|
{true, std::numeric_limits<double>::infinity()},
|
||||||
|
{true, 2},
|
||||||
|
{true, -std::numeric_limits<double>::infinity()},
|
||||||
|
{true, 0},
|
||||||
|
{true, 1.73205080756888},
|
||||||
|
{true, 1},
|
||||||
|
{true, 1.4142135623731},
|
||||||
|
{true, 2.23606797749979},
|
||||||
|
{false, -1},
|
||||||
|
{true, 2.64575131106459},
|
||||||
|
{true, std::numeric_limits<double>::infinity()},
|
||||||
|
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||||
|
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||||
|
{false, -1},
|
||||||
|
{true, -std::numeric_limits<double>::infinity()},
|
||||||
|
{true, 2.2}};
|
||||||
|
for (size_t i = 0; i < inputs.size(); i++) {
|
||||||
|
std::string &input = inputs[i];
|
||||||
|
std::pair<bool, double> expected = expected_results[i];
|
||||||
|
double result;
|
||||||
|
// answer contains a error code and a pointer to the end of the
|
||||||
|
// parsed region (on success).
|
||||||
|
auto answer = fast_float::from_chars(input.data(),
|
||||||
|
input.data() + input.size(), result);
|
||||||
|
if (answer.ec != std::errc()) {
|
||||||
|
std::cout << "could not parse" << std::endl;
|
||||||
|
if (expected.first) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool non_space_trailing_content = false;
|
||||||
|
if (answer.ptr != input.data() + input.size()) {
|
||||||
|
// check that there is no content left
|
||||||
|
for (const char *leftover = answer.ptr;
|
||||||
|
leftover != input.data() + input.size(); leftover++) {
|
||||||
|
if (!fast_float::is_space(uint8_t(*leftover))) {
|
||||||
|
non_space_trailing_content = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (non_space_trailing_content) {
|
||||||
|
std::cout << "found trailing content " << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (non_space_trailing_content) {
|
||||||
|
if (!expected.first) {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::cout << "parsed " << result << std::endl;
|
||||||
|
if (!expected.first) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (result != expected.second) {
|
||||||
|
if (std::isnan(result) && std::isnan(expected.second)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::cout << "results do not match. Expected "<< expected.second << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
if (!eddelbuettel()) {
|
||||||
|
printf("Bug.\n");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
printf("All ok.\n");
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
@ -97,7 +97,12 @@ size_t build_random_string(RandomEngine &rand, char *buffer) {
|
|||||||
if (i == size_t(location_of_decimal_separator)) {
|
if (i == size_t(location_of_decimal_separator)) {
|
||||||
buffer[pos++] = '.';
|
buffer[pos++] = '.';
|
||||||
}
|
}
|
||||||
buffer[pos++] = char(rand.next_digit() + '0');
|
buffer[pos] = char(rand.next_digit() + '0');
|
||||||
|
// We can have a leading zero only if location_of_decimal_separator = 1.
|
||||||
|
while(i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') {
|
||||||
|
buffer[pos] = char(rand.next_digit() + '0');
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
if (rand.next_bool()) {
|
if (rand.next_bool()) {
|
||||||
if (rand.next_bool()) {
|
if (rand.next_bool()) {
|
||||||
@ -174,7 +179,7 @@ bool tester(uint64_t seed, size_t volume) {
|
|||||||
double result_value;
|
double result_value;
|
||||||
auto result =
|
auto result =
|
||||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
printf("parsing %.*s\n", int(length), buffer);
|
printf("parsing %.*s\n", int(length), buffer);
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -197,7 +202,7 @@ bool tester(uint64_t seed, size_t volume) {
|
|||||||
float result_value;
|
float result_value;
|
||||||
auto result =
|
auto result =
|
||||||
fast_float::from_chars(buffer, buffer + length, result_value);
|
fast_float::from_chars(buffer, buffer + length, result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
printf("parsing %.*s\n", int(length), buffer);
|
printf("parsing %.*s\n", int(length), buffer);
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -44,7 +44,7 @@ float cygwin_strtof_l(const char* start, char** end) {
|
|||||||
|
|
||||||
inline void Assert(bool Assertion) {
|
inline void Assert(bool Assertion) {
|
||||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||||
if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; }
|
if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; }
|
||||||
#else
|
#else
|
||||||
if (!Assertion) { throw std::runtime_error("bug"); }
|
if (!Assertion) { throw std::runtime_error("bug"); }
|
||||||
#endif
|
#endif
|
||||||
@ -62,6 +62,7 @@ template <typename T>
|
|||||||
bool test() {
|
bool test() {
|
||||||
std::string input = "0.1 1e1000 100000 3.14159265359 -1e-500 001 1e01 1e0000001 -inf";
|
std::string input = "0.1 1e1000 100000 3.14159265359 -1e-500 001 1e01 1e0000001 -inf";
|
||||||
std::vector<T> answers = {T(0.1), std::numeric_limits<T>::infinity(), 100000, T(3.14159265359), -0.0, 1, 10, 10, -std::numeric_limits<T>::infinity()};
|
std::vector<T> answers = {T(0.1), std::numeric_limits<T>::infinity(), 100000, T(3.14159265359), -0.0, 1, 10, 10, -std::numeric_limits<T>::infinity()};
|
||||||
|
std::vector<std::errc> expected_ec = {std::errc(), std::errc::result_out_of_range, std::errc(), std::errc(), std::errc::result_out_of_range, std::errc(), std::errc(), std::errc(), std::errc()};
|
||||||
const char * begin = input.data();
|
const char * begin = input.data();
|
||||||
const char * end = input.data() + input.size();
|
const char * end = input.data() + input.size();
|
||||||
for(size_t i = 0; i < answers.size(); i++) {
|
for(size_t i = 0; i < answers.size(); i++) {
|
||||||
@ -69,7 +70,7 @@ bool test() {
|
|||||||
while((begin < end) && (std::isspace(*begin))) { begin++; }
|
while((begin < end) && (std::isspace(*begin))) { begin++; }
|
||||||
auto result = fast_float::from_chars(begin, end,
|
auto result = fast_float::from_chars(begin, end,
|
||||||
result_value);
|
result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != expected_ec[i]) {
|
||||||
printf("parsing %.*s\n", int(end - begin), begin);
|
printf("parsing %.*s\n", int(end - begin), begin);
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
@ -238,7 +239,7 @@ bool partow_test() {
|
|||||||
T result_value;
|
T result_value;
|
||||||
auto result = fast_float::from_chars(st.data(), st.data() + st.size(),
|
auto result = fast_float::from_chars(st.data(), st.data() + st.size(),
|
||||||
result_value);
|
result_value);
|
||||||
if (result.ec != std::errc()) {
|
if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) {
|
||||||
printf("parsing %.*s\n", int(st.size()), st.data());
|
printf("parsing %.*s\n", int(st.size()), st.data());
|
||||||
std::cerr << " I could not parse " << std::endl;
|
std::cerr << " I could not parse " << std::endl;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user