mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-07 01:06:48 +08:00
Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f8c573d741 | ||
|
|
9d78a01ff7 | ||
|
|
409d6215b4 | ||
|
|
7b21183a93 | ||
|
|
8ac72791a9 | ||
|
|
e77e2bca7e | ||
|
|
24fa687d45 | ||
|
|
88f6c5e367 | ||
|
|
83a4f696d7 | ||
|
|
1ea4d2563e | ||
|
|
7262d9454e | ||
|
|
23f16adad4 | ||
|
|
fd98fd6689 | ||
|
|
197c0ffca7 | ||
|
|
e9438e64ba | ||
|
|
7abb574ffc | ||
|
|
04e8e545d8 | ||
|
|
e4b949e55b | ||
|
|
01e505797b | ||
|
|
13345cab65 | ||
|
|
88b1e5321c | ||
|
|
2aa6d0ba72 | ||
|
|
0b6d911220 | ||
|
|
2d6574483d | ||
|
|
7a77227521 | ||
|
|
e20c952456 | ||
|
|
bb956b29db | ||
|
|
48fc5404d4 | ||
|
|
e5612e9f72 | ||
|
|
fb384a4205 | ||
|
|
9d81c71aef | ||
|
|
fec4082f01 | ||
|
|
e89e248bd7 | ||
|
|
96fc38fb5a | ||
|
|
6677924083 | ||
|
|
0a230326ab | ||
|
|
7ae62ee0d5 | ||
|
|
4bb6fd1271 | ||
|
|
e12463583f | ||
|
|
6702cd4244 | ||
|
|
20a7383442 | ||
|
|
763558b9ac | ||
|
|
ce511cb9ee | ||
|
|
42db9ac1de | ||
|
|
6be07d66a8 | ||
|
|
42836b48eb | ||
|
|
a9ac1b40c9 | ||
|
|
c0582c27f5 | ||
|
|
cc90f240ee | ||
|
|
a134561e4b | ||
|
|
7b8f04500a | ||
|
|
0a9257e825 | ||
|
|
e1c2c806ed | ||
|
|
6b10835aa9 | ||
|
|
3d09138b1b | ||
|
|
3ee7eb018d | ||
|
|
2d2b42bb38 | ||
|
|
73b27b7d68 | ||
|
|
fbc85aa706 | ||
|
|
5a4b793818 | ||
|
|
e5d93c993e | ||
|
|
a1e272f515 | ||
|
|
447ee0bc82 | ||
|
|
0458c20061 | ||
|
|
81b8306c5f | ||
|
|
b8085ba363 | ||
|
|
c5a3ca37c4 | ||
|
|
d42004eca2 | ||
|
|
3f7ff422bf | ||
|
|
b3fe1e63cf | ||
|
|
50a80a73ab | ||
|
|
f9bed3f7b1 | ||
|
|
c0affad8b4 | ||
|
|
7597ca61aa | ||
|
|
1bf7010153 | ||
|
|
e019768d49 | ||
|
|
c6732cd28b | ||
|
|
b29208f93d | ||
|
|
ba92c88901 | ||
|
|
0305a7d336 | ||
|
|
e720395b61 | ||
|
|
8e5e92b6e2 | ||
|
|
6f0049a2e7 | ||
|
|
95dedd0aed | ||
|
|
1504792a9f | ||
|
|
864e790d22 | ||
|
|
3c5b166f6c | ||
|
|
c584342aa5 | ||
|
|
b7b17e6cac | ||
|
|
810a750306 | ||
|
|
d28a3320c2 | ||
|
|
3e9dc96eb4 | ||
|
|
96067ad107 | ||
|
|
9cfa5ff4a0 | ||
|
|
77cc847c84 | ||
|
|
717112d257 | ||
|
|
f0c709e3e4 | ||
|
|
7a5ee5af60 | ||
|
|
1d50f57dd9 | ||
|
|
3a74bfb500 | ||
|
|
babfa8b85c | ||
|
|
482cc1f251 | ||
|
|
d4d5748ec8 | ||
|
|
be9a8453c4 | ||
|
|
87aae63ed7 | ||
|
|
dc39efa49d | ||
|
|
ce274f7051 | ||
|
|
7226c00490 | ||
|
|
2a6c2604b5 | ||
|
|
f23ced2e4e | ||
|
|
baaf58d2dd | ||
|
|
62f526dfd3 | ||
|
|
63bbefad6b | ||
|
|
ac453a091a | ||
|
|
da819feb74 | ||
|
|
3b9ff76143 | ||
|
|
c62b853648 | ||
|
|
b3acae22ea | ||
|
|
74e00e1401 | ||
|
|
558bec8b9b | ||
|
|
8cee025082 | ||
|
|
6f8fd6728d | ||
|
|
c526899951 | ||
|
|
bfcff49c83 | ||
|
|
31cc0d1a0e | ||
|
|
832307e7b6 | ||
|
|
49aed7782e | ||
|
|
3775a81ced | ||
|
|
0a1bf11560 | ||
|
|
3146e686d0 | ||
|
|
82865ad475 | ||
|
|
0a17150096 | ||
|
|
7178b7dddd | ||
|
|
8832c532b8 | ||
|
|
3f2cd66c1c | ||
|
|
ac33f96a60 | ||
|
|
396feb6353 | ||
|
|
2c4b48d4e0 | ||
|
|
321d3a78b8 | ||
|
|
49068fbfda | ||
|
|
b9793e1cd4 | ||
|
|
6801b0ca20 | ||
|
|
d3f7113d6e | ||
|
|
65911af27c | ||
|
|
1a15c66fb9 | ||
|
|
7f476cd259 | ||
|
|
54782eb30c | ||
|
|
f436996266 | ||
|
|
cf771eaa83 | ||
|
|
3d33762eed | ||
|
|
35d0c8c179 | ||
|
|
74829bb77d | ||
|
|
cb1d42aaa1 | ||
|
|
b9661b41af | ||
|
|
4ed0177782 | ||
|
|
dead62d440 | ||
|
|
50ee38af65 | ||
|
|
7ff885d45c | ||
|
|
3e26cf4cea | ||
|
|
1e188d9715 | ||
|
|
43a428d658 | ||
|
|
cd28b563fc | ||
|
|
d80be1a5b0 | ||
|
|
48252a6483 | ||
|
|
0bbba960f4 | ||
|
|
724834fe8e | ||
|
|
b3526da935 | ||
|
|
b8b5da75a5 | ||
|
|
42fded19ae | ||
|
|
8e5f76ce73 | ||
|
|
b635dec11a | ||
|
|
23787fc71a | ||
|
|
72b2a7382a | ||
|
|
1df71f1e9d | ||
|
|
269867fa43 | ||
|
|
e800cabe48 | ||
|
|
d65285a48c | ||
|
|
7665574628 | ||
|
|
a60ef3b2e1 | ||
|
|
9ab35254a8 | ||
|
|
4266c34620 |
@ -1,2 +1,4 @@
|
||||
BasedOnStyle: LLVM
|
||||
SortIncludes: false
|
||||
SeparateDefinitionBlocks: Always
|
||||
MaxEmptyLinesToKeep: 1
|
||||
|
||||
2
.github/pull_request_template.md
vendored
2
.github/pull_request_template.md
vendored
@ -1,3 +1,5 @@
|
||||
We have benchmarks, please consider running them: [see our README for details](https://github.com/fastfloat/fast_float/blob/main/README.md#benchmarking). We expect you to run our benchmarks if you make claims with respect to the performance.
|
||||
|
||||
|
||||
Our CI tests check formatting automating. If such a test fails, please consider running the bash script:
|
||||
|
||||
|
||||
2
.github/workflows/alpine.yml
vendored
2
.github/workflows/alpine.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
- riscv64
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install latest Alpine Linux for ${{ matrix.arch }}
|
||||
uses: jirutka/setup-alpine@v1
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
name: Amalgamate Ubuntu 20.04 CI (GCC 9)
|
||||
name: Amalgamate Ubuntu 24.04 CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Compile with amalgamation
|
||||
run: |
|
||||
mkdir build &&
|
||||
4
.github/workflows/cifuzz.yml
vendored
4
.github/workflows/cifuzz.yml
vendored
@ -20,14 +20,14 @@ jobs:
|
||||
fuzz-seconds: 300
|
||||
output-sarif: true
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@v5
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
- name: Upload Sarif
|
||||
if: always() && steps.build.outcome == 'success'
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: cifuzz-sarif/results.sarif
|
||||
|
||||
17
.github/workflows/emscripten.yml
vendored
Normal file
17
.github/workflows/emscripten.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.2.2
|
||||
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
|
||||
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
|
||||
- name: Verify
|
||||
run: emcc -v
|
||||
- name: Checkout
|
||||
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v3.6.0
|
||||
- name: Configure
|
||||
run: emcmake cmake -B build
|
||||
- name: Build # We build but do not test
|
||||
run: cmake --build build
|
||||
4
.github/workflows/lint_and_format_check.yml
vendored
4
.github/workflows/lint_and_format_check.yml
vendored
@ -24,10 +24,10 @@ jobs:
|
||||
lint-and-format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@9a9194f87191a7e9055e3e9b95b8cfb13023bb08 # v4.1.7
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
|
||||
|
||||
- name: Run clang-format
|
||||
uses: jidicula/clang-format-action@c74383674bf5f7c69f60ce562019c1c94bc1421a # v4.13.0
|
||||
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # v4.15.0
|
||||
with:
|
||||
clang-format-version: '17'
|
||||
|
||||
|
||||
2
.github/workflows/msys2-clang.yml
vendored
2
.github/workflows/msys2-clang.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
CMAKE_GENERATOR: Ninja
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
|
||||
2
.github/workflows/msys2.yml
vendored
2
.github/workflows/msys2.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
CMAKE_GENERATOR: Ninja
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
|
||||
2
.github/workflows/on-release.yml
vendored
2
.github/workflows/on-release.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
|
||||
- name: Amalgamate fast_float.h
|
||||
run: |
|
||||
|
||||
23
.github/workflows/risc.yml
vendored
Normal file
23
.github/workflows/risc.yml
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
name: Ubuntu RISC-V rvv VLEN=128 (clang 17)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Install packages
|
||||
run: |
|
||||
sudo apt-get update -q -y
|
||||
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-17
|
||||
- name: Build
|
||||
run: |
|
||||
CXX=clang++-17 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" \
|
||||
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
|
||||
cmake --build build/ -j$(nproc)
|
||||
- name: Test VLEN=128
|
||||
run: |
|
||||
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
|
||||
export QEMU_CPU="rv64,v=on,vlen=128,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
|
||||
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)
|
||||
4
.github/workflows/s390x.yml
vendored
4
.github/workflows/s390x.yml
vendored
@ -12,8 +12,8 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
- uses: actions/checkout@v5
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
name: Test
|
||||
id: runcmd
|
||||
with:
|
||||
|
||||
16
.github/workflows/ubuntu20-fastmath.yml
vendored
16
.github/workflows/ubuntu20-fastmath.yml
vendored
@ -1,16 +0,0 @@
|
||||
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@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake -DCMAKE_CXX_FLAGS="-ffast-math" -DFASTFLOAT_TEST=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure
|
||||
21
.github/workflows/ubuntu20.yml
vendored
21
.github/workflows/ubuntu20.yml
vendored
@ -1,21 +0,0 @@
|
||||
name: Ubuntu 20.04 CI (GCC 9)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure &&
|
||||
cmake --install . &&
|
||||
cd ../tests/installation_tests/find &&
|
||||
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . &&
|
||||
cd ../../issue72_installation &&
|
||||
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .
|
||||
2
.github/workflows/ubuntu22-clang.yml
vendored
2
.github/workflows/ubuntu22-clang.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Install clang++-14
|
||||
run: sudo apt-get install -y clang++-14
|
||||
- name: Use cmake
|
||||
|
||||
2
.github/workflows/ubuntu22-gcc12.yml
vendored
2
.github/workflows/ubuntu22-gcc12.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
|
||||
2
.github/workflows/ubuntu22-sanitize.yml
vendored
2
.github/workflows/ubuntu22-sanitize.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
|
||||
2
.github/workflows/ubuntu22.yml
vendored
2
.github/workflows/ubuntu22.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
name: Ubuntu 20.04 CI (C++20)
|
||||
name: Ubuntu 24.04 CI
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
28
.github/workflows/ubuntu24.yml
vendored
28
.github/workflows/ubuntu24.yml
vendored
@ -6,18 +6,24 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v5
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
CXXFLAGS=-Werror cmake -DFASTFLOAT_TEST=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure
|
||||
set -xe
|
||||
cmake -B build \
|
||||
-DFASTFLOAT_TEST=ON \
|
||||
-DFASTFLOAT_BENCHMARKS=ON \
|
||||
-DCMAKE_CXX_FLAGS=' -Werror -Wundef '
|
||||
cmake --build build --parallel
|
||||
( cd build ; ctest --output-on-failure )
|
||||
- name: Use cmake CXX23
|
||||
run: |
|
||||
mkdir build20 &&
|
||||
cd build20 &&
|
||||
CXXFLAGS=-Werror cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_FIXEDWIDTH_TESTS=ON -DFASTFLOAT_CXX_STANDARD=23 -DFASTFLOAT_TEST=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure
|
||||
set -xe
|
||||
cmake -B build20 \
|
||||
-DFASTFLOAT_TEST=ON \
|
||||
-DFASTFLOAT_CONSTEXPR_TESTS=ON \
|
||||
-DFASTFLOAT_FIXEDWIDTH_TESTS=ON \
|
||||
-DFASTFLOAT_CXX_STANDARD=23 \
|
||||
-DCMAKE_CXX_FLAGS=' -Werror -Wundef '
|
||||
cmake --build build20 --parallel
|
||||
( cd build20 ; ctest --output-on-failure )
|
||||
|
||||
4
.github/workflows/vs17-arm-ci.yml
vendored
4
.github/workflows/vs17-arm-ci.yml
vendored
@ -10,13 +10,11 @@ jobs:
|
||||
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@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: configure
|
||||
run: |
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON
|
||||
|
||||
4
.github/workflows/vs17-ci.yml
vendored
4
.github/workflows/vs17-ci.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: configure
|
||||
run: |
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination
|
||||
- name: build
|
||||
run: |
|
||||
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
|
||||
|
||||
4
.github/workflows/vs17-clang-ci.yml
vendored
4
.github/workflows/vs17-clang-ci.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -T ClangCL -DFASTFLOAT_TEST=ON
|
||||
- name: Build
|
||||
run: cmake --build build --config ${{matrix.cfg}} --parallel --verbose
|
||||
- name: Run basic tests
|
||||
|
||||
2
.github/workflows/vs17-cxx20.yml
vendored
2
.github/workflows/vs17-cxx20.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@v5
|
||||
- name: configure
|
||||
run: >-
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,6 +2,7 @@ build/*
|
||||
Testing/*
|
||||
.cache/
|
||||
compile_commands.json
|
||||
bazel-*
|
||||
|
||||
# Visual studio
|
||||
.vs/
|
||||
|
||||
6
BUILD.bazel
Normal file
6
BUILD.bazel
Normal file
@ -0,0 +1,6 @@
|
||||
cc_library(
|
||||
name = "fast_float",
|
||||
hdrs = glob(["include/fast_float/*.h"]),
|
||||
strip_include_prefix = "include",
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
@ -1,9 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
project(fast_float VERSION 6.1.6 LANGUAGES CXX)
|
||||
|
||||
project(fast_float VERSION 8.1.0 LANGUAGES CXX)
|
||||
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
|
||||
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
|
||||
option(FASTFLOAT_TEST "Enable tests" OFF)
|
||||
|
||||
if(FASTFLOAT_TEST)
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
@ -29,6 +31,16 @@ if(FASTFLOAT_INSTALL)
|
||||
endif()
|
||||
|
||||
add_library(fast_float INTERFACE)
|
||||
|
||||
|
||||
option(FASTFLOAT_BENCHMARKS "Enable benchmarks" OFF)
|
||||
if(FASTFLOAT_BENCHMARKS)
|
||||
add_subdirectory(benchmarks)
|
||||
else(FASTFLOAT_BENCHMARKS)
|
||||
message(STATUS "Benchmarks are disabled. Set FASTFLOAT_BENCHMARKS to ON to build benchmarks (assumes C++17).")
|
||||
endif(FASTFLOAT_BENCHMARKS)
|
||||
|
||||
|
||||
add_library(FastFloat::fast_float ALIAS fast_float)
|
||||
target_include_directories(
|
||||
fast_float
|
||||
@ -44,11 +56,15 @@ if(FASTFLOAT_SANITIZE)
|
||||
target_link_libraries(fast_float INTERFACE -fuse-ld=gold)
|
||||
endif()
|
||||
endif()
|
||||
if(MSVC_VERSION GREATER 1910)
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
unset(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
|
||||
CHECK_CXX_COMPILER_FLAG(/permissive- FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
|
||||
|
||||
if(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
|
||||
target_compile_options(fast_float INTERFACE /permissive-)
|
||||
endif()
|
||||
|
||||
|
||||
if(FASTFLOAT_INSTALL)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
|
||||
@ -8,3 +8,4 @@ Lénárd Szolnoki
|
||||
Jan Pharago
|
||||
Maya Warrier
|
||||
Taha Khokhar
|
||||
Anders Dalvander
|
||||
|
||||
9
MODULE.bazel
Normal file
9
MODULE.bazel
Normal file
@ -0,0 +1,9 @@
|
||||
"""fast_float number parsing library: 4x faster than strtod"""
|
||||
|
||||
module(
|
||||
name = "fast_float",
|
||||
version = "6.1.6",
|
||||
compatibility_level = 6,
|
||||
)
|
||||
|
||||
bazel_dep(name = "doctest", version = "2.4.11", dev_dependency = True)
|
||||
547
README.md
547
README.md
@ -1,163 +1,212 @@
|
||||
|
||||
## fast_float number parsing library: 4x faster than strtod
|
||||
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fast_float)
|
||||
|
||||
[](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
|
||||
|
||||
The fast_float library provides fast header-only implementations for the C++ from_chars
|
||||
functions for `float` and `double` types as well as integer types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
|
||||
round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries.
|
||||
The fast_float library provides fast header-only implementations for the C++
|
||||
from_chars functions for `float` and `double` types as well as integer types.
|
||||
These functions convert ASCII strings representing decimal values (e.g.,
|
||||
`1.3e10`) into binary types. We provide exact rounding (including round to
|
||||
even). In our experience, these `fast_float` functions many times faster than
|
||||
comparable number-parsing functions from existing C++ standard libraries.
|
||||
|
||||
Specifically, `fast_float` provides the following two functions to parse floating-point numbers with a C++17-like syntax (the library itself only requires C++11):
|
||||
Specifically, `fast_float` provides the following two functions to parse
|
||||
floating-point numbers with a C++17-like syntax (the library itself only
|
||||
requires C++11):
|
||||
|
||||
```C++
|
||||
from_chars_result from_chars(const char* first, const char* last, float& value, ...);
|
||||
from_chars_result from_chars(const char* first, const char* last, double& value, ...);
|
||||
from_chars_result from_chars(char const *first, char const *last, float &value, ...);
|
||||
from_chars_result from_chars(char const *first, char const *last, double &value, ...);
|
||||
```
|
||||
|
||||
You can also parse integer types:
|
||||
|
||||
|
||||
|
||||
```C++
|
||||
from_chars_result from_chars(char const *first, char const *last, int &value, ...);
|
||||
from_chars_result from_chars(char const *first, char const *last, unsigned &value, ...);
|
||||
```
|
||||
|
||||
The return type (`from_chars_result`) is defined as the struct:
|
||||
|
||||
```C++
|
||||
struct from_chars_result {
|
||||
const char* ptr;
|
||||
std::errc ec;
|
||||
char const *ptr;
|
||||
std::errc ec;
|
||||
};
|
||||
```
|
||||
|
||||
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.
|
||||
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.
|
||||
That is, we provide exact parsing according to the IEEE standard.
|
||||
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. 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. That is, we provide exact parsing according to the IEEE standard.
|
||||
|
||||
Given a successful parse, the pointer (`ptr`) in the returned value is set to
|
||||
point right after the parsed number, and the `value` referenced is set to the
|
||||
parsed value. In case of error, the returned `ec` contains a representative
|
||||
error, otherwise the default (`std::errc()`) value is stored.
|
||||
|
||||
Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
|
||||
parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
|
||||
`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
|
||||
|
||||
The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
|
||||
The implementation does not throw and does not allocate memory (e.g., with `new`
|
||||
or `malloc`).
|
||||
|
||||
It will parse infinity and nan values.
|
||||
|
||||
Example:
|
||||
|
||||
``` C++
|
||||
```C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
const std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
Though the C++17 standard has you do a comparison with `std::errc()` to check whether the conversion worked, you can avoid it by casting the result to a `bool` like so:
|
||||
|
||||
```cpp
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
if(auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result)) {
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
std::cerr << "failed to parse " << result << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
```
|
||||
|
||||
You can parse delimited numbers:
|
||||
|
||||
```C++
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
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()) {
|
||||
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] != ',') {
|
||||
if (answer.ptr[0] != ',') {
|
||||
// unexpected delimiter
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
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] != ',') {
|
||||
if (answer.ptr[0] != ',') {
|
||||
// unexpected delimiter
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
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.
|
||||
```
|
||||
|
||||
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 `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. The default is
|
||||
`fast_float::chars_format::general` which allows both `fixed` and `scientific`.
|
||||
|
||||
The library seeks to follow the C++17 (see
|
||||
[28.2.3.(6.1)](https://eel.is/c++draft/charconv.from.chars#6.1)) specification.
|
||||
|
||||
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
|
||||
`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.
|
||||
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 `from_chars` function does not skip leading white-space characters.
|
||||
* [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.
|
||||
* The `from_chars` function does not skip leading white-space characters (unless
|
||||
`fast_float::chars_format::skip_white_space` is set).
|
||||
* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is
|
||||
forbidden (unless `fast_float::chars_format::allow_leading_plus` is set).
|
||||
* 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:
|
||||
* We only support `float` and `double` types at this time.
|
||||
|
||||
* We support `float` and `double`, but not `long double`. We also support
|
||||
fixed-width floating-point types such as `std::float64_t`, `std::float32_t`,
|
||||
`std::float16_t`, and `std::bfloat16_t`.
|
||||
* We only support the decimal format: we do not support hexadecimal strings.
|
||||
* For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`.
|
||||
* For values that are either very large or very small (e.g., `1e9999`), we
|
||||
represent it using the infinity or negative infinity value and the returned
|
||||
`ec` is set to `std::errc::result_out_of_range`.
|
||||
|
||||
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
|
||||
|
||||
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
|
||||
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`).
|
||||
|
||||
## Integer types
|
||||
|
||||
You can also parse integer types using different bases (e.g., 2, 10, 16). The following code will
|
||||
print the number 22250738585072012 three times:
|
||||
|
||||
You can also parse integer types using different bases (e.g., 2, 10, 16). The
|
||||
following code will print the number 22250738585072012 three times:
|
||||
|
||||
```C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
uint64_t i;
|
||||
const char str[] = "22250738585072012";
|
||||
auto answer = fast_float::from_chars(str, str + strlen(str), i);
|
||||
std::string str = "22250738585072012";
|
||||
auto answer = fast_float::from_chars(str.data(), str.data() + str.size(), i);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cerr << "parsing failure\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "parsed the number "<< i << std::endl;
|
||||
std::cout << "parsed the number " << i << std::endl;
|
||||
|
||||
const char binstr[] = "1001111000011001110110111001001010110100111000110001100";
|
||||
std::string binstr = "1001111000011001110110111001001010110100111000110001100";
|
||||
|
||||
answer = fast_float::from_chars(binstr, binstr + strlen(binstr), i, 2);
|
||||
answer = fast_float::from_chars(binstr.data(), binstr.data() + binstr.size(), i, 2);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cerr << "parsing failure\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "parsed the number "<< i << std::endl;
|
||||
std::cout << "parsed the number " << i << std::endl;
|
||||
|
||||
std::string hexstr = "4f0cedc95a718c";
|
||||
|
||||
const char hexstr[] = "4f0cedc95a718c";
|
||||
|
||||
answer = fast_float::from_chars(hexstr, hexstr + strlen(hexstr), i, 16);
|
||||
answer = fast_float::from_chars(hexstr.data(), hexstr.data() + hexstr.size(), i, 16);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cerr << "parsing failure\n";
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "parsed the number "<< i << std::endl;
|
||||
std::cout << "parsed the number " << i << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
## Behavior of result_out_of_range
|
||||
|
||||
When parsing floating-point values, the numbers can sometimes be too small (e.g., `1e-1000`) or
|
||||
too large (e.g., `1e1000`). The C language established the precedent that these small values are out of range.
|
||||
In such cases, it is customary to parse small values to zero and large
|
||||
values to infinity. That is the behaviour of the C language (e.g., `stdtod`). That is the behaviour followed by the fast_float library.
|
||||
|
||||
|
||||
When parsing floating-point values, the numbers can sometimes be too small
|
||||
(e.g., `1e-1000`) or too large (e.g., `1e1000`). The C language established the
|
||||
precedent that these small values are out of range. In such cases, it is
|
||||
customary to parse small values to zero and large values to infinity. That is
|
||||
the behaviour of the C language (e.g., `stdtod`). That is the behaviour followed
|
||||
by the fast_float library.
|
||||
|
||||
Specifically, we follow Jonathan Wakely's interpretation of the standard:
|
||||
|
||||
> In any case, the resulting value is one of at most two floating-point values closest to the value of the string matching the pattern.
|
||||
> In any case, the resulting value is one of at most two floating-point values
|
||||
> closest to the value of the string matching the pattern.
|
||||
|
||||
It is also the approach taken by the [Microsoft C++ library](https://github.com/microsoft/STL/blob/62205ab155d093e71dd9588a78f02c5396c3c14b/tests/std/tests/P0067R5_charconv/test.cpp#L943-L946).
|
||||
It is also the approach taken by the [Microsoft C++
|
||||
library](https://github.com/microsoft/STL/blob/62205ab155d093e71dd9588a78f02c5396c3c14b/tests/std/tests/P0067R5_charconv/test.cpp#L943-L946).
|
||||
|
||||
Hence, we have the following examples:
|
||||
|
||||
@ -170,7 +219,6 @@ Hence, we have the following examples:
|
||||
// result == 0
|
||||
```
|
||||
|
||||
|
||||
```cpp
|
||||
double result = -1;
|
||||
std::string str = "3e1000";
|
||||
@ -180,26 +228,26 @@ Hence, we have the following examples:
|
||||
// result == std::numeric_limits<double>::infinity()
|
||||
```
|
||||
|
||||
Users who wish for the value to be left unmodified given `std::errc::result_out_of_range` may do so by adding two lines of code:
|
||||
Users who wish for the value to be left unmodified given
|
||||
`std::errc::result_out_of_range` may do so by adding two lines of code:
|
||||
|
||||
```cpp
|
||||
double old_result = result; // make copy
|
||||
auto r = fast_float::from_chars(start, end, result);
|
||||
if(r.ec == std::errc::result_out_of_range) { result = old_result; }
|
||||
if (r.ec == std::errc::result_out_of_range) { result = old_result; }
|
||||
```
|
||||
|
||||
|
||||
## C++20: compile-time evaluation (constexpr)
|
||||
|
||||
In C++20, you may use `fast_float::from_chars` to parse strings
|
||||
at compile-time, as in the following example:
|
||||
In C++20, you may use `fast_float::from_chars` to parse strings at compile-time,
|
||||
as in the following example:
|
||||
|
||||
```C++
|
||||
// consteval forces compile-time evaluation of the function in C++20.
|
||||
consteval double parse(std::string_view input) {
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return -1.0; }
|
||||
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
if (answer.ec != std::errc()) { return -1.0; }
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -212,108 +260,107 @@ constexpr double constexptest() {
|
||||
|
||||
## C++23: Fixed width floating-point types
|
||||
|
||||
The library also supports fixed-width floating-point types such as `std::float32_t` and `std::float64_t`. E.g., you can write:
|
||||
The library also supports fixed-width floating-point types such as
|
||||
`std::float64_t`, `std::float32_t`, `std::float16_t`, and `std::bfloat16_t`.
|
||||
E.g., you can write:
|
||||
|
||||
```C++
|
||||
std::float32_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
``````
|
||||
|
||||
```
|
||||
|
||||
## Non-ASCII Inputs
|
||||
|
||||
We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the following example:
|
||||
We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the
|
||||
following example:
|
||||
|
||||
``` C++
|
||||
```C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
const std::u16string input = u"3.1416 xyz ";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
std::u16string input = u"3.1416 xyz ";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced options: using commas as decimal separator, JSON and Fortran
|
||||
|
||||
## Advanced options: using commas as decimal separator, JSON and Fortran
|
||||
|
||||
The C++ standard stipulate that `from_chars` has to be locale-independent. In
|
||||
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
|
||||
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.,
|
||||
the comma). You may use it as follows.
|
||||
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
|
||||
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., the comma). You may use it as follows.
|
||||
|
||||
```C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
const std::string input = "3,1416 xyz ";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::general, ','};
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
std::string input = "3,1416 xyz ";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::general, ','};
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
|
||||
if ((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
You can also parse Fortran-like inputs:
|
||||
### You can also parse Fortran-like inputs
|
||||
|
||||
```C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
const std::string input = "1d+4";
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::fortran };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
std::string input = "1d+4";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::fortran};
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
|
||||
if ((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6)):
|
||||
|
||||
### You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6))
|
||||
|
||||
```C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
const std::string input = "+.1"; // not valid
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
std::string input = "+.1"; // not valid
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json};
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
By default the JSON format does not allow `inf`:
|
||||
|
||||
```C++
|
||||
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
const std::string input = "inf"; // not valid in JSON
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::json };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||
std::string input = "inf"; // not valid in JSON
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json};
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
You can allow it with a non-standard `json_or_infnan` variant:
|
||||
|
||||
```C++
|
||||
@ -321,54 +368,135 @@ You can allow it with a non-standard `json_or_infnan` variant:
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
const std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
|
||||
double result;
|
||||
fast_float::parse_options options{ fast_float::chars_format::json_or_infnan };
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
|
||||
if(answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json_or_infnan};
|
||||
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
``````
|
||||
```
|
||||
|
||||
## Multiplication of an integer by a power of 10
|
||||
An integer `W` can be multiplied by a power of ten `10^Q` and
|
||||
converted to `double` with correctly rounded value
|
||||
(in "round to nearest, tie to even" fashion) using
|
||||
`fast_float::integer_times_pow10()`, e.g.:
|
||||
```C++
|
||||
const uint64_t W = 12345678901234567;
|
||||
const int Q = 23;
|
||||
const double result = fast_float::integer_times_pow10(W, Q);
|
||||
std::cout.precision(17);
|
||||
std::cout << W << " * 10^" << Q << " = " << result << " ("
|
||||
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
|
||||
```
|
||||
outputs
|
||||
```
|
||||
12345678901234567 * 10^23 = 1.2345678901234567e+39 (==expected)
|
||||
```
|
||||
`fast_float::integer_times_pow10()` gives the same result as
|
||||
using `fast_float::from_chars()` when parsing the string `"WeQ"`
|
||||
(in this example `"12345678901234567e23"`),
|
||||
except `fast_float::integer_times_pow10()` does not report out-of-range errors, and
|
||||
underflows to zero or overflows to infinity when the resulting value is
|
||||
out of range.
|
||||
|
||||
You can use template overloads to get the result converted to different
|
||||
supported floating-point types: `float`, `double`, etc.
|
||||
For example, to get result as `float` use
|
||||
`fast_float::integer_times_pow10<float>()` specialization:
|
||||
```C++
|
||||
const uint64_t W = 12345678;
|
||||
const int Q = 23;
|
||||
const float result = fast_float::integer_times_pow10<float>(W, Q);
|
||||
std::cout.precision(9);
|
||||
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
|
||||
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
|
||||
```
|
||||
outputs
|
||||
```
|
||||
float: 12345678 * 10^23 = 1.23456782e+30 (==expected)
|
||||
```
|
||||
|
||||
Overloads of `fast_float::integer_times_pow10()` are provided for
|
||||
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
|
||||
|
||||
|
||||
## Users and Related Work
|
||||
|
||||
The fast_float library is part of:
|
||||
|
||||
- GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
|
||||
- [Chromium](https://github.com/Chromium/Chromium), the engine behind Google Chrome and Microsoft Edge,
|
||||
- [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
|
||||
- [DuckDB](https://duckdb.org)
|
||||
- [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times
|
||||
- [Google Jsonnet](https://github.com/google/jsonnet)
|
||||
- [ClickHouse](https://github.com/ClickHouse/ClickHouse)
|
||||
* GCC (as of version 12): the `from_chars` function in GCC relies on fast_float,
|
||||
* [Chromium](https://github.com/Chromium/Chromium), the engine behind Google
|
||||
Chrome, Microsoft Edge, and Opera,
|
||||
* Boost JSON, MySQL, etc.
|
||||
* Blender
|
||||
* [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's
|
||||
web browser),
|
||||
* [DuckDB](https://duckdb.org),
|
||||
* [Redis](https://github.com/redis/redis) and [Valkey](https://github.com/valkey-io/valkey),
|
||||
* [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied
|
||||
the number parsing speed by two or three times,
|
||||
* [Google Jsonnet](https://github.com/google/jsonnet),
|
||||
* [ClickHouse](https://github.com/ClickHouse/ClickHouse).
|
||||
|
||||
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 [SerenityOS operating
|
||||
system](https://github.com/SerenityOS/serenity/commit/53b7f5e6a11e663c83df8030c3171c5945cb75ec)
|
||||
has a derived implementation that is inherited by the [Ladybird
|
||||
Browser](https://github.com/LadybirdBrowser/ladybird).
|
||||
|
||||
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).
|
||||
|
||||
|
||||
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).
|
||||
Packages
|
||||
------
|
||||
|
||||
[](https://repology.org/project/fast-float/versions)
|
||||
|
||||
|
||||
## 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 53 (7), 2023.
|
||||
* 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
|
||||
53 (7), 2023.
|
||||
|
||||
## Other programming languages
|
||||
|
||||
- [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 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 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 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`.
|
||||
|
||||
## How fast is it?
|
||||
|
||||
It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations.
|
||||
It can parse random floating-point numbers at a speed of 1 GB/s on some systems.
|
||||
We find that it is often twice as fast as the best available competitor, and
|
||||
many times faster than many standard-library implementations.
|
||||
|
||||
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
|
||||
<img src="https://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png"
|
||||
width="400" alt="fast_float is many times faster than many standard-library
|
||||
implementations">
|
||||
|
||||
```
|
||||
```bash
|
||||
$ ./build/benchmarks/benchmark
|
||||
# parsing random integers in the range [0,1)
|
||||
volume = 2.09808 MB
|
||||
@ -379,91 +507,128 @@ abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfl
|
||||
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
|
||||
```
|
||||
|
||||
See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
|
||||
|
||||
See the [Benchmarking](#benchmarking) section for instructions on how to run our benchmarks.
|
||||
|
||||
## Video
|
||||
|
||||
[](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br />
|
||||
[](https://www.youtube.com/watch?v=AVXgvlMeIm4)
|
||||
|
||||
## Using as a CMake dependency
|
||||
|
||||
This library is header-only by design. The CMake file provides the `fast_float` target
|
||||
which is merely a pointer to the `include` directory.
|
||||
This library is header-only by design. The CMake file provides the `fast_float`
|
||||
target which is merely a pointer to the `include` directory.
|
||||
|
||||
If you drop the `fast_float` repository in your CMake project, you should be able to use
|
||||
it in this manner:
|
||||
If you drop the `fast_float` repository in your CMake project, you should be
|
||||
able to use it in this manner:
|
||||
|
||||
```cmake
|
||||
add_subdirectory(fast_float)
|
||||
target_link_libraries(myprogram PUBLIC fast_float)
|
||||
```
|
||||
|
||||
Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least):
|
||||
Or you may want to retrieve the dependency automatically if you have a
|
||||
sufficiently recent version of CMake (3.11 or better at least):
|
||||
|
||||
```cmake
|
||||
FetchContent_Declare(
|
||||
fast_float
|
||||
GIT_REPOSITORY https://github.com/lemire/fast_float.git
|
||||
GIT_TAG tags/v1.1.2
|
||||
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
|
||||
GIT_TAG tags/v8.1.0
|
||||
GIT_SHALLOW TRUE)
|
||||
|
||||
FetchContent_MakeAvailable(fast_float)
|
||||
target_link_libraries(myprogram PUBLIC fast_float)
|
||||
|
||||
```
|
||||
|
||||
You should change the `GIT_TAG` line so that you recover the version you wish to use.
|
||||
You should change the `GIT_TAG` line so that you recover the version you wish to
|
||||
use.
|
||||
|
||||
You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
|
||||
|
||||
```
|
||||
```cmake
|
||||
CPMAddPackage(
|
||||
NAME fast_float
|
||||
GITHUB_REPOSITORY "fastfloat/fast_float"
|
||||
GIT_TAG v6.1.4)
|
||||
NAME fast_float
|
||||
GITHUB_REPOSITORY "fastfloat/fast_float"
|
||||
GIT_TAG v8.1.0)
|
||||
```
|
||||
|
||||
|
||||
## Using as single header
|
||||
|
||||
The script `script/amalgamate.py` may be used to generate a single header
|
||||
version of the library if so desired.
|
||||
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
|
||||
the command line help.
|
||||
version of the library if so desired. 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 the command line help.
|
||||
|
||||
You may directly download automatically generated single-header files:
|
||||
|
||||
https://github.com/fastfloat/fast_float/releases/download/v6.1.6/fast_float.h
|
||||
<https://github.com/fastfloat/fast_float/releases/download/v8.1.0/fast_float.h>
|
||||
|
||||
## Benchmarking
|
||||
|
||||
The project has its own benchmarks with realistic data inputs. Under Linux or macOS,
|
||||
you can use it as follows if your system supports C++17:
|
||||
|
||||
```
|
||||
cmake -B build -D FASTFLOAT_BENCHMARKS=ON
|
||||
cmake --build build
|
||||
./build/benchmarks/realbenchmark
|
||||
```
|
||||
|
||||
Importantly, by default, the benchmark is built in Release mode.
|
||||
|
||||
The instructions are similar under Windows.
|
||||
|
||||
Under Linux and macOS, it is recommended to run the benchmarks in a privileged manner to get access
|
||||
to hardware performance counters. You may be able to do so with the `sudo` command
|
||||
in some cases:
|
||||
|
||||
```
|
||||
sudo ./build/benchmarks/realbenchmark
|
||||
```
|
||||
|
||||
If you have a text file containing one number per line (`myfile.txt`), you can run a benchmark over it like so:
|
||||
```
|
||||
cmake -B build -D FASTFLOAT_BENCHMARKS=ON
|
||||
cmake --build build
|
||||
./build/benchmarks/realbenchmark myfile.txt
|
||||
```
|
||||
|
||||
|
||||
## Packages
|
||||
|
||||
- The fast_float library is part of the [Conan package manager](https://conan.io/center/recipes/fast_float).
|
||||
- It is part of the [brew package manager](https://formulae.brew.sh/formula/fast_float).
|
||||
- Some Linux distribution like Fedora include fast_float (e.g., as `fast_float-devel`).
|
||||
|
||||
## RFC 7159
|
||||
|
||||
If you need support for RFC 7159 (JSON standard), you may want to consider using the [fast_double_parser](https://github.com/lemire/fast_double_parser/) library instead.
|
||||
* The fast_float library is part of the [Conan package
|
||||
manager](https://conan.io/center/recipes/fast_float).
|
||||
* It is part of the [brew package
|
||||
manager](https://formulae.brew.sh/formula/fast_float).
|
||||
* fast_float is available on [xmake](https://xmake.io) repository.
|
||||
* Some Linux distribution like Fedora include fast_float (e.g., as
|
||||
`fast_float-devel`).
|
||||
|
||||
## Credit
|
||||
|
||||
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
|
||||
invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
|
||||
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 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
|
||||
under the Apache 2.0 license.
|
||||
The library includes code adapted from Google Wuffs (written by Nigel Tao) which
|
||||
was originally published under the Apache 2.0 license.
|
||||
|
||||
## Stars
|
||||
|
||||
|
||||
[](https://www.star-history.com/#fastfloat/fast_float&Date)
|
||||
|
||||
## License
|
||||
|
||||
<sup>
|
||||
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
||||
2.0</a> or <a href="LICENSE-MIT">MIT license</a> or <a href="LICENSE-BOOST">BOOST license</a> .
|
||||
2.0</a> or <a href="LICENSE-MIT">MIT license</a> or <a
|
||||
href="LICENSE-BOOST">BOOST license</a>.
|
||||
</sup>
|
||||
|
||||
<br>
|
||||
<br/>
|
||||
|
||||
<sub>
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted
|
||||
|
||||
26
benchmarks/CMakeLists.txt
Normal file
26
benchmarks/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
add_executable(realbenchmark benchmark.cpp)
|
||||
set_property(
|
||||
TARGET realbenchmark
|
||||
PROPERTY CXX_STANDARD 17)
|
||||
|
||||
target_link_libraries(realbenchmark PUBLIC fast_float)
|
||||
include(ExternalProject)
|
||||
|
||||
# Define the external project
|
||||
ExternalProject_Add(simple_fastfloat_benchmark
|
||||
GIT_REPOSITORY https://github.com/lemire/simple_fastfloat_benchmark.git
|
||||
GIT_TAG master # or specify a particular commit/tag/branch
|
||||
SOURCE_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark
|
||||
BINARY_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark-build
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND ""
|
||||
)
|
||||
set(DATA_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark/data)
|
||||
|
||||
add_custom_target(CopyData ALL
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory ${DATA_DIR} ${CMAKE_CURRENT_BINARY_DIR}/data
|
||||
DEPENDS simple_fastfloat_benchmark
|
||||
)
|
||||
add_dependencies(realbenchmark CopyData)
|
||||
target_compile_definitions(realbenchmark PUBLIC BENCHMARK_DATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data")
|
||||
1117
benchmarks/apple_arm_events.h
Normal file
1117
benchmarks/apple_arm_events.h
Normal file
File diff suppressed because it is too large
Load Diff
246
benchmarks/benchmark.cpp
Normal file
246
benchmarks/benchmark.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
#if defined(__linux__) || (__APPLE__ && __aarch64__)
|
||||
#define USING_COUNTERS
|
||||
#endif
|
||||
#include "event_counter.h"
|
||||
#include <algorithm>
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <chrono>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctype.h>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <locale.h>
|
||||
|
||||
template <typename CharT>
|
||||
double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
|
||||
double answer = 0;
|
||||
double x = 0;
|
||||
for (auto &st : s) {
|
||||
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
|
||||
if (p == st.data()) {
|
||||
throw std::runtime_error("bug in findmax_fastfloat");
|
||||
}
|
||||
answer = answer > x ? answer : x;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
double findmax_fastfloat32(std::vector<std::basic_string<CharT>> &s) {
|
||||
float answer = 0;
|
||||
float x = 0;
|
||||
for (auto &st : s) {
|
||||
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
|
||||
if (p == st.data()) {
|
||||
throw std::runtime_error("bug in findmax_fastfloat");
|
||||
}
|
||||
answer = answer > x ? answer : x;
|
||||
}
|
||||
return answer;
|
||||
}
|
||||
|
||||
event_collector collector{};
|
||||
|
||||
#ifdef USING_COUNTERS
|
||||
template <class T, class CharT>
|
||||
std::vector<event_count>
|
||||
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
||||
size_t repeat) {
|
||||
std::vector<event_count> aggregate;
|
||||
bool printed_bug = false;
|
||||
for (size_t i = 0; i < repeat; i++) {
|
||||
collector.start();
|
||||
double ts = function(lines);
|
||||
if (ts == 0 && !printed_bug) {
|
||||
printf("bug\n");
|
||||
printed_bug = true;
|
||||
}
|
||||
aggregate.push_back(collector.end());
|
||||
}
|
||||
return aggregate;
|
||||
}
|
||||
|
||||
void pretty_print(double volume, size_t number_of_floats, std::string name,
|
||||
std::vector<event_count> events) {
|
||||
double volumeMB = volume / (1024. * 1024.);
|
||||
double average_ns{0};
|
||||
double min_ns{DBL_MAX};
|
||||
double cycles_min{DBL_MAX};
|
||||
double instructions_min{DBL_MAX};
|
||||
double cycles_avg{0};
|
||||
double instructions_avg{0};
|
||||
double branches_min{0};
|
||||
double branches_avg{0};
|
||||
double branch_misses_min{0};
|
||||
double branch_misses_avg{0};
|
||||
for (event_count e : events) {
|
||||
double ns = e.elapsed_ns();
|
||||
average_ns += ns;
|
||||
min_ns = min_ns < ns ? min_ns : ns;
|
||||
|
||||
double cycles = e.cycles();
|
||||
cycles_avg += cycles;
|
||||
cycles_min = cycles_min < cycles ? cycles_min : cycles;
|
||||
|
||||
double instructions = e.instructions();
|
||||
instructions_avg += instructions;
|
||||
instructions_min =
|
||||
instructions_min < instructions ? instructions_min : instructions;
|
||||
|
||||
double branches = e.branches();
|
||||
branches_avg += branches;
|
||||
branches_min = branches_min < branches ? branches_min : branches;
|
||||
|
||||
double branch_misses = e.missed_branches();
|
||||
branch_misses_avg += branch_misses;
|
||||
branch_misses_min =
|
||||
branch_misses_min < branch_misses ? branch_misses_min : branch_misses;
|
||||
}
|
||||
cycles_avg /= events.size();
|
||||
instructions_avg /= events.size();
|
||||
average_ns /= events.size();
|
||||
branches_avg /= events.size();
|
||||
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
|
||||
volumeMB * 1000000000 / min_ns,
|
||||
(average_ns - min_ns) * 100.0 / average_ns);
|
||||
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / min_ns);
|
||||
if (instructions_min > 0) {
|
||||
printf(" %8.2f i/B %8.2f i/f (+/- %.1f %%) ", instructions_min / volume,
|
||||
instructions_min / number_of_floats,
|
||||
(instructions_avg - instructions_min) * 100.0 / instructions_avg);
|
||||
|
||||
printf(" %8.2f c/B %8.2f c/f (+/- %.1f %%) ", cycles_min / volume,
|
||||
cycles_min / number_of_floats,
|
||||
(cycles_avg - cycles_min) * 100.0 / cycles_avg);
|
||||
printf(" %8.2f i/c ", instructions_min / cycles_min);
|
||||
printf(" %8.2f b/f ", branches_avg / number_of_floats);
|
||||
printf(" %8.2f bm/f ", branch_misses_avg / number_of_floats);
|
||||
printf(" %8.2f GHz ", cycles_min / min_ns);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
#else
|
||||
template <class T, class CharT>
|
||||
std::pair<double, double>
|
||||
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
|
||||
size_t repeat) {
|
||||
std::chrono::high_resolution_clock::time_point t1, t2;
|
||||
double average = 0;
|
||||
double min_value = DBL_MAX;
|
||||
bool printed_bug = false;
|
||||
for (size_t i = 0; i < repeat; i++) {
|
||||
t1 = std::chrono::high_resolution_clock::now();
|
||||
double ts = function(lines);
|
||||
if (ts == 0 && !printed_bug) {
|
||||
printf("bug\n");
|
||||
printed_bug = true;
|
||||
}
|
||||
t2 = std::chrono::high_resolution_clock::now();
|
||||
double dif =
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
|
||||
average += dif;
|
||||
min_value = min_value < dif ? min_value : dif;
|
||||
}
|
||||
average /= repeat;
|
||||
return std::make_pair(min_value, average);
|
||||
}
|
||||
|
||||
void pretty_print(double volume, size_t number_of_floats, std::string name,
|
||||
std::pair<double, double> result) {
|
||||
double volumeMB = volume / (1024. * 1024.);
|
||||
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
|
||||
volumeMB * 1000000000 / result.first,
|
||||
(result.second - result.first) * 100.0 / result.second);
|
||||
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / result.first);
|
||||
printf(" %8.2f ns/f \n", double(result.first) / number_of_floats);
|
||||
}
|
||||
#endif
|
||||
|
||||
// this is okay, all chars are ASCII
|
||||
inline std::u16string widen(std::string line) {
|
||||
std::u16string u16line;
|
||||
u16line.resize(line.size());
|
||||
for (size_t i = 0; i < line.size(); ++i) {
|
||||
u16line[i] = char16_t(line[i]);
|
||||
}
|
||||
return u16line;
|
||||
}
|
||||
|
||||
std::vector<std::u16string> widen(const std::vector<std::string> &lines) {
|
||||
std::vector<std::u16string> u16lines;
|
||||
u16lines.reserve(lines.size());
|
||||
for (auto const &line : lines) {
|
||||
u16lines.push_back(widen(line));
|
||||
}
|
||||
return u16lines;
|
||||
}
|
||||
|
||||
void process(std::vector<std::string> &lines, size_t volume) {
|
||||
size_t repeat = 1000;
|
||||
double volumeMB = volume / (1024. * 1024.);
|
||||
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
|
||||
pretty_print(volume, lines.size(), "fastfloat (64)",
|
||||
time_it_ns(lines, findmax_fastfloat64<char>, repeat));
|
||||
pretty_print(volume, lines.size(), "fastfloat (32)",
|
||||
time_it_ns(lines, findmax_fastfloat32<char>, repeat));
|
||||
|
||||
std::vector<std::u16string> lines16 = widen(lines);
|
||||
volume = 2 * volume;
|
||||
volumeMB = volume / (1024. * 1024.);
|
||||
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
|
||||
pretty_print(volume, lines.size(), "fastfloat (64)",
|
||||
time_it_ns(lines16, findmax_fastfloat64<char16_t>, repeat));
|
||||
pretty_print(volume, lines.size(), "fastfloat (32)",
|
||||
time_it_ns(lines16, findmax_fastfloat32<char16_t>, repeat));
|
||||
}
|
||||
|
||||
void fileload(std::string filename) {
|
||||
std::ifstream inputfile(filename);
|
||||
if (!inputfile) {
|
||||
std::cerr << "can't open " << filename << std::endl;
|
||||
return;
|
||||
}
|
||||
std::cout << "#### " << std::endl;
|
||||
std::cout << "# reading " << filename << std::endl;
|
||||
std::cout << "#### " << std::endl;
|
||||
std::string line;
|
||||
std::vector<std::string> lines;
|
||||
lines.reserve(10000); // let us reserve plenty of memory.
|
||||
size_t volume = 0;
|
||||
while (getline(inputfile, line)) {
|
||||
volume += line.size();
|
||||
lines.push_back(line);
|
||||
}
|
||||
std::cout << "# read " << lines.size() << " lines " << std::endl;
|
||||
process(lines, volume);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (collector.has_events()) {
|
||||
std::cout << "# Using hardware counters" << std::endl;
|
||||
} else {
|
||||
#if defined(__linux__) || (__APPLE__ && __aarch64__)
|
||||
std::cout << "# Hardware counters not available, try to run in privileged "
|
||||
"mode (e.g., sudo)."
|
||||
<< std::endl;
|
||||
#endif
|
||||
}
|
||||
if (argc > 1) {
|
||||
fileload(argv[1]);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
|
||||
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
181
benchmarks/event_counter.h
Normal file
181
benchmarks/event_counter.h
Normal file
@ -0,0 +1,181 @@
|
||||
#ifndef __EVENT_COUNTER_H
|
||||
#define __EVENT_COUNTER_H
|
||||
|
||||
#include <cctype>
|
||||
#ifndef _MSC_VER
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
#include <cinttypes>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <chrono>
|
||||
#include <vector>
|
||||
|
||||
#include "linux-perf-events.h"
|
||||
#ifdef __linux__
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#if (defined(__APPLE__) && __APPLE__) && (defined(__aarch64__) && __aarch64__)
|
||||
#include "apple_arm_events.h"
|
||||
#endif
|
||||
|
||||
struct event_count {
|
||||
std::chrono::duration<double> elapsed;
|
||||
std::vector<unsigned long long> event_counts;
|
||||
|
||||
event_count() : elapsed(0), event_counts{0, 0, 0, 0, 0} {}
|
||||
|
||||
event_count(const std::chrono::duration<double> _elapsed,
|
||||
const std::vector<unsigned long long> _event_counts)
|
||||
: elapsed(_elapsed), event_counts(_event_counts) {}
|
||||
|
||||
event_count(const event_count &other)
|
||||
: elapsed(other.elapsed), event_counts(other.event_counts) {}
|
||||
|
||||
// The types of counters (so we can read the getter more easily)
|
||||
enum event_counter_types {
|
||||
CPU_CYCLES = 0,
|
||||
INSTRUCTIONS = 1,
|
||||
BRANCHES = 2,
|
||||
MISSED_BRANCHES = 3
|
||||
};
|
||||
|
||||
double elapsed_sec() const {
|
||||
return std::chrono::duration<double>(elapsed).count();
|
||||
}
|
||||
|
||||
double elapsed_ns() const {
|
||||
return std::chrono::duration<double, std::nano>(elapsed).count();
|
||||
}
|
||||
|
||||
double cycles() const {
|
||||
return static_cast<double>(event_counts[CPU_CYCLES]);
|
||||
}
|
||||
|
||||
double instructions() const {
|
||||
return static_cast<double>(event_counts[INSTRUCTIONS]);
|
||||
}
|
||||
|
||||
double branches() const {
|
||||
return static_cast<double>(event_counts[BRANCHES]);
|
||||
}
|
||||
|
||||
double missed_branches() const {
|
||||
return static_cast<double>(event_counts[MISSED_BRANCHES]);
|
||||
}
|
||||
|
||||
event_count &operator=(const event_count &other) {
|
||||
this->elapsed = other.elapsed;
|
||||
this->event_counts = other.event_counts;
|
||||
return *this;
|
||||
}
|
||||
|
||||
event_count operator+(const event_count &other) const {
|
||||
return event_count(elapsed + other.elapsed,
|
||||
{
|
||||
event_counts[0] + other.event_counts[0],
|
||||
event_counts[1] + other.event_counts[1],
|
||||
event_counts[2] + other.event_counts[2],
|
||||
event_counts[3] + other.event_counts[3],
|
||||
event_counts[4] + other.event_counts[4],
|
||||
});
|
||||
}
|
||||
|
||||
void operator+=(const event_count &other) { *this = *this + other; }
|
||||
};
|
||||
|
||||
struct event_aggregate {
|
||||
bool has_events = false;
|
||||
int iterations = 0;
|
||||
event_count total{};
|
||||
event_count best{};
|
||||
event_count worst{};
|
||||
|
||||
event_aggregate() = default;
|
||||
|
||||
void operator<<(const event_count &other) {
|
||||
if (iterations == 0 || other.elapsed < best.elapsed) {
|
||||
best = other;
|
||||
}
|
||||
if (iterations == 0 || other.elapsed > worst.elapsed) {
|
||||
worst = other;
|
||||
}
|
||||
iterations++;
|
||||
total += other;
|
||||
}
|
||||
|
||||
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
|
||||
|
||||
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
|
||||
|
||||
double cycles() const { return total.cycles() / iterations; }
|
||||
|
||||
double instructions() const { return total.instructions() / iterations; }
|
||||
|
||||
double branches() const { return total.branches() / iterations; }
|
||||
|
||||
double missed_branches() const {
|
||||
return total.missed_branches() / iterations;
|
||||
}
|
||||
};
|
||||
|
||||
struct event_collector {
|
||||
event_count count{};
|
||||
std::chrono::time_point<std::chrono::steady_clock> start_clock{};
|
||||
|
||||
#if defined(__linux__)
|
||||
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
|
||||
|
||||
event_collector()
|
||||
: linux_events(std::vector<int>{
|
||||
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS,
|
||||
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions
|
||||
PERF_COUNT_HW_BRANCH_MISSES}) {}
|
||||
|
||||
bool has_events() { return linux_events.is_working(); }
|
||||
#elif __APPLE__ && __aarch64__
|
||||
performance_counters diff;
|
||||
|
||||
event_collector() : diff(0) { setup_performance_counters(); }
|
||||
|
||||
bool has_events() { return setup_performance_counters(); }
|
||||
#else
|
||||
event_collector() {}
|
||||
|
||||
bool has_events() { return false; }
|
||||
#endif
|
||||
|
||||
inline void start() {
|
||||
#if defined(__linux)
|
||||
linux_events.start();
|
||||
#elif __APPLE__ && __aarch64__
|
||||
if (has_events()) {
|
||||
diff = get_counters();
|
||||
}
|
||||
#endif
|
||||
start_clock = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
inline event_count &end() {
|
||||
const auto end_clock = std::chrono::steady_clock::now();
|
||||
#if defined(__linux)
|
||||
linux_events.end(count.event_counts);
|
||||
#elif __APPLE__ && __aarch64__
|
||||
if (has_events()) {
|
||||
performance_counters end = get_counters();
|
||||
diff = end - diff;
|
||||
}
|
||||
count.event_counts[0] = diff.cycles;
|
||||
count.event_counts[1] = diff.instructions;
|
||||
count.event_counts[2] = diff.branches;
|
||||
count.event_counts[3] = diff.missed_branches;
|
||||
count.event_counts[4] = 0;
|
||||
#endif
|
||||
count.elapsed = end_clock - start_clock;
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
104
benchmarks/linux-perf-events.h
Normal file
104
benchmarks/linux-perf-events.h
Normal file
@ -0,0 +1,104 @@
|
||||
#pragma once
|
||||
#ifdef __linux__
|
||||
|
||||
#include <asm/unistd.h> // for __NR_perf_event_open
|
||||
#include <linux/perf_event.h> // for perf event constants
|
||||
#include <sys/ioctl.h> // for ioctl
|
||||
#include <unistd.h> // for syscall
|
||||
|
||||
#include <cerrno> // for errno
|
||||
#include <cstring> // for memset
|
||||
#include <stdexcept>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
|
||||
int fd;
|
||||
bool working;
|
||||
perf_event_attr attribs{};
|
||||
size_t num_events{};
|
||||
std::vector<uint64_t> temp_result_vec{};
|
||||
std::vector<uint64_t> ids{};
|
||||
|
||||
public:
|
||||
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
|
||||
memset(&attribs, 0, sizeof(attribs));
|
||||
attribs.type = TYPE;
|
||||
attribs.size = sizeof(attribs);
|
||||
attribs.disabled = 1;
|
||||
attribs.exclude_kernel = 1;
|
||||
attribs.exclude_hv = 1;
|
||||
|
||||
attribs.sample_period = 0;
|
||||
attribs.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
|
||||
const int pid = 0; // the current process
|
||||
const int cpu = -1; // all CPUs
|
||||
const unsigned long flags = 0;
|
||||
|
||||
int group = -1; // no group
|
||||
num_events = config_vec.size();
|
||||
ids.resize(config_vec.size());
|
||||
uint32_t i = 0;
|
||||
for (auto config : config_vec) {
|
||||
attribs.config = config;
|
||||
int _fd = static_cast<int>(
|
||||
syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
|
||||
if (_fd == -1) {
|
||||
report_error("perf_event_open");
|
||||
}
|
||||
ioctl(_fd, PERF_EVENT_IOC_ID, &ids[i++]);
|
||||
if (group == -1) {
|
||||
group = _fd;
|
||||
fd = _fd;
|
||||
}
|
||||
}
|
||||
|
||||
temp_result_vec.resize(num_events * 2 + 1);
|
||||
}
|
||||
|
||||
~LinuxEvents() {
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
|
||||
inline void start() {
|
||||
if (fd != -1) {
|
||||
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
|
||||
report_error("ioctl(PERF_EVENT_IOC_RESET)");
|
||||
}
|
||||
|
||||
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
||||
report_error("ioctl(PERF_EVENT_IOC_ENABLE)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void end(std::vector<unsigned long long> &results) {
|
||||
if (fd != -1) {
|
||||
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
||||
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
|
||||
}
|
||||
|
||||
if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) {
|
||||
report_error("read");
|
||||
}
|
||||
}
|
||||
// our actual results are in slots 1,3,5, ... of this structure
|
||||
for (uint32_t i = 1; i < temp_result_vec.size(); i += 2) {
|
||||
results[i / 2] = temp_result_vec[i];
|
||||
}
|
||||
for (uint32_t i = 2; i < temp_result_vec.size(); i += 2) {
|
||||
if (ids[i / 2 - 1] != temp_result_vec[i]) {
|
||||
report_error("event mismatch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_working() { return working; }
|
||||
|
||||
private:
|
||||
void report_error(const std::string &) { working = false; }
|
||||
};
|
||||
#endif
|
||||
4
cmake/toolchains-ci/riscv64-linux-gnu.cmake
Normal file
4
cmake/toolchains-ci/riscv64-linux-gnu.cmake
Normal file
@ -0,0 +1,4 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR riscv64)
|
||||
|
||||
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-riscv64-static")
|
||||
@ -19,7 +19,7 @@ fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) {
|
||||
return chars_format::general;
|
||||
}
|
||||
|
||||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
|
||||
extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) {
|
||||
FuzzedDataProvider fdp(data, size);
|
||||
fast_float::chars_format format = arbitrary_format(fdp);
|
||||
double result_d = 0.0;
|
||||
|
||||
@ -45,7 +45,7 @@ fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
|
||||
// Read 8 UC into a u64. Truncates UC if not char.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
|
||||
read8_to_u64(const UC *chars) {
|
||||
read8_to_u64(UC const *chars) {
|
||||
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
|
||||
uint64_t val = 0;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
@ -65,9 +65,9 @@ read8_to_u64(const UC *chars) {
|
||||
|
||||
#ifdef FASTFLOAT_SSE2
|
||||
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const __m128i data) {
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const __m128i packed = _mm_packus_epi16(data, data);
|
||||
__m128i const packed = _mm_packus_epi16(data, data);
|
||||
#ifdef FASTFLOAT_64BIT
|
||||
return uint64_t(_mm_cvtsi128_si64(packed));
|
||||
#else
|
||||
@ -79,26 +79,26 @@ fastfloat_really_inline uint64_t simd_read8_to_u64(const __m128i data) {
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
return simd_read8_to_u64(
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)));
|
||||
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)));
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
#elif defined(FASTFLOAT_NEON)
|
||||
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const uint16x8_t data) {
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
uint8x8_t utf8_packed = vmovn_u16(data);
|
||||
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
|
||||
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) {
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
return simd_read8_to_u64(
|
||||
vld1q_u16(reinterpret_cast<const uint16_t *>(chars)));
|
||||
vld1q_u16(reinterpret_cast<uint16_t const *>(chars)));
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
}
|
||||
|
||||
@ -118,9 +118,9 @@ uint64_t simd_read8_to_u64(UC const *) {
|
||||
// credit @aqrit
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
|
||||
parse_eight_digits_unrolled(uint64_t val) {
|
||||
const uint64_t mask = 0x000000FF000000FF;
|
||||
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
||||
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
||||
uint64_t const mask = 0x000000FF000000FF;
|
||||
uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
||||
uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
||||
val -= 0x3030303030303030;
|
||||
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
|
||||
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
|
||||
@ -150,20 +150,20 @@ is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
||||
// Using this style (instead of is_made_of_eight_digits_fast() then
|
||||
// parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
simd_parse_if_eight_digits_unrolled(const char16_t *chars,
|
||||
simd_parse_if_eight_digits_unrolled(char16_t const *chars,
|
||||
uint64_t &i) noexcept {
|
||||
if (cpp20_and_in_constexpr()) {
|
||||
return false;
|
||||
}
|
||||
#ifdef FASTFLOAT_SSE2
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const __m128i data =
|
||||
_mm_loadu_si128(reinterpret_cast<const __m128i *>(chars));
|
||||
__m128i const data =
|
||||
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars));
|
||||
|
||||
// (x - '0') <= 9
|
||||
// http://0x80.pl/articles/simd-parsing-int-sequences.html
|
||||
const __m128i t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
|
||||
const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
|
||||
__m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
|
||||
__m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
|
||||
|
||||
if (_mm_movemask_epi8(t1) == 0) {
|
||||
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
||||
@ -173,12 +173,12 @@ simd_parse_if_eight_digits_unrolled(const char16_t *chars,
|
||||
FASTFLOAT_SIMD_RESTORE_WARNINGS
|
||||
#elif defined(FASTFLOAT_NEON)
|
||||
FASTFLOAT_SIMD_DISABLE_WARNINGS
|
||||
const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t *>(chars));
|
||||
uint16x8_t const data = vld1q_u16(reinterpret_cast<uint16_t const *>(chars));
|
||||
|
||||
// (x - '0') <= 9
|
||||
// http://0x80.pl/articles/simd-parsing-int-sequences.html
|
||||
const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
|
||||
const uint16x8_t mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
|
||||
uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0'));
|
||||
uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
|
||||
|
||||
if (vminvq_u16(mask) == 0xFFFF) {
|
||||
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
|
||||
@ -208,7 +208,7 @@ bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
|
||||
|
||||
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
loop_parse_if_eight_digits(const UC *&p, const UC *const pend, uint64_t &i) {
|
||||
loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) {
|
||||
if (!has_simd_opt<UC>()) {
|
||||
return;
|
||||
}
|
||||
@ -220,7 +220,7 @@ loop_parse_if_eight_digits(const UC *&p, const UC *const pend, uint64_t &i) {
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
loop_parse_if_eight_digits(const char *&p, const char *const pend,
|
||||
loop_parse_if_eight_digits(char const *&p, char const *const pend,
|
||||
uint64_t &i) {
|
||||
// optimizes better than parse_if_eight_digits_unrolled() for UC = char.
|
||||
while ((std::distance(p, pend) >= 8) &&
|
||||
@ -259,12 +259,12 @@ template <typename UC> struct parsed_number_string_t {
|
||||
bool valid{false};
|
||||
bool too_many_digits{false};
|
||||
// contains the range of the significant digits
|
||||
span<const UC> integer{}; // non-nullable
|
||||
span<const UC> fraction{}; // nullable
|
||||
span<UC const> integer{}; // non-nullable
|
||||
span<UC const> fraction{}; // nullable
|
||||
parse_error error{parse_error::no_error};
|
||||
};
|
||||
|
||||
using byte_span = span<const char>;
|
||||
using byte_span = span<char const>;
|
||||
using parsed_number_string = parsed_number_string_t<char>;
|
||||
|
||||
template <typename UC>
|
||||
@ -279,33 +279,33 @@ report_parse_error(UC const *p, parse_error error) {
|
||||
|
||||
// Assuming that you use no more than 19 digits, this will
|
||||
// parse an ASCII string.
|
||||
template <typename UC>
|
||||
template <bool basic_json_fmt, typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
||||
parse_number_string(UC const *p, UC const *pend,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
chars_format const fmt = options.format;
|
||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
||||
UC const decimal_point = options.decimal_point;
|
||||
|
||||
parsed_number_string_t<UC> answer;
|
||||
answer.valid = false;
|
||||
answer.too_many_digits = false;
|
||||
// assume p < pend, so dereference without checks;
|
||||
answer.negative = (*p == UC('-'));
|
||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||
if ((*p == UC('-')) || (!(fmt & FASTFLOAT_JSONFMT) && *p == UC('+'))) {
|
||||
#else
|
||||
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||
#endif
|
||||
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||
if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
|
||||
!basic_json_fmt && *p == UC('+'))) {
|
||||
++p;
|
||||
if (p == pend) {
|
||||
return report_parse_error<UC>(
|
||||
p, parse_error::missing_integer_or_dot_after_sign);
|
||||
}
|
||||
if (fmt & FASTFLOAT_JSONFMT) {
|
||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||
if (!is_integer(*p)) { // a sign must be followed by an integer
|
||||
return report_parse_error<UC>(p,
|
||||
parse_error::missing_integer_after_sign);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
if (!is_integer(*p) &&
|
||||
(*p !=
|
||||
decimal_point)) { // a sign must be followed by an integer or the dot
|
||||
@ -328,8 +328,8 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
}
|
||||
UC const *const end_of_integer_part = p;
|
||||
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
||||
answer.integer = span<const UC>(start_digits, size_t(digit_count));
|
||||
if (fmt & FASTFLOAT_JSONFMT) {
|
||||
answer.integer = span<UC const>(start_digits, size_t(digit_count));
|
||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||
// at least 1 digit in integer part, without leading zeros
|
||||
if (digit_count == 0) {
|
||||
return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
|
||||
@ -341,7 +341,7 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
}
|
||||
|
||||
int64_t exponent = 0;
|
||||
const bool has_decimal_point = (p != pend) && (*p == decimal_point);
|
||||
bool const has_decimal_point = (p != pend) && (*p == decimal_point);
|
||||
if (has_decimal_point) {
|
||||
++p;
|
||||
UC const *before = p;
|
||||
@ -355,23 +355,23 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
|
||||
}
|
||||
exponent = before - p;
|
||||
answer.fraction = span<const UC>(before, size_t(p - before));
|
||||
answer.fraction = span<UC const>(before, size_t(p - before));
|
||||
digit_count -= exponent;
|
||||
}
|
||||
if (fmt & FASTFLOAT_JSONFMT) {
|
||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||
// at least 1 digit in fractional part
|
||||
if (has_decimal_point && exponent == 0) {
|
||||
return report_parse_error<UC>(p,
|
||||
parse_error::no_digits_in_fractional_part);
|
||||
}
|
||||
} else if (digit_count ==
|
||||
0) { // we must have encountered at least one integer!
|
||||
}
|
||||
else if (digit_count == 0) { // we must have encountered at least one integer!
|
||||
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
|
||||
}
|
||||
int64_t exp_number = 0; // explicit exponential part
|
||||
if (((fmt & chars_format::scientific) && (p != pend) &&
|
||||
if ((uint64_t(fmt & chars_format::scientific) && (p != pend) &&
|
||||
((UC('e') == *p) || (UC('E') == *p))) ||
|
||||
((fmt & FASTFLOAT_FORTRANFMT) && (p != pend) &&
|
||||
(uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) &&
|
||||
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
|
||||
(UC('D') == *p)))) {
|
||||
UC const *location_of_e = p;
|
||||
@ -389,7 +389,7 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
++p;
|
||||
}
|
||||
if ((p == pend) || !is_integer(*p)) {
|
||||
if (!(fmt & chars_format::fixed)) {
|
||||
if (!uint64_t(fmt & chars_format::fixed)) {
|
||||
// The exponential part is invalid for scientific notation, so it must
|
||||
// be a trailing token for fixed notation. However, fixed notation is
|
||||
// disabled, so report a scientific notation error.
|
||||
@ -412,7 +412,8 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
}
|
||||
} else {
|
||||
// If it scientific and not fixed, we have to bail out.
|
||||
if ((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) {
|
||||
if (uint64_t(fmt & chars_format::scientific) &&
|
||||
!uint64_t(fmt & chars_format::fixed)) {
|
||||
return report_parse_error<UC>(p, parse_error::missing_exponential_part);
|
||||
}
|
||||
}
|
||||
@ -440,17 +441,17 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
if (digit_count > 19) {
|
||||
answer.too_many_digits = true;
|
||||
// Let us start again, this time, avoiding overflows.
|
||||
// We don't need to check if is_integer, since we use the
|
||||
// We don't need to call if is_integer, since we use the
|
||||
// pre-tokenized spans from above.
|
||||
i = 0;
|
||||
p = answer.integer.ptr;
|
||||
UC const *int_end = p + answer.integer.len();
|
||||
const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
|
||||
uint64_t const minimal_nineteen_digit_integer{1000000000000000000};
|
||||
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
|
||||
i = i * 10 + uint64_t(*p - UC('0'));
|
||||
++p;
|
||||
}
|
||||
if (i >= minimal_nineteen_digit_integer) { // We have a big integers
|
||||
if (i >= minimal_nineteen_digit_integer) { // We have a big integer
|
||||
exponent = end_of_integer_part - p + exp_number;
|
||||
} else { // We have a value with a fractional component.
|
||||
p = answer.fraction.ptr;
|
||||
@ -471,22 +472,30 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
|
||||
template <typename T, typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
parse_int_string(UC const *p, UC const *pend, T &value, int base) {
|
||||
parse_int_string(UC const *p, UC const *pend, T &value,
|
||||
parse_options_t<UC> options) {
|
||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
||||
int const base = options.base;
|
||||
|
||||
from_chars_result_t<UC> answer;
|
||||
|
||||
UC const *const first = p;
|
||||
|
||||
bool negative = (*p == UC('-'));
|
||||
bool const negative = (*p == UC('-'));
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable : 4127)
|
||||
#endif
|
||||
if (!std::is_signed<T>::value && negative) {
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
answer.ptr = first;
|
||||
return answer;
|
||||
}
|
||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||
if ((*p == UC('-')) || (*p == UC('+'))) {
|
||||
#else
|
||||
if (*p == UC('-')) {
|
||||
#endif
|
||||
if ((*p == UC('-')) ||
|
||||
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) {
|
||||
++p;
|
||||
}
|
||||
|
||||
@ -496,7 +505,7 @@ parse_int_string(UC const *p, UC const *pend, T &value, int base) {
|
||||
++p;
|
||||
}
|
||||
|
||||
const bool has_leading_zeros = p > start_num;
|
||||
bool const has_leading_zeros = p > start_num;
|
||||
|
||||
UC const *const start_digits = p;
|
||||
|
||||
|
||||
@ -43,8 +43,8 @@ template <uint16_t size> struct stackvec {
|
||||
uint16_t length{0};
|
||||
|
||||
stackvec() = default;
|
||||
stackvec(const stackvec &) = delete;
|
||||
stackvec &operator=(const stackvec &) = delete;
|
||||
stackvec(stackvec const &) = delete;
|
||||
stackvec &operator=(stackvec const &) = delete;
|
||||
stackvec(stackvec &&) = delete;
|
||||
stackvec &operator=(stackvec &&other) = delete;
|
||||
|
||||
@ -57,10 +57,12 @@ template <uint16_t size> struct stackvec {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return data[index];
|
||||
}
|
||||
|
||||
// index from the end of the container
|
||||
FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
@ -72,14 +74,19 @@ template <uint16_t size> struct stackvec {
|
||||
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
|
||||
length = uint16_t(len);
|
||||
}
|
||||
|
||||
constexpr size_t len() const noexcept { return length; }
|
||||
|
||||
constexpr bool is_empty() const noexcept { return length == 0; }
|
||||
|
||||
constexpr size_t capacity() const noexcept { return size; }
|
||||
|
||||
// append item to vector, without bounds checking
|
||||
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
|
||||
data[length] = value;
|
||||
length++;
|
||||
}
|
||||
|
||||
// append item to vector, returning if item was added
|
||||
FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
|
||||
if (len() < capacity()) {
|
||||
@ -89,12 +96,14 @@ template <uint16_t size> struct stackvec {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// add items to the vector, from a span, without bounds checking
|
||||
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
|
||||
limb *ptr = data + length;
|
||||
std::copy_n(s.ptr, s.len(), ptr);
|
||||
set_len(len() + s.len());
|
||||
}
|
||||
|
||||
// try to add items to the vector, returning if items were added
|
||||
FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
|
||||
if (len() + s.len() <= capacity()) {
|
||||
@ -104,6 +113,7 @@ template <uint16_t size> struct stackvec {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// resize the vector, without bounds checking
|
||||
// if the new size is longer than the vector, assign value to each
|
||||
// appended item.
|
||||
@ -119,6 +129,7 @@ template <uint16_t size> struct stackvec {
|
||||
set_len(new_len);
|
||||
}
|
||||
}
|
||||
|
||||
// try to resize the vector, returning if the vector was resized.
|
||||
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
|
||||
if (new_len > capacity()) {
|
||||
@ -128,6 +139,7 @@ template <uint16_t size> struct stackvec {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// check if any limbs are non-zero after the given index.
|
||||
// this needs to be done in reverse order, since the index
|
||||
// is relative to the most significant limbs.
|
||||
@ -140,6 +152,7 @@ template <uint16_t size> struct stackvec {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// normalize the big integer, so most-significant zero limbs are removed.
|
||||
FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
|
||||
while (len() > 0 && rindex(0) == 0) {
|
||||
@ -423,8 +436,9 @@ struct bigint : pow5_tables<> {
|
||||
stackvec<bigint_limbs> vec;
|
||||
|
||||
FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
|
||||
bigint(const bigint &) = delete;
|
||||
bigint &operator=(const bigint &) = delete;
|
||||
|
||||
bigint(bigint const &) = delete;
|
||||
bigint &operator=(bigint const &) = delete;
|
||||
bigint(bigint &&) = delete;
|
||||
bigint &operator=(bigint &&other) = delete;
|
||||
|
||||
@ -473,7 +487,7 @@ struct bigint : pow5_tables<> {
|
||||
// positive, this is larger, otherwise they are equal.
|
||||
// the limbs are stored in little-endian order, so we
|
||||
// must compare the limbs in ever order.
|
||||
FASTFLOAT_CONSTEXPR20 int compare(const bigint &other) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept {
|
||||
if (vec.len() > other.vec.len()) {
|
||||
return 1;
|
||||
} else if (vec.len() < other.vec.len()) {
|
||||
@ -527,7 +541,7 @@ struct bigint : pow5_tables<> {
|
||||
} else if (!vec.is_empty()) {
|
||||
// move limbs
|
||||
limb *dst = vec.data + n;
|
||||
const limb *src = vec.data;
|
||||
limb const *src = vec.data;
|
||||
std::copy_backward(src, src + vec.len(), dst + vec.len());
|
||||
// fill in empty limbs
|
||||
limb *first = vec.data;
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#endif
|
||||
|
||||
// Testing for https://wg21.link/N3652, adopted in C++14
|
||||
#if __cpp_constexpr >= 201304
|
||||
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304
|
||||
#define FASTFLOAT_CONSTEXPR14 constexpr
|
||||
#else
|
||||
#define FASTFLOAT_CONSTEXPR14
|
||||
@ -27,8 +27,15 @@
|
||||
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
|
||||
#define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x)
|
||||
#else
|
||||
#define FASTFLOAT_IF_CONSTEXPR17(x) if (x)
|
||||
#endif
|
||||
|
||||
// Testing for relevant C++20 constexpr library features
|
||||
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \
|
||||
defined(__cpp_lib_constexpr_algorithms) && \
|
||||
__cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
|
||||
#define FASTFLOAT_CONSTEXPR20 constexpr
|
||||
#define FASTFLOAT_IS_CONSTEXPR 1
|
||||
|
||||
@ -20,7 +20,7 @@ namespace fast_float {
|
||||
template <int bit_precision>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
|
||||
compute_product_approximation(int64_t q, uint64_t w) {
|
||||
const int index = 2 * int(q - powers::smallest_power_of_five);
|
||||
int const index = 2 * int(q - powers::smallest_power_of_five);
|
||||
// For small values of q, e.g., q in [0,27], the answer is always exact
|
||||
// because The line value128 firstproduct = full_multiplication(w,
|
||||
// power_of_five_128[index]); gives the exact answer.
|
||||
@ -93,8 +93,8 @@ compute_error(int64_t q, uint64_t w) noexcept {
|
||||
return compute_error_scaled<binary>(q, product.high, lz);
|
||||
}
|
||||
|
||||
// w * 10 ** q
|
||||
// The returned value should be a valid ieee64 number that simply need to be
|
||||
// Computers w * 10 ** q.
|
||||
// The returned value should be a valid number that simply needs to be
|
||||
// packed. However, in some very rare cases, the computation will fail. In such
|
||||
// cases, we return an adjusted_mantissa with a negative power of 2: the caller
|
||||
// should recompute in such cases.
|
||||
@ -158,7 +158,8 @@ compute_float(int64_t q, uint64_t w) noexcept {
|
||||
// next line is safe because -answer.power2 + 1 < 64
|
||||
answer.mantissa >>= -answer.power2 + 1;
|
||||
// Thankfully, we can't have both "round-to-even" and subnormals because
|
||||
// "round-to-even" only occurs for powers close to 0.
|
||||
// "round-to-even" only occurs for powers close to 0 in the 32-bit and
|
||||
// and 64-bit case (with no more than 19 digits).
|
||||
answer.mantissa += (answer.mantissa & 1); // round up
|
||||
answer.mantissa >>= 1;
|
||||
// There is a weird scenario where we don't have a subnormal but just.
|
||||
|
||||
@ -38,11 +38,8 @@ constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
|
||||
// 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
|
||||
// to slow down performance for faster algorithms, and this is still fast.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
|
||||
scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
|
||||
uint64_t mantissa = num.mantissa;
|
||||
int32_t exponent = int32_t(num.exponent);
|
||||
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
|
||||
while (mantissa >= 10000) {
|
||||
mantissa /= 10000;
|
||||
exponent += 4;
|
||||
@ -62,7 +59,7 @@ scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
|
||||
template <typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
|
||||
to_extended(T value) noexcept {
|
||||
using equiv_uint = typename binary_format<T>::equiv_uint;
|
||||
using equiv_uint = equiv_uint_t<T>;
|
||||
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();
|
||||
@ -143,8 +140,8 @@ template <typename callback>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
|
||||
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
|
||||
callback cb) noexcept {
|
||||
const uint64_t mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
|
||||
const uint64_t halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
|
||||
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
|
||||
uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1);
|
||||
uint64_t truncated_bits = am.mantissa & mask;
|
||||
bool is_above = truncated_bits > halfway;
|
||||
bool is_halfway = truncated_bits == halfway;
|
||||
@ -170,6 +167,7 @@ round_down(adjusted_mantissa &am, int32_t shift) noexcept {
|
||||
}
|
||||
am.power2 += shift;
|
||||
}
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
skip_zeros(UC const *&first, UC const *last) noexcept {
|
||||
@ -213,15 +211,16 @@ is_truncated(UC const *first, UC const *last) noexcept {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
is_truncated(span<const UC> s) noexcept {
|
||||
is_truncated(span<UC const> s) noexcept {
|
||||
return is_truncated(s.ptr, s.ptr + s.len());
|
||||
}
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
parse_eight_digits(const UC *&p, limb &value, size_t &counter,
|
||||
parse_eight_digits(UC const *&p, limb &value, size_t &counter,
|
||||
size_t &count) noexcept {
|
||||
value = value * 100000000 + parse_eight_digits_unrolled(p);
|
||||
p += 8;
|
||||
@ -396,7 +395,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
|
||||
}
|
||||
|
||||
// compare digits, and use it to director rounding
|
||||
// compare digits, and use it to direct rounding
|
||||
int ord = real_digits.compare(theor_digits);
|
||||
adjusted_mantissa answer = am;
|
||||
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
|
||||
@ -417,7 +416,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
return answer;
|
||||
}
|
||||
|
||||
// parse the significant digits as a big integer to unambiguously round the
|
||||
// parse the significant digits as a big integer to unambiguously round
|
||||
// the significant digits. here, we are trying to determine how to round
|
||||
// an extended float representation close to `b+h`, halfway between `b`
|
||||
// (the float rounded-down) and `b+u`, the next positive float. this
|
||||
@ -436,7 +435,8 @@ digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
|
||||
// remove the invalid exponent bias
|
||||
am.power2 -= invalid_am_bias;
|
||||
|
||||
int32_t sci_exp = scientific_exponent(num);
|
||||
int32_t sci_exp =
|
||||
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
|
||||
size_t max_digits = binary_format<T>::max_digits();
|
||||
size_t digits = 0;
|
||||
bigint bigmant;
|
||||
|
||||
@ -31,26 +31,61 @@ namespace fast_float {
|
||||
* `scientific`.
|
||||
*/
|
||||
template <typename T, typename UC = char,
|
||||
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
|
||||
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars(UC const *first, UC const *last, T &value,
|
||||
chars_format fmt = chars_format::general) noexcept;
|
||||
|
||||
/**
|
||||
* Like from_chars, but accepts an `options` argument to govern number parsing.
|
||||
* Both for floating-point types and integer types.
|
||||
*/
|
||||
template <typename T, typename UC = char>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept;
|
||||
|
||||
/**
|
||||
* This function multiplies an integer number by a power of 10 and returns
|
||||
* the result as a double precision floating-point value that is correctly
|
||||
* rounded. The resulting floating-point value is the closest floating-point
|
||||
* value, using the "round to nearest, tie to even" convention for values that
|
||||
* would otherwise fall right in-between two values. That is, we provide exact
|
||||
* conversion according to the IEEE standard.
|
||||
*
|
||||
* On overflow infinity is returned, on underflow 0 is returned.
|
||||
*
|
||||
* The implementation does not throw and does not allocate memory (e.g., with
|
||||
* `new` or `malloc`).
|
||||
*/
|
||||
FASTFLOAT_CONSTEXPR20 inline double
|
||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
|
||||
FASTFLOAT_CONSTEXPR20 inline double
|
||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
|
||||
|
||||
/**
|
||||
* This function is a template overload of `integer_times_pow10()`
|
||||
* that returns a floating-point value of type `T` that is one of
|
||||
* supported floating-point types (e.g. `double`, `float`).
|
||||
*/
|
||||
template <typename T>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
|
||||
template <typename T>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
|
||||
|
||||
/**
|
||||
* from_chars for integer types.
|
||||
*/
|
||||
template <typename T, typename UC = char,
|
||||
typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())>
|
||||
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
|
||||
|
||||
} // namespace fast_float
|
||||
|
||||
#include "parse_number.h"
|
||||
#endif // FASTFLOAT_FAST_FLOAT_H
|
||||
|
||||
@ -5,49 +5,81 @@
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
#ifdef __has_include
|
||||
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || _MSVC_LANG > 202002L)
|
||||
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || (defined(_MSVC_LANG) && (_MSVC_LANG > 202002L)))
|
||||
#include <stdfloat>
|
||||
#endif
|
||||
#endif
|
||||
#include "constexpr_feature_detect.h"
|
||||
|
||||
#define FASTFLOAT_VERSION_MAJOR 8
|
||||
#define FASTFLOAT_VERSION_MINOR 1
|
||||
#define FASTFLOAT_VERSION_PATCH 0
|
||||
|
||||
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
|
||||
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
|
||||
|
||||
#define FASTFLOAT_VERSION_STR \
|
||||
FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MAJOR) \
|
||||
"." FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MINOR) "." FASTFLOAT_STRINGIZE( \
|
||||
FASTFLOAT_VERSION_PATCH)
|
||||
|
||||
#define FASTFLOAT_VERSION \
|
||||
(FASTFLOAT_VERSION_MAJOR * 10000 + FASTFLOAT_VERSION_MINOR * 100 + \
|
||||
FASTFLOAT_VERSION_PATCH)
|
||||
|
||||
namespace fast_float {
|
||||
|
||||
#define FASTFLOAT_JSONFMT (1 << 5)
|
||||
#define FASTFLOAT_FORTRANFMT (1 << 6)
|
||||
enum class chars_format : uint64_t;
|
||||
|
||||
enum chars_format {
|
||||
namespace detail {
|
||||
constexpr chars_format basic_json_fmt = chars_format(1 << 5);
|
||||
constexpr chars_format basic_fortran_fmt = chars_format(1 << 6);
|
||||
} // namespace detail
|
||||
|
||||
enum class chars_format : uint64_t {
|
||||
scientific = 1 << 0,
|
||||
fixed = 1 << 2,
|
||||
hex = 1 << 3,
|
||||
no_infnan = 1 << 4,
|
||||
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
|
||||
json = FASTFLOAT_JSONFMT | fixed | scientific | no_infnan,
|
||||
json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan,
|
||||
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
|
||||
json_or_infnan = FASTFLOAT_JSONFMT | fixed | scientific,
|
||||
fortran = FASTFLOAT_FORTRANFMT | fixed | scientific,
|
||||
general = fixed | scientific
|
||||
json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific,
|
||||
fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific,
|
||||
general = fixed | scientific,
|
||||
allow_leading_plus = 1 << 7,
|
||||
skip_white_space = 1 << 8,
|
||||
};
|
||||
|
||||
template <typename UC> struct from_chars_result_t {
|
||||
UC const *ptr;
|
||||
std::errc ec;
|
||||
|
||||
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2497r0.html
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
return ec == std::errc();
|
||||
}
|
||||
};
|
||||
|
||||
using from_chars_result = from_chars_result_t<char>;
|
||||
|
||||
template <typename UC> struct parse_options_t {
|
||||
constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
|
||||
UC dot = UC('.'))
|
||||
: format(fmt), decimal_point(dot) {}
|
||||
UC dot = UC('.'), int b = 10)
|
||||
: format(fmt), decimal_point(dot), base(b) {}
|
||||
|
||||
/** Which number formats are accepted */
|
||||
chars_format format;
|
||||
/** The character used as decimal point */
|
||||
UC decimal_point;
|
||||
/** The base used for integers */
|
||||
int base;
|
||||
};
|
||||
|
||||
using parse_options = parse_options_t<char>;
|
||||
|
||||
} // namespace fast_float
|
||||
@ -61,11 +93,12 @@ using parse_options = parse_options_t<char>;
|
||||
defined(__MINGW64__) || defined(__s390x__) || \
|
||||
(defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
|
||||
defined(__PPC64LE__)) || \
|
||||
defined(__loongarch64))
|
||||
defined(__loongarch64) || (defined(__riscv) && __riscv_xlen == 64))
|
||||
#define FASTFLOAT_64BIT 1
|
||||
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
|
||||
defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \
|
||||
defined(__MINGW32__) || defined(__EMSCRIPTEN__))
|
||||
defined(__MINGW32__) || defined(__EMSCRIPTEN__) || \
|
||||
(defined(__riscv) && __riscv_xlen == 32))
|
||||
#define FASTFLOAT_32BIT 1
|
||||
#else
|
||||
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
|
||||
@ -194,32 +227,58 @@ fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
fastfloat_really_inline constexpr bool is_supported_float_type() {
|
||||
return std::is_same<T, float>::value || std::is_same<T, double>::value
|
||||
#if __STDCPP_FLOAT32_T__
|
||||
|| std::is_same<T, std::float32_t>::value
|
||||
struct is_supported_float_type
|
||||
: std::integral_constant<
|
||||
bool, std::is_same<T, double>::value || std::is_same<T, float>::value
|
||||
#ifdef __STDCPP_FLOAT64_T__
|
||||
|| std::is_same<T, std::float64_t>::value
|
||||
#endif
|
||||
#if __STDCPP_FLOAT64_T__
|
||||
|| std::is_same<T, std::float64_t>::value
|
||||
#ifdef __STDCPP_FLOAT32_T__
|
||||
|| std::is_same<T, std::float32_t>::value
|
||||
#endif
|
||||
;
|
||||
}
|
||||
#ifdef __STDCPP_FLOAT16_T__
|
||||
|| std::is_same<T, std::float16_t>::value
|
||||
#endif
|
||||
#ifdef __STDCPP_BFLOAT16_T__
|
||||
|| std::is_same<T, std::bfloat16_t>::value
|
||||
#endif
|
||||
> {
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using equiv_uint_t = typename std::conditional<
|
||||
sizeof(T) == 1, uint8_t,
|
||||
typename std::conditional<
|
||||
sizeof(T) == 2, uint16_t,
|
||||
typename std::conditional<sizeof(T) == 4, uint32_t,
|
||||
uint64_t>::type>::type>::type;
|
||||
|
||||
template <typename T> struct is_supported_integer_type : std::is_integral<T> {};
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline constexpr bool is_supported_char_type() {
|
||||
return std::is_same<UC, char>::value || std::is_same<UC, wchar_t>::value ||
|
||||
std::is_same<UC, char16_t>::value || std::is_same<UC, char32_t>::value;
|
||||
}
|
||||
struct is_supported_char_type
|
||||
: std::integral_constant<bool, std::is_same<UC, char>::value ||
|
||||
std::is_same<UC, wchar_t>::value ||
|
||||
std::is_same<UC, char16_t>::value ||
|
||||
std::is_same<UC, char32_t>::value
|
||||
#ifdef __cpp_char8_t
|
||||
|| std::is_same<UC, char8_t>::value
|
||||
#endif
|
||||
> {
|
||||
};
|
||||
|
||||
// Compares two ASCII strings in a case insensitive manner.
|
||||
template <typename UC>
|
||||
inline FASTFLOAT_CONSTEXPR14 bool
|
||||
fastfloat_strncasecmp(UC const *input1, UC const *input2, size_t length) {
|
||||
char running_diff{0};
|
||||
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
|
||||
size_t length) {
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
running_diff |= (char(input1[i]) ^ char(input2[i]));
|
||||
UC const actual = actual_mixedcase[i];
|
||||
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (running_diff == 0) || (running_diff == 32);
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifndef FLT_EVAL_METHOD
|
||||
@ -228,9 +287,11 @@ fastfloat_strncasecmp(UC const *input1, UC const *input2, size_t length) {
|
||||
|
||||
// a pointer and a length to a contiguous block of memory
|
||||
template <typename T> struct span {
|
||||
const T *ptr;
|
||||
T const *ptr;
|
||||
size_t length;
|
||||
constexpr span(const T *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
||||
|
||||
constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
||||
|
||||
constexpr span() : ptr(nullptr), length(0) {}
|
||||
|
||||
constexpr size_t len() const noexcept { return length; }
|
||||
@ -244,7 +305,9 @@ template <typename T> struct span {
|
||||
struct value128 {
|
||||
uint64_t low;
|
||||
uint64_t high;
|
||||
|
||||
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
|
||||
|
||||
constexpr value128() : low(0), high(0) {}
|
||||
};
|
||||
|
||||
@ -343,8 +406,8 @@ full_multiplication(uint64_t a, uint64_t b) {
|
||||
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
|
||||
answer.high = __umulh(a, b);
|
||||
answer.low = a * b;
|
||||
#elif defined(FASTFLOAT_32BIT) || \
|
||||
(defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64))
|
||||
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__) && \
|
||||
!defined(_M_ARM64) && !defined(__GNUC__))
|
||||
answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
|
||||
#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
|
||||
__uint128_t r = ((__uint128_t)a) * b;
|
||||
@ -360,10 +423,12 @@ struct adjusted_mantissa {
|
||||
uint64_t mantissa{0};
|
||||
int32_t power2{0}; // a negative value indicates an invalid result
|
||||
adjusted_mantissa() = default;
|
||||
constexpr bool operator==(const adjusted_mantissa &o) const {
|
||||
|
||||
constexpr bool operator==(adjusted_mantissa const &o) const {
|
||||
return mantissa == o.mantissa && power2 == o.power2;
|
||||
}
|
||||
constexpr bool operator!=(const adjusted_mantissa &o) const {
|
||||
|
||||
constexpr bool operator!=(adjusted_mantissa const &o) const {
|
||||
return mantissa != o.mantissa || power2 != o.power2;
|
||||
}
|
||||
};
|
||||
@ -377,28 +442,27 @@ constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
|
||||
template <typename T, typename U = void> struct binary_format_lookup_tables;
|
||||
|
||||
template <typename T> struct binary_format : binary_format_lookup_tables<T> {
|
||||
using equiv_uint =
|
||||
typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
|
||||
using equiv_uint = equiv_uint_t<T>;
|
||||
|
||||
static inline constexpr int mantissa_explicit_bits();
|
||||
static inline constexpr int minimum_exponent();
|
||||
static inline constexpr int infinite_power();
|
||||
static inline constexpr int sign_index();
|
||||
static inline constexpr int
|
||||
static constexpr int mantissa_explicit_bits();
|
||||
static constexpr int minimum_exponent();
|
||||
static constexpr int infinite_power();
|
||||
static constexpr int sign_index();
|
||||
static 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_round_to_even();
|
||||
static inline constexpr int min_exponent_round_to_even();
|
||||
static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
|
||||
static inline constexpr uint64_t
|
||||
static constexpr int max_exponent_fast_path();
|
||||
static constexpr int max_exponent_round_to_even();
|
||||
static constexpr int min_exponent_round_to_even();
|
||||
static constexpr uint64_t max_mantissa_fast_path(int64_t power);
|
||||
static constexpr uint64_t
|
||||
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||
static inline constexpr int largest_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 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();
|
||||
static constexpr int largest_power_of_ten();
|
||||
static constexpr int smallest_power_of_ten();
|
||||
static constexpr T exact_power_of_ten(int64_t power);
|
||||
static constexpr size_t max_digits();
|
||||
static constexpr equiv_uint exponent_mask();
|
||||
static constexpr equiv_uint mantissa_mask();
|
||||
static constexpr equiv_uint hidden_bit_mask();
|
||||
};
|
||||
|
||||
template <typename U> struct binary_format_lookup_tables<double, U> {
|
||||
@ -506,6 +570,7 @@ template <>
|
||||
inline constexpr int binary_format<double>::mantissa_explicit_bits() {
|
||||
return 52;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::mantissa_explicit_bits() {
|
||||
return 23;
|
||||
@ -534,6 +599,7 @@ inline constexpr int binary_format<float>::min_exponent_round_to_even() {
|
||||
template <> inline constexpr int binary_format<double>::minimum_exponent() {
|
||||
return -1023;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::minimum_exponent() {
|
||||
return -127;
|
||||
}
|
||||
@ -541,6 +607,7 @@ template <> inline constexpr int binary_format<float>::minimum_exponent() {
|
||||
template <> inline constexpr int binary_format<double>::infinite_power() {
|
||||
return 0x7FF;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::infinite_power() {
|
||||
return 0xFF;
|
||||
}
|
||||
@ -548,6 +615,7 @@ 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<float>::sign_index() {
|
||||
return 31;
|
||||
}
|
||||
@ -556,6 +624,7 @@ template <>
|
||||
inline constexpr int binary_format<double>::max_exponent_fast_path() {
|
||||
return 22;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<float>::max_exponent_fast_path() {
|
||||
return 10;
|
||||
@ -565,6 +634,261 @@ template <>
|
||||
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
|
||||
// credit: Jakub Jelínek
|
||||
#ifdef __STDCPP_FLOAT16_T__
|
||||
template <typename U> struct binary_format_lookup_tables<std::float16_t, U> {
|
||||
static constexpr std::float16_t powers_of_ten[] = {1e0f16, 1e1f16, 1e2f16,
|
||||
1e3f16, 1e4f16};
|
||||
|
||||
// Largest integer value v so that (5**index * v) <= 1<<11.
|
||||
// 0x800 == 1<<11
|
||||
static constexpr uint64_t max_mantissa[] = {0x800,
|
||||
0x800 / 5,
|
||||
0x800 / (5 * 5),
|
||||
0x800 / (5 * 5 * 5),
|
||||
0x800 / (5 * 5 * 5 * 5),
|
||||
0x800 / (constant_55555)};
|
||||
};
|
||||
|
||||
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
||||
|
||||
template <typename U>
|
||||
constexpr std::float16_t
|
||||
binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
|
||||
|
||||
template <typename U>
|
||||
constexpr uint64_t
|
||||
binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
|
||||
|
||||
#endif
|
||||
|
||||
template <>
|
||||
inline constexpr std::float16_t
|
||||
binary_format<std::float16_t>::exact_power_of_ten(int64_t power) {
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr binary_format<std::float16_t>::equiv_uint
|
||||
binary_format<std::float16_t>::exponent_mask() {
|
||||
return 0x7C00;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr binary_format<std::float16_t>::equiv_uint
|
||||
binary_format<std::float16_t>::mantissa_mask() {
|
||||
return 0x03FF;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr binary_format<std::float16_t>::equiv_uint
|
||||
binary_format<std::float16_t>::hidden_bit_mask() {
|
||||
return 0x0400;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::float16_t>::mantissa_explicit_bits() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<std::float16_t>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
|
||||
// caller is responsible to ensure that
|
||||
// power >= 0 && power <= 4
|
||||
//
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)max_mantissa[0], max_mantissa[power];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int
|
||||
binary_format<std::float16_t>::max_exponent_round_to_even() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int
|
||||
binary_format<std::float16_t>::min_exponent_round_to_even() {
|
||||
return -22;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::float16_t>::minimum_exponent() {
|
||||
return -15;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::float16_t>::infinite_power() {
|
||||
return 0x1F;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<std::float16_t>::sign_index() {
|
||||
return 15;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() {
|
||||
return 4;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() {
|
||||
return -27;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr size_t binary_format<std::float16_t>::max_digits() {
|
||||
return 22;
|
||||
}
|
||||
#endif // __STDCPP_FLOAT16_T__
|
||||
|
||||
// credit: Jakub Jelínek
|
||||
#ifdef __STDCPP_BFLOAT16_T__
|
||||
template <typename U> struct binary_format_lookup_tables<std::bfloat16_t, U> {
|
||||
static constexpr std::bfloat16_t powers_of_ten[] = {1e0bf16, 1e1bf16, 1e2bf16,
|
||||
1e3bf16};
|
||||
|
||||
// Largest integer value v so that (5**index * v) <= 1<<8.
|
||||
// 0x100 == 1<<8
|
||||
static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5),
|
||||
0x100 / (5 * 5 * 5),
|
||||
0x100 / (5 * 5 * 5 * 5)};
|
||||
};
|
||||
|
||||
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
|
||||
|
||||
template <typename U>
|
||||
constexpr std::bfloat16_t
|
||||
binary_format_lookup_tables<std::bfloat16_t, U>::powers_of_ten[];
|
||||
|
||||
template <typename U>
|
||||
constexpr uint64_t
|
||||
binary_format_lookup_tables<std::bfloat16_t, U>::max_mantissa[];
|
||||
|
||||
#endif
|
||||
|
||||
template <>
|
||||
inline constexpr std::bfloat16_t
|
||||
binary_format<std::bfloat16_t>::exact_power_of_ten(int64_t power) {
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
|
||||
binary_format<std::bfloat16_t>::exponent_mask() {
|
||||
return 0x7F80;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
|
||||
binary_format<std::bfloat16_t>::mantissa_mask() {
|
||||
return 0x007F;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
|
||||
binary_format<std::bfloat16_t>::hidden_bit_mask() {
|
||||
return 0x0080;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
|
||||
return 7;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
|
||||
// caller is responsible to ensure that
|
||||
// power >= 0 && power <= 3
|
||||
//
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)max_mantissa[0], max_mantissa[power];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int
|
||||
binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int
|
||||
binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
|
||||
return -24;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::bfloat16_t>::minimum_exponent() {
|
||||
return -127;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::bfloat16_t>::infinite_power() {
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() {
|
||||
return 15;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() {
|
||||
return 38;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() {
|
||||
return -60;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() {
|
||||
return 98;
|
||||
}
|
||||
#endif // __STDCPP_BFLOAT16_T__
|
||||
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<double>::max_mantissa_fast_path(int64_t power) {
|
||||
@ -574,10 +898,7 @@ binary_format<double>::max_mantissa_fast_path(int64_t power) {
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)max_mantissa[0], max_mantissa[power];
|
||||
}
|
||||
template <>
|
||||
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr uint64_t
|
||||
binary_format<float>::max_mantissa_fast_path(int64_t power) {
|
||||
@ -594,6 +915,7 @@ binary_format<double>::exact_power_of_ten(int64_t power) {
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
return (void)powers_of_ten[0], powers_of_ten[power];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
|
||||
// Work around clang bug https://godbolt.org/z/zedh7rrhc
|
||||
@ -603,6 +925,7 @@ inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
|
||||
template <> inline constexpr int binary_format<double>::largest_power_of_ten() {
|
||||
return 308;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::largest_power_of_ten() {
|
||||
return 38;
|
||||
}
|
||||
@ -611,6 +934,7 @@ template <>
|
||||
inline constexpr int binary_format<double>::smallest_power_of_ten() {
|
||||
return -342;
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
|
||||
return -64;
|
||||
}
|
||||
@ -618,6 +942,7 @@ template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
|
||||
template <> inline constexpr size_t binary_format<double>::max_digits() {
|
||||
return 769;
|
||||
}
|
||||
|
||||
template <> inline constexpr size_t binary_format<float>::max_digits() {
|
||||
return 114;
|
||||
}
|
||||
@ -627,6 +952,7 @@ 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() {
|
||||
@ -638,6 +964,7 @@ 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() {
|
||||
@ -649,6 +976,7 @@ 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() {
|
||||
@ -658,11 +986,12 @@ binary_format<double>::hidden_bit_mask() {
|
||||
template <typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
|
||||
to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||
using fastfloat_uint = typename binary_format<T>::equiv_uint;
|
||||
fastfloat_uint word = (fastfloat_uint)am.mantissa;
|
||||
word |= fastfloat_uint(am.power2)
|
||||
<< binary_format<T>::mantissa_explicit_bits();
|
||||
word |= fastfloat_uint(negative) << binary_format<T>::sign_index();
|
||||
using equiv_uint = equiv_uint_t<T>;
|
||||
equiv_uint word = equiv_uint(am.mantissa);
|
||||
word = equiv_uint(word | equiv_uint(am.power2)
|
||||
<< binary_format<T>::mantissa_explicit_bits());
|
||||
word =
|
||||
equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
|
||||
#if FASTFLOAT_HAS_BIT_CAST
|
||||
value = std::bit_cast<T>(word);
|
||||
#else
|
||||
@ -670,7 +999,6 @@ to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||
template <typename = void> struct space_lut {
|
||||
static constexpr bool value[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
@ -692,8 +1020,9 @@ template <typename T> constexpr bool space_lut<T>::value[];
|
||||
|
||||
#endif
|
||||
|
||||
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
|
||||
#endif
|
||||
template <typename UC> constexpr bool is_space(UC c) {
|
||||
return c < 256 && space_lut<>::value[uint8_t(c)];
|
||||
}
|
||||
|
||||
template <typename UC> static constexpr uint64_t int_cmp_zeros() {
|
||||
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
|
||||
@ -704,34 +1033,53 @@ template <typename UC> static constexpr uint64_t int_cmp_zeros() {
|
||||
uint64_t(UC('0')) << 16 | UC('0'))
|
||||
: (uint64_t(UC('0')) << 32 | UC('0'));
|
||||
}
|
||||
|
||||
template <typename UC> static constexpr int int_cmp_len() {
|
||||
return sizeof(uint64_t) / sizeof(UC);
|
||||
}
|
||||
template <typename UC> static constexpr UC const *str_const_nan() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <typename UC> constexpr UC const *str_const_nan();
|
||||
|
||||
template <> constexpr char const *str_const_nan<char>() { return "nan"; }
|
||||
|
||||
template <> constexpr wchar_t const *str_const_nan<wchar_t>() { return L"nan"; }
|
||||
|
||||
template <> constexpr char16_t const *str_const_nan<char16_t>() {
|
||||
return u"nan";
|
||||
}
|
||||
|
||||
template <> constexpr char32_t const *str_const_nan<char32_t>() {
|
||||
return U"nan";
|
||||
}
|
||||
template <typename UC> static constexpr UC const *str_const_inf() {
|
||||
return nullptr;
|
||||
|
||||
#ifdef __cpp_char8_t
|
||||
template <> constexpr char8_t const *str_const_nan<char8_t>() {
|
||||
return u8"nan";
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename UC> constexpr UC const *str_const_inf();
|
||||
|
||||
template <> constexpr char const *str_const_inf<char>() { return "infinity"; }
|
||||
|
||||
template <> constexpr wchar_t const *str_const_inf<wchar_t>() {
|
||||
return L"infinity";
|
||||
}
|
||||
|
||||
template <> constexpr char16_t const *str_const_inf<char16_t>() {
|
||||
return u"infinity";
|
||||
}
|
||||
|
||||
template <> constexpr char32_t const *str_const_inf<char32_t>() {
|
||||
return U"infinity";
|
||||
}
|
||||
|
||||
#ifdef __cpp_char8_t
|
||||
template <> constexpr char8_t const *str_const_inf<char8_t>() {
|
||||
return u8"infinity";
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename = void> struct int_luts {
|
||||
static constexpr uint8_t chdigit[] = {
|
||||
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
|
||||
@ -784,7 +1132,12 @@ template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
|
||||
return int_luts<>::chdigit[static_cast<unsigned char>(c)];
|
||||
// wchar_t and char can be signed, so we need to be careful.
|
||||
using UnsignedUC = typename std::make_unsigned<UC>::type;
|
||||
return int_luts<>::chdigit[static_cast<unsigned char>(
|
||||
static_cast<UnsignedUC>(c) &
|
||||
static_cast<UnsignedUC>(
|
||||
-((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))];
|
||||
}
|
||||
|
||||
fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
|
||||
@ -797,6 +1150,107 @@ fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
|
||||
return int_luts<>::min_safe_u64[base - 2];
|
||||
}
|
||||
|
||||
static_assert(std::is_same<equiv_uint_t<double>, uint64_t>::value,
|
||||
"equiv_uint should be uint64_t for double");
|
||||
static_assert(std::numeric_limits<double>::is_iec559,
|
||||
"double must fulfill the requirements of IEC 559 (IEEE 754)");
|
||||
|
||||
static_assert(std::is_same<equiv_uint_t<float>, uint32_t>::value,
|
||||
"equiv_uint should be uint32_t for float");
|
||||
static_assert(std::numeric_limits<float>::is_iec559,
|
||||
"float must fulfill the requirements of IEC 559 (IEEE 754)");
|
||||
|
||||
#ifdef __STDCPP_FLOAT64_T__
|
||||
static_assert(std::is_same<equiv_uint_t<std::float64_t>, uint64_t>::value,
|
||||
"equiv_uint should be uint64_t for std::float64_t");
|
||||
static_assert(
|
||||
std::numeric_limits<std::float64_t>::is_iec559,
|
||||
"std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)");
|
||||
|
||||
template <>
|
||||
struct binary_format<std::float64_t> : public binary_format<double> {};
|
||||
#endif // __STDCPP_FLOAT64_T__
|
||||
|
||||
#ifdef __STDCPP_FLOAT32_T__
|
||||
static_assert(std::is_same<equiv_uint_t<std::float32_t>, uint32_t>::value,
|
||||
"equiv_uint should be uint32_t for std::float32_t");
|
||||
static_assert(
|
||||
std::numeric_limits<std::float32_t>::is_iec559,
|
||||
"std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)");
|
||||
|
||||
template <>
|
||||
struct binary_format<std::float32_t> : public binary_format<float> {};
|
||||
#endif // __STDCPP_FLOAT32_T__
|
||||
|
||||
#ifdef __STDCPP_FLOAT16_T__
|
||||
static_assert(
|
||||
std::is_same<binary_format<std::float16_t>::equiv_uint, uint16_t>::value,
|
||||
"equiv_uint should be uint16_t for std::float16_t");
|
||||
static_assert(
|
||||
std::numeric_limits<std::float16_t>::is_iec559,
|
||||
"std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)");
|
||||
#endif // __STDCPP_FLOAT16_T__
|
||||
|
||||
#ifdef __STDCPP_BFLOAT16_T__
|
||||
static_assert(
|
||||
std::is_same<binary_format<std::bfloat16_t>::equiv_uint, uint16_t>::value,
|
||||
"equiv_uint should be uint16_t for std::bfloat16_t");
|
||||
static_assert(
|
||||
std::numeric_limits<std::bfloat16_t>::is_iec559,
|
||||
"std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)");
|
||||
#endif // __STDCPP_BFLOAT16_T__
|
||||
|
||||
constexpr chars_format operator~(chars_format rhs) noexcept {
|
||||
using int_type = std::underlying_type<chars_format>::type;
|
||||
return static_cast<chars_format>(~static_cast<int_type>(rhs));
|
||||
}
|
||||
|
||||
constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept {
|
||||
using int_type = std::underlying_type<chars_format>::type;
|
||||
return static_cast<chars_format>(static_cast<int_type>(lhs) &
|
||||
static_cast<int_type>(rhs));
|
||||
}
|
||||
|
||||
constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept {
|
||||
using int_type = std::underlying_type<chars_format>::type;
|
||||
return static_cast<chars_format>(static_cast<int_type>(lhs) |
|
||||
static_cast<int_type>(rhs));
|
||||
}
|
||||
|
||||
constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept {
|
||||
using int_type = std::underlying_type<chars_format>::type;
|
||||
return static_cast<chars_format>(static_cast<int_type>(lhs) ^
|
||||
static_cast<int_type>(rhs));
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
|
||||
operator&=(chars_format &lhs, chars_format rhs) noexcept {
|
||||
return lhs = (lhs & rhs);
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
|
||||
operator|=(chars_format &lhs, chars_format rhs) noexcept {
|
||||
return lhs = (lhs | rhs);
|
||||
}
|
||||
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
|
||||
operator^=(chars_format &lhs, chars_format rhs) noexcept {
|
||||
return lhs = (lhs ^ rhs);
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
// adjust for deprecated feature macros
|
||||
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
|
||||
return fmt
|
||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
|
||||
| chars_format::allow_leading_plus
|
||||
#endif
|
||||
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
|
||||
| chars_format::skip_white_space
|
||||
#endif
|
||||
;
|
||||
}
|
||||
} // namespace detail
|
||||
} // namespace fast_float
|
||||
|
||||
#endif
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <system_error>
|
||||
|
||||
namespace fast_float {
|
||||
|
||||
namespace detail {
|
||||
@ -19,24 +20,20 @@ namespace detail {
|
||||
* strings a null-free and fixed.
|
||||
**/
|
||||
template <typename T, typename UC>
|
||||
from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first,
|
||||
UC const *last,
|
||||
T &value) noexcept {
|
||||
from_chars_result_t<UC>
|
||||
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last,
|
||||
T &value, chars_format fmt) noexcept {
|
||||
from_chars_result_t<UC> answer{};
|
||||
answer.ptr = first;
|
||||
answer.ec = std::errc(); // be optimistic
|
||||
bool minusSign = false;
|
||||
if (*first ==
|
||||
UC('-')) { // assume first < last, so dereference without checks;
|
||||
// C++17 20.19.3.(7.1) explicitly forbids '+' here
|
||||
minusSign = true;
|
||||
// assume first < last, so dereference without checks;
|
||||
bool const minusSign = (*first == UC('-'));
|
||||
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||
if ((*first == UC('-')) ||
|
||||
(uint64_t(fmt & chars_format::allow_leading_plus) &&
|
||||
(*first == UC('+')))) {
|
||||
++first;
|
||||
}
|
||||
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||
if (*first == UC('+')) {
|
||||
++first;
|
||||
}
|
||||
#endif
|
||||
if (last - first >= 3) {
|
||||
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
|
||||
answer.ptr = (first += 3);
|
||||
@ -93,13 +90,13 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
|
||||
// However, it is expected to be much faster than the fegetround()
|
||||
// function call.
|
||||
//
|
||||
// The volatile keywoard prevents the compiler from computing the function
|
||||
// The volatile keyword 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();
|
||||
static float volatile fmin = std::numeric_limits<float>::min();
|
||||
float fmini = fmin; // we copy it so that it gets loaded at most once.
|
||||
//
|
||||
// Explanation:
|
||||
@ -149,7 +146,7 @@ template <typename T> struct from_chars_caller {
|
||||
}
|
||||
};
|
||||
|
||||
#if __STDCPP_FLOAT32_T__ == 1
|
||||
#ifdef __STDCPP_FLOAT32_T__
|
||||
template <> struct from_chars_caller<std::float32_t> {
|
||||
template <typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||
@ -166,7 +163,7 @@ template <> struct from_chars_caller<std::float32_t> {
|
||||
};
|
||||
#endif
|
||||
|
||||
#if __STDCPP_FLOAT64_T__ == 1
|
||||
#ifdef __STDCPP_FLOAT64_T__
|
||||
template <> struct from_chars_caller<std::float64_t> {
|
||||
template <typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||
@ -191,32 +188,17 @@ from_chars(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC>(fmt));
|
||||
}
|
||||
|
||||
/**
|
||||
* This function overload takes parsed_number_string_t structure that is created
|
||||
* and populated either by from_chars_advanced function taking chars range and
|
||||
* parsing options or other parsing custom function implemented by user.
|
||||
*/
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
||||
|
||||
static_assert(is_supported_float_type<T>(),
|
||||
"only some floating-point types are supported");
|
||||
static_assert(is_supported_char_type<UC>(),
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
from_chars_result_t<UC> answer;
|
||||
|
||||
answer.ec = std::errc(); // be optimistic
|
||||
answer.ptr = pns.lastmatch;
|
||||
template <typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
||||
T &value) noexcept {
|
||||
// The implementation of the Clinger's fast path is convoluted because
|
||||
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
||||
// selected on the thread.
|
||||
// We proceed optimistically, assuming that detail::rounds_to_nearest()
|
||||
// returns true.
|
||||
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
|
||||
pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
|
||||
!pns.too_many_digits) {
|
||||
if (binary_format<T>::min_exponent_fast_path() <= exponent &&
|
||||
exponent <= binary_format<T>::max_exponent_fast_path()) {
|
||||
// Unfortunately, the conventional Clinger's fast path is only possible
|
||||
// when the system rounds to the nearest float.
|
||||
//
|
||||
@ -227,41 +209,64 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
||||
if (!cpp20_and_in_constexpr() && 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);
|
||||
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
|
||||
value = T(mantissa);
|
||||
if (exponent < 0) {
|
||||
value = value / binary_format<T>::exact_power_of_ten(-exponent);
|
||||
} else {
|
||||
value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
value = value * binary_format<T>::exact_power_of_ten(exponent);
|
||||
}
|
||||
if (pns.negative) {
|
||||
if (is_negative) {
|
||||
value = -value;
|
||||
}
|
||||
return answer;
|
||||
return true;
|
||||
}
|
||||
} 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 (exponent >= 0 &&
|
||||
mantissa <= binary_format<T>::max_mantissa_fast_path(exponent)) {
|
||||
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
|
||||
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
|
||||
if (pns.mantissa == 0) {
|
||||
value = pns.negative ? T(-0.) : T(0.);
|
||||
return answer;
|
||||
if (mantissa == 0) {
|
||||
value = is_negative ? T(-0.) : T(0.);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
value = T(pns.mantissa) *
|
||||
binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
if (pns.negative) {
|
||||
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
|
||||
if (is_negative) {
|
||||
value = -value;
|
||||
}
|
||||
return answer;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function overload takes parsed_number_string_t structure that is created
|
||||
* and populated either by from_chars_advanced function taking chars range and
|
||||
* parsing options or other parsing custom function implemented by user.
|
||||
*/
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
||||
static_assert(is_supported_float_type<T>::value,
|
||||
"only some floating-point types are supported");
|
||||
static_assert(is_supported_char_type<UC>::value,
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
from_chars_result_t<UC> answer;
|
||||
|
||||
answer.ec = std::errc(); // be optimistic
|
||||
answer.ptr = pns.lastmatch;
|
||||
|
||||
if (!pns.too_many_digits &&
|
||||
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value))
|
||||
return answer;
|
||||
|
||||
adjusted_mantissa am =
|
||||
compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
|
||||
if (pns.too_many_digits && am.power2 >= 0) {
|
||||
@ -286,34 +291,38 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
|
||||
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
from_chars_float_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
|
||||
static_assert(is_supported_float_type<T>(),
|
||||
static_assert(is_supported_float_type<T>::value,
|
||||
"only some floating-point types are supported");
|
||||
static_assert(is_supported_char_type<UC>(),
|
||||
static_assert(is_supported_char_type<UC>::value,
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
||||
|
||||
from_chars_result_t<UC> answer;
|
||||
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
|
||||
first++;
|
||||
if (uint64_t(fmt & chars_format::skip_white_space)) {
|
||||
while ((first != last) && fast_float::is_space(*first)) {
|
||||
first++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (first == last) {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
answer.ptr = first;
|
||||
return answer;
|
||||
}
|
||||
parsed_number_string_t<UC> pns =
|
||||
parse_number_string<UC>(first, last, options);
|
||||
uint64_t(fmt & detail::basic_json_fmt)
|
||||
? parse_number_string<true, UC>(first, last, options)
|
||||
: parse_number_string<false, UC>(first, last, options);
|
||||
if (!pns.valid) {
|
||||
if (options.format & chars_format::no_infnan) {
|
||||
if (uint64_t(fmt & chars_format::no_infnan)) {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
answer.ptr = first;
|
||||
return answer;
|
||||
} else {
|
||||
return detail::parse_infnan(first, last, value);
|
||||
return detail::parse_infnan(first, last, value, fmt);
|
||||
}
|
||||
}
|
||||
|
||||
@ -324,21 +333,153 @@ from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||
template <typename T, typename UC, typename>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
|
||||
static_assert(is_supported_char_type<UC>(),
|
||||
|
||||
static_assert(is_supported_integer_type<T>::value,
|
||||
"only integer types are supported");
|
||||
static_assert(is_supported_char_type<UC>::value,
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
parse_options_t<UC> options;
|
||||
options.base = base;
|
||||
return from_chars_advanced(first, last, value, options);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
|
||||
T value;
|
||||
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
|
||||
return value;
|
||||
|
||||
adjusted_mantissa am =
|
||||
compute_float<binary_format<T>>(decimal_exponent, mantissa);
|
||||
to_float(false, am, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
|
||||
const bool is_negative = mantissa < 0;
|
||||
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
|
||||
|
||||
T value;
|
||||
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
|
||||
return value;
|
||||
|
||||
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m);
|
||||
to_float(is_negative, am, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
FASTFLOAT_CONSTEXPR20 inline double
|
||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
|
||||
return integer_times_pow10<double>(mantissa, decimal_exponent);
|
||||
}
|
||||
|
||||
FASTFLOAT_CONSTEXPR20 inline double
|
||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
|
||||
return integer_times_pow10<double>(mantissa, decimal_exponent);
|
||||
}
|
||||
|
||||
// the following overloads are here to avoid surprising ambiguity for int,
|
||||
// unsigned, etc.
|
||||
template <typename T, typename Int>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value &&
|
||||
std::is_integral<Int>::value &&
|
||||
!std::is_signed<Int>::value,
|
||||
T>::type
|
||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
||||
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
|
||||
decimal_exponent);
|
||||
}
|
||||
|
||||
template <typename T, typename Int>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value &&
|
||||
std::is_integral<Int>::value &&
|
||||
std::is_signed<Int>::value,
|
||||
T>::type
|
||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
||||
return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
|
||||
decimal_exponent);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
|
||||
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
|
||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
||||
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
|
||||
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
|
||||
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
|
||||
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
|
||||
}
|
||||
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_int_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
|
||||
static_assert(is_supported_integer_type<T>::value,
|
||||
"only integer types are supported");
|
||||
static_assert(is_supported_char_type<UC>::value,
|
||||
"only char, wchar_t, char16_t and char32_t are supported");
|
||||
|
||||
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
|
||||
int const base = options.base;
|
||||
|
||||
from_chars_result_t<UC> answer;
|
||||
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
|
||||
first++;
|
||||
if (uint64_t(fmt & chars_format::skip_white_space)) {
|
||||
while ((first != last) && fast_float::is_space(*first)) {
|
||||
first++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (first == last || base < 2 || base > 36) {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
answer.ptr = first;
|
||||
return answer;
|
||||
}
|
||||
return parse_int_string(first, last, value, base);
|
||||
|
||||
return parse_int_string(first, last, value, options);
|
||||
}
|
||||
|
||||
template <size_t TypeIx> struct from_chars_advanced_caller {
|
||||
static_assert(TypeIx > 0, "unsupported type");
|
||||
};
|
||||
|
||||
template <> struct from_chars_advanced_caller<1> {
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||
call(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
return from_chars_float_advanced(first, last, value, options);
|
||||
}
|
||||
};
|
||||
|
||||
template <> struct from_chars_advanced_caller<2> {
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
|
||||
call(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
return from_chars_int_advanced(first, last, value, options);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename UC>
|
||||
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
return from_chars_advanced_caller<
|
||||
size_t(is_supported_float_type<T>::value) +
|
||||
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
|
||||
options);
|
||||
}
|
||||
|
||||
} // namespace fast_float
|
||||
|
||||
@ -1,97 +1,122 @@
|
||||
# text parts
|
||||
processed_files = { }
|
||||
processed_files = {}
|
||||
|
||||
# authors
|
||||
for filename in ['AUTHORS', 'CONTRIBUTORS']:
|
||||
with open(filename, encoding='utf8') as f:
|
||||
text = ''
|
||||
for line in f:
|
||||
if filename == 'AUTHORS':
|
||||
text += '// fast_float by ' + line
|
||||
if filename == 'CONTRIBUTORS':
|
||||
text += '// with contributions from ' + line
|
||||
processed_files[filename] = text + '//\n//\n'
|
||||
for filename in ["AUTHORS", "CONTRIBUTORS"]:
|
||||
with open(filename, encoding="utf8") as f:
|
||||
text = ""
|
||||
for line in f:
|
||||
if filename == "AUTHORS":
|
||||
text += "// fast_float by " + line
|
||||
if filename == "CONTRIBUTORS":
|
||||
text += "// with contributions from " + line
|
||||
processed_files[filename] = text + "//\n//\n"
|
||||
|
||||
# licenses
|
||||
for filename in ['LICENSE-MIT', 'LICENSE-APACHE', 'LICENSE-BOOST']:
|
||||
lines = []
|
||||
with open(filename, encoding='utf8') as f:
|
||||
lines = f.readlines()
|
||||
for filename in ["LICENSE-MIT", "LICENSE-APACHE", "LICENSE-BOOST"]:
|
||||
lines = []
|
||||
with open(filename, encoding="utf8") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Retrieve subset required for inclusion in source
|
||||
if filename == 'LICENSE-APACHE':
|
||||
lines = [
|
||||
' Copyright 2021 The fast_float authors\n',
|
||||
*lines[179:-1]
|
||||
]
|
||||
# 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
|
||||
text = ""
|
||||
for line in lines:
|
||||
line = line.strip()
|
||||
if len(line):
|
||||
line = " " + line
|
||||
text += "//" + line + "\n"
|
||||
processed_files[filename] = text
|
||||
|
||||
# code
|
||||
for filename in [ 'constexpr_feature_detect.h', 'float_common.h', 'fast_float.h', 'ascii_number.h',
|
||||
'fast_table.h', 'decimal_to_binary.h', 'bigint.h', 'digit_comparison.h', 'parse_number.h']:
|
||||
with open('include/fast_float/' + filename, encoding='utf8') as f:
|
||||
text = ''
|
||||
for line in f:
|
||||
if line.startswith('#include "'): continue
|
||||
text += line
|
||||
processed_files[filename] = '\n' + text
|
||||
for filename in [
|
||||
"constexpr_feature_detect.h",
|
||||
"float_common.h",
|
||||
"fast_float.h",
|
||||
"ascii_number.h",
|
||||
"fast_table.h",
|
||||
"decimal_to_binary.h",
|
||||
"bigint.h",
|
||||
"digit_comparison.h",
|
||||
"parse_number.h",
|
||||
]:
|
||||
with open("include/fast_float/" + filename, encoding="utf8") as f:
|
||||
text = ""
|
||||
for line in f:
|
||||
if line.startswith('#include "'):
|
||||
continue
|
||||
text += line
|
||||
processed_files[filename] = "\n" + text
|
||||
|
||||
# command line
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Amalgamate fast_float.')
|
||||
parser.add_argument('--license', default='TRIPLE', choices=['DUAL', 'TRIPLE', 'MIT', 'APACHE', 'BOOST'], help='choose license')
|
||||
parser.add_argument('--output', default='', help='output file (stdout if none')
|
||||
parser = argparse.ArgumentParser(description="Amalgamate fast_float.")
|
||||
parser.add_argument(
|
||||
"--license",
|
||||
default="TRIPLE",
|
||||
choices=["DUAL", "TRIPLE", "MIT", "APACHE", "BOOST"],
|
||||
help="choose license",
|
||||
)
|
||||
parser.add_argument("--output", default="", help="output file (stdout if none")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
|
||||
def license_content(license_arg):
|
||||
result = []
|
||||
if license_arg == 'TRIPLE':
|
||||
result += [
|
||||
'// Licensed under the Apache License, Version 2.0, or the\n',
|
||||
'// MIT License or the Boost License. This file may not be copied,\n',
|
||||
'// modified, or distributed except according to those terms.\n',
|
||||
'//\n'
|
||||
result = []
|
||||
if license_arg == "TRIPLE":
|
||||
result += [
|
||||
"// Licensed under the Apache License, Version 2.0, or the\n",
|
||||
"// MIT License or the Boost License. This file may not be copied,\n",
|
||||
"// modified, or distributed except according to those terms.\n",
|
||||
"//\n",
|
||||
]
|
||||
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", "TRIPLE", "MIT"):
|
||||
result.append("// MIT License Notice\n//\n")
|
||||
result.append(processed_files["LICENSE-MIT"])
|
||||
result.append("//\n")
|
||||
if license_arg in ("DUAL", "TRIPLE", "APACHE"):
|
||||
result.append("// Apache License (Version 2.0) Notice\n//\n")
|
||||
result.append(processed_files["LICENSE-APACHE"])
|
||||
result.append("//\n")
|
||||
if license_arg in ("TRIPLE", "BOOST"):
|
||||
result.append("// BOOST License Notice\n//\n")
|
||||
result.append(processed_files["LICENSE-BOOST"])
|
||||
result.append("//\n")
|
||||
|
||||
return result
|
||||
|
||||
|
||||
text = "".join(
|
||||
[
|
||||
processed_files["AUTHORS"],
|
||||
processed_files["CONTRIBUTORS"],
|
||||
*license_content(args.license),
|
||||
processed_files["constexpr_feature_detect.h"],
|
||||
processed_files["float_common.h"],
|
||||
processed_files["fast_float.h"],
|
||||
processed_files["ascii_number.h"],
|
||||
processed_files["fast_table.h"],
|
||||
processed_files["decimal_to_binary.h"],
|
||||
processed_files["bigint.h"],
|
||||
processed_files["digit_comparison.h"],
|
||||
processed_files["parse_number.h"],
|
||||
]
|
||||
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', 'TRIPLE', 'MIT'):
|
||||
result.append('// MIT License Notice\n//\n')
|
||||
result.append(processed_files['LICENSE-MIT'])
|
||||
result.append('//\n')
|
||||
if license_arg in ('DUAL', 'TRIPLE', 'APACHE'):
|
||||
result.append('// Apache License (Version 2.0) Notice\n//\n')
|
||||
result.append(processed_files['LICENSE-APACHE'])
|
||||
result.append('//\n')
|
||||
if license_arg in ('TRIPLE', 'BOOST'):
|
||||
result.append('// BOOST License Notice\n//\n')
|
||||
result.append(processed_files['LICENSE-BOOST'])
|
||||
result.append('//\n')
|
||||
|
||||
return result
|
||||
|
||||
text = ''.join([
|
||||
processed_files['AUTHORS'], processed_files['CONTRIBUTORS'],
|
||||
*license_content(args.license),
|
||||
processed_files['constexpr_feature_detect.h'],
|
||||
processed_files['float_common.h'], processed_files['fast_float.h'],
|
||||
processed_files['ascii_number.h'], processed_files['fast_table.h'],
|
||||
processed_files['decimal_to_binary.h'], processed_files['bigint.h'],
|
||||
processed_files['digit_comparison.h'], processed_files['parse_number.h']])
|
||||
)
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'wt', encoding='utf8') as f:
|
||||
f.write(text)
|
||||
with open(args.output, "wt", encoding="utf8") as f:
|
||||
f.write(text)
|
||||
else:
|
||||
print(text)
|
||||
print(text)
|
||||
|
||||
@ -1,36 +1,38 @@
|
||||
import sys
|
||||
from math import floor
|
||||
|
||||
|
||||
def log2(x):
|
||||
"""returns ceil(log2(x)))"""
|
||||
y = 0
|
||||
while((1<<y) < x):
|
||||
y = y + 1
|
||||
return y
|
||||
"""returns ceil(log2(x)))"""
|
||||
y = 0
|
||||
while (1 << y) < x:
|
||||
y = y + 1
|
||||
return y
|
||||
|
||||
|
||||
for q in range(1,17+1):
|
||||
d = 5**q
|
||||
for q in range(1, 17 + 1):
|
||||
d = 5 ** q
|
||||
b = 127 + log2(d)
|
||||
t = 2** b
|
||||
c = t//d + 1
|
||||
assert c < 2**128
|
||||
assert c >= 2**127
|
||||
K = 2**127
|
||||
if(not(c * K * d<=( K + 1) * t)):
|
||||
print(q)
|
||||
top = floor(t/(c * d - t))
|
||||
sys.exit(-1)
|
||||
t = 2 ** b
|
||||
c = t // d + 1
|
||||
assert c < 2 ** 128
|
||||
assert c >= 2 ** 127
|
||||
K = 2 ** 127
|
||||
if not (c * K * d <= (K + 1) * t):
|
||||
print(q)
|
||||
top = floor(t / (c * d - t))
|
||||
sys.exit(-1)
|
||||
|
||||
for q in range(18, 344+1):
|
||||
d = 5**q
|
||||
b = 64 + 2*log2(d)
|
||||
t = 2**b
|
||||
c = t//d + 1
|
||||
assert c > 2**(64 +log2(d))
|
||||
K = 2**64
|
||||
if(not(c * K * d<=( K + 1) * t)):
|
||||
print(q)
|
||||
top = floor(t/(c * d - t))
|
||||
sys.exit(-1)
|
||||
for q in range(18, 344 + 1):
|
||||
d = 5 ** q
|
||||
b = 64 + 2 * log2(d)
|
||||
t = 2 ** b
|
||||
c = t // d + 1
|
||||
assert c > 2 ** (64 + log2(d))
|
||||
K = 2 ** 64
|
||||
if not (c * K * d <= (K + 1) * t):
|
||||
print(q)
|
||||
top = floor(t / (c * d - t))
|
||||
sys.exit(-1)
|
||||
|
||||
print("all good")
|
||||
print("all good")
|
||||
|
||||
@ -9,25 +9,25 @@ all_tqs = []
|
||||
# Appendix B of Number parsing at a gigabyte per second.
|
||||
# Software: Practice and Experience 2021;51(8):1700–1727.
|
||||
for q in range(-342, -27):
|
||||
power5 = 5**-q
|
||||
power5 = 5 ** -q
|
||||
z = 0
|
||||
while (1 << z) < power5:
|
||||
z += 1
|
||||
b = 2 * z + 2 * 64
|
||||
c = 2**b // power5 + 1
|
||||
c = 2 ** b // power5 + 1
|
||||
while c >= (1 << 128):
|
||||
c //= 2
|
||||
all_tqs.append(c)
|
||||
for q in range(-27, 0):
|
||||
power5 = 5**-q
|
||||
power5 = 5 ** -q
|
||||
z = 0
|
||||
while (1 << z) < power5:
|
||||
z += 1
|
||||
b = z + 127
|
||||
c = 2**b // power5 + 1
|
||||
c = 2 ** b // power5 + 1
|
||||
all_tqs.append(c)
|
||||
for q in range(0, 308 + 1):
|
||||
power5 = 5**q
|
||||
power5 = 5 ** q
|
||||
while power5 < (1 << 127):
|
||||
power5 *= 2
|
||||
while power5 >= (1 << 128):
|
||||
@ -44,6 +44,7 @@ def continued_fraction(numer, denom):
|
||||
numer, denom = denom, rem
|
||||
return cf
|
||||
|
||||
|
||||
# Given a continued fraction [a0; a1, a2, ..., an], returns
|
||||
# all the convergents of that continued fraction
|
||||
# as pairs of the form (numer, denom), where numer/denom is
|
||||
@ -58,17 +59,22 @@ def convergents(cf):
|
||||
p_n = a_n * p_n_minus_1 + p_n_minus_2
|
||||
q_n = a_n * q_n_minus_1 + q_n_minus_2
|
||||
convergents.append((p_n, q_n))
|
||||
p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = p_n_minus_1, q_n_minus_1, p_n, q_n
|
||||
p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = (
|
||||
p_n_minus_1,
|
||||
q_n_minus_1,
|
||||
p_n,
|
||||
q_n,
|
||||
)
|
||||
return convergents
|
||||
|
||||
|
||||
# Enumerate through all the convergents of T[q] / 2^137 with denominators < 2^64
|
||||
found_solution = False
|
||||
for j, tq in enumerate(all_tqs):
|
||||
for _, w in convergents(continued_fraction(tq, 2**137)):
|
||||
if w >= 2**64:
|
||||
for _, w in convergents(continued_fraction(tq, 2 ** 137)):
|
||||
if w >= 2 ** 64:
|
||||
break
|
||||
if (tq*w) % 2**137 > 2**137 - 2**64:
|
||||
if (tq * w) % 2 ** 137 > 2 ** 137 - 2 ** 64:
|
||||
print(f"SOLUTION: q={j-342} T[q]={tq} w={w}")
|
||||
found_solution = True
|
||||
if not found_solution:
|
||||
|
||||
@ -8,116 +8,176 @@ import subprocess
|
||||
import io
|
||||
import os
|
||||
import fileinput
|
||||
|
||||
if sys.version_info < (3, 0):
|
||||
sys.stdout.write("Sorry, requires Python 3.x or better\n")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def colored(r, g, b, text):
|
||||
return "\033[38;2;{};{};{}m{} \033[38;2;255;255;255m".format(r, g, b, text)
|
||||
return f"\033[38;2;{r};{g};{b}m{text} \033[38;2;255;255;255m"
|
||||
|
||||
|
||||
def extractnumbers(s):
|
||||
return tuple(map(int,re.findall(r"(\d+)\.(\d+)\.(\d+)",str(s))[0]))
|
||||
return tuple(map(int, re.findall(r"(\d+)\.(\d+)\.(\d+)", str(s))[0]))
|
||||
|
||||
|
||||
def toversionstring(major, minor, rev):
|
||||
return str(major)+"."+str(minor)+"."+str(rev)
|
||||
return f"{major}.{minor}.{rev}"
|
||||
|
||||
|
||||
def topaddedversionstring(major, minor, rev):
|
||||
return str(major)+str(minor).zfill(3)+str(rev).zfill(3)
|
||||
print("Calling git rev-parse --abbrev-ref HEAD")
|
||||
pipe = subprocess.Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
pipe = subprocess.Popen(
|
||||
["git", "rev-parse", "--abbrev-ref", "HEAD"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
branchresult = pipe.communicate()[0].decode().strip()
|
||||
|
||||
if(branchresult != "main"):
|
||||
print(colored(255, 0, 0, "We recommend that you release on main, you are on '"+branchresult+"'"))
|
||||
if branchresult != "main":
|
||||
print(
|
||||
colored(
|
||||
255,
|
||||
0,
|
||||
0,
|
||||
f"We recommend that you release on main, you are on '{branchresult}'",
|
||||
)
|
||||
)
|
||||
|
||||
ret = subprocess.call(["git", "remote", "update"])
|
||||
|
||||
if(ret != 0):
|
||||
if ret != 0:
|
||||
sys.exit(ret)
|
||||
print("Calling git log HEAD.. --oneline")
|
||||
pipe = subprocess.Popen(["git", "log", "HEAD..", "--oneline"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
pipe = subprocess.Popen(
|
||||
["git", "log", "HEAD..", "--oneline"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
uptodateresult = pipe.communicate()[0].decode().strip()
|
||||
|
||||
if(len(uptodateresult) != 0):
|
||||
if len(uptodateresult) != 0:
|
||||
print(uptodateresult)
|
||||
sys.exit(-1)
|
||||
|
||||
pipe = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
pipe = subprocess.Popen(
|
||||
["git", "rev-parse", "--show-toplevel"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
maindir = pipe.communicate()[0].decode().strip()
|
||||
scriptlocation = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
print("repository: "+maindir)
|
||||
print(f"repository: {maindir}")
|
||||
|
||||
pipe = subprocess.Popen(["git", "describe", "--abbrev=0", "--tags"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
pipe = subprocess.Popen(
|
||||
["git", "describe", "--abbrev=0", "--tags"],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
versionresult = pipe.communicate()[0].decode().strip()
|
||||
|
||||
print("last version: "+versionresult )
|
||||
print(f"last version: {versionresult}")
|
||||
try:
|
||||
currentv = extractnumbers(versionresult)
|
||||
currentv = extractnumbers(versionresult)
|
||||
except:
|
||||
currentv = [0,0,0]
|
||||
if(len(sys.argv) != 2):
|
||||
nextv = (currentv[0],currentv[1], currentv[2]+1)
|
||||
print ("please specify version number, e.g. "+toversionstring(*nextv))
|
||||
currentv = [0, 0, 0]
|
||||
if len(sys.argv) != 2:
|
||||
nextv = (currentv[0], currentv[1], currentv[2] + 1)
|
||||
print(f"please specify version number, e.g. {toversionstring(*nextv)}")
|
||||
sys.exit(-1)
|
||||
try:
|
||||
newversion = extractnumbers(sys.argv[1])
|
||||
print(newversion)
|
||||
except:
|
||||
print("can't parse version number "+sys.argv[1])
|
||||
print(f"can't parse version number {sys.argv[1]}")
|
||||
sys.exit(-1)
|
||||
|
||||
print("checking that new version is valid")
|
||||
if(newversion[0] != currentv[0]):
|
||||
assert newversion[0] == currentv[0] + 1
|
||||
if newversion[0] != currentv[0]:
|
||||
assert newversion[0] == currentv[0] + 1
|
||||
assert newversion[1] == 0
|
||||
assert newversion[2] == 0
|
||||
elif (newversion[1] != currentv[1]):
|
||||
assert newversion[1] == currentv[1] + 1
|
||||
elif newversion[1] != currentv[1]:
|
||||
assert newversion[1] == currentv[1] + 1
|
||||
assert newversion[2] == 0
|
||||
else :
|
||||
assert newversion[2] == currentv[2] + 1
|
||||
|
||||
atleastminor= (currentv[0] != newversion[0]) or (currentv[1] != newversion[1])
|
||||
|
||||
else:
|
||||
assert newversion[2] == currentv[2] + 1
|
||||
|
||||
atleastminor = (currentv[0] != newversion[0]) or (currentv[1] != newversion[1])
|
||||
|
||||
newmajorversionstring = str(newversion[0])
|
||||
mewminorversionstring = str(newversion[1])
|
||||
newrevversionstring = str(newversion[2])
|
||||
newversionstring = str(newversion[0]) + "." + str(newversion[1]) + "." + str(newversion[2])
|
||||
cmakefile = maindir + os.sep + "CMakeLists.txt"
|
||||
newminorversionstring = str(newversion[1])
|
||||
newpatchversionstring = str(newversion[2])
|
||||
newversionstring = f"{newversion[0]}.{newversion[1]}.{newversion[2]}"
|
||||
cmakefile = f"{maindir}{os.sep}CMakeLists.txt"
|
||||
|
||||
|
||||
for line in fileinput.input(cmakefile, inplace=1, backup='.bak'):
|
||||
line = re.sub(r'project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)','project(fast_float VERSION '+newmajorversionstring+'.'+mewminorversionstring+'.'+newrevversionstring+" LANGUAGES CXX)", line.rstrip())
|
||||
for line in fileinput.input(cmakefile, inplace=1, backup=".bak"):
|
||||
line = re.sub(
|
||||
r"project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)",
|
||||
f"project(fast_float VERSION {newversionstring} LANGUAGES CXX)",
|
||||
line.rstrip(),
|
||||
)
|
||||
print(line)
|
||||
|
||||
print("modified "+cmakefile+", a backup was made")
|
||||
print(f"modified {cmakefile}, a backup was made")
|
||||
|
||||
versionfilerel = f"{os.sep}include{os.sep}fast_float{os.sep}float_common.h"
|
||||
versionfile = f"{maindir}{versionfilerel}"
|
||||
|
||||
readmefile = maindir + os.sep + "README.md"
|
||||
|
||||
|
||||
for line in fileinput.input(readmefile, inplace=1, backup='.bak'):
|
||||
line = re.sub(r'https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h','https://github.com/fastfloat/fast_float/releases/download/v'+newmajorversionstring+'.'+mewminorversionstring+'.'+newrevversionstring+'/fast_float.h', line.rstrip())
|
||||
for line in fileinput.input(versionfile, inplace=1, backup=".bak"):
|
||||
line = re.sub(
|
||||
r"#define FASTFLOAT_VERSION_MAJOR \d+",
|
||||
f"#define FASTFLOAT_VERSION_MAJOR {newmajorversionstring}",
|
||||
line.rstrip(),
|
||||
)
|
||||
line = re.sub(
|
||||
r"#define FASTFLOAT_VERSION_MINOR \d+",
|
||||
f"#define FASTFLOAT_VERSION_MINOR {newminorversionstring}",
|
||||
line.rstrip(),
|
||||
)
|
||||
line = re.sub(
|
||||
r"#define FASTFLOAT_VERSION_PATCH \d+",
|
||||
f"#define FASTFLOAT_VERSION_PATCH {newpatchversionstring}",
|
||||
line.rstrip(),
|
||||
)
|
||||
print(line)
|
||||
|
||||
print("modified "+readmefile+", a backup was made")
|
||||
print(f"{versionfile} modified")
|
||||
|
||||
readmefile = f"{maindir}{os.sep}README.md"
|
||||
|
||||
for line in fileinput.input(readmefile, inplace=1, backup=".bak"):
|
||||
line = re.sub(
|
||||
r"https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h",
|
||||
f"https://github.com/fastfloat/fast_float/releases/download/v{newversionstring}/fast_float.h",
|
||||
line.rstrip(),
|
||||
)
|
||||
line = re.sub(
|
||||
r"GIT_TAG tags/v(\d+\.\d+\.\d+)",
|
||||
f"GIT_TAG tags/v{newversionstring}",
|
||||
line.rstrip(),
|
||||
)
|
||||
line = re.sub(
|
||||
r"GIT_TAG v(\d+\.\d+\.\d+)\)", f"GIT_TAG v{newversionstring})", line.rstrip()
|
||||
)
|
||||
print(line)
|
||||
|
||||
print(f"modified {readmefile}, a backup was made")
|
||||
|
||||
print("running amalgamate.py")
|
||||
with open(maindir+ os.sep + 'fast_float.h', "w") as outfile:
|
||||
cp = subprocess.run(["python3", maindir+ os.sep + "script/amalgamate.py"], stdout=outfile)
|
||||
with open(f"{maindir}{os.sep}fast_float.h", "w") as outfile:
|
||||
cp = subprocess.run(
|
||||
[f"python3", f"{maindir}{os.sep}script{os.sep}amalgamate.py"], stdout=outfile
|
||||
)
|
||||
|
||||
if(cp.returncode != 0):
|
||||
if cp.returncode != 0:
|
||||
print("Failed to run amalgamate")
|
||||
else:
|
||||
print("amalgamate.py ran successfully")
|
||||
print("You should upload "+ maindir+ os.sep + 'fast_float.h')
|
||||
|
||||
print("Please run the tests before issuing a release. \n")
|
||||
print("to issue release, enter \n git commit -a && git push && git tag -a v"+toversionstring(*newversion)+" -m \"version "+toversionstring(*newversion)+"\" && git push --tags \n")
|
||||
|
||||
|
||||
print(f"You should upload {maindir}{os.sep}fast_float.h")
|
||||
|
||||
print("Please run the tests before issuing a release.\n")
|
||||
print(
|
||||
f'to issue release, enter\n git commit -a && git push && git tag -a v{toversionstring(*newversion)} -m "version {toversionstring(*newversion)}" && git push --tags\n'
|
||||
)
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
def format(number):
|
||||
upper = number // (1<<64)
|
||||
lower = number % (1<<64)
|
||||
print(""+hex(upper)+","+hex(lower)+",")
|
||||
upper = number // (1 << 64)
|
||||
lower = number % (1 << 64)
|
||||
print("" + hex(upper) + "," + hex(lower) + ",")
|
||||
|
||||
for q in range(-342,0):
|
||||
|
||||
for q in range(-342, 0):
|
||||
power5 = 5 ** -q
|
||||
z = 0
|
||||
while( (1<<z) < power5) :
|
||||
while (1 << z) < power5:
|
||||
z += 1
|
||||
if(q >= -27):
|
||||
if q >= -27:
|
||||
b = z + 127
|
||||
c = 2 ** b // power5 + 1
|
||||
format(c)
|
||||
@ -16,16 +17,16 @@ for q in range(-342,0):
|
||||
b = 2 * z + 2 * 64
|
||||
c = 2 ** b // power5 + 1
|
||||
# truncate
|
||||
while(c >= (1<<128)):
|
||||
c //= 2
|
||||
while c >= (1 << 128):
|
||||
c //= 2
|
||||
format(c)
|
||||
|
||||
for q in range(0,308+1):
|
||||
for q in range(0, 308 + 1):
|
||||
power5 = 5 ** q
|
||||
# move the most significant bit in position
|
||||
while(power5 < (1<<127)):
|
||||
while power5 < (1 << 127):
|
||||
power5 *= 2
|
||||
# *truncate*
|
||||
while(power5 >= (1<<128)):
|
||||
while power5 >= (1 << 128):
|
||||
power5 //= 2
|
||||
format(power5)
|
||||
|
||||
116
tests/BUILD.bazel
Normal file
116
tests/BUILD.bazel
Normal file
@ -0,0 +1,116 @@
|
||||
cc_test(
|
||||
name = "basictest",
|
||||
srcs = ["basictest.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "example_test",
|
||||
srcs = ["example_test.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "example_comma_test",
|
||||
srcs = ["example_comma_test.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fast_int",
|
||||
srcs = ["fast_int.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fixedwidthtest",
|
||||
srcs = ["fixedwidthtest.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "fortran",
|
||||
srcs = ["fortran.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "json_fmt",
|
||||
srcs = ["json_fmt.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "long_test",
|
||||
srcs = ["long_test.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "powersoffive_hardround",
|
||||
srcs = ["powersoffive_hardround.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "rcppfastfloat_test",
|
||||
srcs = ["rcppfastfloat_test.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "wide_char_test",
|
||||
srcs = ["wide_char_test.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "supported_chars_test",
|
||||
srcs = ["supported_chars_test.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
|
||||
cc_test(
|
||||
name = "string_test",
|
||||
srcs = ["string_test.cpp"],
|
||||
deps = [
|
||||
"//:fast_float",
|
||||
"@doctest//doctest",
|
||||
],
|
||||
)
|
||||
@ -9,8 +9,7 @@ option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON)
|
||||
|
||||
if (NOT SYSTEM_DOCTEST)
|
||||
FetchContent_Declare(doctest
|
||||
GIT_REPOSITORY https://github.com/onqtam/doctest.git
|
||||
GIT_TAG v2.4.11)
|
||||
GIT_REPOSITORY https://github.com/lemire/doctest.git)
|
||||
else ()
|
||||
find_package(doctest REQUIRED)
|
||||
endif()
|
||||
@ -23,24 +22,15 @@ endif()
|
||||
|
||||
# FetchContent_MakeAvailable() was only introduced in 3.14
|
||||
# https://cmake.org/cmake/help/v3.14/release/3.14.html#modules
|
||||
# FetchContent_MakeAvailable(doctest)
|
||||
if (NOT SYSTEM_DOCTEST)
|
||||
FetchContent_GetProperties(doctest)
|
||||
if(NOT doctest_POPULATED)
|
||||
FetchContent_Populate(doctest)
|
||||
add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR})
|
||||
endif()
|
||||
FetchContent_MakeAvailable(doctest)
|
||||
endif()
|
||||
|
||||
add_library(supplemental-data INTERFACE)
|
||||
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
|
||||
FetchContent_GetProperties(supplemental_test_files)
|
||||
if(NOT supplemental_test_files_POPULATED)
|
||||
message(STATUS "Supplemental tests enabled. Retrieving test files.")
|
||||
FetchContent_Populate(supplemental_test_files)
|
||||
message(STATUS "Supplemental test files retrieved.")
|
||||
add_subdirectory(${supplemental_test_files_SOURCE_DIR} ${supplemental_test_files_BINARY_DIR})
|
||||
endif()
|
||||
message(STATUS "Supplemental tests enabled. Retrieving test files.")
|
||||
FetchContent_MakeAvailable(supplemental_test_files)
|
||||
message(STATUS "Supplemental test files retrieved.")
|
||||
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
|
||||
endif()
|
||||
|
||||
@ -67,8 +57,11 @@ function(fast_float_add_cpp_test TEST_NAME)
|
||||
endfunction(fast_float_add_cpp_test)
|
||||
|
||||
fast_float_add_cpp_test(rcppfastfloat_test)
|
||||
fast_float_add_cpp_test(wide_char_test)
|
||||
fast_float_add_cpp_test(supported_chars_test)
|
||||
fast_float_add_cpp_test(example_test)
|
||||
fast_float_add_cpp_test(example_comma_test)
|
||||
fast_float_add_cpp_test(example_integer_times_pow10)
|
||||
fast_float_add_cpp_test(basictest)
|
||||
option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF)
|
||||
if (FASTFLOAT_CONSTEXPR_TESTS)
|
||||
@ -80,7 +73,7 @@ endif()
|
||||
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
|
||||
target_compile_definitions(basictest PRIVATE FASTFLOAT_SUPPLEMENTAL_TESTS)
|
||||
endif()
|
||||
|
||||
fast_float_add_cpp_test(p2497)
|
||||
fast_float_add_cpp_test(long_test)
|
||||
fast_float_add_cpp_test(powersoffive_hardround)
|
||||
fast_float_add_cpp_test(string_test)
|
||||
|
||||
1142
tests/basictest.cpp
1142
tests/basictest.cpp
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get1(const char *input) {
|
||||
double get1(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get10(const char *input) {
|
||||
double get10(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get2(const char *input) {
|
||||
double get2(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get3(const char *input) {
|
||||
double get3(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get4(const char *input) {
|
||||
double get4(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get5(const char *input) {
|
||||
double get5(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get6(const char *input) {
|
||||
double get6(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get7(const char *input) {
|
||||
double get7(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get8(const char *input) {
|
||||
double get8(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get9(const char *input) {
|
||||
double get9(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
|
||||
|
||||
double get1(const char *input);
|
||||
double get2(const char *input);
|
||||
double get3(const char *input);
|
||||
double get4(const char *input);
|
||||
double get5(const char *input);
|
||||
double get6(const char *input);
|
||||
double get7(const char *input);
|
||||
double get8(const char *input);
|
||||
double get9(const char *input);
|
||||
double get10(const char *input);
|
||||
double get1(char const *input);
|
||||
double get2(char const *input);
|
||||
double get3(char const *input);
|
||||
double get4(char const *input);
|
||||
double get5(char const *input);
|
||||
double get6(char const *input);
|
||||
double get7(char const *input);
|
||||
double get8(char const *input);
|
||||
double get9(char const *input);
|
||||
double get10(char const *input);
|
||||
|
||||
int main(int arg, char **argv) {
|
||||
double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) +
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
double get(const char *input) {
|
||||
double get(char const *input) {
|
||||
double result_value;
|
||||
auto result =
|
||||
fast_float::from_chars(input, input + strlen(input), result_value);
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
#include "test.h"
|
||||
|
||||
void foo() {}
|
||||
@ -1,2 +1,3 @@
|
||||
#include "test.h"
|
||||
|
||||
int main() { return 0; }
|
||||
@ -5,7 +5,7 @@
|
||||
#include <system_error>
|
||||
|
||||
int main() {
|
||||
const std::string input = "3,1416 xyz ";
|
||||
std::string const input = "3,1416 xyz ";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::general, ','};
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
|
||||
36
tests/example_integer_times_pow10.cpp
Normal file
36
tests/example_integer_times_pow10.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
void default_overload() {
|
||||
const uint64_t W = 12345678901234567;
|
||||
const int Q = 23;
|
||||
const double result = fast_float::integer_times_pow10(W, Q);
|
||||
std::cout.precision(17);
|
||||
std::cout << W << " * 10^" << Q << " = " << result << " ("
|
||||
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
|
||||
}
|
||||
|
||||
void double_specialization() {
|
||||
const uint64_t W = 12345678901234567;
|
||||
const int Q = 23;
|
||||
const double result = fast_float::integer_times_pow10<double>(W, Q);
|
||||
std::cout.precision(17);
|
||||
std::cout << "double: " << W << " * 10^" << Q << " = " << result << " ("
|
||||
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
|
||||
}
|
||||
|
||||
void float_specialization() {
|
||||
const uint64_t W = 12345678;
|
||||
const int Q = 23;
|
||||
const float result = fast_float::integer_times_pow10<float>(W, Q);
|
||||
std::cout.precision(9);
|
||||
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
|
||||
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
|
||||
}
|
||||
|
||||
int main() {
|
||||
default_overload();
|
||||
double_specialization();
|
||||
float_specialization();
|
||||
}
|
||||
@ -5,7 +5,7 @@
|
||||
#include <system_error>
|
||||
|
||||
bool many() {
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
std::string const input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
auto answer =
|
||||
fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
@ -41,10 +41,10 @@ bool many() {
|
||||
}
|
||||
|
||||
void many_loop() {
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
std::string const input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
const char *pointer = input.data();
|
||||
const char *end_pointer = input.data() + input.size();
|
||||
char const *pointer = input.data();
|
||||
char const *end_pointer = input.data() + input.size();
|
||||
|
||||
while (pointer < end_pointer) {
|
||||
auto answer = fast_float::from_chars(pointer, end_pointer, result);
|
||||
@ -112,7 +112,7 @@ bool large() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
const std::string input = "3.1416 xyz ";
|
||||
std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
auto answer =
|
||||
fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
@ -121,6 +121,20 @@ int main() {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
#ifdef __STDCPP_FLOAT16_T__
|
||||
printf("16-bit float\n");
|
||||
// Parse as 16-bit float
|
||||
std::float16_t parsed_16{};
|
||||
input = "10000e-1452";
|
||||
auto fast_float_r16 = fast_float::from_chars(
|
||||
input.data(), input.data() + input.size(), parsed_16);
|
||||
if (fast_float_r16.ec != std::errc() &&
|
||||
fast_float_r16.ec != std::errc::result_out_of_range) {
|
||||
std::cerr << "16-bit fast_float parsing failure for: " + input + "\n";
|
||||
return false;
|
||||
}
|
||||
std::cout << "parsed the 16-bit value " << float(parsed_16) << std::endl;
|
||||
#endif
|
||||
if (!small()) {
|
||||
printf("Bug\n");
|
||||
return EXIT_FAILURE;
|
||||
|
||||
@ -27,7 +27,7 @@ void allvalues() {
|
||||
memcpy(&v, &word, sizeof(v));
|
||||
|
||||
{
|
||||
const char *string_end = to_string(v, buffer);
|
||||
char const *string_end = to_string(v, buffer);
|
||||
float result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
|
||||
@ -58,7 +58,7 @@ void all_32bit_values() {
|
||||
double v = v32;
|
||||
|
||||
{
|
||||
const char *string_end = to_string(v, buffer);
|
||||
char const *string_end = to_string(v, buffer);
|
||||
std::string s(buffer, size_t(string_end - buffer));
|
||||
if (!basic_test_64bit(s, v)) {
|
||||
return;
|
||||
|
||||
@ -14,8 +14,9 @@
|
||||
// gcc.
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double cygwin_strtod_l(char const *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -31,7 +32,8 @@ double cygwin_strtod_l(const char *start, char **end) {
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
|
||||
float cygwin_strtof_l(char const *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -55,7 +57,7 @@ template <typename T> char *to_string(T d, char *buffer) {
|
||||
return buffer + written;
|
||||
}
|
||||
|
||||
void strtof_from_string(const char *st, float &d) {
|
||||
void strtof_from_string(char const *st, float &d) {
|
||||
char *pr = (char *)st;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
@ -97,7 +99,7 @@ bool allvalues() {
|
||||
double midv{v1 + (v2 - v1) / 2};
|
||||
float expected_midv = float(midv);
|
||||
|
||||
const char *string_end = to_string(midv, buffer);
|
||||
char const *string_end = to_string(midv, buffer);
|
||||
float str_answer;
|
||||
strtof_from_string(buffer, str_answer);
|
||||
|
||||
@ -164,6 +166,7 @@ inline void Assert(bool Assertion) {
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int main() {
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
|
||||
@ -46,12 +46,12 @@ ignores all zeroes in front of valid number after converted from base
|
||||
|
||||
int main() {
|
||||
// int basic test
|
||||
const std::vector<int> int_basic_test_expected{0, 10, -40, 1001, 9};
|
||||
const std::vector<std::string_view> int_basic_test{"0", "10 ", "-40",
|
||||
std::vector<int> const int_basic_test_expected{0, 10, -40, 1001, 9};
|
||||
std::vector<std::string_view> const int_basic_test{"0", "10 ", "-40",
|
||||
"1001 with text", "9.999"};
|
||||
|
||||
for (std::size_t i = 0; i < int_basic_test.size(); ++i) {
|
||||
const auto f = int_basic_test[i];
|
||||
auto const f = int_basic_test[i];
|
||||
int result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
|
||||
@ -75,12 +75,12 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned basic test
|
||||
const std::vector<unsigned> unsigned_basic_test_expected{0, 10, 1001, 9};
|
||||
const std::vector<std::string_view> unsigned_basic_test{
|
||||
std::vector<unsigned> const unsigned_basic_test_expected{0, 10, 1001, 9};
|
||||
std::vector<std::string_view> const unsigned_basic_test{
|
||||
"0", "10 ", "1001 with text", "9.999"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) {
|
||||
const auto &f = unsigned_basic_test[i];
|
||||
auto const &f = unsigned_basic_test[i];
|
||||
unsigned result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
@ -96,11 +96,11 @@ int main() {
|
||||
}
|
||||
|
||||
// int invalid error test
|
||||
const std::vector<std::string_view> int_invalid_argument_test{
|
||||
std::vector<std::string_view> const int_invalid_argument_test{
|
||||
"text", "text with 1002", "+50", " 50"};
|
||||
|
||||
for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) {
|
||||
const auto &f = int_invalid_argument_test[i];
|
||||
auto const &f = int_invalid_argument_test[i];
|
||||
int result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::invalid_argument) {
|
||||
@ -111,11 +111,11 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned invalid error test
|
||||
const std::vector<std::string_view> unsigned_invalid_argument_test{
|
||||
std::vector<std::string_view> const unsigned_invalid_argument_test{
|
||||
"text", "text with 1002", "+50", " 50", "-50"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) {
|
||||
const auto &f = unsigned_invalid_argument_test[i];
|
||||
auto const &f = unsigned_invalid_argument_test[i];
|
||||
unsigned result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::invalid_argument) {
|
||||
@ -126,11 +126,11 @@ int main() {
|
||||
}
|
||||
|
||||
// int out of range error test #1 (8 bit)
|
||||
const std::vector<std::string_view> int_out_of_range_test_1{
|
||||
std::vector<std::string_view> const int_out_of_range_test_1{
|
||||
"2000000000000000000000", "128", "-129"};
|
||||
|
||||
for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) {
|
||||
const auto &f = int_out_of_range_test_1[i];
|
||||
auto const &f = int_out_of_range_test_1[i];
|
||||
int8_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -141,11 +141,11 @@ int main() {
|
||||
}
|
||||
|
||||
// int out of range error test #2 (16 bit)
|
||||
const std::vector<std::string_view> int_out_of_range_test_2{
|
||||
std::vector<std::string_view> const int_out_of_range_test_2{
|
||||
"2000000000000000000000", "32768", "-32769"};
|
||||
|
||||
for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) {
|
||||
const auto &f = int_out_of_range_test_2[i];
|
||||
auto const &f = int_out_of_range_test_2[i];
|
||||
int16_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -156,11 +156,11 @@ int main() {
|
||||
}
|
||||
|
||||
// int out of range error test #3 (32 bit)
|
||||
const std::vector<std::string_view> int_out_of_range_test_3{
|
||||
std::vector<std::string_view> const int_out_of_range_test_3{
|
||||
"2000000000000000000000", "2147483648", "-2147483649"};
|
||||
|
||||
for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) {
|
||||
const auto &f = int_out_of_range_test_3[i];
|
||||
auto const &f = int_out_of_range_test_3[i];
|
||||
int32_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -171,11 +171,11 @@ int main() {
|
||||
}
|
||||
|
||||
// int out of range error test #4 (64 bit)
|
||||
const std::vector<std::string_view> int_out_of_range_test_4{
|
||||
std::vector<std::string_view> const int_out_of_range_test_4{
|
||||
"2000000000000000000000", "9223372036854775808", "-9223372036854775809"};
|
||||
|
||||
for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) {
|
||||
const auto &f = int_out_of_range_test_4[i];
|
||||
auto const &f = int_out_of_range_test_4[i];
|
||||
int64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -186,11 +186,11 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned out of range error test #1 (8 bit)
|
||||
const std::vector<std::string_view> unsigned_out_of_range_test_1{
|
||||
std::vector<std::string_view> const unsigned_out_of_range_test_1{
|
||||
"2000000000000000000000", "256"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) {
|
||||
const auto &f = unsigned_out_of_range_test_1[i];
|
||||
auto const &f = unsigned_out_of_range_test_1[i];
|
||||
uint8_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -201,11 +201,11 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned out of range error test #2 (16 bit)
|
||||
const std::vector<std::string_view> unsigned_out_of_range_test_2{
|
||||
std::vector<std::string_view> const unsigned_out_of_range_test_2{
|
||||
"2000000000000000000000", "65536"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) {
|
||||
const auto &f = unsigned_out_of_range_test_2[i];
|
||||
auto const &f = unsigned_out_of_range_test_2[i];
|
||||
uint16_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -216,11 +216,11 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned out of range error test #3 (32 bit)
|
||||
const std::vector<std::string_view> unsigned_out_of_range_test_3{
|
||||
std::vector<std::string_view> const unsigned_out_of_range_test_3{
|
||||
"2000000000000000000000", "4294967296"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) {
|
||||
const auto &f = unsigned_out_of_range_test_3[i];
|
||||
auto const &f = unsigned_out_of_range_test_3[i];
|
||||
uint32_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -231,11 +231,11 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned out of range error test #4 (64 bit)
|
||||
const std::vector<std::string_view> unsigned_out_of_range_test_4{
|
||||
std::vector<std::string_view> const unsigned_out_of_range_test_4{
|
||||
"2000000000000000000000", "18446744073709551616"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) {
|
||||
const auto &f = unsigned_out_of_range_test_4[i];
|
||||
auto const &f = unsigned_out_of_range_test_4[i];
|
||||
uint64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc::result_out_of_range) {
|
||||
@ -246,10 +246,10 @@ int main() {
|
||||
}
|
||||
|
||||
// int pointer test #1 (only numbers)
|
||||
const std::vector<std::string_view> int_pointer_test_1{"0", "010", "-40"};
|
||||
std::vector<std::string_view> const int_pointer_test_1{"0", "010", "-40"};
|
||||
|
||||
for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) {
|
||||
const auto &f = int_pointer_test_1[i];
|
||||
auto const &f = int_pointer_test_1[i];
|
||||
int result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
@ -264,9 +264,9 @@ int main() {
|
||||
}
|
||||
|
||||
// int pointer test #2 (string behind numbers)
|
||||
const std::string_view int_pointer_test_2 = "1001 with text";
|
||||
std::string_view const int_pointer_test_2 = "1001 with text";
|
||||
|
||||
const auto &f2 = int_pointer_test_2;
|
||||
auto const &f2 = int_pointer_test_2;
|
||||
int result2;
|
||||
auto answer2 =
|
||||
fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2);
|
||||
@ -277,9 +277,9 @@ int main() {
|
||||
}
|
||||
|
||||
// int pointer test #3 (string with newline behind numbers)
|
||||
const std::string_view int_pointer_test_3 = "1001 with text\n";
|
||||
std::string_view const int_pointer_test_3 = "1001 with text\n";
|
||||
|
||||
const auto &f3 = int_pointer_test_3;
|
||||
auto const &f3 = int_pointer_test_3;
|
||||
int result3;
|
||||
auto answer3 =
|
||||
fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3);
|
||||
@ -290,9 +290,9 @@ int main() {
|
||||
}
|
||||
|
||||
// int pointer test #4 (float)
|
||||
const std::string_view int_pointer_test_4 = "9.999";
|
||||
std::string_view const int_pointer_test_4 = "9.999";
|
||||
|
||||
const auto &f4 = int_pointer_test_4;
|
||||
auto const &f4 = int_pointer_test_4;
|
||||
int result4;
|
||||
auto answer4 =
|
||||
fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4);
|
||||
@ -303,9 +303,9 @@ int main() {
|
||||
}
|
||||
|
||||
// int pointer test #5 (invalid int)
|
||||
const std::string_view int_pointer_test_5 = "+50";
|
||||
std::string_view const int_pointer_test_5 = "+50";
|
||||
|
||||
const auto &f5 = int_pointer_test_5;
|
||||
auto const &f5 = int_pointer_test_5;
|
||||
int result5;
|
||||
auto answer5 =
|
||||
fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5);
|
||||
@ -316,9 +316,9 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned pointer test #2 (string behind numbers)
|
||||
const std::string_view unsigned_pointer_test_1 = "1001 with text";
|
||||
std::string_view const unsigned_pointer_test_1 = "1001 with text";
|
||||
|
||||
const auto &f6 = unsigned_pointer_test_1;
|
||||
auto const &f6 = unsigned_pointer_test_1;
|
||||
unsigned result6;
|
||||
auto answer6 =
|
||||
fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6);
|
||||
@ -329,9 +329,9 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned pointer test #2 (invalid unsigned)
|
||||
const std::string_view unsigned_pointer_test_2 = "-50";
|
||||
std::string_view const unsigned_pointer_test_2 = "-50";
|
||||
|
||||
const auto &f7 = unsigned_pointer_test_2;
|
||||
auto const &f7 = unsigned_pointer_test_2;
|
||||
unsigned result7;
|
||||
auto answer7 =
|
||||
fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7);
|
||||
@ -342,12 +342,12 @@ int main() {
|
||||
}
|
||||
|
||||
// int base 2 test
|
||||
const std::vector<int> int_base_2_test_expected{0, 1, 4, 2, -1};
|
||||
const std::vector<std::string_view> int_base_2_test{"0", "1", "100", "010",
|
||||
std::vector<int> const int_base_2_test_expected{0, 1, 4, 2, -1};
|
||||
std::vector<std::string_view> const int_base_2_test{"0", "1", "100", "010",
|
||||
"-1"};
|
||||
|
||||
for (std::size_t i = 0; i < int_base_2_test.size(); ++i) {
|
||||
const auto f = int_base_2_test[i];
|
||||
auto const f = int_base_2_test[i];
|
||||
int result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
|
||||
@ -363,12 +363,12 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned base 2 test
|
||||
const std::vector<unsigned> unsigned_base_2_test_expected{0, 1, 4, 2};
|
||||
const std::vector<std::string_view> unsigned_base_2_test{"0", "1", "100",
|
||||
std::vector<unsigned> const unsigned_base_2_test_expected{0, 1, 4, 2};
|
||||
std::vector<std::string_view> const unsigned_base_2_test{"0", "1", "100",
|
||||
"010"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) {
|
||||
const auto &f = unsigned_base_2_test[i];
|
||||
auto const &f = unsigned_base_2_test[i];
|
||||
unsigned result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
|
||||
@ -384,11 +384,11 @@ int main() {
|
||||
}
|
||||
|
||||
// int invalid error base 2 test
|
||||
const std::vector<std::string_view> int_invalid_argument_base_2_test{"2", "A",
|
||||
std::vector<std::string_view> const int_invalid_argument_base_2_test{"2", "A",
|
||||
"-2"};
|
||||
|
||||
for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) {
|
||||
const auto &f = int_invalid_argument_base_2_test[i];
|
||||
auto const &f = int_invalid_argument_base_2_test[i];
|
||||
int result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
|
||||
@ -400,12 +400,12 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned invalid error base 2 test
|
||||
const std::vector<std::string_view> unsigned_invalid_argument_base_2_test{
|
||||
std::vector<std::string_view> const unsigned_invalid_argument_base_2_test{
|
||||
"2", "A", "-1", "-2"};
|
||||
|
||||
for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size();
|
||||
++i) {
|
||||
const auto &f = unsigned_invalid_argument_base_2_test[i];
|
||||
auto const &f = unsigned_invalid_argument_base_2_test[i];
|
||||
unsigned result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
|
||||
@ -417,12 +417,12 @@ int main() {
|
||||
}
|
||||
|
||||
// octal test
|
||||
const std::vector<int> base_octal_test_expected{0, 1, 7, 8, 9};
|
||||
const std::vector<std::string_view> base_octal_test{"0", "1", "07", "010",
|
||||
std::vector<int> const base_octal_test_expected{0, 1, 7, 8, 9};
|
||||
std::vector<std::string_view> const base_octal_test{"0", "1", "07", "010",
|
||||
"0011"};
|
||||
|
||||
for (std::size_t i = 0; i < base_octal_test.size(); ++i) {
|
||||
const auto &f = base_octal_test[i];
|
||||
auto const &f = base_octal_test[i];
|
||||
int result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, 8);
|
||||
@ -438,12 +438,12 @@ int main() {
|
||||
}
|
||||
|
||||
// hex test
|
||||
const std::vector<int> base_hex_test_expected{0, 1, 15, 31, 0, 16};
|
||||
const std::vector<std::string_view> base_hex_test{"0", "1", "F",
|
||||
std::vector<int> const base_hex_test_expected{0, 1, 15, 31, 0, 16};
|
||||
std::vector<std::string_view> const base_hex_test{"0", "1", "F",
|
||||
"01f", "0x11", "10X11"};
|
||||
|
||||
for (std::size_t i = 0; i < base_hex_test.size(); ++i) {
|
||||
const auto &f = base_hex_test[i];
|
||||
auto const &f = base_hex_test[i];
|
||||
int result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, 16);
|
||||
@ -459,11 +459,11 @@ int main() {
|
||||
}
|
||||
|
||||
// invalid base test #1 (-1)
|
||||
const std::vector<std::string_view> invalid_base_test_1{"0", "1", "-1", "F",
|
||||
std::vector<std::string_view> const invalid_base_test_1{"0", "1", "-1", "F",
|
||||
"10Z"};
|
||||
|
||||
for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) {
|
||||
const auto &f = invalid_base_test_1[i];
|
||||
auto const &f = invalid_base_test_1[i];
|
||||
int result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, -1);
|
||||
@ -475,11 +475,11 @@ int main() {
|
||||
}
|
||||
|
||||
// invalid base test #2 (37)
|
||||
const std::vector<std::string_view> invalid_base_test_2{"0", "1", "F", "Z",
|
||||
std::vector<std::string_view> const invalid_base_test_2{"0", "1", "F", "Z",
|
||||
"10Z"};
|
||||
|
||||
for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) {
|
||||
const auto &f = invalid_base_test_2[i];
|
||||
auto const &f = invalid_base_test_2[i];
|
||||
int result;
|
||||
auto answer =
|
||||
fast_float::from_chars(f.data(), f.data() + f.size(), result, 37);
|
||||
@ -491,7 +491,7 @@ int main() {
|
||||
}
|
||||
|
||||
// int out of range error base test (64 bit)
|
||||
const std::vector<std::string_view> int_out_of_range_base_test{
|
||||
std::vector<std::string_view> const int_out_of_range_base_test{
|
||||
"1000000000000000000000000000000000000000000000000000000000000000",
|
||||
"-1000000000000000000000000000000000000000000000000000000000000001",
|
||||
"2021110011022210012102010021220101220222",
|
||||
@ -564,7 +564,7 @@ int main() {
|
||||
"-1Y2P0IJ32E8E9"};
|
||||
|
||||
for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) {
|
||||
const auto &f = int_out_of_range_base_test[i];
|
||||
auto const &f = int_out_of_range_base_test[i];
|
||||
int64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
|
||||
int(2 + (i / 2)));
|
||||
@ -576,7 +576,7 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned out of range error base test (64 bit)
|
||||
const std::vector<std::string_view> unsigned_out_of_range_base_test{
|
||||
std::vector<std::string_view> const unsigned_out_of_range_base_test{
|
||||
"10000000000000000000000000000000000000000000000000000000000000000",
|
||||
"11112220022122120101211020120210210211221",
|
||||
"100000000000000000000000000000000",
|
||||
@ -614,7 +614,7 @@ int main() {
|
||||
"3W5E11264SGSG"};
|
||||
int base_unsigned = 2;
|
||||
for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) {
|
||||
const auto &f = unsigned_out_of_range_base_test[i];
|
||||
auto const &f = unsigned_out_of_range_base_test[i];
|
||||
uint64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
|
||||
base_unsigned);
|
||||
@ -627,7 +627,7 @@ int main() {
|
||||
}
|
||||
|
||||
// just within range base test (64 bit)
|
||||
const std::vector<std::string_view> int_within_range_base_test{
|
||||
std::vector<std::string_view> const int_within_range_base_test{
|
||||
"111111111111111111111111111111111111111111111111111111111111111",
|
||||
"-1000000000000000000000000000000000000000000000000000000000000000",
|
||||
"2021110011022210012102010021220101220221",
|
||||
@ -700,7 +700,7 @@ int main() {
|
||||
"-1Y2P0IJ32E8E8"};
|
||||
|
||||
for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) {
|
||||
const auto &f = int_within_range_base_test[i];
|
||||
auto const &f = int_within_range_base_test[i];
|
||||
int64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
|
||||
int(2 + (i / 2)));
|
||||
@ -712,7 +712,7 @@ int main() {
|
||||
}
|
||||
|
||||
// unsigned within range base test (64 bit)
|
||||
const std::vector<std::string_view> unsigned_within_range_base_test{
|
||||
std::vector<std::string_view> const unsigned_within_range_base_test{
|
||||
"1111111111111111111111111111111111111111111111111111111111111111",
|
||||
"11112220022122120101211020120210210211220",
|
||||
"33333333333333333333333333333333",
|
||||
@ -750,7 +750,7 @@ int main() {
|
||||
"3W5E11264SGSF"};
|
||||
int base_unsigned2 = 2;
|
||||
for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) {
|
||||
const auto &f = unsigned_within_range_base_test[i];
|
||||
auto const &f = unsigned_within_range_base_test[i];
|
||||
uint64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
|
||||
base_unsigned2);
|
||||
@ -764,7 +764,7 @@ int main() {
|
||||
}
|
||||
|
||||
// int leading zeros test
|
||||
const std::vector<std::string_view> int_leading_zeros_test{
|
||||
std::vector<std::string_view> const int_leading_zeros_test{
|
||||
"000000000000000000000000000000000000000000000000000000000000000000000011"
|
||||
"11110111",
|
||||
"000000000000000000000000000000000000000000000000001101121",
|
||||
@ -803,7 +803,7 @@ int main() {
|
||||
"00000000000000000000S7"};
|
||||
|
||||
for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) {
|
||||
const auto &f = int_leading_zeros_test[i];
|
||||
auto const &f = int_leading_zeros_test[i];
|
||||
int result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
|
||||
int(i + 2));
|
||||
@ -831,6 +831,275 @@ int main() {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
// dont parse UTF-16 code units of emojis as int if low byte is ascii digit
|
||||
{
|
||||
const std::u16string emojis[] = {
|
||||
u"ℹ", u"ℹ️", u"☸", u"☸️", u"☹", u"☹️", u"✳", u"✳️",
|
||||
u"✴", u"✴️", u"⤴", u"⤴️", u"⤵", u"⤵️", u"〰", u"〰️",
|
||||
};
|
||||
bool failed = false;
|
||||
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
|
||||
for (size_t i = 0; i < array_size; i++) {
|
||||
auto e = emojis[i];
|
||||
int foo;
|
||||
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
|
||||
if (answer.ec == std::errc()) {
|
||||
failed = true;
|
||||
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
|
||||
<< "." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
// dont parse UTF-32 code points of emojis as int if low byte is ascii digit
|
||||
{
|
||||
const std::u32string emojis[] = {
|
||||
U"ℹ",
|
||||
U"ℹ️",
|
||||
U"☸",
|
||||
U"☸️",
|
||||
U"☹",
|
||||
U"☹️",
|
||||
U"✳",
|
||||
U"✳️",
|
||||
U"✴",
|
||||
U"✴️",
|
||||
U"⤴",
|
||||
U"⤴️",
|
||||
U"⤵",
|
||||
U"⤵️",
|
||||
U"〰",
|
||||
U"〰️",
|
||||
U"🈲",
|
||||
U"🈳",
|
||||
U"🈴",
|
||||
U"🈵",
|
||||
U"🈶",
|
||||
U"🈷",
|
||||
U"🈷️",
|
||||
U"🈸",
|
||||
U"🈹",
|
||||
U"🌰",
|
||||
U"🌱",
|
||||
U"🌲",
|
||||
U"🌳",
|
||||
U"🌴",
|
||||
U"🌵",
|
||||
U"🌶",
|
||||
U"🌶️",
|
||||
U"🌷",
|
||||
U"🌸",
|
||||
U"🌹",
|
||||
U"🐰",
|
||||
U"🐱",
|
||||
U"🐲",
|
||||
U"🐳",
|
||||
U"🐴",
|
||||
U"🐵",
|
||||
U"🐶",
|
||||
U"🐷",
|
||||
U"🐸",
|
||||
U"🐹",
|
||||
U"🔰",
|
||||
U"🔱",
|
||||
U"🔲",
|
||||
U"🔳",
|
||||
U"🔴",
|
||||
U"🔵",
|
||||
U"🔶",
|
||||
U"🔷",
|
||||
U"🔸",
|
||||
U"🔹",
|
||||
U"😰",
|
||||
U"😱",
|
||||
U"😲",
|
||||
U"😳",
|
||||
U"😴",
|
||||
U"😵",
|
||||
U"😵💫",
|
||||
U"😶",
|
||||
U"😶🌫",
|
||||
U"😶🌫️",
|
||||
U"😷",
|
||||
U"😸",
|
||||
U"😹",
|
||||
U"🤰",
|
||||
U"🤰🏻",
|
||||
U"🤰🏼",
|
||||
U"🤰🏽",
|
||||
U"🤰🏾",
|
||||
U"🤰🏿",
|
||||
U"🤱",
|
||||
U"🤱🏻",
|
||||
U"🤱🏼",
|
||||
U"🤱🏽",
|
||||
U"🤱🏾",
|
||||
U"🤱🏿",
|
||||
U"🤲",
|
||||
U"🤲🏻",
|
||||
U"🤲🏼",
|
||||
U"🤲🏽",
|
||||
U"🤲🏾",
|
||||
U"🤲🏿",
|
||||
U"🤳",
|
||||
U"🤳🏻",
|
||||
U"🤳🏼",
|
||||
U"🤳🏽",
|
||||
U"🤳🏾",
|
||||
U"🤳🏿",
|
||||
U"🤴",
|
||||
U"🤴🏻",
|
||||
U"🤴🏼",
|
||||
U"🤴🏽",
|
||||
U"🤴🏾",
|
||||
U"🤴🏿",
|
||||
U"🤵",
|
||||
U"🤵♀",
|
||||
U"🤵♀️",
|
||||
U"🤵♂",
|
||||
U"🤵♂️",
|
||||
U"🤵🏻",
|
||||
U"🤵🏻♀",
|
||||
U"🤵🏻♀️",
|
||||
U"🤵🏻♂",
|
||||
U"🤵🏻♂️",
|
||||
U"🤵🏼",
|
||||
U"🤵🏼♀",
|
||||
U"🤵🏼♀️",
|
||||
U"🤵🏼♂",
|
||||
U"🤵🏼♂️",
|
||||
U"🤵🏽",
|
||||
U"🤵🏽♀",
|
||||
U"🤵🏽♀️",
|
||||
U"🤵🏽♂",
|
||||
U"🤵🏽♂️",
|
||||
U"🤵🏾",
|
||||
U"🤵🏾♀",
|
||||
U"🤵🏾♀️",
|
||||
U"🤵🏾♂",
|
||||
U"🤵🏾♂️",
|
||||
U"🤵🏿",
|
||||
U"🤵🏿♀",
|
||||
U"🤵🏿♀️",
|
||||
U"🤵🏿♂",
|
||||
U"🤵🏿♂️",
|
||||
U"🤶",
|
||||
U"🤶🏻",
|
||||
U"🤶🏼",
|
||||
U"🤶🏽",
|
||||
U"🤶🏾",
|
||||
U"🤶🏿",
|
||||
U"🤷",
|
||||
U"🤷♀",
|
||||
U"🤷♀️",
|
||||
U"🤷♂",
|
||||
U"🤷♂️",
|
||||
U"🤷🏻",
|
||||
U"🤷🏻♀",
|
||||
U"🤷🏻♀️",
|
||||
U"🤷🏻♂",
|
||||
U"🤷🏻♂️",
|
||||
U"🤷🏼",
|
||||
U"🤷🏼♀",
|
||||
U"🤷🏼♀️",
|
||||
U"🤷🏼♂",
|
||||
U"🤷🏼♂️",
|
||||
U"🤷🏽",
|
||||
U"🤷🏽♀",
|
||||
U"🤷🏽♀️",
|
||||
U"🤷🏽♂",
|
||||
U"🤷🏽♂️",
|
||||
U"🤷🏾",
|
||||
U"🤷🏾♀",
|
||||
U"🤷🏾♀️",
|
||||
U"🤷🏾♂",
|
||||
U"🤷🏾♂️",
|
||||
U"🤷🏿",
|
||||
U"🤷🏿♀",
|
||||
U"🤷🏿♀️",
|
||||
U"🤷🏿♂",
|
||||
U"🤷🏿♂️",
|
||||
U"🤸",
|
||||
U"🤸♀",
|
||||
U"🤸♀️",
|
||||
U"🤸♂",
|
||||
U"🤸♂️",
|
||||
U"🤸🏻",
|
||||
U"🤸🏻♀",
|
||||
U"🤸🏻♀️",
|
||||
U"🤸🏻♂",
|
||||
U"🤸🏻♂️",
|
||||
U"🤸🏼",
|
||||
U"🤸🏼♀",
|
||||
U"🤸🏼♀️",
|
||||
U"🤸🏼♂",
|
||||
U"🤸🏼♂️",
|
||||
U"🤸🏽",
|
||||
U"🤸🏽♀",
|
||||
U"🤸🏽♀️",
|
||||
U"🤸🏽♂",
|
||||
U"🤸🏽♂️",
|
||||
U"🤸🏾",
|
||||
U"🤸🏾♀",
|
||||
U"🤸🏾♀️",
|
||||
U"🤸🏾♂",
|
||||
U"🤸🏾♂️",
|
||||
U"🤸🏿",
|
||||
U"🤸🏿♀",
|
||||
U"🤸🏿♀️",
|
||||
U"🤸🏿♂",
|
||||
U"🤸🏿♂️",
|
||||
U"🤹",
|
||||
U"🤹♀",
|
||||
U"🤹♀️",
|
||||
U"🤹♂",
|
||||
U"🤹♂️",
|
||||
U"🤹🏻",
|
||||
U"🤹🏻♀",
|
||||
U"🤹🏻♀️",
|
||||
U"🤹🏻♂",
|
||||
U"🤹🏻♂️",
|
||||
U"🤹🏼",
|
||||
U"🤹🏼♀",
|
||||
U"🤹🏼♀️",
|
||||
U"🤹🏼♂",
|
||||
U"🤹🏼♂️",
|
||||
U"🤹🏽",
|
||||
U"🤹🏽♀",
|
||||
U"🤹🏽♀️",
|
||||
U"🤹🏽♂",
|
||||
U"🤹🏽♂️",
|
||||
U"🤹🏾",
|
||||
U"🤹🏾♀",
|
||||
U"🤹🏾♀️",
|
||||
U"🤹🏾♂",
|
||||
U"🤹🏾♂️",
|
||||
U"🤹🏿",
|
||||
U"🤹🏿♀",
|
||||
U"🤹🏿♀️",
|
||||
U"🤹🏿♂",
|
||||
U"🤹🏿♂️",
|
||||
};
|
||||
bool failed = false;
|
||||
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
|
||||
for (size_t i = 0; i < array_size; i++) {
|
||||
auto e = emojis[i];
|
||||
int foo;
|
||||
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
|
||||
if (answer.ec == std::errc()) {
|
||||
failed = true;
|
||||
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
|
||||
<< "." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@ -842,4 +1111,4 @@ int main() {
|
||||
std::cerr << "The test requires C++17." << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -4,19 +4,22 @@
|
||||
#include <cstring>
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <cstdint>
|
||||
|
||||
#if __cplusplus >= 202300L
|
||||
#include <stdfloat>
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
// Write some testcases for the parsing of floating point numbers in the
|
||||
// float32_t type. We use the from_chars function defined in this library.
|
||||
#if __STDCPP_FLOAT32_T__
|
||||
const std::vector<std::float32_t> float32_test_expected{
|
||||
#ifdef __STDCPP_FLOAT32_T__
|
||||
std::vector<std::float32_t> const float32_test_expected{
|
||||
123.456f, -78.9f, 0.0001f, 3.40282e+038f};
|
||||
const std::vector<std::string_view> float32_test{"123.456", "-78.9", "0.0001",
|
||||
std::vector<std::string_view> const float32_test{"123.456", "-78.9", "0.0001",
|
||||
"3.40282e+038"};
|
||||
std::cout << "runing float32 test" << std::endl;
|
||||
for (std::size_t i = 0; i < float32_test.size(); ++i) {
|
||||
const auto &f = float32_test[i];
|
||||
auto const &f = float32_test[i];
|
||||
std::float32_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
|
||||
@ -34,16 +37,16 @@ int main() {
|
||||
std::cout << "No std::float32_t type available." << std::endl;
|
||||
#endif
|
||||
|
||||
#if __STDCPP_FLOAT64_T__
|
||||
#ifdef __STDCPP_FLOAT64_T__
|
||||
// Test cases for std::float64_t
|
||||
const std::vector<std::float64_t> float64_test_expected{
|
||||
std::vector<std::float64_t> const float64_test_expected{
|
||||
1.23e4, -5.67e-8, 1.7976931348623157e+308, -1.7976931348623157e+308};
|
||||
const std::vector<std::string_view> float64_test{"1.23e4", "-5.67e-8",
|
||||
std::vector<std::string_view> const float64_test{"1.23e4", "-5.67e-8",
|
||||
"1.7976931348623157e+308",
|
||||
"-1.7976931348623157e+308"};
|
||||
std::cout << "runing float64 test" << std::endl;
|
||||
for (std::size_t i = 0; i < float64_test.size(); ++i) {
|
||||
const auto &f = float64_test[i];
|
||||
auto const &f = float64_test[i];
|
||||
std::float64_t result;
|
||||
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
|
||||
|
||||
|
||||
@ -4,15 +4,14 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
#define FASTFLOAT_ALLOWS_LEADING_PLUS
|
||||
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
int main_readme() {
|
||||
const std::string input = "1d+4";
|
||||
std::string const input = "1d+4";
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::fortran};
|
||||
fast_float::parse_options options{
|
||||
fast_float::chars_format::fortran |
|
||||
fast_float::chars_format::allow_leading_plus};
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if ((answer.ec != std::errc()) || ((result != 10000))) {
|
||||
@ -24,15 +23,17 @@ int main_readme() {
|
||||
}
|
||||
|
||||
int main() {
|
||||
const std::vector<double> expected{10000, 1000, 100, 10, 1,
|
||||
std::vector<double> const expected{10000, 1000, 100, 10, 1,
|
||||
.1, .01, .001, .0001};
|
||||
const std::vector<std::string> fmt1{"1+4", "1+3", "1+2", "1+1", "1+0",
|
||||
std::vector<std::string> const fmt1{"1+4", "1+3", "1+2", "1+1", "1+0",
|
||||
"1-1", "1-2", "1-3", "1-4"};
|
||||
const std::vector<std::string> fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0",
|
||||
std::vector<std::string> const fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0",
|
||||
"1d-1", "1d-2", "1d-3", "1d-4"};
|
||||
const std::vector<std::string> fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
|
||||
std::vector<std::string> const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
|
||||
"+1-1", "+1-2", "+1-3", "+1-4"};
|
||||
const fast_float::parse_options options{fast_float::chars_format::fortran};
|
||||
fast_float::parse_options const options{
|
||||
fast_float::chars_format::fortran |
|
||||
fast_float::chars_format::allow_leading_plus};
|
||||
|
||||
for (auto const &f : fmt1) {
|
||||
auto d{std::distance(&fmt1[0], &f)};
|
||||
|
||||
@ -17,10 +17,10 @@ file(WRITE main.cpp "
|
||||
#include <iostream>
|
||||
|
||||
int main() {
|
||||
const std::string input = \"3.1416 xyz \";
|
||||
std::string input = \"3.1416 xyz \";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { std::cerr << \"parsing failure\\n\"; return EXIT_FAILURE; }
|
||||
if (answer.ec != std::errc()) { std::cerr << \"parsing failure\\n\"; return EXIT_FAILURE; }
|
||||
std::cout << \"parsed the number \" << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}")
|
||||
|
||||
@ -2,16 +2,14 @@
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
// test that this option is ignored
|
||||
#define FASTFLOAT_ALLOWS_LEADING_PLUS
|
||||
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
int main_readme() {
|
||||
const std::string input = "+.1"; // not valid
|
||||
std::string const input = "+.1"; // not valid
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json};
|
||||
fast_float::parse_options options{
|
||||
fast_float::chars_format::json |
|
||||
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec == std::errc()) {
|
||||
@ -22,9 +20,11 @@ int main_readme() {
|
||||
}
|
||||
|
||||
int main_readme2() {
|
||||
const std::string input = "inf"; // not valid in JSON
|
||||
std::string const input = "inf"; // not valid in JSON
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json};
|
||||
fast_float::parse_options options{
|
||||
fast_float::chars_format::json |
|
||||
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec == std::errc()) {
|
||||
@ -35,10 +35,12 @@ int main_readme2() {
|
||||
}
|
||||
|
||||
int main_readme3() {
|
||||
const std::string input =
|
||||
std::string const input =
|
||||
"inf"; // not valid in JSON but we allow it with json_or_infnan
|
||||
double result;
|
||||
fast_float::parse_options options{fast_float::chars_format::json_or_infnan};
|
||||
fast_float::parse_options options{
|
||||
fast_float::chars_format::json_or_infnan |
|
||||
fast_float::chars_format::allow_leading_plus}; // should be ignored
|
||||
auto answer = fast_float::from_chars_advanced(
|
||||
input.data(), input.data() + input.size(), result, options);
|
||||
if (answer.ec != std::errc() || (!std::isinf(result))) {
|
||||
@ -52,6 +54,7 @@ struct ExpectedResult {
|
||||
double value;
|
||||
std::string junk_chars;
|
||||
};
|
||||
|
||||
struct AcceptedValue {
|
||||
std::string input;
|
||||
ExpectedResult expected;
|
||||
@ -61,13 +64,14 @@ struct RejectReason {
|
||||
fast_float::parse_error error;
|
||||
intptr_t location_offset;
|
||||
};
|
||||
|
||||
struct RejectedValue {
|
||||
std::string input;
|
||||
RejectReason reason;
|
||||
};
|
||||
|
||||
int main() {
|
||||
const std::vector<AcceptedValue> accept{
|
||||
std::vector<AcceptedValue> const accept{
|
||||
{"-0.2", {-0.2, ""}},
|
||||
{"0.02", {0.02, ""}},
|
||||
{"0.002", {0.002, ""}},
|
||||
@ -76,7 +80,7 @@ int main() {
|
||||
{"1e", {1., "e"}},
|
||||
{"1e+", {1., "e+"}},
|
||||
{"inf", {std::numeric_limits<double>::infinity(), ""}}};
|
||||
const std::vector<RejectedValue> reject{
|
||||
std::vector<RejectedValue> const reject{
|
||||
{"-.2", {fast_float::parse_error::missing_integer_after_sign, 1}},
|
||||
{"00.02", {fast_float::parse_error::leading_zeros_in_integer_part, 0}},
|
||||
{"0.e+1", {fast_float::parse_error::no_digits_in_fractional_part, 2}},
|
||||
@ -90,8 +94,8 @@ int main() {
|
||||
{"nan(snan)", {fast_float::parse_error::no_digits_in_integer_part, 0}}};
|
||||
|
||||
for (std::size_t i = 0; i < accept.size(); ++i) {
|
||||
const auto &s = accept[i].input;
|
||||
const auto &expected = accept[i].expected;
|
||||
auto const &s = accept[i].input;
|
||||
auto const &expected = accept[i].expected;
|
||||
double result;
|
||||
auto answer =
|
||||
fast_float::from_chars(s.data(), s.data() + s.size(), result,
|
||||
@ -114,7 +118,7 @@ int main() {
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < reject.size(); ++i) {
|
||||
const auto &s = reject[i].input;
|
||||
auto const &s = reject[i].input;
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result,
|
||||
fast_float::chars_format::json);
|
||||
@ -125,11 +129,13 @@ int main() {
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < reject.size(); ++i) {
|
||||
const auto &f = reject[i].input;
|
||||
const auto &expected_reason = reject[i].reason;
|
||||
auto answer = fast_float::parse_number_string(
|
||||
auto const &f = reject[i].input;
|
||||
auto const &expected_reason = reject[i].reason;
|
||||
auto answer = fast_float::parse_number_string<true>(
|
||||
f.data(), f.data() + f.size(),
|
||||
fast_float::parse_options(fast_float::chars_format::json));
|
||||
fast_float::parse_options(
|
||||
fast_float::chars_format::json |
|
||||
fast_float::chars_format::allow_leading_plus)); // should be ignored
|
||||
if (answer.valid) {
|
||||
std::cerr << "json parse accepted invalid json " << f << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
@ -154,6 +160,11 @@ int main() {
|
||||
if (main_readme2() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#ifndef __FAST_MATH__
|
||||
if (main_readme3() != EXIT_SUCCESS) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@ -25,7 +25,7 @@ void allvalues() {
|
||||
memcpy(&v, &word, sizeof(v));
|
||||
|
||||
{
|
||||
const char *string_end = to_string(v, buffer);
|
||||
char const *string_end = to_string(v, buffer);
|
||||
float result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
|
||||
@ -24,7 +24,7 @@ void all_32bit_values() {
|
||||
double v = v32;
|
||||
|
||||
{
|
||||
const char *string_end = to_string(v, buffer);
|
||||
char const *string_end = to_string(v, buffer);
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
|
||||
@ -53,7 +53,7 @@ void random_values(size_t N) {
|
||||
double v;
|
||||
memcpy(&v, &word, sizeof(v));
|
||||
{
|
||||
const char *string_end = to_string(v, buffer);
|
||||
char const *string_end = to_string(v, buffer);
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
|
||||
@ -19,8 +19,8 @@ template <typename T> bool test() {
|
||||
"2.71828182845904523536028747135266249775724709369995";
|
||||
std::vector<T> answers = {T(0.15625), T(3.141592653589793),
|
||||
T(2.718281828459045)};
|
||||
const char *begin = input.data();
|
||||
const char *end = input.data() + input.size();
|
||||
char const *begin = input.data();
|
||||
char const *end = input.data() + input.size();
|
||||
for (size_t i = 0; i < answers.size(); i++) {
|
||||
T result_value;
|
||||
while ((begin < end) && (std::isspace(*begin))) {
|
||||
|
||||
16
tests/p2497.cpp
Normal file
16
tests/p2497.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
if (auto answer = fast_float::from_chars(
|
||||
input.data(), input.data() + input.size(), result)) {
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
std::cerr << "failed to parse " << result << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
@ -15,8 +15,9 @@
|
||||
// always use this fallback because we cannot rely on it behaving as normal
|
||||
// gcc.
|
||||
#include <locale>
|
||||
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double cygwin_strtod_l(char const *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -32,7 +33,8 @@ double cygwin_strtod_l(const char *start, char **end) {
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
|
||||
float cygwin_strtof_l(char const *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -50,7 +52,7 @@ float cygwin_strtof_l(const char *start, char **end) {
|
||||
}
|
||||
#endif
|
||||
|
||||
std::pair<double, bool> strtod_from_string(const char *st) {
|
||||
std::pair<double, bool> strtod_from_string(char const *st) {
|
||||
double d;
|
||||
char *pr;
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
|
||||
@ -56,7 +56,7 @@ void random_values(size_t N) {
|
||||
memcpy(&v, &word, sizeof(v));
|
||||
// if (!std::isnormal(v))
|
||||
{
|
||||
const char *string_end = to_string(v, buffer);
|
||||
char const *string_end = to_string(v, buffer);
|
||||
double result_value;
|
||||
auto result = fast_float::from_chars(buffer, string_end, result_value);
|
||||
// Starting with version 4.0 for fast_float, we return result_out_of_range
|
||||
|
||||
@ -14,8 +14,9 @@
|
||||
// gcc.
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double cygwin_strtod_l(char const *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -31,7 +32,8 @@ double cygwin_strtod_l(const char *start, char **end) {
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
|
||||
float cygwin_strtof_l(char const *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -53,6 +55,7 @@ class RandomEngine {
|
||||
public:
|
||||
RandomEngine() = delete;
|
||||
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
|
||||
|
||||
uint64_t next() {
|
||||
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
|
||||
// Inspired from
|
||||
@ -65,9 +68,13 @@ public:
|
||||
uint64_t m2 = (tmp.high) ^ tmp.low;
|
||||
return m2;
|
||||
}
|
||||
|
||||
bool next_bool() { return (next() & 1) == 1; }
|
||||
|
||||
int next_int() { return static_cast<int>(next()); }
|
||||
|
||||
char next_char() { return static_cast<char>(next()); }
|
||||
|
||||
double next_double() { return static_cast<double>(next()); }
|
||||
|
||||
int next_ranged_int(int min, int max) { // min and max are included
|
||||
@ -90,6 +97,7 @@ public:
|
||||
}
|
||||
return int(m.high) + min;
|
||||
}
|
||||
|
||||
int next_digit() { return next_ranged_int(0, 9); }
|
||||
|
||||
private:
|
||||
|
||||
@ -2,91 +2,70 @@
|
||||
* 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>
|
||||
|
||||
struct test_data {
|
||||
std::string input;
|
||||
bool expected_success;
|
||||
double expected_result;
|
||||
};
|
||||
|
||||
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",
|
||||
"1d+4",
|
||||
"1d-1",
|
||||
"0.",
|
||||
"-.1",
|
||||
"+.1",
|
||||
"1e+1",
|
||||
"+1e1"};
|
||||
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},
|
||||
{false, -1},
|
||||
{false, -1},
|
||||
{true, 0},
|
||||
{true, -0.1},
|
||||
{true, 0.1},
|
||||
{true, 10},
|
||||
{true, 10},
|
||||
std::vector<test_data> const test_datas = {
|
||||
{"infinity", true, std::numeric_limits<double>::infinity()},
|
||||
{" \r\n\t\f\v3.16227766016838 \r\n\t\f\v", true, 3.16227766016838},
|
||||
{" \r\n\t\f\v3 \r\n\t\f\v", true, 3.0},
|
||||
{" 1970-01-01", false, 0.0},
|
||||
{"-NaN", true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{"-inf", true, -std::numeric_limits<double>::infinity()},
|
||||
{" \r\n\t\f\v2.82842712474619 \r\n\t\f\v", true, 2.82842712474619},
|
||||
{"nan", true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{" \r\n\t\f\v2.44948974278318 \r\n\t\f\v", true, 2.44948974278318},
|
||||
{"Inf", true, std::numeric_limits<double>::infinity()},
|
||||
{" \r\n\t\f\v2 \r\n\t\f\v", true, 2.0},
|
||||
{"-infinity", true, -std::numeric_limits<double>::infinity()},
|
||||
{" \r\n\t\f\v0 \r\n\t\f\v", true, 0.0},
|
||||
{" \r\n\t\f\v1.73205080756888 \r\n\t\f\v", true, 1.73205080756888},
|
||||
{" \r\n\t\f\v1 \r\n\t\f\v", true, 1.0},
|
||||
{" \r\n\t\f\v1.4142135623731 \r\n\t\f\v", true, 1.4142135623731},
|
||||
{" \r\n\t\f\v2.23606797749979 \r\n\t\f\v", true, 2.23606797749979},
|
||||
{"1970-01-02 ", false, 0.0},
|
||||
{" \r\n\t\f\v2.64575131106459 \r\n\t\f\v", true, 2.64575131106459},
|
||||
{"inf", true, std::numeric_limits<double>::infinity()},
|
||||
{"-nan", true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{"NaN", true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{"", false, 0.0},
|
||||
{"-Inf", true, -std::numeric_limits<double>::infinity()},
|
||||
{"+2.2", true, 2.2},
|
||||
{"1d+4", false, 0.0},
|
||||
{"1d-1", false, 0.0},
|
||||
{"0.", true, 0.0},
|
||||
{"-.1", true, -0.1},
|
||||
{"+.1", true, 0.1},
|
||||
{"1e+1", true, 10.0},
|
||||
{"+1e1", true, 10.0},
|
||||
{"-+0", false, 0.0},
|
||||
{"-+inf", false, 0.0},
|
||||
{"-+nan", false, 0.0},
|
||||
};
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
const std::string &input = inputs[i];
|
||||
std::pair<bool, double> expected = expected_results[i];
|
||||
for (size_t i = 0; i < test_datas.size(); i++) {
|
||||
auto const &input = test_datas[i].input;
|
||||
auto const expected_success = test_datas[i].expected_success;
|
||||
auto const expected_result = test_datas[i].expected_result;
|
||||
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);
|
||||
auto const answer = fast_float::from_chars(
|
||||
input.data(), input.data() + input.size(), result,
|
||||
fast_float::chars_format::general |
|
||||
fast_float::chars_format::allow_leading_plus |
|
||||
fast_float::chars_format::skip_white_space);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cout << "could not parse" << std::endl;
|
||||
if (expected.first) {
|
||||
if (expected_success) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@ -94,9 +73,9 @@ bool eddelbuettel() {
|
||||
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;
|
||||
for (char const *leftover = answer.ptr;
|
||||
leftover != input.data() + input.size(); leftover++) {
|
||||
if (!fast_float::is_space(uint8_t(*leftover))) {
|
||||
if (!fast_float::is_space(*leftover)) {
|
||||
non_space_trailing_content = true;
|
||||
break;
|
||||
}
|
||||
@ -104,24 +83,19 @@ bool eddelbuettel() {
|
||||
}
|
||||
if (non_space_trailing_content) {
|
||||
std::cout << "found trailing content " << std::endl;
|
||||
}
|
||||
|
||||
if (non_space_trailing_content) {
|
||||
if (!expected.first) {
|
||||
if (!expected_success) {
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
std::cout << "parsed " << result << std::endl;
|
||||
if (!expected.first) {
|
||||
if (!expected_success) {
|
||||
return false;
|
||||
}
|
||||
if (result != expected.second) {
|
||||
if (std::isnan(result) && std::isnan(expected.second)) {
|
||||
continue;
|
||||
}
|
||||
std::cout << "results do not match. Expected " << expected.second
|
||||
if (result != expected_result &&
|
||||
!(std::isnan(result) && std::isnan(expected_result))) {
|
||||
std::cout << "results do not match. Expected " << expected_result
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -14,8 +14,9 @@
|
||||
// gcc.
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double cygwin_strtod_l(char const *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -31,7 +32,8 @@ double cygwin_strtod_l(const char *start, char **end) {
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
|
||||
float cygwin_strtof_l(char const *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -53,6 +55,7 @@ class RandomEngine {
|
||||
public:
|
||||
RandomEngine() = delete;
|
||||
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
|
||||
|
||||
uint64_t next() {
|
||||
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
|
||||
// Inspired from
|
||||
@ -65,9 +68,13 @@ public:
|
||||
uint64_t m2 = (tmp.high) ^ tmp.low;
|
||||
return m2;
|
||||
}
|
||||
|
||||
bool next_bool() { return (next() & 1) == 1; }
|
||||
|
||||
int next_int() { return static_cast<int>(next()); }
|
||||
|
||||
char next_char() { return static_cast<char>(next()); }
|
||||
|
||||
double next_double() { return static_cast<double>(next()); }
|
||||
|
||||
int next_ranged_int(int min, int max) { // min and max are included
|
||||
@ -90,6 +97,7 @@ public:
|
||||
}
|
||||
return int(m.high) + min;
|
||||
}
|
||||
|
||||
int next_digit() { return next_ranged_int(0, 9); }
|
||||
|
||||
private:
|
||||
|
||||
@ -16,8 +16,9 @@
|
||||
// gcc.
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
// workaround for CYGWIN
|
||||
double cygwin_strtod_l(const char *start, char **end) {
|
||||
double cygwin_strtod_l(char const *start, char **end) {
|
||||
double d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -33,7 +34,8 @@ double cygwin_strtod_l(const char *start, char **end) {
|
||||
*end = const_cast<char *>(start) + nread;
|
||||
return d;
|
||||
}
|
||||
float cygwin_strtof_l(const char *start, char **end) {
|
||||
|
||||
float cygwin_strtof_l(char const *start, char **end) {
|
||||
float d;
|
||||
std::stringstream ss;
|
||||
ss.imbue(std::locale::classic());
|
||||
@ -93,8 +95,8 @@ template <typename T> bool test() {
|
||||
std::errc(),
|
||||
std::errc(),
|
||||
std::errc()};
|
||||
const char *begin = input.data();
|
||||
const char *end = input.data() + input.size();
|
||||
char const *begin = input.data();
|
||||
char const *end = input.data() + input.size();
|
||||
for (size_t i = 0; i < answers.size(); i++) {
|
||||
T result_value;
|
||||
while ((begin < end) && (std::isspace(*begin))) {
|
||||
@ -120,9 +122,9 @@ template <typename T> bool test() {
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> void strtod_from_string(const std::string &st, T &d);
|
||||
template <typename T> void strtod_from_string(std::string const &st, T &d);
|
||||
|
||||
template <> void strtod_from_string(const std::string &st, double &d) {
|
||||
template <> void strtod_from_string(std::string const &st, double &d) {
|
||||
char *pr = (char *)st.c_str();
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
@ -139,7 +141,7 @@ template <> void strtod_from_string(const std::string &st, double &d) {
|
||||
}
|
||||
}
|
||||
|
||||
template <> void strtod_from_string(const std::string &st, float &d) {
|
||||
template <> void strtod_from_string(std::string const &st, float &d) {
|
||||
char *pr = (char *)st.c_str();
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
|
||||
defined(sun) || defined(__sun)
|
||||
@ -160,7 +162,7 @@ template <typename T> bool partow_test() {
|
||||
// credit:
|
||||
// https://github.com/ArashPartow/strtk/blob/master/strtk_tokenizer_cmp.cpp#L568
|
||||
// MIT license
|
||||
const std::string strint_list[] = {
|
||||
std::string const strint_list[] = {
|
||||
"9007199254740993",
|
||||
"9007199254740994",
|
||||
"9007199254740995",
|
||||
@ -1085,7 +1087,7 @@ template <typename T> bool partow_test() {
|
||||
"1234567890",
|
||||
"-1234567890",
|
||||
};
|
||||
for (const std::string &st : strint_list) {
|
||||
for (std::string const &st : strint_list) {
|
||||
T expected_value;
|
||||
strtod_from_string(st, expected_value);
|
||||
T result_value;
|
||||
|
||||
53
tests/supported_chars_test.cpp
Normal file
53
tests/supported_chars_test.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
template <typename UC> bool test(std::string s, double expected) {
|
||||
std::basic_string<UC> input(s.begin(), s.end());
|
||||
double result;
|
||||
auto answer =
|
||||
fast_float::from_chars(input.data(), input.data() + input.size(), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cerr << "parsing of \"" << s << "\" should succeed\n";
|
||||
return false;
|
||||
}
|
||||
if (result != expected && !(std::isnan(result) && std::isnan(expected))) {
|
||||
std::cerr << "parsing of \"" << s << "\" succeeded, expected " << expected
|
||||
<< " got " << result << "\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (!test<char>("4.2", 4.2)) {
|
||||
std::cout << "test failure for char" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!test<wchar_t>("4.2", 4.2)) {
|
||||
std::cout << "test failure for wchar_t" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
#ifdef __cpp_char8_t
|
||||
if (!test<char8_t>("4.2", 4.2)) {
|
||||
std::cout << "test failure for char8_t" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!test<char16_t>("4.2", 4.2)) {
|
||||
std::cout << "test failure for char16_t" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!test<char32_t>("4.2", 4.2)) {
|
||||
std::cout << "test failure for char32_t" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << "all ok" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
57
tests/wide_char_test.cpp
Normal file
57
tests/wide_char_test.cpp
Normal file
@ -0,0 +1,57 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
bool tester(std::string s, double expected,
|
||||
fast_float::chars_format fmt = fast_float::chars_format::general) {
|
||||
std::wstring input(s.begin(), s.end());
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(
|
||||
input.data(), input.data() + input.size(), result, fmt);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cerr << "parsing of \"" << s << "\" should succeed\n";
|
||||
return false;
|
||||
}
|
||||
if (result != expected && !(std::isnan(result) && std::isnan(expected))) {
|
||||
std::cerr << "parsing of \"" << s << "\" succeeded, expected " << expected
|
||||
<< " got " << result << "\n";
|
||||
return false;
|
||||
}
|
||||
input[0] += 256;
|
||||
answer = fast_float::from_chars(input.data(), input.data() + input.size(),
|
||||
result, fmt);
|
||||
if (answer.ec == std::errc()) {
|
||||
std::cerr << "parsing of altered \"" << s << "\" should fail\n";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool test_minus() { return tester("-42", -42); }
|
||||
|
||||
bool test_plus() {
|
||||
return tester("+42", 42,
|
||||
fast_float::chars_format::general |
|
||||
fast_float::chars_format::allow_leading_plus);
|
||||
}
|
||||
|
||||
bool test_space() {
|
||||
return tester(" 42", 42,
|
||||
fast_float::chars_format::general |
|
||||
fast_float::chars_format::skip_white_space);
|
||||
}
|
||||
|
||||
bool test_nan() {
|
||||
return tester("nan", std::numeric_limits<double>::quiet_NaN());
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (test_minus() && test_plus() && test_space() && test_nan()) {
|
||||
std::cout << "all ok" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
std::cout << "test failure" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user