mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-24 12:34:53 +08:00
Merge branch 'main' into dlemire/adding_bloat_analysis
This commit is contained in:
commit
06333da7fe
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 &&
|
||||||
|
|||||||
18
.github/workflows/ubuntu22-clang.yml
vendored
Normal file
18
.github/workflows/ubuntu22-clang.yml
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
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
|
||||||
16
.github/workflows/ubuntu22-gcc12.yml
vendored
Normal file
16
.github/workflows/ubuntu22-gcc12.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
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
|
||||||
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
|
||||||
29
.github/workflows/vs17-cxx20.yml
vendored
Normal file
29
.github/workflows/vs17-cxx20.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
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 -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}}
|
||||||
@ -1,9 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.9)
|
cmake_minimum_required(VERSION 3.9)
|
||||||
|
|
||||||
project(fast_float VERSION 3.2.0 LANGUAGES CXX)
|
project(fast_float VERSION 3.10.0 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()
|
||||||
|
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
if(FASTFLOAT_INSTALL)
|
||||||
|
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")
|
||||||
|
|
||||||
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
|
if(${CMAKE_VERSION} VERSION_LESS "3.14")
|
||||||
configure_package_config_file("cmake/config.cmake.in"
|
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
|
||||||
"${FASTFLOAT_PROJECT_CONFIG}"
|
else()
|
||||||
INSTALL_DESTINATION "${FASTFLOAT_INSTALL_DIR}")
|
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT)
|
||||||
|
endif()
|
||||||
|
configure_package_config_file("cmake/config.cmake.in"
|
||||||
|
"${FASTFLOAT_PROJECT_CONFIG}"
|
||||||
|
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()
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
108
README.md
108
README.md
@ -1,12 +1,5 @@
|
|||||||
## fast_float number parsing library: 4x faster than strtod
|
## fast_float number parsing library: 4x faster than strtod
|
||||||
|
|
||||||
/badge.svg)
|
|
||||||
/badge.svg)
|
|
||||||

|
|
||||||

|
|
||||||

