Merge branch 'main' into dlemire/adding_bloat_analysis

This commit is contained in:
Daniel Lemire 2023-02-28 17:10:05 -05:00 committed by GitHub
commit 06333da7fe
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 1836 additions and 1103 deletions

View File

@ -1,27 +1,47 @@
name: Alpine Linux name: Alpine Linux
'on': on:
- push - push
- pull_request - pull_request
jobs: jobs:
ubuntu-build: build:
name: Build on Alpine ${{ matrix.arch }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
matrix:
arch:
- x86_64
- x86
- aarch64
- armv7
- ppc64le
- riscv64
steps: steps:
- uses: actions/checkout@v2 - name: Checkout repository
- name: start docker uses: actions/checkout@v1
- name: Install latest Alpine Linux for ${{ matrix.arch }}
uses: jirutka/setup-alpine@v1
with:
arch: ${{ matrix.arch }}
branch: ${{ matrix.arch == 'riscv64' && 'edge' || 'latest-stable' }}
packages: >
build-base
cmake
g++
linux-headers
git
bash
build-base
- name: Prepare
run: | run: |
docker run -w /src -dit --name alpine -v $PWD:/src alpine:latest cmake -DFASTFLOAT_TEST=ON -B build
echo 'docker exec alpine "$@";' > ./alpine.sh shell: alpine.sh {0}
chmod +x ./alpine.sh - name: Build
- name: install packages
run: | run: |
./alpine.sh apk update cmake --build build
./alpine.sh apk add build-base cmake g++ linux-headers git bash shell: alpine.sh {0}
- name: cmake - name: Test
run: | run: |
./alpine.sh cmake -DFASTFLOAT_TEST=ON -B build_for_alpine ctest --test-dir build -R basictest
- name: build shell: alpine.sh {0}
run: |
./alpine.sh cmake --build build_for_alpine
- name: test
run: |
./alpine.sh bash -c "cd build_for_alpine && ctest -R basictest"

View File

@ -1,20 +1,12 @@
name: Amalgamate Ubuntu 20.04 CI (GCC 9, 8) name: Amalgamate Ubuntu 20.04 CI (GCC 9)
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
include:
# Legacy/x86 compilers cause CI failures.
#- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: }
- {cxx: , arch: } # default=gcc9
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Compile with amalgamation - name: Compile with amalgamation
run: | run: |
mkdir build && mkdir build &&

View File

@ -23,7 +23,7 @@ jobs:
CMAKE_GENERATOR: Ninja CMAKE_GENERATOR: Ninja
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
with: with:
update: true update: true

View File

@ -29,7 +29,7 @@ jobs:
CMAKE_GENERATOR: Ninja CMAKE_GENERATOR: Ninja
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
with: with:
update: true update: true

30
.github/workflows/s390x.yml vendored Normal file
View 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

View File

@ -1,21 +1,12 @@
name: Ubuntu 18.04 CI (GCC 7, 6, 5) name: Ubuntu 18.04 CI (GCC 7)
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-18.04 runs-on: ubuntu-18.04
strategy:
fail-fast: false
matrix:
include:
# Legacy/x86 compilers cause CI failures.
#- {cxx: -DCMAKE_CXX_COMPILER=g++-5, arch: }
#- {cxx: -DCMAKE_CXX_COMPILER=g++-6, arch: }
- {cxx: , arch: } # default=gcc7
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc7
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Setup cmake - name: Setup cmake
uses: jwlawson/actions-setup-cmake@v1.4 uses: jwlawson/actions-setup-cmake@v1.4
with: with:

View File

@ -8,7 +8,7 @@ jobs:
strategy: strategy:
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Use cmake - name: Use cmake
run: | run: |
mkdir build && mkdir build &&

16
.github/workflows/ubuntu20-fastmath.yml vendored Normal file
View 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

View File

@ -5,16 +5,8 @@ on: [push, pull_request]
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-20.04 runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
include:
# Legacy/x86 compilers cause CI failures.
#- {cxx: -DCMAKE_CXX_COMPILER=g++-8, arch: }
- {cxx: , arch: } # default=gcc9
#- {cxx: , arch: -DCMAKE_CXX_FLAGS="-m32"} # default=gcc9
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v3
- name: Use cmake - name: Use cmake
run: | run: |
mkdir build && mkdir build &&

18
.github/workflows/ubuntu22-clang.yml vendored Normal file
View 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
View 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
View 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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View 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
View 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
View 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
View 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}}

View File

