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

|
||||

|
||||

|
||||
[](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml)
|
||||
|
||||
The fast_float library provides fast header-only implementations for the C++ from_chars
|
||||
functions for `float` and `double` types. These functions convert ASCII strings representing
|
||||
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
|
||||
@ -28,8 +21,8 @@ struct from_chars_result {
|
||||
```
|
||||
|
||||
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),
|
||||
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.
|
||||
|
||||
@ -47,7 +40,7 @@ Example:
|
||||
``` C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
|
||||
|
||||
int main() {
|
||||
const std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
@ -60,15 +53,15 @@ int main() {
|
||||
|
||||
|
||||
Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
|
||||
the type `fast_float::chars_format`. It is a bitset value: we check whether
|
||||
the type `fast_float::chars_format`. It is a bitset value: we check whether
|
||||
`fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
|
||||
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 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.
|
||||
* 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.
|
||||
@ -77,22 +70,22 @@ Furthermore, we have the following restrictions:
|
||||
|
||||
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
|
||||
|
||||
|
||||
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
|
||||
|
||||
## Using commas as decimal separator
|
||||
|
||||
|
||||
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
|
||||
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.,
|
||||
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;
|
||||
@ -104,23 +97,62 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
You can parse delimited numbers:
|
||||
```C++
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
// check error
|
||||
}
|
||||
// we have result == 234532.3426362.
|
||||
if(answer.ptr[0] != ',') {
|
||||
// unexpected delimiter
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
// check error
|
||||
}
|
||||
// we have result == 7869234.9823.
|
||||
if(answer.ptr[0] != ',') {
|
||||
// unexpected delimiter
|
||||
}
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) {
|
||||
// check error
|
||||
}
|
||||
// we have result == 324562.645.
|
||||
```
|
||||
|
||||
## Reference
|
||||
|
||||
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021.
|
||||
## Relation With Other Work
|
||||
|
||||
The fast_float library is part of:
|
||||
|
||||
- GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
|
||||
- [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
|
||||
|
||||
|
||||
The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
|
||||
|
||||
There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS).
|
||||
|
||||
|
||||
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
|
||||
|
||||
## References
|
||||
|
||||
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021.
|
||||
- Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience (to appear)
|
||||
|
||||
## Other programming languages
|
||||
|
||||
- [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`.
|
||||
- [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`.
|
||||
|
||||
|
||||
## Relation With Other Work
|
||||
|
||||
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
|
||||
|
||||
## Users
|
||||
|
||||
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet).
|
||||
@ -133,14 +165,14 @@ It can parse random floating-point numbers at a speed of 1 GB/s on some systems.
|
||||
<img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
|
||||
|
||||
```
|
||||
$ ./build/benchmarks/benchmark
|
||||
$ ./build/benchmarks/benchmark
|
||||
# parsing random integers in the range [0,1)
|
||||
volume = 2.09808 MB
|
||||
netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
|
||||
doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
|
||||
strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
|
||||
abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
|
||||
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
|
||||
volume = 2.09808 MB
|
||||
netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s
|
||||
doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s
|
||||
strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s
|
||||
abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s
|
||||
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
|
||||
```
|
||||
|
||||
See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
|
||||
@ -181,23 +213,23 @@ You should change the `GIT_TAG` line so that you recover the version you wish to
|
||||
|
||||
## Using as single header
|
||||
|
||||
The script `script/amalgamate.py` may be used to generate a single header
|
||||
The script `script/amalgamate.py` may be used to generate a single header
|
||||
version of the library if so desired.
|
||||
Just run the script from the root directory of this repository.
|
||||
Just run the script from the root directory of this repository.
|
||||
You can customize the license type and output file if desired as described in
|
||||
the command line help.
|
||||
|
||||
You may directly download automatically generated single-header files:
|
||||
|
||||
https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h
|
||||
https://github.com/fastfloat/fast_float/releases/download/v3.4.0/fast_float.h
|
||||
|
||||
## Credit
|
||||
|
||||
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
|
||||
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
|
||||
The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
|
||||
under the Apache 2.0 license.
|
||||
|
||||
## License
|
||||
|
||||
@ -12,9 +12,11 @@ namespace fast_float {
|
||||
|
||||
// Next function can be micro-optimized, but compilers are entirely
|
||||
// able to optimize it well.
|
||||
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; }
|
||||
fastfloat_really_inline constexpr bool is_integer(char c) noexcept {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
fastfloat_really_inline uint64_t byteswap(uint64_t val) {
|
||||
fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
|
||||
return (val & 0xFF00000000000000) >> 56
|
||||
| (val & 0x00FF000000000000) >> 40
|
||||
| (val & 0x0000FF0000000000) >> 24
|
||||
@ -44,7 +46,8 @@ fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
|
||||
}
|
||||
|
||||
// credit @aqrit
|
||||
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
uint32_t parse_eight_digits_unrolled(uint64_t val) {
|
||||
const uint64_t mask = 0x000000FF000000FF;
|
||||
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
|
||||
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
|
||||
@ -59,7 +62,7 @@ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars)
|
||||
}
|
||||
|
||||
// credit @aqrit
|
||||
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
||||
fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
|
||||
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
|
||||
0x8080808080808080));
|
||||
}
|
||||
@ -93,7 +96,11 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
||||
answer.valid = false;
|
||||
answer.too_many_digits = false;
|
||||
answer.negative = (*p == '-');
|
||||
#if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
|
||||
if ((*p == '-') || (*p == '+')) {
|
||||
#else
|
||||
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||
#endif
|
||||
++p;
|
||||
if (p == pend) {
|
||||
return answer;
|
||||
@ -106,10 +113,6 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
|
||||
|
||||
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
|
||||
|
||||
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
|
||||
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
|
||||
p += 8;
|
||||
}
|
||||
while ((p != pend) && is_integer(*p)) {
|
||||
// a multiplication by 10 is cheaper than an arbitrary integer
|
||||
// multiplication
|
||||
|
||||
@ -17,7 +17,7 @@ namespace fast_float {
|
||||
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid
|
||||
// doing `8 * sizeof(limb)`.
|
||||
#if defined(FASTFLOAT_64BIT) && !defined(__sparc)
|
||||
#define FASTFLOAT_64BIT_LIMB
|
||||
#define FASTFLOAT_64BIT_LIMB 1
|
||||
typedef uint64_t limb;
|
||||
constexpr size_t limb_bits = 64;
|
||||
#else
|
||||
@ -54,23 +54,23 @@ struct stackvec {
|
||||
FASTFLOAT_ASSERT(try_extend(s));
|
||||
}
|
||||
|
||||
limb& operator[](size_t index) noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return data[index];
|
||||
}
|
||||
const limb& operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return data[index];
|
||||
}
|
||||
// index from the end of the container
|
||||
const limb& rindex(size_t index) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
size_t rindex = length - index - 1;
|
||||
return data[rindex];
|
||||
}
|
||||
|
||||
// set the length, without bounds checking.
|
||||
void set_len(size_t len) noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
|
||||
length = uint16_t(len);
|
||||
}
|
||||
constexpr size_t len() const noexcept {
|
||||
@ -83,12 +83,12 @@ struct stackvec {
|
||||
return size;
|
||||
}
|
||||
// append item to vector, without bounds checking
|
||||
void push_unchecked(limb value) noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
|
||||
data[length] = value;
|
||||
length++;
|
||||
}
|
||||
// append item to vector, returning if item was added
|
||||
bool try_push(limb value) noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
|
||||
if (len() < capacity()) {
|
||||
push_unchecked(value);
|
||||
return true;
|
||||
@ -137,7 +137,7 @@ struct stackvec {
|
||||
// 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.
|
||||
bool nonzero(size_t index) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
|
||||
while (index < len()) {
|
||||
if (rindex(index) != 0) {
|
||||
return true;
|
||||
@ -147,14 +147,14 @@ struct stackvec {
|
||||
return false;
|
||||
}
|
||||
// normalize the big integer, so most-significant zero limbs are removed.
|
||||
void normalize() noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
|
||||
while (len() > 0 && rindex(0) == 0) {
|
||||
length--;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
fastfloat_really_inline
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
uint64_t empty_hi64(bool& truncated) noexcept {
|
||||
truncated = false;
|
||||
return 0;
|
||||
|
||||
@ -63,7 +63,7 @@ namespace detail {
|
||||
// create an adjusted mantissa, biased by the invalid power2
|
||||
// for significant digits already multiplied by 10 ** q.
|
||||
template <typename binary>
|
||||
fastfloat_really_inline
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
|
||||
int hilz = int(w >> 63) ^ 1;
|
||||
adjusted_mantissa answer;
|
||||
|
||||
@ -23,7 +23,8 @@ constexpr static uint64_t powers_of_ten_uint64[] = {
|
||||
// this algorithm is not even close to optimized, but it has no practical
|
||||
// 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.
|
||||
fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
int32_t scientific_exponent(parsed_number_string& num) noexcept {
|
||||
uint64_t mantissa = num.mantissa;
|
||||
int32_t exponent = int32_t(num.exponent);
|
||||
while (mantissa >= 10000) {
|
||||
@ -44,40 +45,24 @@ fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) n
|
||||
// this converts a native floating-point number to an extended-precision float.
|
||||
template <typename T>
|
||||
fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
|
||||
using equiv_uint = typename binary_format<T>::equiv_uint;
|
||||
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
|
||||
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
|
||||
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
|
||||
|
||||
adjusted_mantissa am;
|
||||
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
|
||||
if (std::is_same<T, float>::value) {
|
||||
constexpr uint32_t exponent_mask = 0x7F800000;
|
||||
constexpr uint32_t mantissa_mask = 0x007FFFFF;
|
||||
constexpr uint64_t hidden_bit_mask = 0x00800000;
|
||||
uint32_t bits;
|
||||
::memcpy(&bits, &value, sizeof(T));
|
||||
if ((bits & exponent_mask) == 0) {
|
||||
// denormal
|
||||
am.power2 = 1 - bias;
|
||||
am.mantissa = bits & mantissa_mask;
|
||||
} else {
|
||||
// normal
|
||||
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
|
||||
am.power2 -= bias;
|
||||
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
||||
}
|
||||
equiv_uint bits;
|
||||
::memcpy(&bits, &value, sizeof(T));
|
||||
if ((bits & exponent_mask) == 0) {
|
||||
// denormal
|
||||
am.power2 = 1 - bias;
|
||||
am.mantissa = bits & mantissa_mask;
|
||||
} else {
|
||||
constexpr uint64_t exponent_mask = 0x7FF0000000000000;
|
||||
constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF;
|
||||
constexpr uint64_t hidden_bit_mask = 0x0010000000000000;
|
||||
uint64_t bits;
|
||||
::memcpy(&bits, &value, sizeof(T));
|
||||
if ((bits & exponent_mask) == 0) {
|
||||
// denormal
|
||||
am.power2 = 1 - bias;
|
||||
am.mantissa = bits & mantissa_mask;
|
||||
} else {
|
||||
// normal
|
||||
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
|
||||
am.power2 -= bias;
|
||||
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
||||
}
|
||||
// normal
|
||||
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
|
||||
am.power2 -= bias;
|
||||
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
|
||||
}
|
||||
|
||||
return am;
|
||||
@ -97,12 +82,13 @@ fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept
|
||||
|
||||
// round an extended-precision float to the nearest machine float.
|
||||
template <typename T, typename callback>
|
||||
fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void round(adjusted_mantissa& am, callback cb) noexcept {
|
||||
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
|
||||
if (-am.power2 >= mantissa_shift) {
|
||||
// have a denormal float
|
||||
int32_t shift = -am.power2 + 1;
|
||||
cb(am, std::min(shift, 64));
|
||||
cb(am, std::min<int32_t>(shift, 64));
|
||||
// check for round-up: if rounding-nearest carried us to the hidden bit.
|
||||
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
|
||||
return;
|
||||
@ -126,23 +112,19 @@ fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept
|
||||
}
|
||||
|
||||
template <typename callback>
|
||||
fastfloat_really_inline
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
|
||||
uint64_t mask;
|
||||
uint64_t halfway;
|
||||
if (shift == 64) {
|
||||
mask = UINT64_MAX;
|
||||
} else {
|
||||
mask = (uint64_t(1) << shift) - 1;
|
||||
}
|
||||
if (shift == 0) {
|
||||
halfway = 0;
|
||||
} else {
|
||||
halfway = uint64_t(1) << (shift - 1);
|
||||
}
|
||||
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 truncated_bits = am.mantissa & mask;
|
||||
uint64_t is_above = truncated_bits > halfway;
|
||||
uint64_t is_halfway = truncated_bits == halfway;
|
||||
bool is_above = truncated_bits > halfway;
|
||||
bool is_halfway = truncated_bits == halfway;
|
||||
|
||||
// shift digits into position
|
||||
if (shift == 64) {
|
||||
@ -156,7 +138,8 @@ void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) n
|
||||
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
|
||||
}
|
||||
|
||||
fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
|
||||
if (shift == 64) {
|
||||
am.mantissa = 0;
|
||||
} else {
|
||||
@ -215,7 +198,7 @@ void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& co
|
||||
count += 8;
|
||||
}
|
||||
|
||||
fastfloat_really_inline
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14
|
||||
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
|
||||
value = value * 10 + limb(*p - '0');
|
||||
p++;
|
||||
|
||||
@ -44,7 +44,7 @@ struct parse_options {
|
||||
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
|
||||
* 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 allowe the fixed point and scientific notation respectively.
|
||||
* to determine whether we allow the fixed point and scientific notation respectively.
|
||||
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
|
||||
*/
|
||||
template<typename T>
|
||||
@ -58,6 +58,6 @@ template<typename T>
|
||||
from_chars_result from_chars_advanced(const char *first, const char *last,
|
||||
T &value, parse_options options) noexcept;
|
||||
|
||||
}
|
||||
} // namespace fast_float
|
||||
#include "parse_number.h"
|
||||
#endif // FASTFLOAT_FAST_FLOAT_H
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -5,18 +5,18 @@
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|
||||
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|
||||
|| defined(__MINGW64__) \
|
||||
|| defined(__s390x__) \
|
||||
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \
|
||||
|| defined(__EMSCRIPTEN__))
|
||||
#define FASTFLOAT_64BIT
|
||||
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) )
|
||||
#define FASTFLOAT_64BIT 1
|
||||
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|
||||
|| defined(__arm__) || defined(_M_ARM) \
|
||||
|| defined(__MINGW32__))
|
||||
#define FASTFLOAT_32BIT
|
||||
|| defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \
|
||||
|| defined(__MINGW32__) || defined(__EMSCRIPTEN__))
|
||||
#define FASTFLOAT_32BIT 1
|
||||
#else
|
||||
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
|
||||
// We can never tell the register width, but the SIZE_MAX is a good approximation.
|
||||
@ -24,9 +24,9 @@
|
||||
#if SIZE_MAX == 0xffff
|
||||
#error Unknown platform (16-bit, unsupported)
|
||||
#elif SIZE_MAX == 0xffffffff
|
||||
#define FASTFLOAT_32BIT
|
||||
#define FASTFLOAT_32BIT 1
|
||||
#elif SIZE_MAX == 0xffffffffffffffff
|
||||
#define FASTFLOAT_64BIT
|
||||
#define FASTFLOAT_64BIT 1
|
||||
#else
|
||||
#error Unknown platform (not 32-bit, not 64-bit?)
|
||||
#endif
|
||||
@ -40,7 +40,9 @@
|
||||
#define FASTFLOAT_VISUAL_STUDIO 1
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
|
||||
#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
|
||||
#elif defined _WIN32
|
||||
#define FASTFLOAT_IS_BIG_ENDIAN 0
|
||||
#else
|
||||
#if defined(__APPLE__) || defined(__FreeBSD__)
|
||||
@ -48,7 +50,11 @@
|
||||
#elif defined(sun) || defined(__sun)
|
||||
#include <sys/byteorder.h>
|
||||
#else
|
||||
#ifdef __has_include
|
||||
#if __has_include(<endian.h>)
|
||||
#include <endian.h>
|
||||
#endif //__has_include(<endian.h>)
|
||||
#endif //__has_include
|
||||
#endif
|
||||
#
|
||||
#ifndef __BYTE_ORDER__
|
||||
@ -75,22 +81,28 @@
|
||||
#endif
|
||||
|
||||
#ifndef FASTFLOAT_ASSERT
|
||||
#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); }
|
||||
#define FASTFLOAT_ASSERT(x) { ((void)(x)); }
|
||||
#endif
|
||||
|
||||
#ifndef FASTFLOAT_DEBUG_ASSERT
|
||||
#include <cassert>
|
||||
#define FASTFLOAT_DEBUG_ASSERT(x) assert(x)
|
||||
#define FASTFLOAT_DEBUG_ASSERT(x) { ((void)(x)); }
|
||||
#endif
|
||||
|
||||
// rust style `try!()` macro, or `?` operator
|
||||
#define FASTFLOAT_TRY(x) { if (!(x)) return false; }
|
||||
|
||||
// Testing for https://wg21.link/N3652, adopted in C++14
|
||||
#if __cpp_constexpr >= 201304
|
||||
#define FASTFLOAT_CONSTEXPR14 constexpr
|
||||
#else
|
||||
#define FASTFLOAT_CONSTEXPR14
|
||||
#endif
|
||||
|
||||
namespace fast_float {
|
||||
|
||||
// Compares two ASCII strings in a case insensitive manner.
|
||||
inline bool fastfloat_strncasecmp(const char *input1, const char *input2,
|
||||
size_t length) {
|
||||
inline FASTFLOAT_CONSTEXPR14 bool
|
||||
fastfloat_strncasecmp(const char *input1, const char *input2, size_t length) {
|
||||
char running_diff{0};
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
running_diff |= (input1[i] ^ input2[i]);
|
||||
@ -107,14 +119,14 @@ template <typename T>
|
||||
struct span {
|
||||
const T* ptr;
|
||||
size_t length;
|
||||
span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
||||
span() : ptr(nullptr), length(0) {}
|
||||
constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
|
||||
constexpr span() : ptr(nullptr), length(0) {}
|
||||
|
||||
constexpr size_t len() const noexcept {
|
||||
return length;
|
||||
}
|
||||
|
||||
const T& operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_CONSTEXPR14 const T& operator[](size_t index) const noexcept {
|
||||
FASTFLOAT_DEBUG_ASSERT(index < length);
|
||||
return ptr[index];
|
||||
}
|
||||
@ -123,8 +135,8 @@ struct span {
|
||||
struct value128 {
|
||||
uint64_t low;
|
||||
uint64_t high;
|
||||
value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
|
||||
value128() : low(0), high(0) {}
|
||||
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
|
||||
constexpr value128() : low(0), high(0) {}
|
||||
};
|
||||
|
||||
/* result might be undefined when input_num is zero */
|
||||
@ -155,14 +167,14 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
|
||||
#ifdef FASTFLOAT_32BIT
|
||||
|
||||
// slow emulation routine for 32-bit
|
||||
fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) {
|
||||
fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
|
||||
return x * (uint64_t)y;
|
||||
}
|
||||
|
||||
// slow emulation routine for 32-bit
|
||||
#if !defined(__MINGW64__)
|
||||
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
|
||||
uint64_t *hi) {
|
||||
fastfloat_really_inline constexpr uint64_t _umul128(uint64_t ab, uint64_t cd,
|
||||
uint64_t *hi) {
|
||||
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
|
||||
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
|
||||
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
|
||||
@ -181,8 +193,9 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
|
||||
fastfloat_really_inline value128 full_multiplication(uint64_t a,
|
||||
uint64_t b) {
|
||||
value128 answer;
|
||||
#ifdef _M_ARM64
|
||||
#if defined(_M_ARM64) && !defined(__MINGW32__)
|
||||
// ARM64 has native support for 64-bit multiplications, no need to emulate
|
||||
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
|
||||
answer.high = __umulh(a, b);
|
||||
answer.low = a * b;
|
||||
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
|
||||
@ -201,10 +214,10 @@ struct adjusted_mantissa {
|
||||
uint64_t mantissa{0};
|
||||
int32_t power2{0}; // a negative value indicates an invalid result
|
||||
adjusted_mantissa() = default;
|
||||
bool operator==(const adjusted_mantissa &o) const {
|
||||
constexpr bool operator==(const adjusted_mantissa &o) const {
|
||||
return mantissa == o.mantissa && power2 == o.power2;
|
||||
}
|
||||
bool operator!=(const adjusted_mantissa &o) const {
|
||||
constexpr bool operator!=(const adjusted_mantissa &o) const {
|
||||
return mantissa != o.mantissa || power2 != o.power2;
|
||||
}
|
||||
};
|
||||
@ -215,25 +228,91 @@ constexpr static int32_t invalid_am_bias = -0x8000;
|
||||
constexpr static double powers_of_ten_double[] = {
|
||||
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
|
||||
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
|
||||
constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5,
|
||||
1e6, 1e7, 1e8, 1e9, 1e10};
|
||||
constexpr static float powers_of_ten_float[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
|
||||
1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
|
||||
// used for max_mantissa_double and max_mantissa_float
|
||||
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
|
||||
// Largest integer value v so that (5**index * v) <= 1<<53.
|
||||
// 0x10000000000000 == 1 << 53
|
||||
constexpr static uint64_t max_mantissa_double[] = {
|
||||
0x10000000000000,
|
||||
0x10000000000000 / 5,
|
||||
0x10000000000000 / (5 * 5),
|
||||
0x10000000000000 / (5 * 5 * 5),
|
||||
0x10000000000000 / (5 * 5 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555),
|
||||
0x10000000000000 / (constant_55555 * 5),
|
||||
0x10000000000000 / (constant_55555 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * 5 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
|
||||
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)};
|
||||
// Largest integer value v so that (5**index * v) <= 1<<24.
|
||||
// 0x1000000 == 1<<24
|
||||
constexpr static uint64_t max_mantissa_float[] = {
|
||||
0x1000000,
|
||||
0x1000000 / 5,
|
||||
0x1000000 / (5 * 5),
|
||||
0x1000000 / (5 * 5 * 5),
|
||||
0x1000000 / (5 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555),
|
||||
0x1000000 / (constant_55555 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
|
||||
0x1000000 / (constant_55555 * constant_55555),
|
||||
0x1000000 / (constant_55555 * constant_55555 * 5)};
|
||||
|
||||
template <typename T> struct binary_format {
|
||||
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
|
||||
|
||||
static inline constexpr int mantissa_explicit_bits();
|
||||
static inline constexpr int minimum_exponent();
|
||||
static inline constexpr int infinite_power();
|
||||
static inline constexpr int sign_index();
|
||||
static inline constexpr int min_exponent_fast_path();
|
||||
static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||
static inline constexpr int max_exponent_fast_path();
|
||||
static inline constexpr int max_exponent_round_to_even();
|
||||
static inline constexpr int min_exponent_round_to_even();
|
||||
static inline constexpr uint64_t max_mantissa_fast_path();
|
||||
static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
|
||||
static inline constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
|
||||
static inline constexpr int largest_power_of_ten();
|
||||
static inline constexpr int 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();
|
||||
};
|
||||
|
||||
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||
return 0;
|
||||
#else
|
||||
return -22;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||
return 0;
|
||||
#else
|
||||
return -10;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
|
||||
return 52;
|
||||
}
|
||||
@ -274,34 +353,30 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
|
||||
template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
|
||||
template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
|
||||
|
||||
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
|
||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||
return 0;
|
||||
#else
|
||||
return -22;
|
||||
#endif
|
||||
}
|
||||
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
|
||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||
return 0;
|
||||
#else
|
||||
return -10;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
|
||||
return 22;
|
||||
}
|
||||
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
|
||||
return 10;
|
||||
}
|
||||
|
||||
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<double>::max_mantissa_fast_path(int64_t power) {
|
||||
// caller is responsible to ensure that
|
||||
// power >= 0 && power <= 22
|
||||
//
|
||||
return max_mantissa_double[power];
|
||||
}
|
||||
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
|
||||
return uint64_t(2) << mantissa_explicit_bits();
|
||||
}
|
||||
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path(int64_t power) {
|
||||
// caller is responsible to ensure that
|
||||
// power >= 0 && power <= 10
|
||||
//
|
||||
return max_mantissa_float[power];
|
||||
}
|
||||
|
||||
template <>
|
||||
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
|
||||
@ -339,6 +414,33 @@ template <> inline constexpr size_t binary_format<float>::max_digits() {
|
||||
return 114;
|
||||
}
|
||||
|
||||
template <> inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::exponent_mask() {
|
||||
return 0x7F800000;
|
||||
}
|
||||
template <> inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::exponent_mask() {
|
||||
return 0x7FF0000000000000;
|
||||
}
|
||||
|
||||
template <> inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::mantissa_mask() {
|
||||
return 0x007FFFFF;
|
||||
}
|
||||
template <> inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::mantissa_mask() {
|
||||
return 0x000FFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
template <> inline constexpr binary_format<float>::equiv_uint
|
||||
binary_format<float>::hidden_bit_mask() {
|
||||
return 0x00800000;
|
||||
}
|
||||
template <> inline constexpr binary_format<double>::equiv_uint
|
||||
binary_format<double>::hidden_bit_mask() {
|
||||
return 0x0010000000000000;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
|
||||
uint64_t word = am.mantissa;
|
||||
@ -357,6 +459,28 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va
|
||||
#endif
|
||||
}
|
||||
|
||||
#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||
template <typename = void>
|
||||
struct space_lut {
|
||||
static constexpr bool value[] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
constexpr bool space_lut<T>::value[];
|
||||
|
||||
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
|
||||
#endif
|
||||
} // namespace fast_float
|
||||
|
||||
#endif
|
||||
|
||||
@ -60,6 +60,70 @@ from_chars_result parse_infnan(const char *first, const char *last, T &value) n
|
||||
return answer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the floating-pointing rounding mode is to 'nearest'.
|
||||
* It is the default on most system. This function is meant to be inexpensive.
|
||||
* Credit : @mwalcott3
|
||||
*/
|
||||
fastfloat_really_inline bool rounds_to_nearest() noexcept {
|
||||
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/
|
||||
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
|
||||
return false;
|
||||
#endif
|
||||
// See
|
||||
// A fast function to check your floating-point rounding mode
|
||||
// https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
|
||||
//
|
||||
// This function is meant to be equivalent to :
|
||||
// prior: #include <cfenv>
|
||||
// return fegetround() == FE_TONEAREST;
|
||||
// However, it is expected to be much faster than the fegetround()
|
||||
// function call.
|
||||
//
|
||||
// The volatile keywoard prevents the compiler from computing the function
|
||||
// at compile-time.
|
||||
// There might be other ways to prevent compile-time optimizations (e.g., asm).
|
||||
// The value does not need to be std::numeric_limits<float>::min(), any small
|
||||
// value so that 1 + x should round to 1 would do (after accounting for excess
|
||||
// precision, as in 387 instructions).
|
||||
static volatile float fmin = std::numeric_limits<float>::min();
|
||||
float fmini = fmin; // we copy it so that it gets loaded at most once.
|
||||
//
|
||||
// Explanation:
|
||||
// Only when fegetround() == FE_TONEAREST do we have that
|
||||
// fmin + 1.0f == 1.0f - fmin.
|
||||
//
|
||||
// FE_UPWARD:
|
||||
// fmin + 1.0f > 1
|
||||
// 1.0f - fmin == 1
|
||||
//
|
||||
// FE_DOWNWARD or FE_TOWARDZERO:
|
||||
// fmin + 1.0f == 1
|
||||
// 1.0f - fmin < 1
|
||||
//
|
||||
// Note: This may fail to be accurate if fast-math has been
|
||||
// enabled, as rounding conventions may not apply.
|
||||
#if FASTFLOAT_VISUAL_STUDIO
|
||||
# pragma warning(push)
|
||||
// todo: is there a VS warning?
|
||||
// see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
|
||||
#elif defined(__clang__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wfloat-equal"
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wfloat-equal"
|
||||
#endif
|
||||
return (fmini + 1.0f == 1.0f - fmini);
|
||||
#if FASTFLOAT_VISUAL_STUDIO
|
||||
# pragma warning(pop)
|
||||
#elif defined(__clang__)
|
||||
# pragma clang diagnostic pop
|
||||
#elif defined(__GNUC__)
|
||||
# pragma GCC diagnostic pop
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<typename T>
|
||||
@ -76,6 +140,11 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
|
||||
|
||||
|
||||
from_chars_result answer;
|
||||
#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
|
||||
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
|
||||
first++;
|
||||
}
|
||||
#endif
|
||||
if (first == last) {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
answer.ptr = first;
|
||||
@ -87,13 +156,45 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
|
||||
}
|
||||
answer.ec = std::errc(); // be optimistic
|
||||
answer.ptr = pns.lastmatch;
|
||||
// Next is Clinger's fast path.
|
||||
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) {
|
||||
value = T(pns.mantissa);
|
||||
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
|
||||
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
|
||||
if (pns.negative) { value = -value; }
|
||||
return answer;
|
||||
// The implementation of the Clinger's fast path is convoluted because
|
||||
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
||||
// selected on the thread.
|
||||
// We proceed optimistically, assuming that detail::rounds_to_nearest() returns
|
||||
// true.
|
||||
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
|
||||
// Unfortunately, the conventional Clinger's fast path is only possible
|
||||
// when the system rounds to the nearest float.
|
||||
//
|
||||
// We expect the next branch to almost always be selected.
|
||||
// We could check it first (before the previous branch), but
|
||||
// there might be performance advantages at having the check
|
||||
// be last.
|
||||
if(detail::rounds_to_nearest()) {
|
||||
// We have that fegetround() == FE_TONEAREST.
|
||||
// Next is Clinger's fast path.
|
||||
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
|
||||
value = T(pns.mantissa);
|
||||
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
|
||||
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
|
||||
if (pns.negative) { value = -value; }
|
||||
return answer;
|
||||
}
|
||||
} else {
|
||||
// We do not have that fegetround() == FE_TONEAREST.
|
||||
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
|
||||
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
|
||||
#if defined(__clang__)
|
||||
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
|
||||
if(pns.mantissa == 0) {
|
||||
value = 0;
|
||||
return answer;
|
||||
}
|
||||
#endif
|
||||
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
if (pns.negative) { value = -value; }
|
||||
return answer;
|
||||
}
|
||||
}
|
||||
}
|
||||
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
|
||||
if(pns.too_many_digits && am.power2 >= 0) {
|
||||
|
||||
@ -137,7 +137,7 @@ inline uint64_t round(decimal &h) {
|
||||
}
|
||||
bool round_up = false;
|
||||
if (dp < h.num_digits) {
|
||||
round_up = h.digits[dp] >= 5; // normally, we round up
|
||||
round_up = h.digits[dp] >= 5; // normally, we round up
|
||||
// but we may need to round to even!
|
||||
if ((h.digits[dp] == 5) && (dp + 1 == h.num_digits)) {
|
||||
round_up = h.truncated || ((dp > 0) && (1 & h.digits[dp - 1]));
|
||||
@ -266,7 +266,7 @@ adjusted_mantissa compute_float(decimal &d) {
|
||||
return answer;
|
||||
} else if(d.decimal_point >= 310) {
|
||||
// We have something at least as large as 0.1e310 which is
|
||||
// always infinite.
|
||||
// always infinite.
|
||||
answer.power2 = binary::infinite_power();
|
||||
answer.mantissa = 0;
|
||||
return answer;
|
||||
|
||||
@ -3,46 +3,78 @@ processed_files = { }
|
||||
|
||||
# authors
|
||||
for filename in ['AUTHORS', 'CONTRIBUTORS']:
|
||||
with open(filename) as f:
|
||||
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
|
||||
processed_files[filename] = text + '//\n'
|
||||
|
||||
# licenses
|
||||
for filename in ['LICENSE-MIT', 'LICENSE-APACHE']:
|
||||
with open(filename) as f:
|
||||
text = ''
|
||||
for line in f:
|
||||
text += '// ' + line
|
||||
processed_files[filename] = text
|
||||
lines = []
|
||||
with open(filename, encoding='utf8') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Retrieve subset required for inclusion in source
|
||||
if filename == 'LICENSE-APACHE':
|
||||
lines = [
|
||||
' Copyright 2021 The fast_float authors\n',
|
||||
*lines[179:-1]
|
||||
]
|
||||
|
||||
text = ''
|
||||
for line in lines:
|
||||
text += '// ' + line.strip() + '\n'
|
||||
processed_files[filename] = text
|
||||
|
||||
# code
|
||||
for filename in [ 'fast_float.h', 'float_common.h', 'ascii_number.h',
|
||||
'fast_table.h', 'decimal_to_binary.h', 'bigint.h',
|
||||
'ascii_number.h', 'digit_comparison.h', 'parse_number.h']:
|
||||
with open('include/fast_float/' + filename) as f:
|
||||
with open('include/fast_float/' + filename, encoding='utf8') as f:
|
||||
text = ''
|
||||
for line in f:
|
||||
if line.startswith('#include "'): continue
|
||||
text += line
|
||||
processed_files[filename] = text
|
||||
processed_files[filename] = '\n' + text
|
||||
|
||||
# command line
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Amalgamate fast_float.')
|
||||
parser.add_argument('--license', default='MIT', help='choose license')
|
||||
parser.add_argument('--license', default='DUAL', choices=['DUAL', 'MIT', 'APACHE'], help='choose license')
|
||||
parser.add_argument('--output', default='', help='output file (stdout if none')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
text = '\n\n'.join([
|
||||
def license_content(license_arg):
|
||||
result = []
|
||||
|
||||
if license_arg == 'DUAL':
|
||||
result += [
|
||||
'// Licensed under the Apache License, Version 2.0, or the\n',
|
||||
'// MIT License at your option. This file may not be copied,\n',
|
||||
'// modified, or distributed except according to those terms.\n',
|
||||
'//\n'
|
||||
]
|
||||
|
||||
if license_arg in ('DUAL', 'MIT'):
|
||||
result.append('// MIT License Notice\n//\n')
|
||||
result.append(processed_files['LICENSE-MIT'])
|
||||
result.append('//\n')
|
||||
if license_arg in ('DUAL', 'APACHE'):
|
||||
result.append('// Apache License (Version 2.0) Notice\n//\n')
|
||||
result.append(processed_files['LICENSE-APACHE'])
|
||||
result.append('//\n')
|
||||
|
||||
return result
|
||||
|
||||
text = ''.join([
|
||||
processed_files['AUTHORS'], processed_files['CONTRIBUTORS'],
|
||||
processed_files['LICENSE-' + args.license],
|
||||
*license_content(args.license),
|
||||
processed_files['fast_float.h'], processed_files['float_common.h'],
|
||||
processed_files['ascii_number.h'], processed_files['fast_table.h'],
|
||||
processed_files['decimal_to_binary.h'], processed_files['bigint.h'],
|
||||
@ -50,7 +82,7 @@ text = '\n\n'.join([
|
||||
processed_files['parse_number.h']])
|
||||
|
||||
if args.output:
|
||||
with open(args.output, 'wt') as f:
|
||||
with open(args.output, 'wt', encoding='utf8') as f:
|
||||
f.write(text)
|
||||
else:
|
||||
print(text)
|
||||
|
||||
@ -38,7 +38,11 @@ add_library(supplemental-data INTERFACE)
|
||||
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
|
||||
function(fast_float_add_cpp_test TEST_NAME)
|
||||
add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
|
||||
add_test(${TEST_NAME} ${TEST_NAME})
|
||||
if(CMAKE_CROSSCOMPILING)
|
||||
set(ccemulator ${CMAKE_CROSSCOMPILING_EMULATOR})
|
||||
endif()
|
||||
add_test(NAME ${TEST_NAME}
|
||||
COMMAND ${ccemulator} $<TARGET_FILE:${TEST_NAME}>)
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
|
||||
target_compile_options(${TEST_NAME} PUBLIC /EHsc)
|
||||
endif()
|
||||
@ -52,10 +56,12 @@ function(fast_float_add_cpp_test TEST_NAME)
|
||||
endif()
|
||||
endfunction(fast_float_add_cpp_test)
|
||||
|
||||
|
||||
fast_float_add_cpp_test(rcppfastfloat_test)
|
||||
fast_float_add_cpp_test(example_test)
|
||||
fast_float_add_cpp_test(example_comma_test)
|
||||
fast_float_add_cpp_test(basictest)
|
||||
target_compile_features(basictest PRIVATE cxx_std_17)
|
||||
|
||||
fast_float_add_cpp_test(long_test)
|
||||
fast_float_add_cpp_test(powersoffive_hardround)
|
||||
fast_float_add_cpp_test(string_test)
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
#include <cfenv>
|
||||
|
||||
#ifndef SUPPLEMENTAL_TEST_DATA_DIR
|
||||
#define SUPPLEMENTAL_TEST_DATA_DIR "data/"
|
||||
@ -42,6 +43,147 @@
|
||||
#define FASTFLOAT_ODDPLATFORM 1
|
||||
#endif
|
||||
|
||||
|
||||
#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"
|
||||
#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")"
|
||||
|
||||
|
||||
const char * round_name(int d) {
|
||||
switch(d) {
|
||||
case FE_UPWARD:
|
||||
return "FE_UPWARD";
|
||||
case FE_DOWNWARD:
|
||||
return "FE_DOWNWARD";
|
||||
case FE_TOWARDZERO:
|
||||
return "FE_TOWARDZERO";
|
||||
case FE_TONEAREST:
|
||||
return "FE_TONEAREST";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define FASTFLOAT_STR(x) #x
|
||||
#define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))
|
||||
|
||||
TEST_CASE("system_info") {
|
||||
std::cout << "system info:" << std::endl;
|
||||
#ifdef _MSC_VER
|
||||
SHOW_DEFINE(_MSC_VER);
|
||||
#endif
|
||||
#ifdef FASTFLOAT_64BIT_LIMB
|
||||
SHOW_DEFINE(FASTFLOAT_64BIT_LIMB);
|
||||
#endif
|
||||
#ifdef __clang__
|
||||
SHOW_DEFINE(__clang__);
|
||||
#endif
|
||||
#ifdef FASTFLOAT_VISUAL_STUDIO
|
||||
SHOW_DEFINE(FASTFLOAT_VISUAL_STUDIO);
|
||||
#endif
|
||||
#ifdef FASTFLOAT_IS_BIG_ENDIAN
|
||||
#if FASTFLOAT_IS_BIG_ENDIAN
|
||||
printf("big endian\n");
|
||||
#else
|
||||
printf("little endian\n");
|
||||
#endif
|
||||
#endif
|
||||
#ifdef FASTFLOAT_32BIT
|
||||
SHOW_DEFINE(FASTFLOAT_32BIT);
|
||||
#endif
|
||||
#ifdef FASTFLOAT_64BIT
|
||||
SHOW_DEFINE(FASTFLOAT_64BIT);
|
||||
#endif
|
||||
#ifdef FLT_EVAL_METHOD
|
||||
SHOW_DEFINE(FLT_EVAL_METHOD);
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
SHOW_DEFINE(_WIN32);
|
||||
#endif
|
||||
#ifdef _WIN64
|
||||
SHOW_DEFINE(_WIN64);
|
||||
#endif
|
||||
std::cout << "fegetround() = " << round_name(fegetround()) << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("rounds_to_nearest") {
|
||||
//
|
||||
// If this function fails, we may be left in a non-standard rounding state.
|
||||
//
|
||||
static volatile float fmin = std::numeric_limits<float>::min();
|
||||
fesetround(FE_UPWARD);
|
||||
std::cout << "FE_UPWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||
CHECK(fegetround() == FE_UPWARD);
|
||||
CHECK(fast_float::detail::rounds_to_nearest() == false);
|
||||
|
||||
fesetround(FE_DOWNWARD);
|
||||
std::cout << "FE_DOWNWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||
CHECK(fegetround() == FE_DOWNWARD);
|
||||
CHECK(fast_float::detail::rounds_to_nearest() == false);
|
||||
|
||||
fesetround(FE_TOWARDZERO);
|
||||
std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||
CHECK(fegetround() == FE_TOWARDZERO);
|
||||
CHECK(fast_float::detail::rounds_to_nearest() == false);
|
||||
|
||||
fesetround(FE_TONEAREST);
|
||||
std::cout << "FE_TONEAREST: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
|
||||
CHECK(fegetround() == FE_TONEAREST);
|
||||
#if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0)
|
||||
CHECK(fast_float::detail::rounds_to_nearest() == true);
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST_CASE("parse_zero") {
|
||||
//
|
||||
// If this function fails, we may be left in a non-standard rounding state.
|
||||
//
|
||||
const char * zero = "0";
|
||||
uint64_t float64_parsed;
|
||||
double f = 0;
|
||||
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||
CHECK(float64_parsed == 0);
|
||||
|
||||
fesetround(FE_UPWARD);
|
||||
auto r1 = fast_float::from_chars(zero, zero + 1, f);
|
||||
CHECK(r1.ec == std::errc());
|
||||
std::cout << "FE_UPWARD parsed zero as " << iHexAndDec(f) << std::endl;
|
||||
CHECK(f == 0);
|
||||
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||
CHECK(float64_parsed == 0);
|
||||
|
||||
fesetround(FE_TOWARDZERO);
|
||||
auto r2 = fast_float::from_chars(zero, zero + 1, f);
|
||||
CHECK(r2.ec == std::errc());
|
||||
std::cout << "FE_TOWARDZERO parsed zero as " << iHexAndDec(f) << std::endl;
|
||||
CHECK(f == 0);
|
||||
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||
CHECK(float64_parsed == 0);
|
||||
|
||||
fesetround(FE_DOWNWARD);
|
||||
auto r3 = fast_float::from_chars(zero, zero + 1, f);
|
||||
CHECK(r3.ec == std::errc());
|
||||
std::cout << "FE_DOWNWARD parsed zero as " << iHexAndDec(f) << std::endl;
|
||||
CHECK(f == 0);
|
||||
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||
CHECK(float64_parsed == 0);
|
||||
|
||||
fesetround(FE_TONEAREST);
|
||||
auto r4 = fast_float::from_chars(zero, zero + 1, f);
|
||||
CHECK(r4.ec == std::errc());
|
||||
std::cout << "FE_TONEAREST parsed zero as " << iHexAndDec(f) << std::endl;
|
||||
CHECK(f == 0);
|
||||
::memcpy(&float64_parsed, &f, sizeof(f));
|
||||
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
|
||||
CHECK(float64_parsed == 0);
|
||||
}
|
||||
|
||||
// C++ 17 because it is otherwise annoying to browse all files in a directory.
|
||||
// We also only run these tests on little endian systems.
|
||||
#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM)
|
||||
@ -50,59 +192,77 @@
|
||||
#include <filesystem>
|
||||
#include <charconv>
|
||||
|
||||
// return true on succcess
|
||||
|
||||
|
||||
// return true on success
|
||||
bool check_file(std::string file_name) {
|
||||
std::cout << "Checking " << file_name << std::endl;
|
||||
size_t number{0};
|
||||
std::fstream newfile(file_name, std::ios::in);
|
||||
if (newfile.is_open()) {
|
||||
std::string str;
|
||||
while (std::getline(newfile, str)) {
|
||||
if (str.size() > 0) {
|
||||
// Read 32-bit hex
|
||||
uint32_t float32;
|
||||
auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(),
|
||||
// We check all rounding directions, for each file.
|
||||
std::vector<int> directions = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST};
|
||||
for (int d : directions) {
|
||||
std::cout << "fesetround to " << round_name(d) << std::endl;
|
||||
fesetround(d);
|
||||
size_t number{0};
|
||||
std::fstream newfile(file_name, std::ios::in);
|
||||
if (newfile.is_open()) {
|
||||
std::string str;
|
||||
while (std::getline(newfile, str)) {
|
||||
if (str.size() > 0) {
|
||||
// Read 32-bit hex
|
||||
uint32_t float32;
|
||||
auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(),
|
||||
float32, 16);
|
||||
if(r32.ec != std::errc()) { std::cerr << "32-bit parsing failure\n"; return false; }
|
||||
// Read 64-bit hex
|
||||
uint64_t float64;
|
||||
auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(),
|
||||
if(r32.ec != std::errc()) { std::cerr << "32-bit parsing failure\n"; return false; }
|
||||
// Read 64-bit hex
|
||||
uint64_t float64;
|
||||
auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(),
|
||||
float64, 16);
|
||||
if(r64.ec != std::errc()) { std::cerr << "64-bit parsing failure\n"; return false; }
|
||||
// The string to parse:
|
||||
const char *number_string = str.data() + 31;
|
||||
const char *end_of_string = str.data() + str.size();
|
||||
// Parse as 32-bit float
|
||||
float parsed_32;
|
||||
auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32);
|
||||
if(fast_float_r32.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
|
||||
// Parse as 64-bit float
|
||||
double parsed_64;
|
||||
auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64);
|
||||
if(fast_float_r64.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
|
||||
// Convert the floats to unsigned ints.
|
||||
uint32_t float32_parsed;
|
||||
uint64_t float64_parsed;
|
||||
::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32));
|
||||
::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64));
|
||||
// Compare with expected results
|
||||
if (float32_parsed != float32) {
|
||||
std::cout << "bad 32 " << str << std::endl;
|
||||
return false;
|
||||
if(r64.ec != std::errc()) { std::cerr << "64-bit parsing failure\n"; return false; }
|
||||
// The string to parse:
|
||||
const char *number_string = str.data() + 31;
|
||||
const char *end_of_string = str.data() + str.size();
|
||||
// Parse as 32-bit float
|
||||
float parsed_32;
|
||||
auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32);
|
||||
if(fast_float_r32.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
|
||||
// Parse as 64-bit float
|
||||
double parsed_64;
|
||||
auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64);
|
||||
if(fast_float_r64.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
|
||||
// Convert the floats to unsigned ints.
|
||||
uint32_t float32_parsed;
|
||||
uint64_t float64_parsed;
|
||||
::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32));
|
||||
::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64));
|
||||
// Compare with expected results
|
||||
if (float32_parsed != float32) {
|
||||
std::cout << "bad 32 " << str << std::endl;
|
||||
std::cout << "parsed as " << iHexAndDec(parsed_32) << std::endl;
|
||||
std::cout << "as raw uint32_t, parsed = " << float32_parsed << ", expected = " << float32 << std::endl;
|
||||
std::cout << "fesetround: " << round_name(d) << std::endl;
|
||||
fesetround(FE_TONEAREST);
|
||||
return false;
|
||||
}
|
||||
if (float64_parsed != float64) {
|
||||
std::cout << "bad 64 " << str << std::endl;
|
||||
std::cout << "parsed as " << iHexAndDec(parsed_64) << std::endl;
|
||||
std::cout << "as raw uint64_t, parsed = " << float64_parsed << ", expected = " << float64 << std::endl;
|
||||
std::cout << "fesetround: " << round_name(d) << std::endl;
|
||||
fesetround(FE_TONEAREST);
|
||||
return false;
|
||||
}
|
||||
number++;
|
||||
}
|
||||
if (float64_parsed != float64) {
|
||||
std::cout << "bad 64 " << str << std::endl;
|
||||
return false;
|
||||
}
|
||||
number++;
|
||||
}
|
||||
std::cout << "checked " << std::defaultfloat << number << " values" << std::endl;
|
||||
newfile.close(); // close the file object
|
||||
} else {
|
||||
std::cout << "Could not read " << file_name << std::endl;
|
||||
fesetround(FE_TONEAREST);
|
||||
return false;
|
||||
}
|
||||
std::cout << "checked " << std::defaultfloat << number << " values" << std::endl;
|
||||
newfile.close(); // close the file object
|
||||
} else {
|
||||
std::cout << "Could not read " << file_name << std::endl;
|
||||
return false;
|
||||
}
|
||||
fesetround(FE_TONEAREST);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -125,9 +285,6 @@ TEST_CASE("leading_zeroes") {
|
||||
CHECK(fast_float::leading_zeroes(bit << 63) == 0);
|
||||
}
|
||||
|
||||
#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"
|
||||
#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")"
|
||||
|
||||
void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) {
|
||||
fast_float::value128 v;
|
||||
v = fast_float::full_multiplication(lhs, rhs);
|
||||
@ -224,6 +381,33 @@ TEST_CASE("decimal_point_parsing") {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("issue19") {
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 234532.3426362\n");
|
||||
CHECK_MESSAGE(answer.ptr == input.data() + 14,
|
||||
"Parsed the number " << result
|
||||
<< " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters");
|
||||
CHECK_MESSAGE(result == 234532.3426362, "We want to parse234532.3426362\n");
|
||||
CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n");
|
||||
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 7869234.9823\n");
|
||||
CHECK_MESSAGE(answer.ptr == input.data() + 27,
|
||||
"Parsed the number " << result
|
||||
<< " and stopped at the wrong character " << (answer.ptr - input.data()));
|
||||
CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n");
|
||||
CHECK_MESSAGE(result == 7869234.9823, "We want to parse up 7869234.9823\n");
|
||||
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 324562.645\n");
|
||||
CHECK_MESSAGE(answer.ptr == input.data() + 38,
|
||||
"Parsed the number " << result
|
||||
<< " and stopped at the wrong character " << (answer.ptr - input.data()));
|
||||
CHECK_MESSAGE(result == 324562.645, "We want to parse up 7869234.9823\n");
|
||||
}
|
||||
|
||||
TEST_CASE("issue19") {
|
||||
const std::string input = "3.14e";
|
||||
double result;
|
||||
@ -454,6 +638,9 @@ TEST_CASE("64bit.inf") {
|
||||
}
|
||||
|
||||
TEST_CASE("64bit.general") {
|
||||
verify("22250738585072012e-324",0x1p-1022); /* limit between normal and subnormal*/
|
||||
verify("-22250738585072012e-324",-0x1p-1022); /* limit between normal and subnormal*/
|
||||
verify("-1e-999",-0.0);
|
||||
verify("-2.2222222222223e-322",-0x1.68p-1069);
|
||||
verify("9007199254740993.0", 0x1p+53);
|
||||
verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328);
|
||||
@ -588,6 +775,7 @@ TEST_CASE("32bit.inf") {
|
||||
}
|
||||
|
||||
TEST_CASE("32bit.general") {
|
||||
verify("-1e-999",-0.0f);
|
||||
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f);
|
||||
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f);
|
||||
verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f);
|
||||
|
||||
@ -3,12 +3,57 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
|
||||
|
||||
bool many() {
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return false; }
|
||||
if(result != 234532.3426362) { return false; }
|
||||
if(answer.ptr[0] != ',') { return false; }
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return false; }
|
||||
if(result != 7869234.9823) { return false; }
|
||||
if(answer.ptr[0] != ',') { return false; }
|
||||
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
|
||||
if(answer.ec != std::errc()) { return false; }
|
||||
if(result != 324562.645) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void many_loop() {
|
||||
const std::string input = "234532.3426362,7869234.9823,324562.645";
|
||||
double result;
|
||||
const char* pointer = input.data();
|
||||
const char* end_pointer = input.data() + input.size();
|
||||
|
||||
while(pointer < end_pointer) {
|
||||
auto answer = fast_float::from_chars(pointer, end_pointer, result);
|
||||
if(answer.ec != std::errc()) {
|
||||
std::cerr << "error while parsing" << std::endl;
|
||||
break;
|
||||
}
|
||||
std::cout << "parsed: " << result << std::endl;
|
||||
pointer = answer.ptr;
|
||||
if ((answer.ptr < end_pointer) && (*pointer == ',')) {
|
||||
pointer++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
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()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
|
||||
if(!many()) {
|
||||
printf("Bug\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
many_loop();
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ bool allvalues() {
|
||||
|
||||
inline void Assert(bool Assertion) {
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; }
|
||||
if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; }
|
||||
#else
|
||||
if (!Assertion) { throw std::runtime_error("bug"); }
|
||||
#endif
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.15)
|
||||
|
||||
project(test_simdjson_install VERSION 0.1.0 LANGUAGES CXX)
|
||||
project(test_install VERSION 0.1.0 LANGUAGES CXX)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
@ -27,4 +27,4 @@ int main() {
|
||||
|
||||
|
||||
add_executable(repro main.cpp)
|
||||
target_link_libraries(repro PUBLIC FastFloat::fast_float)
|
||||
target_link_libraries(repro PUBLIC FastFloat::fast_float)
|
||||
|
||||
123
tests/rcppfastfloat_test.cpp
Normal file
123
tests/rcppfastfloat_test.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/**
|
||||
* See https://github.com/eddelbuettel/rcppfastfloat/issues/4
|
||||
*/
|
||||
|
||||
#define FASTFLOAT_ALLOWS_LEADING_PLUS 1
|
||||
#define FASTFLOAT_SKIP_WHITE_SPACE 1 // important !
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool eddelbuettel() {
|
||||
std::vector<std::string> inputs = {"infinity",
|
||||
" \r\n\t\f\v3.16227766016838 \r\n\t\f\v",
|
||||
" \r\n\t\f\v3 \r\n\t\f\v",
|
||||
" 1970-01-01",
|
||||
"-NaN",
|
||||
"-inf",
|
||||
" \r\n\t\f\v2.82842712474619 \r\n\t\f\v",
|
||||
"nan",
|
||||
" \r\n\t\f\v2.44948974278318 \r\n\t\f\v",
|
||||
"Inf",
|
||||
" \r\n\t\f\v2 \r\n\t\f\v",
|
||||
"-infinity",
|
||||
" \r\n\t\f\v0 \r\n\t\f\v",
|
||||
" \r\n\t\f\v1.73205080756888 \r\n\t\f\v",
|
||||
" \r\n\t\f\v1 \r\n\t\f\v",
|
||||
" \r\n\t\f\v1.4142135623731 \r\n\t\f\v",
|
||||
" \r\n\t\f\v2.23606797749979 \r\n\t\f\v",
|
||||
"1970-01-02 ",
|
||||
" \r\n\t\f\v2.64575131106459 \r\n\t\f\v",
|
||||
"inf",
|
||||
"-nan",
|
||||
"NaN",
|
||||
"",
|
||||
"-Inf",
|
||||
"+2.2"};
|
||||
std::vector<std::pair<bool, double>> expected_results = {
|
||||
{true, std::numeric_limits<double>::infinity()},
|
||||
{true, 3.16227766016838},
|
||||
{true, 3},
|
||||
{false, -1},
|
||||
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{true, -std::numeric_limits<double>::infinity()},
|
||||
{true, 2.82842712474619},
|
||||
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{true, 2.44948974278318},
|
||||
{true, std::numeric_limits<double>::infinity()},
|
||||
{true, 2},
|
||||
{true, -std::numeric_limits<double>::infinity()},
|
||||
{true, 0},
|
||||
{true, 1.73205080756888},
|
||||
{true, 1},
|
||||
{true, 1.4142135623731},
|
||||
{true, 2.23606797749979},
|
||||
{false, -1},
|
||||
{true, 2.64575131106459},
|
||||
{true, std::numeric_limits<double>::infinity()},
|
||||
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{true, std::numeric_limits<double>::quiet_NaN()},
|
||||
{false, -1},
|
||||
{true, -std::numeric_limits<double>::infinity()},
|
||||
{true, 2.2}};
|
||||
for (size_t i = 0; i < inputs.size(); i++) {
|
||||
std::string &input = inputs[i];
|
||||
std::pair<bool, double> expected = expected_results[i];
|
||||
double result;
|
||||
// answer contains a error code and a pointer to the end of the
|
||||
// parsed region (on success).
|
||||
auto answer = fast_float::from_chars(input.data(),
|
||||
input.data() + input.size(), result);
|
||||
if (answer.ec != std::errc()) {
|
||||
std::cout << "could not parse" << std::endl;
|
||||
if (expected.first) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
bool non_space_trailing_content = false;
|
||||
if (answer.ptr != input.data() + input.size()) {
|
||||
// check that there is no content left
|
||||
for (const char *leftover = answer.ptr;
|
||||
leftover != input.data() + input.size(); leftover++) {
|
||||
if (!fast_float::is_space(uint8_t(*leftover))) {
|
||||
non_space_trailing_content = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (non_space_trailing_content) {
|
||||
std::cout << "found trailing content " << std::endl;
|
||||
}
|
||||
|
||||
if (non_space_trailing_content) {
|
||||
if (!expected.first) {
|
||||
continue;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
std::cout << "parsed " << result << std::endl;
|
||||
if (!expected.first) {
|
||||
return false;
|
||||
}
|
||||
if (result != expected.second) {
|
||||
if (std::isnan(result) && std::isnan(expected.second)) {
|
||||
continue;
|
||||
}
|
||||
std::cout << "results do not match. Expected "<< expected.second << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int main() {
|
||||
if (!eddelbuettel()) {
|
||||
printf("Bug.\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
printf("All ok.\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@ -44,8 +44,8 @@ float cygwin_strtof_l(const char* start, char** end) {
|
||||
|
||||
inline void Assert(bool Assertion) {
|
||||
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
|
||||
if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; }
|
||||
#else
|
||||
if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; }
|
||||
#else
|
||||
if (!Assertion) { throw std::runtime_error("bug"); }
|
||||
#endif
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user