|
|
||||||
[](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
|
||||||
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
|
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
|
||||||
@ -28,8 +21,8 @@ struct from_chars_result {
|
|||||||
```
|
```
|
||||||
|
|
||||||
It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
|
It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
|
||||||
a locale-independent format equivalent to the C++17 from_chars function.
|
a locale-independent format equivalent to the C++17 from_chars function.
|
||||||
The resulting floating-point value is the closest floating-point values (using either float or double),
|
The resulting floating-point value is the closest floating-point values (using either float or double),
|
||||||
using the "round to even" convention for values that would otherwise fall right in-between two values.
|
using the "round to even" convention for values that would otherwise fall right in-between two values.
|
||||||
That is, we provide exact parsing according to the IEEE standard.
|
That is, we provide exact parsing according to the IEEE standard.
|
||||||
|
|
||||||
@ -47,7 +40,7 @@ Example:
|
|||||||
``` C++
|
``` C++
|
||||||
#include "fast_float/fast_float.h"
|
#include "fast_float/fast_float.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
const std::string input = "3.1416 xyz ";
|
const std::string input = "3.1416 xyz ";
|
||||||
double result;
|
double result;
|
||||||
@ -60,15 +53,15 @@ int main() {
|
|||||||
|
|
||||||
|
|
||||||
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 allow 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`.
|
||||||
|
|
||||||
The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification.
|
The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification.
|
||||||
* The `from_chars` function does not skip leading white-space characters.
|
* The `from_chars` function does not skip leading white-space characters.
|
||||||
* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden.
|
* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden.
|
||||||
* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers.
|
* It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers.
|
||||||
|
|
||||||
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.
|
||||||
@ -77,22 +70,22 @@ Furthermore, we have the following restrictions:
|
|||||||
|
|
||||||
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`).
|
||||||
|
|
||||||
## Using commas as decimal separator
|
## Using commas as decimal separator
|
||||||
|
|
||||||
|
|
||||||
The C++ standard stipulate that `from_chars` has to be locale-independent. In
|
The C++ standard stipulate that `from_chars` has to be locale-independent. In
|
||||||
particular, the decimal separator has to be the period (`.`). However,
|
particular, the decimal separator has to be the period (`.`). However,
|
||||||
some users still want to use the `fast_float` library with in a locale-dependent
|
some users still want to use the `fast_float` library with in a locale-dependent
|
||||||
manner. Using a separate function called `from_chars_advanced`, we allow the users
|
manner. Using a separate function called `from_chars_advanced`, we allow the users
|
||||||
to pass a `parse_options` instance which contains a custom decimal separator (e.g.,
|
to pass a `parse_options` instance which contains a custom decimal separator (e.g.,
|
||||||
the comma). You may use it as follows.
|
the comma). You may use it as follows.
|
||||||
|
|
||||||
```C++
|
```C++
|
||||||
#include "fast_float/fast_float.h"
|
#include "fast_float/fast_float.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
const std::string input = "3,1416 xyz ";
|
const std::string input = "3,1416 xyz ";
|
||||||
double result;
|
double result;
|
||||||
@ -104,23 +97,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).
|
||||||
@ -133,14 +165,14 @@ It can parse random floating-point numbers at a speed of 1 GB/s on some systems.
|
|||||||
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
|
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
|
||||||
|
|
||||||
```
|
```
|
||||||
$ ./build/benchmarks/benchmark
|
$ ./build/benchmarks/benchmark
|
||||||
# parsing random integers in the range [0,1)
|
# parsing random integers in the range [0,1)
|
||||||
volume = 2.09808 MB
|
volume = 2.09808 MB
|
||||||
netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
|
netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
|
||||||
doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
|
doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
|
||||||
strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
|
strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
|
||||||
abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
|
abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
|
||||||
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
|
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
|
||||||
```
|
```
|
||||||
|
|
||||||
See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
|
See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
|
||||||
@ -181,23 +213,23 @@ You should change the `GIT_TAG` line so that you recover the version you wish to
|
|||||||
|
|
||||||
## Using as single header
|
## Using as single header
|
||||||
|
|
||||||
The script `script/amalgamate.py` may be used to generate a single header
|
The script `script/amalgamate.py` may be used to generate a single header
|
||||||
version of the library if so desired.
|
version of the library if so desired.
|
||||||
Just run the script from the root directory of this repository.
|
Just run the script from the root directory of this repository.
|
||||||
You can customize the license type and output file if desired as described in
|
You can customize the license type and output file if desired as described in
|
||||||
the command line help.
|
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
|
||||||
|
|
||||||
Though this work is inspired by many different people, this work benefited especially from exchanges with
|
Though this work is inspired by many different people, this work benefited especially from exchanges with
|
||||||
Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided
|
Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided
|
||||||
invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
|
invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
|
||||||
|
|
||||||
The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
|
The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
|
||||||
under the Apache 2.0 license.
|
under the Apache 2.0 license.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|||||||
@ -12,9 +12,11 @@ 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'; }
|
fastfloat_really_inline constexpr bool is_integer(char c) noexcept {
|
||||||
|
return c >= '0' && c <= '9';
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
@ -44,7 +46,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)
|
||||||
@ -59,7 +62,7 @@ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *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));
|
||||||
}
|
}
|
||||||
@ -93,7 +96,11 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
answer.valid = false;
|
answer.valid = false;
|
||||||
answer.too_many_digits = false;
|
answer.too_many_digits = false;
|
||||||
answer.negative = (*p == '-');
|
answer.negative = (*p == '-');
|
||||||
|
#if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||||
|
if ((*p == '-') || (*p == '+')) {
|
||||||
|
#else
|
||||||
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||||
|
#endif
|
||||||
++p;
|
++p;
|
||||||
if (p == pend) {
|
if (p == pend) {
|
||||||
return answer;
|
return answer;
|
||||||
@ -106,10 +113,6 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
|||||||
|
|
||||||
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
|
||||||
|
|||||||
@ -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
|
||||||
@ -54,23 +54,23 @@ struct stackvec {
|
|||||||
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;
|
||||||
@ -137,7 +137,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,14 +147,14 @@ 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;
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -23,7 +23,8 @@ 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 {
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
|
int32_t scientific_exponent(parsed_number_string& 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) {
|
||||||
@ -44,40 +45,24 @@ 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 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;
|
::memcpy(&bits, &value, sizeof(T));
|
||||||
constexpr uint32_t mantissa_mask = 0x007FFFFF;
|
if ((bits & exponent_mask) == 0) {
|
||||||
constexpr uint64_t hidden_bit_mask = 0x00800000;
|
// denormal
|
||||||
uint32_t bits;
|
am.power2 = 1 - bias;
|
||||||
::memcpy(&bits, &value, sizeof(T));
|
am.mantissa = bits & mantissa_mask;
|
||||||
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;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
constexpr uint64_t exponent_mask = 0x7FF0000000000000;
|
// normal
|
||||||
constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF;
|
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
|
||||||
constexpr uint64_t hidden_bit_mask = 0x0010000000000000;
|
am.power2 -= bias;
|
||||||
uint64_t bits;
|
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
||||||
::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;
|
||||||
@ -97,12 +82,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 +112,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 +138,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 {
|
||||||
@ -215,7 +198,7 @@ void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& co
|
|||||||
count += 8;
|
count += 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
fastfloat_really_inline
|
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||||
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
||||||
value = value * 10 + limb(*p - '0');
|
value = value * 10 + limb(*p - '0');
|
||||||
p++;
|
p++;
|
||||||
|
|||||||
@ -44,7 +44,7 @@ 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>
|
||||||
@ -58,6 +58,6 @@ template<typename T>
|
|||||||
from_chars_result from_chars_advanced(const char *first, const char *last,
|
from_chars_result from_chars_advanced(const char *first, const char *last,
|
||||||
T &value, parse_options options) noexcept;
|
T &value, parse_options options) noexcept;
|
||||||
|
|
||||||
}
|
} // namespace fast_float
|
||||||
#include "parse_number.h"
|
#include "parse_number.h"
|
||||||
#endif // FASTFLOAT_FAST_FLOAT_H
|
#endif // FASTFLOAT_FAST_FLOAT_H
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -5,18 +5,18 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
#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 +24,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 +40,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 +50,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,22 +81,28 @@
|
|||||||
#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
|
||||||
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
|
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
|
||||||
|
|
||||||
|
// Testing for https://wg21.link/N3652, adopted in C++14
|
||||||
|
#if __cpp_constexpr >= 201304
|
||||||
|
#define FASTFLOAT_CONSTEXPR14 constexpr
|
||||||
|
#else
|
||||||
|
#define FASTFLOAT_CONSTEXPR14
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace fast_float {
|
namespace fast_float {
|
||||||
|
|
||||||
// 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,
|
inline FASTFLOAT_CONSTEXPR14 bool
|
||||||
size_t length) {
|
fastfloat_strncasecmp(const char *input1, const char *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 |= (input1[i] ^ input2[i]);
|
||||||
@ -107,14 +119,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,8 +135,8 @@ 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) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* result might be undefined when input_num is zero */
|
/* result might be undefined when input_num is zero */
|
||||||
@ -155,14 +167,14 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
|
|||||||
#ifdef FASTFLOAT_32BIT
|
#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
|
// slow emulation routine for 32-bit
|
||||||
#if !defined(__MINGW64__)
|
#if !defined(__MINGW64__)
|
||||||
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
|
fastfloat_really_inline constexpr uint64_t _umul128(uint64_t ab, uint64_t cd,
|
||||||
uint64_t *hi) {
|
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));
|
||||||
@ -181,8 +193,9 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
|
|||||||
fastfloat_really_inline value128 full_multiplication(uint64_t a,
|
fastfloat_really_inline value128 full_multiplication(uint64_t a,
|
||||||
uint64_t b) {
|
uint64_t b) {
|
||||||
value128 answer;
|
value128 answer;
|
||||||
#ifdef _M_ARM64
|
#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__))
|
||||||
@ -201,10 +214,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 +228,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 +353,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,6 +414,33 @@ template <> inline constexpr size_t binary_format<float>::max_digits() {
|
|||||||
return 114;
|
return 114;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <> inline constexpr binary_format<float>::equiv_uint
|
||||||
|
binary_format<float>::exponent_mask() {
|
||||||
|
return 0x7F800000;
|
||||||
|
}
|
||||||
|
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>
|
template<typename T>
|
||||||
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
|
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||||
uint64_t word = am.mantissa;
|
uint64_t word = am.mantissa;
|
||||||
@ -357,6 +459,28 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 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
|
||||||
} // namespace fast_float
|
} // namespace fast_float
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -60,6 +60,70 @@ from_chars_result parse_infnan(const char *first, const char *last, T &value) n
|
|||||||
return answer;
|
return answer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the floating-pointing rounding mode is to 'nearest'.
|
||||||
|
* It is the default on most system. This function is meant to be inexpensive.
|
||||||
|
* Credit : @mwalcott3
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
#if 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);
|
||||||
|
#if FASTFLOAT_VISUAL_STUDIO
|
||||||
|
# pragma warning(pop)
|
||||||
|
#elif defined(__clang__)
|
||||||
|
# pragma clang diagnostic pop
|
||||||
|
#elif defined(__GNUC__)
|
||||||
|
# pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@ -76,6 +140,11 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
|
|||||||
|
|
||||||
|
|
||||||
from_chars_result answer;
|
from_chars_result answer;
|
||||||
|
#if 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;
|
||||||
@ -87,13 +156,45 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
|
|||||||
}
|
}
|
||||||
answer.ec = std::errc(); // be optimistic
|
answer.ec = std::errc(); // be optimistic
|
||||||
answer.ptr = pns.lastmatch;
|
answer.ptr = pns.lastmatch;
|
||||||
// Next is Clinger's fast path.
|
// The implementation of the Clinger's fast path is convoluted because
|
||||||
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) {
|
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
||||||
value = T(pns.mantissa);
|
// selected on the thread.
|
||||||
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
|
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
|
||||||
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
|
// true.
|
||||||
if (pns.negative) { value = -value; }
|
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
|
||||||
return answer;
|
// 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(detail::rounds_to_nearest()) {
|
||||||
|
// We have that fegetround() == FE_TONEAREST.
|
||||||
|
// Next is Clinger's fast path.
|
||||||
|
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
|
||||||
|
value = T(pns.mantissa);
|
||||||
|
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); }
|
||||||
|
if (pns.negative) { value = -value; }
|
||||||
|
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 = 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) {
|
||||||
|
|||||||
@ -137,7 +137,7 @@ inline uint64_t round(decimal &h) {
|
|||||||
}
|
}
|
||||||
bool round_up = false;
|
bool round_up = false;
|
||||||
if (dp < h.num_digits) {
|
if (dp < h.num_digits) {
|
||||||
round_up = h.digits[dp] >= 5; // normally, we round up
|
round_up = h.digits[dp] >= 5; // normally, we round up
|
||||||
// but we may need to round to even!
|
// but we may need to round to even!
|
||||||
if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) {
|
if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) {
|
||||||
round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1]));
|
round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1]));
|
||||||
@ -266,7 +266,7 @@ adjusted_mantissa compute_float(decimal &d) {
|
|||||||
return answer;
|
return answer;
|
||||||
} else if(d.decimal_point >= 310) {
|
} else if(d.decimal_point >= 310) {
|
||||||
// We have something at least as large as 0.1e310 which is
|
// We have something at least as large as 0.1e310 which is
|
||||||
// always infinite.
|
// always infinite.
|
||||||
answer.power2 = binary::infinite_power();
|
answer.power2 = binary::infinite_power();
|
||||||
answer.mantissa = 0;
|
answer.mantissa = 0;
|
||||||
return answer;
|
return answer;
|
||||||
|
|||||||
@ -3,46 +3,78 @@ 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 = []
|
||||||
text = ''
|
with open(filename, encoding='utf8') as f:
|
||||||
for line in f:
|
lines = f.readlines()
|
||||||
text += '// ' + line
|
|
||||||
processed_files[filename] = text
|
# Retrieve subset required for inclusion in source
|
||||||
|
if filename == 'LICENSE-APACHE':
|
||||||
|
lines = [
|
||||||
|
' Copyright 2021 The fast_float authors\n',
|
||||||
|
*lines[179:-1]
|
||||||
|
]
|
||||||
|
|
||||||
|
text = ''
|
||||||
|
for line in lines:
|
||||||
|
text += '// ' + line.strip() + '\n'
|
||||||
|
processed_files[filename] = text
|
||||||
|
|
||||||
# code
|
# code
|
||||||
for filename in [ 'fast_float.h', 'float_common.h', 'ascii_number.h',
|
for filename in [ '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['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 +82,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)
|
||||||
|
|||||||
@ -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,12 @@ 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)
|
||||||
|
target_compile_features(basictest PRIVATE cxx_std_17)
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <system_error>
|
#include <system_error>
|
||||||
|
#include <cfenv>
|
||||||
|
|
||||||
#ifndef SUPPLEMENTAL_TEST_DATA_DIR
|
#ifndef SUPPLEMENTAL_TEST_DATA_DIR
|
||||||
#define SUPPLEMENTAL_TEST_DATA_DIR "data/"
|
#define SUPPLEMENTAL_TEST_DATA_DIR "data/"
|
||||||
@ -42,6 +43,147 @@
|
|||||||
#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 _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);
|
||||||
|
}
|
||||||
|
|
||||||
// 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,59 +192,77 @@
|
|||||||
#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;
|
||||||
size_t number{0};
|
// We check all rounding directions, for each file.
|
||||||
std::fstream newfile(file_name, std::ios::in);
|
std::vector<int> directions = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST};
|
||||||
if (newfile.is_open()) {
|
for (int d : directions) {
|
||||||
std::string str;
|
std::cout << "fesetround to " << round_name(d) << std::endl;
|
||||||
while (std::getline(newfile, str)) {
|
fesetround(d);
|
||||||
if (str.size() > 0) {
|
size_t number{0};
|
||||||
// Read 32-bit hex
|
std::fstream newfile(file_name, std::ios::in);
|
||||||
uint32_t float32;
|
if (newfile.is_open()) {
|
||||||
auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(),
|
std::string str;
|
||||||
|
while (std::getline(newfile, str)) {
|
||||||
|
if (str.size() > 0) {
|
||||||
|
// Read 32-bit hex
|
||||||
|
uint32_t float32;
|
||||||
|
auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(),
|
||||||
float32, 16);
|
float32, 16);
|
||||||
if(r32.ec != std::errc()) { std::cerr << "32-bit parsing failure\n"; return false; }
|
if(r32.ec != std::errc()) { std::cerr << "32-bit parsing failure\n"; return false; }
|
||||||
// Read 64-bit hex
|
// Read 64-bit hex
|
||||||
uint64_t float64;
|
uint64_t float64;
|
||||||
auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(),
|
auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(),
|
||||||
float64, 16);
|
float64, 16);
|
||||||
if(r64.ec != std::errc()) { std::cerr << "64-bit parsing failure\n"; return false; }
|
if(r64.ec != std::errc()) { std::cerr << "64-bit parsing failure\n"; return false; }
|
||||||
// The string to parse:
|
// The string to parse:
|
||||||
const char *number_string = str.data() + 31;
|
const char *number_string = str.data() + 31;
|
||||||
const char *end_of_string = str.data() + str.size();
|
const char *end_of_string = str.data() + str.size();
|
||||||
// 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()) { std::cerr << "parsing failure\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()) { std::cerr << "parsing failure\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;
|
||||||
::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32));
|
::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32));
|
||||||
::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64));
|
::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64));
|
||||||
// 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;
|
||||||
return false;
|
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;
|
||||||
|
}
|
||||||
|
if (float64_parsed != float64) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
number++;
|
||||||
}
|
}
|
||||||
if (float64_parsed != float64) {
|
|
||||||
std::cout << "bad 64 " << str << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
number++;
|
|
||||||
}
|
}
|
||||||
|
std::cout << "checked " << std::defaultfloat << number << " values" << std::endl;
|
||||||
|
newfile.close(); // close the file object
|
||||||
|
} else {
|
||||||
|
std::cout << "Could not read " << file_name << std::endl;
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
std::cout << "checked " << std::defaultfloat << number << " values" << std::endl;
|
|
||||||
newfile.close(); // close the file object
|
|
||||||
} else {
|
|
||||||
std::cout << "Could not read " << file_name << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
fesetround(FE_TONEAREST);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,9 +285,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 +381,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;
|
||||||
@ -454,6 +638,9 @@ TEST_CASE("64bit.inf") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
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);
|
||||||
@ -588,6 +775,7 @@ TEST_CASE("32bit.inf") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("32bit.general") {
|
TEST_CASE("32bit.general") {
|
||||||
|
verify("-1e-999",-0.0f);
|
||||||
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(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f);
|
||||||
|
|||||||
@ -3,12 +3,57 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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();
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -132,7 +132,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)
|
||||||
@ -27,4 +27,4 @@ int main() {
|
|||||||
|
|
||||||
|
|
||||||
add_executable(repro main.cpp)
|
add_executable(repro main.cpp)
|
||||||
target_link_libraries(repro PUBLIC FastFloat::fast_float)
|
target_link_libraries(repro PUBLIC FastFloat::fast_float)
|
||||||
|
|||||||
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;
|
||||||
|
}
|
||||||
@ -44,8 +44,8 @@ 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
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user