@ -1,9 +1,7 @@
cmake_minimum_required(VERSION 3.9) cmake_minimum_required(VERSION 3.9)
project(fast_float VERSION 3.2.0 LANGUAGES CXX) project(fast_float VERSION 3.10.0 LANGUAGES CXX)
option(FASTFLOAT_TEST "Enable tests" OFF) option(FASTFLOAT_TEST "Enable tests" OFF)
set(CMAKE_CXX_STANDARD 11 CACHE STRING "C++ standard to be used")
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(FASTFLOAT_TEST) if(FASTFLOAT_TEST)
enable_testing() enable_testing()
add_subdirectory(tests) add_subdirectory(tests)
@ -22,14 +20,21 @@ if (NOT CMAKE_BUILD_TYPE)
endif() endif()
endif() endif()
option(FASTFLOAT_INSTALL "Enable install" ON)
if(FASTFLOAT_INSTALL)
include(GNUInstallDirs)
endif()
add_library(fast_float INTERFACE) add_library(fast_float INTERFACE)
add_library(FastFloat::fast_float ALIAS fast_float)
target_include_directories( target_include_directories(
fast_float fast_float
INTERFACE INTERFACE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include> $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
) )
target_compile_features(fast_float INTERFACE cxx_std_11)
if(FASTFLOAT_SANITIZE) if(FASTFLOAT_SANITIZE)
target_compile_options(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all) target_compile_options(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
target_link_libraries(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all) target_link_libraries(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all)
@ -42,24 +47,30 @@ if(MSVC_VERSION GREATER 1910)
endif() endif()
include(CMakePackageConfigHelpers) if(FASTFLOAT_INSTALL)
include(CMakePackageConfigHelpers)
set(FASTFLOAT_VERSION_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfigVersion.cmake") set(FASTFLOAT_VERSION_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfigVersion.cmake")
set(FASTFLOAT_PROJECT_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfig.cmake") set(FASTFLOAT_PROJECT_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfig.cmake")
set(FASTFLOAT_INSTALL_DIR "share/FastFloat") set(FASTFLOAT_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake/FastFloat")
write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion) if(${CMAKE_VERSION} VERSION_LESS "3.14")
configure_package_config_file("cmake/config.cmake.in" write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion)
"${FASTFLOAT_PROJECT_CONFIG}" else()
INSTALL_DESTINATION "${FASTFLOAT_INSTALL_DIR}") write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT)
endif()
configure_package_config_file("cmake/config.cmake.in"
"${FASTFLOAT_PROJECT_CONFIG}"
INSTALL_DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/fast_float" DESTINATION "include") install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/fast_float" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
install(FILES "${FASTFLOAT_PROJECT_CONFIG}" "${FASTFLOAT_VERSION_CONFIG}" DESTINATION "${FASTFLOAT_INSTALL_DIR}") install(FILES "${FASTFLOAT_PROJECT_CONFIG}" "${FASTFLOAT_VERSION_CONFIG}" DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
install(EXPORT ${PROJECT_NAME}-targets NAMESPACE FastFloat:: DESTINATION "${FASTFLOAT_INSTALL_DIR}") install(EXPORT ${PROJECT_NAME}-targets NAMESPACE FastFloat:: DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}")
install(TARGETS fast_float install(TARGETS fast_float
EXPORT ${PROJECT_NAME}-targets EXPORT ${PROJECT_NAME}-targets
RUNTIME DESTINATION bin RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION lib ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
LIBRARY DESTINATION lib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
) )
endif()

View File

@ -175,18 +175,7 @@
END OF TERMS AND CONDITIONS END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work. Copyright 2021 The fast_float authors
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright 2020 The fast_float authors
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -1,3 +1,7 @@
MIT License
Copyright (c) 2021 The fast_float authors
Permission is hereby granted, free of charge, to any Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the documentation files (the "Software"), to deal in the

View File

@ -1,12 +1,5 @@
## fast_float number parsing library: 4x faster than strtod ## fast_float number parsing library: 4x faster than strtod
![Ubuntu 20.04 CI (GCC 9)](https://github.com/lemire/fast_float/workflows/Ubuntu%2020.04%20CI%20(GCC%209)/badge.svg)
![Ubuntu 18.04 CI (GCC 7)](https://github.com/lemire/fast_float/workflows/Ubuntu%2018.04%20CI%20(GCC%207)/badge.svg)
![Alpine Linux](https://github.com/lemire/fast_float/workflows/Alpine%20Linux/badge.svg)
![MSYS2-CI](https://github.com/lemire/fast_float/workflows/MSYS2-CI/badge.svg)
![VS16-CLANG-CI](https://github.com/lemire/fast_float/workflows/VS16-CLANG-CI/badge.svg)
[![VS16-CI](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/vs16-ci.yml)
The fast_float library provides fast header-only implementations for the C++ from_chars The fast_float library provides fast header-only implementations for the C++ from_chars
functions for `float` and `double` types. These functions convert ASCII strings representing functions for `float` and `double` types. These functions convert ASCII strings representing
decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
@ -77,7 +70,7 @@ Furthermore, we have the following restrictions:
We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems. We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
## Using commas as decimal separator ## Using commas as decimal separator
@ -104,23 +97,62 @@ int main() {
} }
``` ```
You can parse delimited numbers:
```C++
const std::string input = "234532.3426362,7869234.9823,324562.645";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 234532.3426362.
if(answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 7869234.9823.
if(answer.ptr[0] != ',') {
// unexpected delimiter
}
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) {
// check error
}
// we have result == 324562.645.
```
## Reference
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Pratice and Experience 51 (8), 2021. ## Relation With Other Work
The fast_float library is part of:
- GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
- [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS).
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
## References
- Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021.
- Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience (to appear)
## Other programming languages ## Other programming languages
- [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`. - [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
- [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`. - [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`.
- [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. - [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core).
- [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`. - [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
## Relation With Other Work
The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM).
## Users ## Users
The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet). The fast_float library is used by [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times. It is also used by [Yandex ClickHouse](https://github.com/ClickHouse/ClickHouse) and by [Google Jsonnet](https://github.com/google/jsonnet).
@ -189,7 +221,7 @@ the command line help.
You may directly download automatically generated single-header files: You may directly download automatically generated single-header files:
https://github.com/fastfloat/fast_float/releases/download/v1.1.2/fast_float.h https://github.com/fastfloat/fast_float/releases/download/v3.4.0/fast_float.h
## Credit ## Credit

View File

@ -12,9 +12,11 @@ namespace fast_float {
// Next function can be micro-optimized, but compilers are entirely // Next function can be micro-optimized, but compilers are entirely
// able to optimize it well. // able to optimize it well.
fastfloat_really_inline bool is_integer(char c) noexcept { return c >= '0' && c <= '9'; } fastfloat_really_inline constexpr bool is_integer(char c) noexcept {
return c >= '0' && c <= '9';
}
fastfloat_really_inline uint64_t byteswap(uint64_t val) { fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
return (val & 0xFF00000000000000) >> 56 return (val & 0xFF00000000000000) >> 56
| (val & 0x00FF000000000000) >> 40 | (val & 0x00FF000000000000) >> 40
| (val & 0x0000FF0000000000) >> 24 | (val & 0x0000FF0000000000) >> 24
@ -44,7 +46,8 @@ fastfloat_really_inline void write_u64(uint8_t *chars, uint64_t val) {
} }
// credit @aqrit // credit @aqrit
fastfloat_really_inline uint32_t parse_eight_digits_unrolled(uint64_t val) { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint32_t parse_eight_digits_unrolled(uint64_t val) {
const uint64_t mask = 0x000000FF000000FF; const uint64_t mask = 0x000000FF000000FF;
const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
@ -59,7 +62,7 @@ fastfloat_really_inline uint32_t parse_eight_digits_unrolled(const char *chars)
} }
// credit @aqrit // credit @aqrit
fastfloat_really_inline bool is_made_of_eight_digits_fast(uint64_t val) noexcept { fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept {
return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) &
0x8080808080808080)); 0x8080808080808080));
} }
@ -93,7 +96,11 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
answer.valid = false; answer.valid = false;
answer.too_many_digits = false; answer.too_many_digits = false;
answer.negative = (*p == '-'); answer.negative = (*p == '-');
#if FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if ((*p == '-') || (*p == '+')) {
#else
if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here if (*p == '-') { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
#endif
++p; ++p;
if (p == pend) { if (p == pend) {
return answer; return answer;
@ -106,10 +113,6 @@ parsed_number_string parse_number_string(const char *p, const char *pend, parse_
uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad)
while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(p)) {
i = i * 100000000 + parse_eight_digits_unrolled(p); // in rare cases, this will overflow, but that's ok
p += 8;
}
while ((p != pend) && is_integer(*p)) { while ((p != pend) && is_integer(*p)) {
// a multiplication by 10 is cheaper than an arbitrary integer // a multiplication by 10 is cheaper than an arbitrary integer
// multiplication // multiplication

View File

@ -17,7 +17,7 @@ namespace fast_float {
// we might have platforms where `CHAR_BIT` is not 8, so let's avoid // we might have platforms where `CHAR_BIT` is not 8, so let's avoid
// doing `8 * sizeof(limb)`. // doing `8 * sizeof(limb)`.
#if defined(FASTFLOAT_64BIT) && !defined(__sparc) #if defined(FASTFLOAT_64BIT) && !defined(__sparc)
#define FASTFLOAT_64BIT_LIMB #define FASTFLOAT_64BIT_LIMB 1
typedef uint64_t limb; typedef uint64_t limb;
constexpr size_t limb_bits = 64; constexpr size_t limb_bits = 64;
#else #else
@ -54,23 +54,23 @@ struct stackvec {
FASTFLOAT_ASSERT(try_extend(s)); FASTFLOAT_ASSERT(try_extend(s));
} }
limb& operator[](size_t index) noexcept { FASTFLOAT_CONSTEXPR14 limb& operator[](size_t index) noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
const limb& operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
// index from the end of the container // index from the end of the container
const limb& rindex(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const limb& rindex(size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
size_t rindex = length - index - 1; size_t rindex = length - index - 1;
return data[rindex]; return data[rindex];
} }
// set the length, without bounds checking. // set the length, without bounds checking.
void set_len(size_t len) noexcept { FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept {
length = uint16_t(len); length = uint16_t(len);
} }
constexpr size_t len() const noexcept { constexpr size_t len() const noexcept {
@ -83,12 +83,12 @@ struct stackvec {
return size; return size;
} }
// append item to vector, without bounds checking // append item to vector, without bounds checking
void push_unchecked(limb value) noexcept { FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept {
data[length] = value; data[length] = value;
length++; length++;
} }
// append item to vector, returning if item was added // append item to vector, returning if item was added
bool try_push(limb value) noexcept { FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
if (len() < capacity()) { if (len() < capacity()) {
push_unchecked(value); push_unchecked(value);
return true; return true;
@ -137,7 +137,7 @@ struct stackvec {
// check if any limbs are non-zero after the given index. // check if any limbs are non-zero after the given index.
// this needs to be done in reverse order, since the index // this needs to be done in reverse order, since the index
// is relative to the most significant limbs. // is relative to the most significant limbs.
bool nonzero(size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept {
while (index < len()) { while (index < len()) {
if (rindex(index) != 0) { if (rindex(index) != 0) {
return true; return true;
@ -147,14 +147,14 @@ struct stackvec {
return false; return false;
} }
// normalize the big integer, so most-significant zero limbs are removed. // normalize the big integer, so most-significant zero limbs are removed.
void normalize() noexcept { FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) { while (len() > 0 && rindex(0) == 0) {
length--; length--;
} }
} }
}; };
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
uint64_t empty_hi64(bool& truncated) noexcept { uint64_t empty_hi64(bool& truncated) noexcept {
truncated = false; truncated = false;
return 0; return 0;

View File

@ -63,7 +63,7 @@ namespace detail {
// create an adjusted mantissa, biased by the invalid power2 // create an adjusted mantissa, biased by the invalid power2
// for significant digits already multiplied by 10 ** q. // for significant digits already multiplied by 10 ** q.
template <typename binary> template <typename binary>
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept {
int hilz = int(w >> 63) ^ 1; int hilz = int(w >> 63) ^ 1;
adjusted_mantissa answer; adjusted_mantissa answer;

View File

@ -23,7 +23,8 @@ constexpr static uint64_t powers_of_ten_uint64[] = {
// this algorithm is not even close to optimized, but it has no practical // this algorithm is not even close to optimized, but it has no practical
// effect on performance: in order to have a faster algorithm, we'd need // effect on performance: in order to have a faster algorithm, we'd need
// to slow down performance for faster algorithms, and this is still fast. // to slow down performance for faster algorithms, and this is still fast.
fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) noexcept { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
int32_t scientific_exponent(parsed_number_string& num) noexcept {
uint64_t mantissa = num.mantissa; uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent); int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) { while (mantissa >= 10000) {
@ -44,40 +45,24 @@ fastfloat_really_inline int32_t scientific_exponent(parsed_number_string& num) n
// this converts a native floating-point number to an extended-precision float. // this converts a native floating-point number to an extended-precision float.
template <typename T> template <typename T>
fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept { fastfloat_really_inline adjusted_mantissa to_extended(T value) noexcept {
using equiv_uint = typename binary_format<T>::equiv_uint;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
adjusted_mantissa am; adjusted_mantissa am;
int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent(); int32_t bias = binary_format<T>::mantissa_explicit_bits() - binary_format<T>::minimum_exponent();
if (std::is_same<T, float>::value) { equiv_uint bits;
constexpr uint32_t exponent_mask = 0x7F800000; ::memcpy(&bits, &value, sizeof(T));
constexpr uint32_t mantissa_mask = 0x007FFFFF; if ((bits & exponent_mask) == 0) {
constexpr uint64_t hidden_bit_mask = 0x00800000; // denormal
uint32_t bits; am.power2 = 1 - bias;
::memcpy(&bits, &value, sizeof(T)); am.mantissa = bits & mantissa_mask;
if ((bits & exponent_mask) == 0) {
// denormal
am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
}
} else { } else {
constexpr uint64_t exponent_mask = 0x7FF0000000000000; // normal
constexpr uint64_t mantissa_mask = 0x000FFFFFFFFFFFFF; am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
constexpr uint64_t hidden_bit_mask = 0x0010000000000000; am.power2 -= bias;
uint64_t bits; am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
::memcpy(&bits, &value, sizeof(T));
if ((bits & exponent_mask) == 0) {
// denormal
am.power2 = 1 - bias;
am.mantissa = bits & mantissa_mask;
} else {
// normal
am.power2 = int32_t((bits & exponent_mask) >> binary_format<T>::mantissa_explicit_bits());
am.power2 -= bias;
am.mantissa = (bits & mantissa_mask) | hidden_bit_mask;
}
} }
return am; return am;
@ -97,12 +82,13 @@ fastfloat_really_inline adjusted_mantissa to_extended_halfway(T value) noexcept
// round an extended-precision float to the nearest machine float. // round an extended-precision float to the nearest machine float.
template <typename T, typename callback> template <typename T, typename callback>
fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round(adjusted_mantissa& am, callback cb) noexcept {
int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1; int32_t mantissa_shift = 64 - binary_format<T>::mantissa_explicit_bits() - 1;
if (-am.power2 >= mantissa_shift) { if (-am.power2 >= mantissa_shift) {
// have a denormal float // have a denormal float
int32_t shift = -am.power2 + 1; int32_t shift = -am.power2 + 1;
cb(am, std::min(shift, 64)); cb(am, std::min<int32_t>(shift, 64));
// check for round-up: if rounding-nearest carried us to the hidden bit. // check for round-up: if rounding-nearest carried us to the hidden bit.
am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1; am.power2 = (am.mantissa < (uint64_t(1) << binary_format<T>::mantissa_explicit_bits())) ? 0 : 1;
return; return;
@ -126,23 +112,19 @@ fastfloat_really_inline void round(adjusted_mantissa& am, callback cb) noexcept
} }
template <typename callback> template <typename callback>
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept { void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) noexcept {
uint64_t mask; const uint64_t mask
uint64_t halfway; = (shift == 64)
if (shift == 64) { ? UINT64_MAX
mask = UINT64_MAX; : (uint64_t(1) << shift) - 1;
} else { const uint64_t halfway
mask = (uint64_t(1) << shift) - 1; = (shift == 0)
} ? 0
if (shift == 0) { : uint64_t(1) << (shift - 1);
halfway = 0;
} else {
halfway = uint64_t(1) << (shift - 1);
}
uint64_t truncated_bits = am.mantissa & mask; uint64_t truncated_bits = am.mantissa & mask;
uint64_t is_above = truncated_bits > halfway; bool is_above = truncated_bits > halfway;
uint64_t is_halfway = truncated_bits == halfway; bool is_halfway = truncated_bits == halfway;
// shift digits into position // shift digits into position
if (shift == 64) { if (shift == 64) {
@ -156,7 +138,8 @@ void round_nearest_tie_even(adjusted_mantissa& am, int32_t shift, callback cb) n
am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above));
} }
fastfloat_really_inline void round_down(adjusted_mantissa& am, int32_t shift) noexcept { fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void round_down(adjusted_mantissa& am, int32_t shift) noexcept {
if (shift == 64) { if (shift == 64) {
am.mantissa = 0; am.mantissa = 0;
} else { } else {
@ -215,7 +198,7 @@ void parse_eight_digits(const char*& p, limb& value, size_t& counter, size_t& co
count += 8; count += 8;
} }
fastfloat_really_inline fastfloat_really_inline FASTFLOAT_CONSTEXPR14
void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept { void parse_one_digit(const char*& p, limb& value, size_t& counter, size_t& count) noexcept {
value = value * 10 + limb(*p - '0'); value = value * 10 + limb(*p - '0');
p++; p++;

View File

@ -44,7 +44,7 @@ struct parse_options {
* Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of * Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
* the type `fast_float::chars_format`. It is a bitset value: we check whether * the type `fast_float::chars_format`. It is a bitset value: we check whether
* `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set * `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
* to determine whether we allowe the fixed point and scientific notation respectively. * to determine whether we allow the fixed point and scientific notation respectively.
* The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`. * The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
*/ */
template<typename T> template<typename T>
@ -58,6 +58,6 @@ template<typename T>
from_chars_result from_chars_advanced(const char *first, const char *last, from_chars_result from_chars_advanced(const char *first, const char *last,
T &value, parse_options options) noexcept; T &value, parse_options options) noexcept;
} } // namespace fast_float
#include "parse_number.h" #include "parse_number.h"
#endif // FASTFLOAT_FAST_FLOAT_H #endif // FASTFLOAT_FAST_FLOAT_H

File diff suppressed because it is too large Load Diff

View File

@ -5,18 +5,18 @@
#include <cstdint> #include <cstdint>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <type_traits>
#if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \ #if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) \
|| defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \ || defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) \
|| defined(__MINGW64__) \ || defined(__MINGW64__) \
|| defined(__s390x__) \ || defined(__s390x__) \
|| (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) \ || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) )
|| defined(__EMSCRIPTEN__)) #define FASTFLOAT_64BIT 1
#define FASTFLOAT_64BIT
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \ #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) \
|| defined(__arm__) || defined(_M_ARM) \ || defined(__arm__) || defined(_M_ARM) || defined(__ppc__) \
|| defined(__MINGW32__)) || defined(__MINGW32__) || defined(__EMSCRIPTEN__))
#define FASTFLOAT_32BIT #define FASTFLOAT_32BIT 1
#else #else
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
// We can never tell the register width, but the SIZE_MAX is a good approximation. // We can never tell the register width, but the SIZE_MAX is a good approximation.
@ -24,9 +24,9 @@
#if SIZE_MAX == 0xffff #if SIZE_MAX == 0xffff
#error Unknown platform (16-bit, unsupported) #error Unknown platform (16-bit, unsupported)
#elif SIZE_MAX == 0xffffffff #elif SIZE_MAX == 0xffffffff
#define FASTFLOAT_32BIT #define FASTFLOAT_32BIT 1
#elif SIZE_MAX == 0xffffffffffffffff #elif SIZE_MAX == 0xffffffffffffffff
#define FASTFLOAT_64BIT #define FASTFLOAT_64BIT 1
#else #else
#error Unknown platform (not 32-bit, not 64-bit?) #error Unknown platform (not 32-bit, not 64-bit?)
#endif #endif
@ -40,7 +40,9 @@
#define FASTFLOAT_VISUAL_STUDIO 1 #define FASTFLOAT_VISUAL_STUDIO 1
#endif #endif
#ifdef _WIN32 #if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
#define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
#elif defined _WIN32
#define FASTFLOAT_IS_BIG_ENDIAN 0 #define FASTFLOAT_IS_BIG_ENDIAN 0
#else #else
#if defined(__APPLE__) || defined(__FreeBSD__) #if defined(__APPLE__) || defined(__FreeBSD__)
@ -48,7 +50,11 @@
#elif defined(sun) || defined(__sun) #elif defined(sun) || defined(__sun)
#include <sys/byteorder.h> #include <sys/byteorder.h>
#else #else
#ifdef __has_include
#if __has_include(<endian.h>)
#include <endian.h> #include <endian.h>
#endif //__has_include(<endian.h>)
#endif //__has_include
#endif #endif
# #
#ifndef __BYTE_ORDER__ #ifndef __BYTE_ORDER__
@ -75,22 +81,28 @@
#endif #endif
#ifndef FASTFLOAT_ASSERT #ifndef FASTFLOAT_ASSERT
#define FASTFLOAT_ASSERT(x) { if (!(x)) abort(); } #define FASTFLOAT_ASSERT(x) { ((void)(x)); }
#endif #endif
#ifndef FASTFLOAT_DEBUG_ASSERT #ifndef FASTFLOAT_DEBUG_ASSERT
#include <cassert> #define FASTFLOAT_DEBUG_ASSERT(x) { ((void)(x)); }
#define FASTFLOAT_DEBUG_ASSERT(x) assert(x)
#endif #endif
// rust style `try!()` macro, or `?` operator // rust style `try!()` macro, or `?` operator
#define FASTFLOAT_TRY(x) { if (!(x)) return false; } #define FASTFLOAT_TRY(x) { if (!(x)) return false; }
// Testing for https://wg21.link/N3652, adopted in C++14
#if __cpp_constexpr >= 201304
#define FASTFLOAT_CONSTEXPR14 constexpr
#else
#define FASTFLOAT_CONSTEXPR14
#endif
namespace fast_float { namespace fast_float {
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
inline bool fastfloat_strncasecmp(const char *input1, const char *input2, inline FASTFLOAT_CONSTEXPR14 bool
size_t length) { fastfloat_strncasecmp(const char *input1, const char *input2, size_t length) {
char running_diff{0}; char running_diff{0};
for (size_t i = 0; i < length; i++) { for (size_t i = 0; i < length; i++) {
running_diff |= (input1[i] ^ input2[i]); running_diff |= (input1[i] ^ input2[i]);
@ -107,14 +119,14 @@ template <typename T>
struct span { struct span {
const T* ptr; const T* ptr;
size_t length; size_t length;
span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {} constexpr span(const T* _ptr, size_t _length) : ptr(_ptr), length(_length) {}
span() : ptr(nullptr), length(0) {} constexpr span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept { constexpr size_t len() const noexcept {
return length; return length;
} }
const T& operator[](size_t index) const noexcept { FASTFLOAT_CONSTEXPR14 const T& operator[](size_t index) const noexcept {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return ptr[index]; return ptr[index];
} }
@ -123,8 +135,8 @@ struct span {
struct value128 { struct value128 {
uint64_t low; uint64_t low;
uint64_t high; uint64_t high;
value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
value128() : low(0), high(0) {} constexpr value128() : low(0), high(0) {}
}; };
/* result might be undefined when input_num is zero */ /* result might be undefined when input_num is zero */
@ -155,14 +167,14 @@ fastfloat_really_inline int leading_zeroes(uint64_t input_num) {
#ifdef FASTFLOAT_32BIT #ifdef FASTFLOAT_32BIT
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
fastfloat_really_inline uint64_t emulu(uint32_t x, uint32_t y) { fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) {
return x * (uint64_t)y; return x * (uint64_t)y;
} }
// slow emulation routine for 32-bit // slow emulation routine for 32-bit
#if !defined(__MINGW64__) #if !defined(__MINGW64__)
fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd, fastfloat_really_inline constexpr uint64_t _umul128(uint64_t ab, uint64_t cd,
uint64_t *hi) { uint64_t *hi) {
uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd);
uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd);
uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32));
@ -181,8 +193,9 @@ fastfloat_really_inline uint64_t _umul128(uint64_t ab, uint64_t cd,
fastfloat_really_inline value128 full_multiplication(uint64_t a, fastfloat_really_inline value128 full_multiplication(uint64_t a,
uint64_t b) { uint64_t b) {
value128 answer; value128 answer;
#ifdef _M_ARM64 #if defined(_M_ARM64) && !defined(__MINGW32__)
// ARM64 has native support for 64-bit multiplications, no need to emulate // ARM64 has native support for 64-bit multiplications, no need to emulate
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
answer.high = __umulh(a, b); answer.high = __umulh(a, b);
answer.low = a * b; answer.low = a * b;
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__)) #elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__))
@ -201,10 +214,10 @@ struct adjusted_mantissa {
uint64_t mantissa{0}; uint64_t mantissa{0};
int32_t power2{0}; // a negative value indicates an invalid result int32_t power2{0}; // a negative value indicates an invalid result
adjusted_mantissa() = default; adjusted_mantissa() = default;
bool operator==(const adjusted_mantissa &o) const { constexpr bool operator==(const adjusted_mantissa &o) const {
return mantissa == o.mantissa && power2 == o.power2; return mantissa == o.mantissa && power2 == o.power2;
} }
bool operator!=(const adjusted_mantissa &o) const { constexpr bool operator!=(const adjusted_mantissa &o) const {
return mantissa != o.mantissa || power2 != o.power2; return mantissa != o.mantissa || power2 != o.power2;
} }
}; };
@ -215,25 +228,91 @@ constexpr static int32_t invalid_am_bias = -0x8000;
constexpr static double powers_of_ten_double[] = { constexpr static double powers_of_ten_double[] = {
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11,
1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22};
constexpr static float powers_of_ten_float[] = {1e0, 1e1, 1e2, 1e3, 1e4, 1e5, constexpr static float powers_of_ten_float[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f,
1e6, 1e7, 1e8, 1e9, 1e10}; 1e6f, 1e7f, 1e8f, 1e9f, 1e10f};
// used for max_mantissa_double and max_mantissa_float
constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
// Largest integer value v so that (5**index * v) <= 1<<53.
// 0x10000000000000 == 1 << 53
constexpr static uint64_t max_mantissa_double[] = {
0x10000000000000,
0x10000000000000 / 5,
0x10000000000000 / (5 * 5),
0x10000000000000 / (5 * 5 * 5),
0x10000000000000 / (5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555),
0x10000000000000 / (constant_55555 * 5),
0x10000000000000 / (constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * 5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5),
0x10000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)};
// Largest integer value v so that (5**index * v) <= 1<<24.
// 0x1000000 == 1<<24
constexpr static uint64_t max_mantissa_float[] = {
0x1000000,
0x1000000 / 5,
0x1000000 / (5 * 5),
0x1000000 / (5 * 5 * 5),
0x1000000 / (5 * 5 * 5 * 5),
0x1000000 / (constant_55555),
0x1000000 / (constant_55555 * 5),
0x1000000 / (constant_55555 * 5 * 5),
0x1000000 / (constant_55555 * 5 * 5 * 5),
0x1000000 / (constant_55555 * 5 * 5 * 5 * 5),
0x1000000 / (constant_55555 * constant_55555),
0x1000000 / (constant_55555 * constant_55555 * 5)};
template <typename T> struct binary_format { template <typename T> struct binary_format {
using equiv_uint = typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
static inline constexpr int mantissa_explicit_bits(); static inline constexpr int mantissa_explicit_bits();
static inline constexpr int minimum_exponent(); static inline constexpr int minimum_exponent();
static inline constexpr int infinite_power(); static inline constexpr int infinite_power();
static inline constexpr int sign_index(); static inline constexpr int sign_index();
static inline constexpr int min_exponent_fast_path(); static inline constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int max_exponent_fast_path(); static inline constexpr int max_exponent_fast_path();
static inline constexpr int max_exponent_round_to_even(); static inline constexpr int max_exponent_round_to_even();
static inline constexpr int min_exponent_round_to_even(); static inline constexpr int min_exponent_round_to_even();
static inline constexpr uint64_t max_mantissa_fast_path(); static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
static inline constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static inline constexpr int largest_power_of_ten(); static inline constexpr int largest_power_of_ten();
static inline constexpr int smallest_power_of_ten(); static inline constexpr int smallest_power_of_ten();
static inline constexpr T exact_power_of_ten(int64_t power); static inline constexpr T exact_power_of_ten(int64_t power);
static inline constexpr size_t max_digits(); static inline constexpr size_t max_digits();
static inline constexpr equiv_uint exponent_mask();
static inline constexpr equiv_uint mantissa_mask();
static inline constexpr equiv_uint hidden_bit_mask();
}; };
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -22;
#endif
}
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -10;
#endif
}
template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() { template <> inline constexpr int binary_format<double>::mantissa_explicit_bits() {
return 52; return 52;
} }
@ -274,34 +353,30 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
template <> inline constexpr int binary_format<double>::sign_index() { return 63; } template <> inline constexpr int binary_format<double>::sign_index() { return 63; }
template <> inline constexpr int binary_format<float>::sign_index() { return 31; } template <> inline constexpr int binary_format<float>::sign_index() { return 31; }
template <> inline constexpr int binary_format<double>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -22;
#endif
}
template <> inline constexpr int binary_format<float>::min_exponent_fast_path() {
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return 0;
#else
return -10;
#endif
}
template <> inline constexpr int binary_format<double>::max_exponent_fast_path() { template <> inline constexpr int binary_format<double>::max_exponent_fast_path() {
return 22; return 22;
} }
template <> inline constexpr int binary_format<float>::max_exponent_fast_path() { template <> inline constexpr int binary_format<float>::max_exponent_fast_path() {
return 10; return 10;
} }
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits(); return uint64_t(2) << mantissa_explicit_bits();
} }
template <> inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 22
//
return max_mantissa_double[power];
}
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() { template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits(); return uint64_t(2) << mantissa_explicit_bits();
} }
template <> inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 10
//
return max_mantissa_float[power];
}
template <> template <>
inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) { inline constexpr double binary_format<double>::exact_power_of_ten(int64_t power) {
@ -339,6 +414,33 @@ template <> inline constexpr size_t binary_format<float>::max_digits() {
return 114; return 114;
} }
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::exponent_mask() {
return 0x7F800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::exponent_mask() {
return 0x7FF0000000000000;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::mantissa_mask() {
return 0x007FFFFF;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::mantissa_mask() {
return 0x000FFFFFFFFFFFFF;
}
template <> inline constexpr binary_format<float>::equiv_uint
binary_format<float>::hidden_bit_mask() {
return 0x00800000;
}
template <> inline constexpr binary_format<double>::equiv_uint
binary_format<double>::hidden_bit_mask() {
return 0x0010000000000000;
}
template<typename T> template<typename T>
fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) { fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &value) {
uint64_t word = am.mantissa; uint64_t word = am.mantissa;
@ -357,6 +459,28 @@ fastfloat_really_inline void to_float(bool negative, adjusted_mantissa am, T &va
#endif #endif
} }
#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
template <typename = void>
struct space_lut {
static constexpr bool value[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
};
template <typename T>
constexpr bool space_lut<T>::value[];
inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
#endif
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -60,6 +60,70 @@ from_chars_result parse_infnan(const char *first, const char *last, T &value) n
return answer; return answer;
} }
/**
* Returns true if the floating-pointing rounding mode is to 'nearest'.
* It is the default on most system. This function is meant to be inexpensive.
* Credit : @mwalcott3
*/
fastfloat_really_inline bool rounds_to_nearest() noexcept {
// https://lemire.me/blog/2020/06/26/gcc-not-nearest/
#if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0)
return false;
#endif
// See
// A fast function to check your floating-point rounding mode
// https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/
//
// This function is meant to be equivalent to :
// prior: #include <cfenv>
// return fegetround() == FE_TONEAREST;
// However, it is expected to be much faster than the fegetround()
// function call.
//
// The volatile keywoard prevents the compiler from computing the function
// at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g., asm).
// The value does not need to be std::numeric_limits<float>::min(), any small
// value so that 1 + x should round to 1 would do (after accounting for excess
// precision, as in 387 instructions).
static volatile float fmin = std::numeric_limits<float>::min();
float fmini = fmin; // we copy it so that it gets loaded at most once.
//
// Explanation:
// Only when fegetround() == FE_TONEAREST do we have that
// fmin + 1.0f == 1.0f - fmin.
//
// FE_UPWARD:
// fmin + 1.0f > 1
// 1.0f - fmin == 1
//
// FE_DOWNWARD or FE_TOWARDZERO:
// fmin + 1.0f == 1
// 1.0f - fmin < 1
//
// Note: This may fail to be accurate if fast-math has been
// enabled, as rounding conventions may not apply.
#if FASTFLOAT_VISUAL_STUDIO
# pragma warning(push)
// todo: is there a VS warning?
// see https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013
#elif defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wfloat-equal"
#elif defined(__GNUC__)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
return (fmini + 1.0f == 1.0f - fmini);
#if FASTFLOAT_VISUAL_STUDIO
# pragma warning(pop)
#elif defined(__clang__)
# pragma clang diagnostic pop
#elif defined(__GNUC__)
# pragma GCC diagnostic pop
#endif
}
} // namespace detail } // namespace detail
template<typename T> template<typename T>
@ -76,6 +140,11 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
from_chars_result answer; from_chars_result answer;
#if FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++;
}
#endif
if (first == last) { if (first == last) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
@ -87,13 +156,45 @@ from_chars_result from_chars_advanced(const char *first, const char *last,
} }
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch; answer.ptr = pns.lastmatch;
// Next is Clinger's fast path. // The implementation of the Clinger's fast path is convoluted because
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && pns.mantissa <=binary_format<T>::max_mantissa_fast_path() && !pns.too_many_digits) { // we want round-to-nearest in all cases, irrespective of the rounding mode
value = T(pns.mantissa); // selected on the thread.
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); } // We proceed optimistically, assuming that detail::rounds_to_nearest() returns
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); } // true.
if (pns.negative) { value = -value; } if (binary_format<T>::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format<T>::max_exponent_fast_path() && !pns.too_many_digits) {
return answer; // Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float.
//
// We expect the next branch to almost always be selected.
// We could check it first (before the previous branch), but
// there might be performance advantages at having the check
// be last.
if(detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path.
if (pns.mantissa <=binary_format<T>::max_mantissa_fast_path()) {
value = T(pns.mantissa);
if (pns.exponent < 0) { value = value / binary_format<T>::exact_power_of_ten(-pns.exponent); }
else { value = value * binary_format<T>::exact_power_of_ten(pns.exponent); }
if (pns.negative) { value = -value; }
return answer;
}
} else {
// We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's proposal
if (pns.exponent >= 0 && pns.mantissa <=binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if defined(__clang__)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if(pns.mantissa == 0) {
value = 0;
return answer;
}
#endif
value = T(pns.mantissa) * binary_format<T>::exact_power_of_ten(pns.exponent);
if (pns.negative) { value = -value; }
return answer;
}
}
} }
adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa); adjusted_mantissa am = compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
if(pns.too_many_digits && am.power2 >= 0) { if(pns.too_many_digits && am.power2 >= 0) {

View File

@ -3,46 +3,78 @@ processed_files = { }
# authors # authors
for filename in ['AUTHORS', 'CONTRIBUTORS']: for filename in ['AUTHORS', 'CONTRIBUTORS']:
with open(filename) as f: with open(filename, encoding='utf8') as f:
text = '' text = ''
for line in f: for line in f:
if filename == 'AUTHORS': if filename == 'AUTHORS':
text += '// fast_float by ' + line text += '// fast_float by ' + line
if filename == 'CONTRIBUTORS': if filename == 'CONTRIBUTORS':
text += '// with contributions from ' + line text += '// with contributions from ' + line
processed_files[filename] = text processed_files[filename] = text + '//\n'
# licenses # licenses
for filename in ['LICENSE-MIT', 'LICENSE-APACHE']: for filename in ['LICENSE-MIT', 'LICENSE-APACHE']:
with open(filename) as f: lines = []
text = '' with open(filename, encoding='utf8') as f:
for line in f: lines = f.readlines()
text += '// ' + line
processed_files[filename] = text # Retrieve subset required for inclusion in source
if filename == 'LICENSE-APACHE':
lines = [
' Copyright 2021 The fast_float authors\n',
*lines[179:-1]
]
text = ''
for line in lines:
text += '// ' + line.strip() + '\n'
processed_files[filename] = text
# code # code
for filename in [ 'fast_float.h', 'float_common.h', 'ascii_number.h', for filename in [ 'fast_float.h', 'float_common.h', 'ascii_number.h',
'fast_table.h', 'decimal_to_binary.h', 'bigint.h', 'fast_table.h', 'decimal_to_binary.h', 'bigint.h',
'ascii_number.h', 'digit_comparison.h', 'parse_number.h']: 'ascii_number.h', 'digit_comparison.h', 'parse_number.h']:
with open('include/fast_float/' + filename) as f: with open('include/fast_float/' + filename, encoding='utf8') as f:
text = '' text = ''
for line in f: for line in f:
if line.startswith('#include "'): continue if line.startswith('#include "'): continue
text += line text += line
processed_files[filename] = text processed_files[filename] = '\n' + text
# command line # command line
import argparse import argparse
parser = argparse.ArgumentParser(description='Amalgamate fast_float.') parser = argparse.ArgumentParser(description='Amalgamate fast_float.')
parser.add_argument('--license', default='MIT', help='choose license') parser.add_argument('--license', default='DUAL', choices=['DUAL', 'MIT', 'APACHE'], help='choose license')
parser.add_argument('--output', default='', help='output file (stdout if none') parser.add_argument('--output', default='', help='output file (stdout if none')
args = parser.parse_args() args = parser.parse_args()
text = '\n\n'.join([ def license_content(license_arg):
result = []
if license_arg == 'DUAL':
result += [
'// Licensed under the Apache License, Version 2.0, or the\n',
'// MIT License at your option. This file may not be copied,\n',
'// modified, or distributed except according to those terms.\n',
'//\n'
]
if license_arg in ('DUAL', 'MIT'):
result.append('// MIT License Notice\n//\n')
result.append(processed_files['LICENSE-MIT'])
result.append('//\n')
if license_arg in ('DUAL', 'APACHE'):
result.append('// Apache License (Version 2.0) Notice\n//\n')
result.append(processed_files['LICENSE-APACHE'])
result.append('//\n')
return result
text = ''.join([
processed_files['AUTHORS'], processed_files['CONTRIBUTORS'], processed_files['AUTHORS'], processed_files['CONTRIBUTORS'],
processed_files['LICENSE-' + args.license], *license_content(args.license),
processed_files['fast_float.h'], processed_files['float_common.h'], processed_files['fast_float.h'], processed_files['float_common.h'],
processed_files['ascii_number.h'], processed_files['fast_table.h'], processed_files['ascii_number.h'], processed_files['fast_table.h'],
processed_files['decimal_to_binary.h'], processed_files['bigint.h'], processed_files['decimal_to_binary.h'], processed_files['bigint.h'],
@ -50,7 +82,7 @@ text = '\n\n'.join([
processed_files['parse_number.h']]) processed_files['parse_number.h']])
if args.output: if args.output:
with open(args.output, 'wt') as f: with open(args.output, 'wt', encoding='utf8') as f:
f.write(text) f.write(text)
else: else:
print(text) print(text)

View File

@ -38,7 +38,11 @@ add_library(supplemental-data INTERFACE)
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data") target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
function(fast_float_add_cpp_test TEST_NAME) function(fast_float_add_cpp_test TEST_NAME)
add_executable(${TEST_NAME} ${TEST_NAME}.cpp) add_executable(${TEST_NAME} ${TEST_NAME}.cpp)
add_test(${TEST_NAME} ${TEST_NAME}) if(CMAKE_CROSSCOMPILING)
set(ccemulator ${CMAKE_CROSSCOMPILING_EMULATOR})
endif()
add_test(NAME ${TEST_NAME}
COMMAND ${ccemulator} $<TARGET_FILE:${TEST_NAME}>)
if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
target_compile_options(${TEST_NAME} PUBLIC /EHsc) target_compile_options(${TEST_NAME} PUBLIC /EHsc)
endif() endif()
@ -52,10 +56,12 @@ function(fast_float_add_cpp_test TEST_NAME)
endif() endif()
endfunction(fast_float_add_cpp_test) endfunction(fast_float_add_cpp_test)
fast_float_add_cpp_test(rcppfastfloat_test)
fast_float_add_cpp_test(example_test) fast_float_add_cpp_test(example_test)
fast_float_add_cpp_test(example_comma_test) fast_float_add_cpp_test(example_comma_test)
fast_float_add_cpp_test(basictest) fast_float_add_cpp_test(basictest)
target_compile_features(basictest PRIVATE cxx_std_17)
fast_float_add_cpp_test(long_test) fast_float_add_cpp_test(long_test)
fast_float_add_cpp_test(powersoffive_hardround) fast_float_add_cpp_test(powersoffive_hardround)
fast_float_add_cpp_test(string_test) fast_float_add_cpp_test(string_test)

View File

@ -10,6 +10,7 @@
#include <limits> #include <limits>
#include <string> #include <string>
#include <system_error> #include <system_error>
#include <cfenv>
#ifndef SUPPLEMENTAL_TEST_DATA_DIR #ifndef SUPPLEMENTAL_TEST_DATA_DIR
#define SUPPLEMENTAL_TEST_DATA_DIR "data/" #define SUPPLEMENTAL_TEST_DATA_DIR "data/"
@ -42,6 +43,147 @@
#define FASTFLOAT_ODDPLATFORM 1 #define FASTFLOAT_ODDPLATFORM 1
#endif #endif
#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"
#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")"
const char * round_name(int d) {
switch(d) {
case FE_UPWARD:
return "FE_UPWARD";
case FE_DOWNWARD:
return "FE_DOWNWARD";
case FE_TOWARDZERO:
return "FE_TOWARDZERO";
case FE_TONEAREST:
return "FE_TONEAREST";
default:
return "UNKNOWN";
}
}
#define FASTFLOAT_STR(x) #x
#define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x))
TEST_CASE("system_info") {
std::cout << "system info:" << std::endl;
#ifdef _MSC_VER
SHOW_DEFINE(_MSC_VER);
#endif
#ifdef FASTFLOAT_64BIT_LIMB
SHOW_DEFINE(FASTFLOAT_64BIT_LIMB);
#endif
#ifdef __clang__
SHOW_DEFINE(__clang__);
#endif
#ifdef FASTFLOAT_VISUAL_STUDIO
SHOW_DEFINE(FASTFLOAT_VISUAL_STUDIO);
#endif
#ifdef FASTFLOAT_IS_BIG_ENDIAN
#if FASTFLOAT_IS_BIG_ENDIAN
printf("big endian\n");
#else
printf("little endian\n");
#endif
#endif
#ifdef FASTFLOAT_32BIT
SHOW_DEFINE(FASTFLOAT_32BIT);
#endif
#ifdef FASTFLOAT_64BIT
SHOW_DEFINE(FASTFLOAT_64BIT);
#endif
#ifdef FLT_EVAL_METHOD
SHOW_DEFINE(FLT_EVAL_METHOD);
#endif
#ifdef _WIN32
SHOW_DEFINE(_WIN32);
#endif
#ifdef _WIN64
SHOW_DEFINE(_WIN64);
#endif
std::cout << "fegetround() = " << round_name(fegetround()) << std::endl;
std::cout << std::endl;
}
TEST_CASE("rounds_to_nearest") {
//
// If this function fails, we may be left in a non-standard rounding state.
//
static volatile float fmin = std::numeric_limits<float>::min();
fesetround(FE_UPWARD);
std::cout << "FE_UPWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
CHECK(fegetround() == FE_UPWARD);
CHECK(fast_float::detail::rounds_to_nearest() == false);
fesetround(FE_DOWNWARD);
std::cout << "FE_DOWNWARD: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
CHECK(fegetround() == FE_DOWNWARD);
CHECK(fast_float::detail::rounds_to_nearest() == false);
fesetround(FE_TOWARDZERO);
std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
CHECK(fegetround() == FE_TOWARDZERO);
CHECK(fast_float::detail::rounds_to_nearest() == false);
fesetround(FE_TONEAREST);
std::cout << "FE_TONEAREST: fmin + 1.0f = " << iHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << iHexAndDec(1.0f - fmin) << std::endl;
CHECK(fegetround() == FE_TONEAREST);
#if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0)
CHECK(fast_float::detail::rounds_to_nearest() == true);
#endif
}
TEST_CASE("parse_zero") {
//
// If this function fails, we may be left in a non-standard rounding state.
//
const char * zero = "0";
uint64_t float64_parsed;
double f = 0;
::memcpy(&float64_parsed, &f, sizeof(f));
CHECK(float64_parsed == 0);
fesetround(FE_UPWARD);
auto r1 = fast_float::from_chars(zero, zero + 1, f);
CHECK(r1.ec == std::errc());
std::cout << "FE_UPWARD parsed zero as " << iHexAndDec(f) << std::endl;
CHECK(f == 0);
::memcpy(&float64_parsed, &f, sizeof(f));
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
CHECK(float64_parsed == 0);
fesetround(FE_TOWARDZERO);
auto r2 = fast_float::from_chars(zero, zero + 1, f);
CHECK(r2.ec == std::errc());
std::cout << "FE_TOWARDZERO parsed zero as " << iHexAndDec(f) << std::endl;
CHECK(f == 0);
::memcpy(&float64_parsed, &f, sizeof(f));
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
CHECK(float64_parsed == 0);
fesetround(FE_DOWNWARD);
auto r3 = fast_float::from_chars(zero, zero + 1, f);
CHECK(r3.ec == std::errc());
std::cout << "FE_DOWNWARD parsed zero as " << iHexAndDec(f) << std::endl;
CHECK(f == 0);
::memcpy(&float64_parsed, &f, sizeof(f));
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
CHECK(float64_parsed == 0);
fesetround(FE_TONEAREST);
auto r4 = fast_float::from_chars(zero, zero + 1, f);
CHECK(r4.ec == std::errc());
std::cout << "FE_TONEAREST parsed zero as " << iHexAndDec(f) << std::endl;
CHECK(f == 0);
::memcpy(&float64_parsed, &f, sizeof(f));
std::cout << "double as uint64_t is " << float64_parsed << std::endl;
CHECK(float64_parsed == 0);
}
// C++ 17 because it is otherwise annoying to browse all files in a directory. // C++ 17 because it is otherwise annoying to browse all files in a directory.
// We also only run these tests on little endian systems. // We also only run these tests on little endian systems.
#if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM) #if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && !defined(FASTFLOAT_ODDPLATFORM)
@ -50,59 +192,77 @@
#include <filesystem> #include <filesystem>
#include <charconv> #include <charconv>
// return true on succcess
// return true on success
bool check_file(std::string file_name) { bool check_file(std::string file_name) {
std::cout << "Checking " << file_name << std::endl; std::cout << "Checking " << file_name << std::endl;
size_t number{0}; // We check all rounding directions, for each file.
std::fstream newfile(file_name, std::ios::in); std::vector<int> directions = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST};
if (newfile.is_open()) { for (int d : directions) {
std::string str; std::cout << "fesetround to " << round_name(d) << std::endl;
while (std::getline(newfile, str)) { fesetround(d);
if (str.size() > 0) { size_t number{0};
// Read 32-bit hex std::fstream newfile(file_name, std::ios::in);
uint32_t float32; if (newfile.is_open()) {
auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(), std::string str;
while (std::getline(newfile, str)) {
if (str.size() > 0) {
// Read 32-bit hex
uint32_t float32;
auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(),
float32, 16); float32, 16);
if(r32.ec != std::errc()) { std::cerr << "32-bit parsing failure\n"; return false; } if(r32.ec != std::errc()) { std::cerr << "32-bit parsing failure\n"; return false; }
// Read 64-bit hex // Read 64-bit hex
uint64_t float64; uint64_t float64;
auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(), auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(),
float64, 16); float64, 16);
if(r64.ec != std::errc()) { std::cerr << "64-bit parsing failure\n"; return false; } if(r64.ec != std::errc()) { std::cerr << "64-bit parsing failure\n"; return false; }
// The string to parse: // The string to parse:
const char *number_string = str.data() + 31; const char *number_string = str.data() + 31;
const char *end_of_string = str.data() + str.size(); const char *end_of_string = str.data() + str.size();
// Parse as 32-bit float // Parse as 32-bit float
float parsed_32; float parsed_32;
auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32); auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32);
if(fast_float_r32.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } if(fast_float_r32.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
// Parse as 64-bit float // Parse as 64-bit float
double parsed_64; double parsed_64;
auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64); auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64);
if(fast_float_r64.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; } if(fast_float_r64.ec != std::errc()) { std::cerr << "parsing failure\n"; return false; }
// Convert the floats to unsigned ints. // Convert the floats to unsigned ints.
uint32_t float32_parsed; uint32_t float32_parsed;
uint64_t float64_parsed; uint64_t float64_parsed;
::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32)); ::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32));
::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64)); ::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64));
// Compare with expected results // Compare with expected results
if (float32_parsed != float32) { if (float32_parsed != float32) {
std::cout << "bad 32 " << str << std::endl; std::cout << "bad 32 " << str << std::endl;
return false; std::cout << "parsed as " << iHexAndDec(parsed_32) << std::endl;
std::cout << "as raw uint32_t, parsed = " << float32_parsed << ", expected = " << float32 << std::endl;
std::cout << "fesetround: " << round_name(d) << std::endl;
fesetround(FE_TONEAREST);
return false;
}
if (float64_parsed != float64) {
std::cout << "bad 64 " << str << std::endl;
std::cout << "parsed as " << iHexAndDec(parsed_64) << std::endl;
std::cout << "as raw uint64_t, parsed = " << float64_parsed << ", expected = " << float64 << std::endl;
std::cout << "fesetround: " << round_name(d) << std::endl;
fesetround(FE_TONEAREST);
return false;
}
number++;
} }
if (float64_parsed != float64) {
std::cout << "bad 64 " << str << std::endl;
return false;
}
number++;
} }
std::cout << "checked " << std::defaultfloat << number << " values" << std::endl;
newfile.close(); // close the file object
} else {
std::cout << "Could not read " << file_name << std::endl;
fesetround(FE_TONEAREST);
return false;
} }
std::cout << "checked " << std::defaultfloat << number << " values" << std::endl;
newfile.close(); // close the file object
} else {
std::cout << "Could not read " << file_name << std::endl;
return false;
} }
fesetround(FE_TONEAREST);
return true; return true;
} }
@ -125,9 +285,6 @@ TEST_CASE("leading_zeroes") {
CHECK(fast_float::leading_zeroes(bit << 63) == 0); CHECK(fast_float::leading_zeroes(bit << 63) == 0);
} }
#define iHexAndDec(v) std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"
#define fHexAndDec(v) std::hexfloat << (v) << " (" << std::defaultfloat << (v) << ")"
void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) { void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) {
fast_float::value128 v; fast_float::value128 v;
v = fast_float::full_multiplication(lhs, rhs); v = fast_float::full_multiplication(lhs, rhs);
@ -224,6 +381,33 @@ TEST_CASE("decimal_point_parsing") {
} }
} }
TEST_CASE("issue19") {
const std::string input = "234532.3426362,7869234.9823,324562.645";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 234532.3426362\n");
CHECK_MESSAGE(answer.ptr == input.data() + 14,
"Parsed the number " << result
<< " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters");
CHECK_MESSAGE(result == 234532.3426362, "We want to parse234532.3426362\n");
CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n");
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 7869234.9823\n");
CHECK_MESSAGE(answer.ptr == input.data() + 27,
"Parsed the number " << result
<< " and stopped at the wrong character " << (answer.ptr - input.data()));
CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n");
CHECK_MESSAGE(result == 7869234.9823, "We want to parse up 7869234.9823\n");
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 324562.645\n");
CHECK_MESSAGE(answer.ptr == input.data() + 38,
"Parsed the number " << result
<< " and stopped at the wrong character " << (answer.ptr - input.data()));
CHECK_MESSAGE(result == 324562.645, "We want to parse up 7869234.9823\n");
}
TEST_CASE("issue19") { TEST_CASE("issue19") {
const std::string input = "3.14e"; const std::string input = "3.14e";
double result; double result;
@ -454,6 +638,9 @@ TEST_CASE("64bit.inf") {
} }
TEST_CASE("64bit.general") { TEST_CASE("64bit.general") {
verify("22250738585072012e-324",0x1p-1022); /* limit between normal and subnormal*/
verify("-22250738585072012e-324",-0x1p-1022); /* limit between normal and subnormal*/
verify("-1e-999",-0.0);
verify("-2.2222222222223e-322",-0x1.68p-1069); verify("-2.2222222222223e-322",-0x1.68p-1069);
verify("9007199254740993.0", 0x1p+53); verify("9007199254740993.0", 0x1p+53);
verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328); verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328);
@ -588,6 +775,7 @@ TEST_CASE("32bit.inf") {
} }
TEST_CASE("32bit.general") { TEST_CASE("32bit.general") {
verify("-1e-999",-0.0f);
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f); verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f);
verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f); verify("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f);
verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f); verify(append_zeros("1.1754941406275178592461758986628081843312458647327962400313859427181746759860647699724722770042717456817626953125",655), 0x1.2ced3p+0f);

View File

@ -4,11 +4,56 @@
#include <string> #include <string>
#include <system_error> #include <system_error>
bool many() {
const std::string input = "234532.3426362,7869234.9823,324562.645";
double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if(answer.ec != std::errc()) { return false; }
if(result != 234532.3426362) { return false; }
if(answer.ptr[0] != ',') { return false; }
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) { return false; }
if(result != 7869234.9823) { return false; }
if(answer.ptr[0] != ',') { return false; }
answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if(answer.ec != std::errc()) { return false; }
if(result != 324562.645) { return false; }
return true;
}
void many_loop() {
const std::string input = "234532.3426362,7869234.9823,324562.645";
double result;
const char* pointer = input.data();
const char* end_pointer = input.data() + input.size();
while(pointer < end_pointer) {
auto answer = fast_float::from_chars(pointer, end_pointer, result);
if(answer.ec != std::errc()) {
std::cerr << "error while parsing" << std::endl;
break;
}
std::cout << "parsed: " << result << std::endl;
pointer = answer.ptr;
if ((answer.ptr < end_pointer) && (*pointer == ',')) {
pointer++;
}
}
}
int main() { int main() {
const std::string input = "3.1416 xyz "; const std::string input = "3.1416 xyz ";
double result; double result;
auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result);
if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } if((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl; std::cout << "parsed the number " << result << std::endl;
if(!many()) {
printf("Bug\n");
return EXIT_FAILURE;
}
many_loop();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -132,7 +132,7 @@ bool allvalues() {
inline void Assert(bool Assertion) { inline void Assert(bool Assertion) {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; } if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; }
#else #else
if (!Assertion) { throw std::runtime_error("bug"); } if (!Assertion) { throw std::runtime_error("bug"); }
#endif #endif

View File

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.15) cmake_minimum_required(VERSION 3.15)
project(test_simdjson_install VERSION 0.1.0 LANGUAGES CXX) project(test_install VERSION 0.1.0 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)

View 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;
}

View File

@ -44,7 +44,7 @@ float cygwin_strtof_l(const char* start, char** end) {
inline void Assert(bool Assertion) { inline void Assert(bool Assertion) {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun) #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || defined(sun) || defined(__sun)
if (!Assertion) { std::cerr << "Omitting hard falure on msys/cygwin/sun systems."; } if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; }
#else #else
if (!Assertion) { throw std::runtime_error("bug"); } if (!Assertion) { throw std::runtime_error("bug"); }
#endif #endif