Compare commits

..

No commits in common. "main" and "v6.1.5" have entirely different histories.
main ... v6.1.5

92 changed files with 1041 additions and 5194 deletions

View File

@ -1 +0,0 @@
build --cxxopt="--std=c++17"

View File

@ -1,4 +1,2 @@
BasedOnStyle: LLVM BasedOnStyle: LLVM
SortIncludes: false SortIncludes: false
SeparateDefinitionBlocks: Always
MaxEmptyLinesToKeep: 1

View File

@ -1,5 +1,3 @@
We have benchmarks, please consider running them: [see our README for details](https://github.com/fastfloat/fast_float/blob/main/README.md#benchmarking). We expect you to run our benchmarks if you make claims with respect to the performance.
Our CI tests check formatting automating. If such a test fails, please consider running the bash script: Our CI tests check formatting automating. If such a test fails, please consider running the bash script:

View File

@ -18,7 +18,7 @@ jobs:
- riscv64 - riscv64
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Install latest Alpine Linux for ${{ matrix.arch }} - name: Install latest Alpine Linux for ${{ matrix.arch }}
uses: jirutka/setup-alpine@v1 uses: jirutka/setup-alpine@v1

View File

@ -1,12 +1,12 @@
name: Amalgamate Ubuntu 24.04 CI 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-24.04 runs-on: ubuntu-20.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Compile with amalgamation - name: Compile with amalgamation
run: | run: |
mkdir build && mkdir build &&

View File

@ -20,14 +20,14 @@ jobs:
fuzz-seconds: 300 fuzz-seconds: 300
output-sarif: true output-sarif: true
- name: Upload Crash - name: Upload Crash
uses: actions/upload-artifact@v5 uses: actions/upload-artifact@v4
if: failure() && steps.build.outcome == 'success' if: failure() && steps.build.outcome == 'success'
with: with:
name: artifacts name: artifacts
path: ./out/artifacts path: ./out/artifacts
- name: Upload Sarif - name: Upload Sarif
if: always() && steps.build.outcome == 'success' if: always() && steps.build.outcome == 'success'
uses: github/codeql-action/upload-sarif@v4 uses: github/codeql-action/upload-sarif@v3
with: with:
# Path to SARIF file relative to the root of the repository # Path to SARIF file relative to the root of the repository
sarif_file: cifuzz-sarif/results.sarif sarif_file: cifuzz-sarif/results.sarif

View File

@ -1,17 +0,0 @@
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.2.2
- uses: actions/setup-node@2028fbc5c25fe9cf00d9f06a71cc4710d4507903 # v6.0.0
- uses: mymindstorm/setup-emsdk@6ab9eb1bda2574c4ddb79809fc9247783eaf9021 # v14
- name: Verify
run: emcc -v
- name: Checkout
uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v3.6.0
- name: Configure
run: emcmake cmake -B build
- name: Build # We build but do not test
run: cmake --build build

View File

@ -24,10 +24,10 @@ jobs:
lint-and-format: lint-and-format:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7 - uses: actions/checkout@9a9194f87191a7e9055e3e9b95b8cfb13023bb08 # v4.1.7
- name: Run clang-format - name: Run clang-format
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # v4.15.0 uses: jidicula/clang-format-action@c74383674bf5f7c69f60ce562019c1c94bc1421a # v4.13.0
with: with:
clang-format-version: '17' clang-format-version: '17'

View File

@ -23,7 +23,7 @@ jobs:
CMAKE_GENERATOR: Ninja CMAKE_GENERATOR: Ninja
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- 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@v5 - uses: actions/checkout@v4
- uses: msys2/setup-msys2@v2 - uses: msys2/setup-msys2@v2
with: with:
update: true update: true

View File

@ -16,7 +16,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Amalgamate fast_float.h - name: Amalgamate fast_float.h
run: | run: |

View File

@ -1,23 +0,0 @@
name: Ubuntu RISC-V rvv VLEN=128 (clang 17)
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- name: Install packages
run: |
sudo apt-get update -q -y
sudo apt-get install -y cmake make g++-riscv64-linux-gnu qemu-user-static clang-17
- name: Build
run: |
CXX=clang++-17 CXXFLAGS="--target=riscv64-linux-gnu -march=rv64gcv" \
cmake --toolchain=cmake/toolchains-ci/riscv64-linux-gnu.cmake -DCMAKE_BUILD_TYPE=Release -B build
cmake --build build/ -j$(nproc)
- name: Test VLEN=128
run: |
export QEMU_LD_PREFIX="/usr/riscv64-linux-gnu"
export QEMU_CPU="rv64,v=on,vlen=128,rvv_ta_all_1s=on,rvv_ma_all_1s=on"
ctest --timeout 1800 --output-on-failure --test-dir build -j $(nproc)

View File

@ -12,8 +12,8 @@ jobs:
build: build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- uses: uraimo/run-on-arch-action@v3 - uses: uraimo/run-on-arch-action@v2
name: Test name: Test
id: runcmd id: runcmd
with: with:

View File

@ -1,14 +1,14 @@
name: Ubuntu 24.04 CI name: Ubuntu 20.04 CI (C++20)
on: [push, pull_request] on: [push, pull_request]
jobs: jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
strategy: strategy:
fail-fast: false fail-fast: false
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- 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@v4
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake -DCMAKE_CXX_FLAGS="-ffast-math" -DFASTFLOAT_TEST=ON .. &&
cmake --build . &&
ctest --output-on-failure

21
.github/workflows/ubuntu20.yml vendored Normal file
View File

@ -0,0 +1,21 @@
name: Ubuntu 20.04 CI (GCC 9)
on: [push, pull_request]
jobs:
ubuntu-build:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v4
- name: Use cmake
run: |
mkdir build &&
cd build &&
cmake ${{matrix.cxx}} ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. &&
cmake --build . &&
ctest --output-on-failure &&
cmake --install . &&
cd ../tests/installation_tests/find &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build . &&
cd ../../issue72_installation &&
mkdir build && cd build && cmake -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination .. && cmake --build .

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Install clang++-14 - name: Install clang++-14
run: sudo apt-get install -y clang++-14 run: sudo apt-get install -y clang++-14
- name: Use cmake - name: Use cmake

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Use cmake - name: Use cmake
run: | run: |
mkdir build && mkdir build &&

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Use cmake - name: Use cmake
run: | run: |
mkdir build && mkdir build &&

View File

@ -6,7 +6,7 @@ jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-22.04 runs-on: ubuntu-22.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Use cmake - name: Use cmake
run: | run: |
mkdir build && mkdir build &&

View File

@ -6,24 +6,18 @@ jobs:
ubuntu-build: ubuntu-build:
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
- uses: actions/checkout@v5 - uses: actions/checkout@v4
- name: Use cmake - name: Use cmake
run: | run: |
set -xe mkdir build &&
cmake -B build \ cd build &&
-DFASTFLOAT_TEST=ON \ CXXFLAGS=-Werror cmake -DFASTFLOAT_TEST=ON .. &&
-DFASTFLOAT_BENCHMARKS=ON \ cmake --build . &&
-DCMAKE_CXX_FLAGS=' -Werror -Wundef ' ctest --output-on-failure
cmake --build build --parallel
( cd build ; ctest --output-on-failure )
- name: Use cmake CXX23 - name: Use cmake CXX23
run: | run: |
set -xe mkdir build20 &&
cmake -B build20 \ cd build20 &&
-DFASTFLOAT_TEST=ON \ CXXFLAGS=-Werror cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_FIXEDWIDTH_TESTS=ON -DFASTFLOAT_CXX_STANDARD=23 -DFASTFLOAT_TEST=ON .. &&
-DFASTFLOAT_CONSTEXPR_TESTS=ON \ cmake --build . &&
-DFASTFLOAT_FIXEDWIDTH_TESTS=ON \ ctest --output-on-failure
-DFASTFLOAT_CXX_STANDARD=23 \
-DCMAKE_CXX_FLAGS=' -Werror -Wundef '
cmake --build build20 --parallel
( cd build20 ; ctest --output-on-failure )

View File

@ -10,11 +10,13 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
include: 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: Release}
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: configure - name: configure
run: | run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON

View File

@ -16,10 +16,10 @@ jobs:
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: configure - name: configure
run: | run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination
- name: build - name: build
run: | run: |
cmake --build build --verbose --config ${{matrix.cfg}} --parallel cmake --build build --verbose --config ${{matrix.cfg}} --parallel

View File

@ -16,10 +16,10 @@ jobs:
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: Configure - name: Configure
run: | run: |
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_BENCHMARKS=ON -T ClangCL -DFASTFLOAT_TEST=ON cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON
- name: Build - name: Build
run: cmake --build build --config ${{matrix.cfg}} --parallel --verbose run: cmake --build build --config ${{matrix.cfg}} --parallel --verbose
- name: Run basic tests - name: Run basic tests

View File

@ -16,7 +16,7 @@ jobs:
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
steps: steps:
- name: checkout - name: checkout
uses: actions/checkout@v5 uses: actions/checkout@v4
- name: configure - name: configure
run: >- run: >-
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}

1
.gitignore vendored
View File

@ -2,7 +2,6 @@ build/*
Testing/* Testing/*
.cache/ .cache/
compile_commands.json compile_commands.json
bazel-*
# Visual studio # Visual studio
.vs/ .vs/

View File

@ -1,6 +0,0 @@
cc_library(
name = "fast_float",
hdrs = glob(["include/fast_float/*.h"]),
strip_include_prefix = "include",
visibility = ["//visibility:public"],
)

View File

@ -1,11 +1,9 @@
cmake_minimum_required(VERSION 3.14) cmake_minimum_required(VERSION 3.9)
project(fast_float VERSION 6.1.5 LANGUAGES CXX)
project(fast_float VERSION 8.1.0 LANGUAGES CXX)
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat") set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD}) set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
option(FASTFLOAT_TEST "Enable tests" OFF) option(FASTFLOAT_TEST "Enable tests" OFF)
if(FASTFLOAT_TEST) if(FASTFLOAT_TEST)
enable_testing() enable_testing()
add_subdirectory(tests) add_subdirectory(tests)
@ -31,16 +29,6 @@ if(FASTFLOAT_INSTALL)
endif() endif()
add_library(fast_float INTERFACE) add_library(fast_float INTERFACE)
option(FASTFLOAT_BENCHMARKS "Enable benchmarks" OFF)
if(FASTFLOAT_BENCHMARKS)
add_subdirectory(benchmarks)
else(FASTFLOAT_BENCHMARKS)
message(STATUS "Benchmarks are disabled. Set FASTFLOAT_BENCHMARKS to ON to build benchmarks (assumes C++17).")
endif(FASTFLOAT_BENCHMARKS)
add_library(FastFloat::fast_float ALIAS fast_float) add_library(FastFloat::fast_float ALIAS fast_float)
target_include_directories( target_include_directories(
fast_float fast_float
@ -56,15 +44,11 @@ if(FASTFLOAT_SANITIZE)
target_link_libraries(fast_float INTERFACE -fuse-ld=gold) target_link_libraries(fast_float INTERFACE -fuse-ld=gold)
endif() endif()
endif() endif()
if(MSVC_VERSION GREATER 1910)
include(CheckCXXCompilerFlag)
unset(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
CHECK_CXX_COMPILER_FLAG(/permissive- FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
if(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
target_compile_options(fast_float INTERFACE /permissive-) target_compile_options(fast_float INTERFACE /permissive-)
endif() endif()
if(FASTFLOAT_INSTALL) if(FASTFLOAT_INSTALL)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)

View File

@ -8,4 +8,3 @@ Lénárd Szolnoki
Jan Pharago Jan Pharago
Maya Warrier Maya Warrier
Taha Khokhar Taha Khokhar
Anders Dalvander

View File

@ -1,9 +0,0 @@
"""fast_float number parsing library: 4x faster than strtod"""
module(
name = "fast_float",
version = "6.1.6",
compatibility_level = 6,
)
bazel_dep(name = "doctest", version = "2.4.11", dev_dependency = True)

549
README.md
View File

@ -1,212 +1,163 @@
## fast_float number parsing library: 4x faster than strtod ## fast_float number parsing library: 4x faster than strtod
[![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fast_float.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fast_float)
[![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml) [![Ubuntu 22.04 CI (GCC 11)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml/badge.svg)](https://github.com/fastfloat/fast_float/actions/workflows/ubuntu22.yml)
The fast_float library provides fast header-only implementations for the C++ The fast_float library provides fast header-only implementations for the C++ from_chars
from_chars functions for `float` and `double` types as well as integer types. functions for `float` and `double` types as well as integer types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including
These functions convert ASCII strings representing decimal values (e.g., round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries.
`1.3e10`) into binary types. We provide exact rounding (including round to
even). In our experience, these `fast_float` functions many times faster than
comparable number-parsing functions from existing C++ standard libraries.
Specifically, `fast_float` provides the following two functions to parse Specifically, `fast_float` provides the following two functions to parse floating-point numbers with a C++17-like syntax (the library itself only requires C++11):
floating-point numbers with a C++17-like syntax (the library itself only
requires C++11):
```C++ ```C++
from_chars_result from_chars(char const *first, char const *last, float &value, ...); from_chars_result from_chars(const char* first, const char* last, float& value, ...);
from_chars_result from_chars(char const *first, char const *last, double &value, ...); from_chars_result from_chars(const char* first, const char* last, double& value, ...);
``` ```
You can also parse integer types: You can also parse integer types:
```C++
from_chars_result from_chars(char const *first, char const *last, int &value, ...);
from_chars_result from_chars(char const *first, char const *last, unsigned &value, ...);
```
The return type (`from_chars_result`) is defined as the struct: The return type (`from_chars_result`) is defined as the struct:
```C++ ```C++
struct from_chars_result { struct from_chars_result {
char const *ptr; const char* ptr;
std::errc ec; std::errc ec;
}; };
``` ```
It parses the character sequence `[first, last)` for a number. It parses It parses the character sequence [first,last) for a number. It parses floating-point numbers expecting
floating-point numbers expecting a locale-independent format equivalent to the a locale-independent format equivalent to the C++17 from_chars function.
C++17 from_chars function. The resulting floating-point value is the closest The resulting floating-point value is the closest floating-point values (using either float or double),
floating-point values (using either `float` or `double`), using the "round to using the "round to even" convention for values that would otherwise fall right in-between two values.
even" convention for values that would otherwise fall right in-between two That is, we provide exact parsing according to the IEEE standard.
values. That is, we provide exact parsing according to the IEEE standard.
Given a successful parse, the pointer (`ptr`) in the returned value is set to
point right after the parsed number, and the `value` referenced is set to the
parsed value. In case of error, the returned `ec` contains a representative
error, otherwise the default (`std::errc()`) value is stored.
The implementation does not throw and does not allocate memory (e.g., with `new` Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the
or `malloc`). parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned
`ec` contains a representative error, otherwise the default (`std::errc()`) value is stored.
The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`).
It will parse infinity and nan values. It will parse infinity and nan values.
Example: Example:
```C++ ``` C++
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <iostream> #include <iostream>
#include <string>
int main() { int main() {
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()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
```
Though the C++17 standard has you do a comparison with `std::errc()` to check whether the conversion worked, you can avoid it by casting the result to a `bool` like so:
```cpp
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
int main() {
std::string input = "3.1416 xyz ";
double result;
if(auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result)) {
std::cout << "parsed the number " << result << std::endl; std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
}
std::cerr << "failed to parse " << result << std::endl;
return EXIT_FAILURE;
} }
``` ```
You can parse delimited numbers: You can parse delimited numbers:
```C++ ```C++
std::string input = "234532.3426362,7869234.9823,324562.645"; const std::string input = "234532.3426362,7869234.9823,324562.645";
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()) { if(answer.ec != std::errc()) {
// check error // check error
} }
// we have result == 234532.3426362. // we have result == 234532.3426362.
if (answer.ptr[0] != ',') { if(answer.ptr[0] != ',') {
// unexpected delimiter // unexpected delimiter
} }
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if (answer.ec != std::errc()) { if(answer.ec != std::errc()) {
// check error // check error
} }
// we have result == 7869234.9823. // we have result == 7869234.9823.
if (answer.ptr[0] != ',') { if(answer.ptr[0] != ',') {
// unexpected delimiter // unexpected delimiter
} }
answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); answer = fast_float::from_chars(answer.ptr + 1, input.data()+input.size(), result);
if (answer.ec != std::errc()) { if(answer.ec != std::errc()) {
// check error // check error
} }
// we have result == 324562.645. // we have result == 324562.645.
``` ```
Like the C++17 standard, the `fast_float::from_chars` functions take an optional
last argument of the type `fast_float::chars_format`. It is a bitset value: we
check whether `fmt & fast_float::chars_format::fixed` and `fmt &
fast_float::chars_format::scientific` are set to determine whether we allow the
fixed point and scientific notation respectively. The default is
`fast_float::chars_format::general` which allows both `fixed` and `scientific`.
The library seeks to follow the C++17 (see
[28.2.3.(6.1)](https://eel.is/c++draft/charconv.from.chars#6.1)) specification.
* The `from_chars` function does not skip leading white-space characters (unless Like the C++17 standard, the `fast_float::from_chars` functions take an optional last argument of
`fast_float::chars_format::skip_white_space` is set). the type `fast_float::chars_format`. It is a bitset value: we check whether
* [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is `fmt & fast_float::chars_format::fixed` and `fmt & fast_float::chars_format::scientific` are set
forbidden (unless `fast_float::chars_format::allow_leading_plus` is set). to determine whether we allow the fixed point and scientific notation respectively.
* It is generally impossible to represent a decimal value exactly as binary The default is `fast_float::chars_format::general` which allows both `fixed` and `scientific`.
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 The library seeks to follow the C++17 (see [20.19.3](http://eel.is/c++draft/charconv.from.chars).(7.1)) specification.
numbers. * 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.
Furthermore, we have the following restrictions: Furthermore, we have the following restrictions:
* We only support `float` and `double` types at this time.
* We support `float` and `double`, but not `long double`. We also support
fixed-width floating-point types such as `std::float64_t`, `std::float32_t`,
`std::float16_t`, and `std::bfloat16_t`.
* We only support the decimal format: we do not support hexadecimal strings. * We only support the decimal format: we do not support hexadecimal strings.
* For values that are either very large or very small (e.g., `1e9999`), we * For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`.
represent it using the infinity or negative infinity value and the returned
`ec` is set to `std::errc::result_out_of_range`.
We support Visual Studio, macOS, Linux, freeBSD. We support big and little We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems.
endian. We support 32-bit and 64-bit systems.
We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`).
We assume that the rounding mode is set to nearest (`std::fegetround() ==
FE_TONEAREST`).
## Integer types ## Integer types
You can also parse integer types using different bases (e.g., 2, 10, 16). The You can also parse integer types using different bases (e.g., 2, 10, 16). The following code will
following code will print the number 22250738585072012 three times: print the number 22250738585072012 three times:
```C++ ```C++
#include "fast_float/fast_float.h"
#include <iostream>
int main() {
uint64_t i; uint64_t i;
std::string str = "22250738585072012"; const char str[] = "22250738585072012";
auto answer = fast_float::from_chars(str.data(), str.data() + str.size(), i); auto answer = fast_float::from_chars(str, str + strlen(str), i);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n"; std::cerr << "parsing failure\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }
std::cout << "parsed the number " << i << std::endl; std::cout << "parsed the number "<< i << std::endl;
std::string binstr = "1001111000011001110110111001001010110100111000110001100"; const char binstr[] = "1001111000011001110110111001001010110100111000110001100";
answer = fast_float::from_chars(binstr.data(), binstr.data() + binstr.size(), i, 2); answer = fast_float::from_chars(binstr, binstr + strlen(binstr), i, 2);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n"; std::cerr << "parsing failure\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }
std::cout << "parsed the number " << i << std::endl; std::cout << "parsed the number "<< i << std::endl;
std::string hexstr = "4f0cedc95a718c";
answer = fast_float::from_chars(hexstr.data(), hexstr.data() + hexstr.size(), i, 16); const char hexstr[] = "4f0cedc95a718c";
answer = fast_float::from_chars(hexstr, hexstr + strlen(hexstr), i, 16);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cerr << "parsing failure\n"; std::cerr << "parsing failure\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }
std::cout << "parsed the number " << i << std::endl; std::cout << "parsed the number "<< i << std::endl;
return EXIT_SUCCESS;
}
``` ```
## Behavior of result_out_of_range ## Behavior of result_out_of_range
When parsing floating-point values, the numbers can sometimes be too small When parsing floating-point values, the numbers can sometimes be too small (e.g., `1e-1000`) or
(e.g., `1e-1000`) or too large (e.g., `1e1000`). The C language established the too large (e.g., `1e1000`). The C language established the precedent that these small values are out of range.
precedent that these small values are out of range. In such cases, it is In such cases, it is customary to parse small values to zero and large
customary to parse small values to zero and large values to infinity. That is values to infinity. That is the behaviour of the C language (e.g., `stdtod`). That is the behaviour followed by the fast_float library.
the behaviour of the C language (e.g., `stdtod`). That is the behaviour followed
by the fast_float library.
Specifically, we follow Jonathan Wakely's interpretation of the standard: Specifically, we follow Jonathan Wakely's interpretation of the standard:
> In any case, the resulting value is one of at most two floating-point values > In any case, the resulting value is one of at most two floating-point values closest to the value of the string matching the pattern.
> closest to the value of the string matching the pattern.
It is also the approach taken by the [Microsoft C++ It is also the approach taken by the [Microsoft C++ library](https://github.com/microsoft/STL/blob/62205ab155d093e71dd9588a78f02c5396c3c14b/tests/std/tests/P0067R5_charconv/test.cpp#L943-L946).
library](https://github.com/microsoft/STL/blob/62205ab155d093e71dd9588a78f02c5396c3c14b/tests/std/tests/P0067R5_charconv/test.cpp#L943-L946).
Hence, we have the following examples: Hence, we have the following examples:
@ -219,6 +170,7 @@ Hence, we have the following examples:
// result == 0 // result == 0
``` ```
```cpp ```cpp
double result = -1; double result = -1;
std::string str = "3e1000"; std::string str = "3e1000";
@ -228,26 +180,26 @@ Hence, we have the following examples:
// result == std::numeric_limits<double>::infinity() // result == std::numeric_limits<double>::infinity()
``` ```
Users who wish for the value to be left unmodified given Users who wish for the value to be left unmodified given `std::errc::result_out_of_range` may do so by adding two lines of code:
`std::errc::result_out_of_range` may do so by adding two lines of code:
```cpp ```cpp
double old_result = result; // make copy double old_result = result; // make copy
auto r = fast_float::from_chars(start, end, result); auto r = fast_float::from_chars(start, end, result);
if (r.ec == std::errc::result_out_of_range) { result = old_result; } if(r.ec == std::errc::result_out_of_range) { result = old_result; }
``` ```
## C++20: compile-time evaluation (constexpr) ## C++20: compile-time evaluation (constexpr)
In C++20, you may use `fast_float::from_chars` to parse strings at compile-time, In C++20, you may use `fast_float::from_chars` to parse strings
as in the following example: at compile-time, as in the following example:
```C++ ```C++
// consteval forces compile-time evaluation of the function in C++20. // consteval forces compile-time evaluation of the function in C++20.
consteval double parse(std::string_view input) { consteval double parse(std::string_view input) {
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()) { return -1.0; } if(answer.ec != std::errc()) { return -1.0; }
return result; return result;
} }
@ -260,107 +212,108 @@ constexpr double constexptest() {
## C++23: Fixed width floating-point types ## C++23: Fixed width floating-point types
The library also supports fixed-width floating-point types such as The library also supports fixed-width floating-point types such as `std::float32_t` and `std::float64_t`. E.g., you can write:
`std::float64_t`, `std::float32_t`, `std::float16_t`, and `std::bfloat16_t`.
E.g., you can write:
```C++ ```C++
std::float32_t result; std::float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
``` ``````
## Non-ASCII Inputs ## Non-ASCII Inputs
We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the following example:
following example:
```C++ ``` C++
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <iostream> #include <iostream>
int main() { int main() {
std::u16string input = u"3.1416 xyz "; const std::u16string input = u"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()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } if(answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl; std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
``` ```
## Advanced options: using commas as decimal separator, JSON and Fortran ## Advanced options: using commas as decimal separator, JSON and Fortran
The C++ standard stipulate that `from_chars` has to be locale-independent. In The C++ standard stipulate that `from_chars` has to be locale-independent. In
particular, the decimal separator has to be the period (`.`). However, some particular, the decimal separator has to be the period (`.`). However,
users still want to use the `fast_float` library with in a locale-dependent some users still want to use the `fast_float` library with in a locale-dependent
manner. Using a separate function called `from_chars_advanced`, we allow the manner. Using a separate function called `from_chars_advanced`, we allow the users
users to pass a `parse_options` instance which contains a custom decimal to pass a `parse_options` instance which contains a custom decimal separator (e.g.,
separator (e.g., the comma). You may use it as follows. the comma). You may use it as follows.
```C++ ```C++
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <iostream> #include <iostream>
int main() { int main() {
std::string input = "3,1416 xyz "; const std::string input = "3,1416 xyz ";
double result; double result;
fast_float::parse_options options{fast_float::chars_format::general, ','}; fast_float::parse_options options{fast_float::chars_format::general, ','};
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if ((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } 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;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
``` ```
### You can also parse Fortran-like inputs You can also parse Fortran-like inputs:
```C++ ```C++
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <iostream> #include <iostream>
int main() { int main() {
std::string input = "1d+4"; const std::string input = "1d+4";
double result; double result;
fast_float::parse_options options{fast_float::chars_format::fortran}; fast_float::parse_options options{ fast_float::chars_format::fortran };
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if ((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } if((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; }
std::cout << "parsed the number " << result << std::endl; std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
``` ```
### You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6)) You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6)):
```C++ ```C++
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <iostream> #include <iostream>
int main() { int main() {
std::string input = "+.1"; // not valid const std::string input = "+.1"; // not valid
double result; double result;
fast_float::parse_options options{fast_float::chars_format::json}; fast_float::parse_options options{ fast_float::chars_format::json };
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
``` ```
By default the JSON format does not allow `inf`: By default the JSON format does not allow `inf`:
```C++ ```C++
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <iostream> #include <iostream>
int main() { int main() {
std::string input = "inf"; // not valid in JSON const std::string input = "inf"; // not valid in JSON
double result; double result;
fast_float::parse_options options{fast_float::chars_format::json}; fast_float::parse_options options{ fast_float::chars_format::json };
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } if(answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS;
} }
``` ```
You can allow it with a non-standard `json_or_infnan` variant: You can allow it with a non-standard `json_or_infnan` variant:
```C++ ```C++
@ -368,135 +321,54 @@ You can allow it with a non-standard `json_or_infnan` variant:
#include <iostream> #include <iostream>
int main() { int main() {
std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan const std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan
double result; double result;
fast_float::parse_options options{fast_float::chars_format::json_or_infnan}; fast_float::parse_options options{ fast_float::chars_format::json_or_infnan };
auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); auto answer = fast_float::from_chars_advanced(input.data(), input.data()+input.size(), result, options);
if (answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; } if(answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; }
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
``` ``````
## Multiplication of an integer by a power of 10
An integer `W` can be multiplied by a power of ten `10^Q` and
converted to `double` with correctly rounded value
(in "round to nearest, tie to even" fashion) using
`fast_float::integer_times_pow10()`, e.g.:
```C++
const uint64_t W = 12345678901234567;
const int Q = 23;
const double result = fast_float::integer_times_pow10(W, Q);
std::cout.precision(17);
std::cout << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
```
outputs
```
12345678901234567 * 10^23 = 1.2345678901234567e+39 (==expected)
```
`fast_float::integer_times_pow10()` gives the same result as
using `fast_float::from_chars()` when parsing the string `"WeQ"`
(in this example `"12345678901234567e23"`),
except `fast_float::integer_times_pow10()` does not report out-of-range errors, and
underflows to zero or overflows to infinity when the resulting value is
out of range.
You can use template overloads to get the result converted to different
supported floating-point types: `float`, `double`, etc.
For example, to get result as `float` use
`fast_float::integer_times_pow10<float>()` specialization:
```C++
const uint64_t W = 12345678;
const int Q = 23;
const float result = fast_float::integer_times_pow10<float>(W, Q);
std::cout.precision(9);
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
```
outputs
```
float: 12345678 * 10^23 = 1.23456782e+30 (==expected)
```
Overloads of `fast_float::integer_times_pow10()` are provided for
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
## Users and Related Work ## Users and Related Work
The fast_float library is part of: The fast_float library is part of:
* GCC (as of version 12): the `from_chars` function in GCC relies on fast_float, - GCC (as of version 12): the `from_chars` function in GCC relies on fast_float.
* [Chromium](https://github.com/Chromium/Chromium), the engine behind Google - [Chromium](https://github.com/Chromium/Chromium), the engine behind Google Chrome and Microsoft Edge,
Chrome, Microsoft Edge, and Opera, - [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser)
* Boost JSON, MySQL, etc. - [DuckDB](https://duckdb.org)
* Blender - [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times
* [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's - [Google Jsonnet](https://github.com/google/jsonnet)
web browser), - [ClickHouse](https://github.com/ClickHouse/ClickHouse)
* [DuckDB](https://duckdb.org),
* [Redis](https://github.com/redis/redis) and [Valkey](https://github.com/valkey-io/valkey),
* [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied
the number parsing speed by two or three times,
* [Google Jsonnet](https://github.com/google/jsonnet),
* [ClickHouse](https://github.com/ClickHouse/ClickHouse).
The fastfloat algorithm is part of the [LLVM standard
libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba).
There is a [derived implementation part of
AdaCore](https://github.com/AdaCore/VSS). The [SerenityOS operating
system](https://github.com/SerenityOS/serenity/commit/53b7f5e6a11e663c83df8030c3171c5945cb75ec)
has a derived implementation that is inherited by the [Ladybird
Browser](https://github.com/LadybirdBrowser/ladybird).
The fast_float library provides a performance similar to that of the
[fast_double_parser](https://github.com/lemire/fast_double_parser) library but
using an updated algorithm reworked from the ground up, and while offering an
API more in line with the expectations of C++ programmers. The
fast_double_parser library is part of the [Microsoft LightGBM machine-learning
framework](https://github.com/microsoft/LightGBM).
The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS).
Packages 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).
------
[![Packaging status](https://repology.org/badge/vertical-allrepos/fast-float.svg)](https://repology.org/project/fast-float/versions)
## References ## References
* Daniel Lemire, [Number Parsing at a Gigabyte per - Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021.
Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience - Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience 53 (7), 2023.
51 (8), 2021.
* Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without
Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience
53 (7), 2023.
## Other programming languages ## Other programming languages
* [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called - [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`.
`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 - [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).
library](https://github.com/aldanor/fast-float-rust/) called - [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
`fast-float-rust`.
* [There is a Java port of the fast_float
library](https://github.com/wrandelshofer/FastDoubleParser) called
`FastDoubleParser`. It used for important systems such as
[Jackson](https://github.com/FasterXML/jackson-core).
* [There is a C# port of the fast_float
library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`.
## How fast is it? ## How fast is it?
It can parse random floating-point numbers at a speed of 1 GB/s on some systems. It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations.
We find that it is often twice as fast as the best available competitor, and
many times faster than many standard-library implementations.
<img src="https://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" <img src="http://lemire.me/blog/wp-content/uploads/2020/11/fastfloat_speed.png" width="400">
width="400" alt="fast_float is many times faster than many standard-library
implementations">
```bash ```
$ ./build/benchmarks/benchmark $ ./build/benchmarks/benchmark
# parsing random integers in the range [0,1) # parsing random integers in the range [0,1)
volume = 2.09808 MB volume = 2.09808 MB
@ -507,128 +379,91 @@ abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfl
fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s
``` ```
See the [Benchmarking](#benchmarking) section for instructions on how to run our benchmarks. See https://github.com/lemire/simple_fastfloat_benchmark for our benchmarking code.
## Video ## Video
[![Go Systems 2020](https://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](https://www.youtube.com/watch?v=AVXgvlMeIm4) [![Go Systems 2020](http://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](http://www.youtube.com/watch?v=AVXgvlMeIm4)<br />
## Using as a CMake dependency ## Using as a CMake dependency
This library is header-only by design. The CMake file provides the `fast_float` This library is header-only by design. The CMake file provides the `fast_float` target
target which is merely a pointer to the `include` directory. which is merely a pointer to the `include` directory.
If you drop the `fast_float` repository in your CMake project, you should be If you drop the `fast_float` repository in your CMake project, you should be able to use
able to use it in this manner: it in this manner:
```cmake ```cmake
add_subdirectory(fast_float) add_subdirectory(fast_float)
target_link_libraries(myprogram PUBLIC fast_float) target_link_libraries(myprogram PUBLIC fast_float)
``` ```
Or you may want to retrieve the dependency automatically if you have a Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least):
sufficiently recent version of CMake (3.11 or better at least):
```cmake ```cmake
FetchContent_Declare( FetchContent_Declare(
fast_float fast_float
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git GIT_REPOSITORY https://github.com/lemire/fast_float.git
GIT_TAG tags/v8.1.0 GIT_TAG tags/v1.1.2
GIT_SHALLOW TRUE) GIT_SHALLOW TRUE)
FetchContent_MakeAvailable(fast_float) FetchContent_MakeAvailable(fast_float)
target_link_libraries(myprogram PUBLIC fast_float) target_link_libraries(myprogram PUBLIC fast_float)
``` ```
You should change the `GIT_TAG` line so that you recover the version you wish to You should change the `GIT_TAG` line so that you recover the version you wish to use.
use.
You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so: You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
```cmake
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v8.1.0)
``` ```
CPMAddPackage(
NAME fast_float
GITHUB_REPOSITORY "fastfloat/fast_float"
GIT_TAG v6.1.4)
```
## Using as single header ## Using as single header
The script `script/amalgamate.py` may be used to generate a single header The script `script/amalgamate.py` may be used to generate a single header
version of the library if so desired. Just run the script from the root version of the library if so desired.
directory of this repository. You can customize the license type and output file Just run the script from the root directory of this repository.
if desired as described in the command line help. 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: You may directly download automatically generated single-header files:
<https://github.com/fastfloat/fast_float/releases/download/v8.1.0/fast_float.h> https://github.com/fastfloat/fast_float/releases/download/v6.1.5/fast_float.h
## Benchmarking
The project has its own benchmarks with realistic data inputs. Under Linux or macOS,
you can use it as follows if your system supports C++17:
```
cmake -B build -D FASTFLOAT_BENCHMARKS=ON
cmake --build build
./build/benchmarks/realbenchmark
```
Importantly, by default, the benchmark is built in Release mode.
The instructions are similar under Windows.
Under Linux and macOS, it is recommended to run the benchmarks in a privileged manner to get access
to hardware performance counters. You may be able to do so with the `sudo` command
in some cases:
```
sudo ./build/benchmarks/realbenchmark
```
If you have a text file containing one number per line (`myfile.txt`), you can run a benchmark over it like so:
```
cmake -B build -D FASTFLOAT_BENCHMARKS=ON
cmake --build build
./build/benchmarks/realbenchmark myfile.txt
```
## Packages ## Packages
* The fast_float library is part of the [Conan package - The fast_float library is part of the [Conan package manager](https://conan.io/center/recipes/fast_float).
manager](https://conan.io/center/recipes/fast_float). - It is part of the [brew package manager](https://formulae.brew.sh/formula/fast_float).
* It is part of the [brew package - Some Linux distribution like Fedora include fast_float (e.g., as `fast_float-devel`).
manager](https://formulae.brew.sh/formula/fast_float).
* fast_float is available on [xmake](https://xmake.io) repository. ## RFC 7159
* Some Linux distribution like Fedora include fast_float (e.g., as
`fast_float-devel`). If you need support for RFC 7159 (JSON standard), you may want to consider using the [fast_double_parser](https://github.com/lemire/fast_double_parser/) library instead.
## Credit ## Credit
Though this work is inspired by many different people, this work benefited Though this work is inspired by many different people, this work benefited especially from exchanges with
especially from exchanges with Michael Eisel, who motivated the original Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided
research with his key insights, and with Nigel Tao who provided invaluable invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits.
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 The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published
was originally published under the Apache 2.0 license. under the Apache 2.0 license.
## Stars
[![Star History Chart](https://api.star-history.com/svg?repos=fastfloat/fast_float&type=Date)](https://www.star-history.com/#fastfloat/fast_float&Date)
## License ## License
<sup> <sup>
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
2.0</a> or <a href="LICENSE-MIT">MIT license</a> or <a 2.0</a> or <a href="LICENSE-MIT">MIT license</a> or <a href="LICENSE-BOOST">BOOST license</a> .
href="LICENSE-BOOST">BOOST license</a>.
</sup> </sup>
<br/> <br>
<sub> <sub>
Unless you explicitly state otherwise, any contribution intentionally submitted Unless you explicitly state otherwise, any contribution intentionally submitted

View File

@ -1,26 +0,0 @@
add_executable(realbenchmark benchmark.cpp)
set_property(
TARGET realbenchmark
PROPERTY CXX_STANDARD 17)
target_link_libraries(realbenchmark PUBLIC fast_float)
include(ExternalProject)
# Define the external project
ExternalProject_Add(simple_fastfloat_benchmark
GIT_REPOSITORY https://github.com/lemire/simple_fastfloat_benchmark.git
GIT_TAG master # or specify a particular commit/tag/branch
SOURCE_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark
BINARY_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark-build
CONFIGURE_COMMAND ""
BUILD_COMMAND ""
INSTALL_COMMAND ""
)
set(DATA_DIR ${CMAKE_BINARY_DIR}/simple_fastfloat_benchmark/data)
add_custom_target(CopyData ALL
COMMAND ${CMAKE_COMMAND} -E copy_directory ${DATA_DIR} ${CMAKE_CURRENT_BINARY_DIR}/data
DEPENDS simple_fastfloat_benchmark
)
add_dependencies(realbenchmark CopyData)
target_compile_definitions(realbenchmark PUBLIC BENCHMARK_DATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data")

File diff suppressed because it is too large Load Diff

View File

@ -1,246 +0,0 @@
#if defined(__linux__) || (__APPLE__ && __aarch64__)
#define USING_COUNTERS
#endif
#include "event_counter.h"
#include <algorithm>
#include "fast_float/fast_float.h"
#include <chrono>
#include <climits>
#include <cmath>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctype.h>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <random>
#include <sstream>
#include <stdio.h>
#include <string>
#include <vector>
#include <locale.h>
template <typename CharT>
double findmax_fastfloat64(std::vector<std::basic_string<CharT>> &s) {
double answer = 0;
double x = 0;
for (auto &st : s) {
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
if (p == st.data()) {
throw std::runtime_error("bug in findmax_fastfloat");
}
answer = answer > x ? answer : x;
}
return answer;
}
template <typename CharT>
double findmax_fastfloat32(std::vector<std::basic_string<CharT>> &s) {
float answer = 0;
float x = 0;
for (auto &st : s) {
auto [p, ec] = fast_float::from_chars(st.data(), st.data() + st.size(), x);
if (p == st.data()) {
throw std::runtime_error("bug in findmax_fastfloat");
}
answer = answer > x ? answer : x;
}
return answer;
}
event_collector collector{};
#ifdef USING_COUNTERS
template <class T, class CharT>
std::vector<event_count>
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
size_t repeat) {
std::vector<event_count> aggregate;
bool printed_bug = false;
for (size_t i = 0; i < repeat; i++) {
collector.start();
double ts = function(lines);
if (ts == 0 && !printed_bug) {
printf("bug\n");
printed_bug = true;
}
aggregate.push_back(collector.end());
}
return aggregate;
}
void pretty_print(double volume, size_t number_of_floats, std::string name,
std::vector<event_count> events) {
double volumeMB = volume / (1024. * 1024.);
double average_ns{0};
double min_ns{DBL_MAX};
double cycles_min{DBL_MAX};
double instructions_min{DBL_MAX};
double cycles_avg{0};
double instructions_avg{0};
double branches_min{0};
double branches_avg{0};
double branch_misses_min{0};
double branch_misses_avg{0};
for (event_count e : events) {
double ns = e.elapsed_ns();
average_ns += ns;
min_ns = min_ns < ns ? min_ns : ns;
double cycles = e.cycles();
cycles_avg += cycles;
cycles_min = cycles_min < cycles ? cycles_min : cycles;
double instructions = e.instructions();
instructions_avg += instructions;
instructions_min =
instructions_min < instructions ? instructions_min : instructions;
double branches = e.branches();
branches_avg += branches;
branches_min = branches_min < branches ? branches_min : branches;
double branch_misses = e.missed_branches();
branch_misses_avg += branch_misses;
branch_misses_min =
branch_misses_min < branch_misses ? branch_misses_min : branch_misses;
}
cycles_avg /= events.size();
instructions_avg /= events.size();
average_ns /= events.size();
branches_avg /= events.size();
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
volumeMB * 1000000000 / min_ns,
(average_ns - min_ns) * 100.0 / average_ns);
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / min_ns);
if (instructions_min > 0) {
printf(" %8.2f i/B %8.2f i/f (+/- %.1f %%) ", instructions_min / volume,
instructions_min / number_of_floats,
(instructions_avg - instructions_min) * 100.0 / instructions_avg);
printf(" %8.2f c/B %8.2f c/f (+/- %.1f %%) ", cycles_min / volume,
cycles_min / number_of_floats,
(cycles_avg - cycles_min) * 100.0 / cycles_avg);
printf(" %8.2f i/c ", instructions_min / cycles_min);
printf(" %8.2f b/f ", branches_avg / number_of_floats);
printf(" %8.2f bm/f ", branch_misses_avg / number_of_floats);
printf(" %8.2f GHz ", cycles_min / min_ns);
}
printf("\n");
}
#else
template <class T, class CharT>
std::pair<double, double>
time_it_ns(std::vector<std::basic_string<CharT>> &lines, T const &function,
size_t repeat) {
std::chrono::high_resolution_clock::time_point t1, t2;
double average = 0;
double min_value = DBL_MAX;
bool printed_bug = false;
for (size_t i = 0; i < repeat; i++) {
t1 = std::chrono::high_resolution_clock::now();
double ts = function(lines);
if (ts == 0 && !printed_bug) {
printf("bug\n");
printed_bug = true;
}
t2 = std::chrono::high_resolution_clock::now();
double dif =
std::chrono::duration_cast<std::chrono::nanoseconds>(t2 - t1).count();
average += dif;
min_value = min_value < dif ? min_value : dif;
}
average /= repeat;
return std::make_pair(min_value, average);
}
void pretty_print(double volume, size_t number_of_floats, std::string name,
std::pair<double, double> result) {
double volumeMB = volume / (1024. * 1024.);
printf("%-40s: %8.2f MB/s (+/- %.1f %%) ", name.data(),
volumeMB * 1000000000 / result.first,
(result.second - result.first) * 100.0 / result.second);
printf("%8.2f Mfloat/s ", number_of_floats * 1000 / result.first);
printf(" %8.2f ns/f \n", double(result.first) / number_of_floats);
}
#endif
// this is okay, all chars are ASCII
inline std::u16string widen(std::string line) {
std::u16string u16line;
u16line.resize(line.size());
for (size_t i = 0; i < line.size(); ++i) {
u16line[i] = char16_t(line[i]);
}
return u16line;
}
std::vector<std::u16string> widen(const std::vector<std::string> &lines) {
std::vector<std::u16string> u16lines;
u16lines.reserve(lines.size());
for (auto const &line : lines) {
u16lines.push_back(widen(line));
}
return u16lines;
}
void process(std::vector<std::string> &lines, size_t volume) {
size_t repeat = 1000;
double volumeMB = volume / (1024. * 1024.);
std::cout << "ASCII volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)",
time_it_ns(lines, findmax_fastfloat64<char>, repeat));
pretty_print(volume, lines.size(), "fastfloat (32)",
time_it_ns(lines, findmax_fastfloat32<char>, repeat));
std::vector<std::u16string> lines16 = widen(lines);
volume = 2 * volume;
volumeMB = volume / (1024. * 1024.);
std::cout << "UTF-16 volume = " << volumeMB << " MB " << std::endl;
pretty_print(volume, lines.size(), "fastfloat (64)",
time_it_ns(lines16, findmax_fastfloat64<char16_t>, repeat));
pretty_print(volume, lines.size(), "fastfloat (32)",
time_it_ns(lines16, findmax_fastfloat32<char16_t>, repeat));
}
void fileload(std::string filename) {
std::ifstream inputfile(filename);
if (!inputfile) {
std::cerr << "can't open " << filename << std::endl;
return;
}
std::cout << "#### " << std::endl;
std::cout << "# reading " << filename << std::endl;
std::cout << "#### " << std::endl;
std::string line;
std::vector<std::string> lines;
lines.reserve(10000); // let us reserve plenty of memory.
size_t volume = 0;
while (getline(inputfile, line)) {
volume += line.size();
lines.push_back(line);
}
std::cout << "# read " << lines.size() << " lines " << std::endl;
process(lines, volume);
}
int main(int argc, char **argv) {
if (collector.has_events()) {
std::cout << "# Using hardware counters" << std::endl;
} else {
#if defined(__linux__) || (__APPLE__ && __aarch64__)
std::cout << "# Hardware counters not available, try to run in privileged "
"mode (e.g., sudo)."
<< std::endl;
#endif
}
if (argc > 1) {
fileload(argv[1]);
return EXIT_SUCCESS;
}
fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt");
fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt");
return EXIT_SUCCESS;
}

View File

@ -1,181 +0,0 @@
#ifndef __EVENT_COUNTER_H
#define __EVENT_COUNTER_H
#include <cctype>
#ifndef _MSC_VER
#include <dirent.h>
#endif
#include <cinttypes>
#include <cstring>
#include <chrono>
#include <vector>
#include "linux-perf-events.h"
#ifdef __linux__
#include <libgen.h>
#endif
#if (defined(__APPLE__) && __APPLE__) && (defined(__aarch64__) && __aarch64__)
#include "apple_arm_events.h"
#endif
struct event_count {
std::chrono::duration<double> elapsed;
std::vector<unsigned long long> event_counts;
event_count() : elapsed(0), event_counts{0, 0, 0, 0, 0} {}
event_count(const std::chrono::duration<double> _elapsed,
const std::vector<unsigned long long> _event_counts)
: elapsed(_elapsed), event_counts(_event_counts) {}
event_count(const event_count &other)
: elapsed(other.elapsed), event_counts(other.event_counts) {}
// The types of counters (so we can read the getter more easily)
enum event_counter_types {
CPU_CYCLES = 0,
INSTRUCTIONS = 1,
BRANCHES = 2,
MISSED_BRANCHES = 3
};
double elapsed_sec() const {
return std::chrono::duration<double>(elapsed).count();
}
double elapsed_ns() const {
return std::chrono::duration<double, std::nano>(elapsed).count();
}
double cycles() const {
return static_cast<double>(event_counts[CPU_CYCLES]);
}
double instructions() const {
return static_cast<double>(event_counts[INSTRUCTIONS]);
}
double branches() const {
return static_cast<double>(event_counts[BRANCHES]);
}
double missed_branches() const {
return static_cast<double>(event_counts[MISSED_BRANCHES]);
}
event_count &operator=(const event_count &other) {
this->elapsed = other.elapsed;
this->event_counts = other.event_counts;
return *this;
}
event_count operator+(const event_count &other) const {
return event_count(elapsed + other.elapsed,
{
event_counts[0] + other.event_counts[0],
event_counts[1] + other.event_counts[1],
event_counts[2] + other.event_counts[2],
event_counts[3] + other.event_counts[3],
event_counts[4] + other.event_counts[4],
});
}
void operator+=(const event_count &other) { *this = *this + other; }
};
struct event_aggregate {
bool has_events = false;
int iterations = 0;
event_count total{};
event_count best{};
event_count worst{};
event_aggregate() = default;
void operator<<(const event_count &other) {
if (iterations == 0 || other.elapsed < best.elapsed) {
best = other;
}
if (iterations == 0 || other.elapsed > worst.elapsed) {
worst = other;
}
iterations++;
total += other;
}
double elapsed_sec() const { return total.elapsed_sec() / iterations; }
double elapsed_ns() const { return total.elapsed_ns() / iterations; }
double cycles() const { return total.cycles() / iterations; }
double instructions() const { return total.instructions() / iterations; }
double branches() const { return total.branches() / iterations; }
double missed_branches() const {
return total.missed_branches() / iterations;
}
};
struct event_collector {
event_count count{};
std::chrono::time_point<std::chrono::steady_clock> start_clock{};
#if defined(__linux__)
LinuxEvents<PERF_TYPE_HARDWARE> linux_events;
event_collector()
: linux_events(std::vector<int>{
PERF_COUNT_HW_CPU_CYCLES, PERF_COUNT_HW_INSTRUCTIONS,
PERF_COUNT_HW_BRANCH_INSTRUCTIONS, // Retired branch instructions
PERF_COUNT_HW_BRANCH_MISSES}) {}
bool has_events() { return linux_events.is_working(); }
#elif __APPLE__ && __aarch64__
performance_counters diff;
event_collector() : diff(0) { setup_performance_counters(); }
bool has_events() { return setup_performance_counters(); }
#else
event_collector() {}
bool has_events() { return false; }
#endif
inline void start() {
#if defined(__linux)
linux_events.start();
#elif __APPLE__ && __aarch64__
if (has_events()) {
diff = get_counters();
}
#endif
start_clock = std::chrono::steady_clock::now();
}
inline event_count &end() {
const auto end_clock = std::chrono::steady_clock::now();
#if defined(__linux)
linux_events.end(count.event_counts);
#elif __APPLE__ && __aarch64__
if (has_events()) {
performance_counters end = get_counters();
diff = end - diff;
}
count.event_counts[0] = diff.cycles;
count.event_counts[1] = diff.instructions;
count.event_counts[2] = diff.branches;
count.event_counts[3] = diff.missed_branches;
count.event_counts[4] = 0;
#endif
count.elapsed = end_clock - start_clock;
return count;
}
};
#endif

View File

@ -1,104 +0,0 @@
#pragma once
#ifdef __linux__
#include <asm/unistd.h> // for __NR_perf_event_open
#include <linux/perf_event.h> // for perf event constants
#include <sys/ioctl.h> // for ioctl
#include <unistd.h> // for syscall
#include <cerrno> // for errno
#include <cstring> // for memset
#include <stdexcept>
#include <iostream>
#include <vector>
template <int TYPE = PERF_TYPE_HARDWARE> class LinuxEvents {
int fd;
bool working;
perf_event_attr attribs{};
size_t num_events{};
std::vector<uint64_t> temp_result_vec{};
std::vector<uint64_t> ids{};
public:
explicit LinuxEvents(std::vector<int> config_vec) : fd(0), working(true) {
memset(&attribs, 0, sizeof(attribs));
attribs.type = TYPE;
attribs.size = sizeof(attribs);
attribs.disabled = 1;
attribs.exclude_kernel = 1;
attribs.exclude_hv = 1;
attribs.sample_period = 0;
attribs.read_format = PERF_FORMAT_GROUP | PERF_FORMAT_ID;
const int pid = 0; // the current process
const int cpu = -1; // all CPUs
const unsigned long flags = 0;
int group = -1; // no group
num_events = config_vec.size();
ids.resize(config_vec.size());
uint32_t i = 0;
for (auto config : config_vec) {
attribs.config = config;
int _fd = static_cast<int>(
syscall(__NR_perf_event_open, &attribs, pid, cpu, group, flags));
if (_fd == -1) {
report_error("perf_event_open");
}
ioctl(_fd, PERF_EVENT_IOC_ID, &ids[i++]);
if (group == -1) {
group = _fd;
fd = _fd;
}
}
temp_result_vec.resize(num_events * 2 + 1);
}
~LinuxEvents() {
if (fd != -1) {
close(fd);
}
}
inline void start() {
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_RESET)");
}
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_ENABLE)");
}
}
}
inline void end(std::vector<unsigned long long> &results) {
if (fd != -1) {
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
report_error("ioctl(PERF_EVENT_IOC_DISABLE)");
}
if (read(fd, temp_result_vec.data(), temp_result_vec.size() * 8) == -1) {
report_error("read");
}
}
// our actual results are in slots 1,3,5, ... of this structure
for (uint32_t i = 1; i < temp_result_vec.size(); i += 2) {
results[i / 2] = temp_result_vec[i];
}
for (uint32_t i = 2; i < temp_result_vec.size(); i += 2) {
if (ids[i / 2 - 1] != temp_result_vec[i]) {
report_error("event mismatch");
}
}
}
bool is_working() { return working; }
private:
void report_error(const std::string &) { working = false; }
};
#endif

View File

@ -1,4 +0,0 @@
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR riscv64)
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-riscv64-static")

View File

@ -19,7 +19,7 @@ fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) {
return chars_format::general; return chars_format::general;
} }
extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) { extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fdp(data, size); FuzzedDataProvider fdp(data, size);
fast_float::chars_format format = arbitrary_format(fdp); fast_float::chars_format format = arbitrary_format(fdp);
double result_d = 0.0; double result_d = 0.0;

View File

@ -45,7 +45,7 @@ fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) {
// Read 8 UC into a u64. Truncates UC if not char. // Read 8 UC into a u64. Truncates UC if not char.
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t
read8_to_u64(UC const *chars) { read8_to_u64(const UC *chars) {
if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) { if (cpp20_and_in_constexpr() || !std::is_same<UC, char>::value) {
uint64_t val = 0; uint64_t val = 0;
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
@ -65,9 +65,9 @@ read8_to_u64(UC const *chars) {
#ifdef FASTFLOAT_SSE2 #ifdef FASTFLOAT_SSE2
fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) { fastfloat_really_inline uint64_t simd_read8_to_u64(const __m128i data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
__m128i const packed = _mm_packus_epi16(data, data); const __m128i packed = _mm_packus_epi16(data, data);
#ifdef FASTFLOAT_64BIT #ifdef FASTFLOAT_64BIT
return uint64_t(_mm_cvtsi128_si64(packed)); return uint64_t(_mm_cvtsi128_si64(packed));
#else #else
@ -79,26 +79,26 @@ fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) {
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64( return simd_read8_to_u64(
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars))); _mm_loadu_si128(reinterpret_cast<const __m128i *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
#elif defined(FASTFLOAT_NEON) #elif defined(FASTFLOAT_NEON)
fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) { fastfloat_really_inline uint64_t simd_read8_to_u64(const uint16x8_t data) {
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
uint8x8_t utf8_packed = vmovn_u16(data); uint8x8_t utf8_packed = vmovn_u16(data);
return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0); return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0);
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { fastfloat_really_inline uint64_t simd_read8_to_u64(const char16_t *chars) {
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
return simd_read8_to_u64( return simd_read8_to_u64(
vld1q_u16(reinterpret_cast<uint16_t const *>(chars))); vld1q_u16(reinterpret_cast<const uint16_t *>(chars)));
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
} }
@ -118,9 +118,9 @@ uint64_t simd_read8_to_u64(UC const *) {
// credit @aqrit // credit @aqrit
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t
parse_eight_digits_unrolled(uint64_t val) { parse_eight_digits_unrolled(uint64_t val) {
uint64_t const mask = 0x000000FF000000FF; const uint64_t mask = 0x000000FF000000FF;
uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) const uint64_t mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32)
uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) const uint64_t mul2 = 0x0000271000000001; // 1 + (10000ULL << 32)
val -= 0x3030303030303030; val -= 0x3030303030303030;
val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8;
val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32;
@ -150,20 +150,20 @@ is_made_of_eight_digits_fast(uint64_t val) noexcept {
// Using this style (instead of is_made_of_eight_digits_fast() then // Using this style (instead of is_made_of_eight_digits_fast() then
// parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice. // parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice.
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
simd_parse_if_eight_digits_unrolled(char16_t const *chars, simd_parse_if_eight_digits_unrolled(const char16_t *chars,
uint64_t &i) noexcept { uint64_t &i) noexcept {
if (cpp20_and_in_constexpr()) { if (cpp20_and_in_constexpr()) {
return false; return false;
} }
#ifdef FASTFLOAT_SSE2 #ifdef FASTFLOAT_SSE2
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
__m128i const data = const __m128i data =
_mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)); _mm_loadu_si128(reinterpret_cast<const __m128i *>(chars));
// (x - '0') <= 9 // (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html // http://0x80.pl/articles/simd-parsing-int-sequences.html
__m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720)); const __m128i t0 = _mm_add_epi16(data, _mm_set1_epi16(32720));
__m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759)); const __m128i t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759));
if (_mm_movemask_epi8(t1) == 0) { if (_mm_movemask_epi8(t1) == 0) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
@ -173,12 +173,12 @@ simd_parse_if_eight_digits_unrolled(char16_t const *chars,
FASTFLOAT_SIMD_RESTORE_WARNINGS FASTFLOAT_SIMD_RESTORE_WARNINGS
#elif defined(FASTFLOAT_NEON) #elif defined(FASTFLOAT_NEON)
FASTFLOAT_SIMD_DISABLE_WARNINGS FASTFLOAT_SIMD_DISABLE_WARNINGS
uint16x8_t const data = vld1q_u16(reinterpret_cast<uint16_t const *>(chars)); const uint16x8_t data = vld1q_u16(reinterpret_cast<const uint16_t *>(chars));
// (x - '0') <= 9 // (x - '0') <= 9
// http://0x80.pl/articles/simd-parsing-int-sequences.html // http://0x80.pl/articles/simd-parsing-int-sequences.html
uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0')); const uint16x8_t t0 = vsubq_u16(data, vmovq_n_u16('0'));
uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1)); const uint16x8_t mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1));
if (vminvq_u16(mask) == 0xFFFF) { if (vminvq_u16(mask) == 0xFFFF) {
i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data));
@ -208,7 +208,7 @@ bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) {
template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0> template <typename UC, FASTFLOAT_ENABLE_IF(!std::is_same<UC, char>::value) = 0>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) { loop_parse_if_eight_digits(const UC *&p, const UC *const pend, uint64_t &i) {
if (!has_simd_opt<UC>()) { if (!has_simd_opt<UC>()) {
return; return;
} }
@ -220,7 +220,7 @@ loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) {
} }
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
loop_parse_if_eight_digits(char const *&p, char const *const pend, loop_parse_if_eight_digits(const char *&p, const char *const pend,
uint64_t &i) { uint64_t &i) {
// optimizes better than parse_if_eight_digits_unrolled() for UC = char. // optimizes better than parse_if_eight_digits_unrolled() for UC = char.
while ((std::distance(p, pend) >= 8) && while ((std::distance(p, pend) >= 8) &&
@ -259,12 +259,12 @@ template <typename UC> struct parsed_number_string_t {
bool valid{false}; bool valid{false};
bool too_many_digits{false}; bool too_many_digits{false};
// contains the range of the significant digits // contains the range of the significant digits
span<UC const> integer{}; // non-nullable span<const UC> integer{}; // non-nullable
span<UC const> fraction{}; // nullable span<const UC> fraction{}; // nullable
parse_error error{parse_error::no_error}; parse_error error{parse_error::no_error};
}; };
using byte_span = span<char const>; using byte_span = span<const char>;
using parsed_number_string = parsed_number_string_t<char>; using parsed_number_string = parsed_number_string_t<char>;
template <typename UC> template <typename UC>
@ -279,33 +279,33 @@ report_parse_error(UC const *p, parse_error error) {
// Assuming that you use no more than 19 digits, this will // Assuming that you use no more than 19 digits, this will
// parse an ASCII string. // parse an ASCII string.
template <bool basic_json_fmt, typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
parse_number_string(UC const *p, UC const *pend, parse_number_string(UC const *p, UC const *pend,
parse_options_t<UC> options) noexcept { parse_options_t<UC> options) noexcept {
chars_format const fmt = detail::adjust_for_feature_macros(options.format); chars_format const fmt = options.format;
UC const decimal_point = options.decimal_point; UC const decimal_point = options.decimal_point;
parsed_number_string_t<UC> answer; parsed_number_string_t<UC> answer;
answer.valid = false; answer.valid = false;
answer.too_many_digits = false; answer.too_many_digits = false;
// assume p < pend, so dereference without checks;
answer.negative = (*p == UC('-')); answer.negative = (*p == UC('-'));
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) && if ((*p == UC('-')) || (!(fmt & FASTFLOAT_JSONFMT) && *p == UC('+'))) {
!basic_json_fmt && *p == UC('+'))) { #else
if (*p == UC('-')) { // C++17 20.19.3.(7.1) explicitly forbids '+' sign here
#endif
++p; ++p;
if (p == pend) { if (p == pend) {
return report_parse_error<UC>( return report_parse_error<UC>(
p, parse_error::missing_integer_or_dot_after_sign); p, parse_error::missing_integer_or_dot_after_sign);
} }
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { if (fmt & FASTFLOAT_JSONFMT) {
if (!is_integer(*p)) { // a sign must be followed by an integer if (!is_integer(*p)) { // a sign must be followed by an integer
return report_parse_error<UC>(p, return report_parse_error<UC>(p,
parse_error::missing_integer_after_sign); parse_error::missing_integer_after_sign);
} }
} } else {
else {
if (!is_integer(*p) && if (!is_integer(*p) &&
(*p != (*p !=
decimal_point)) { // a sign must be followed by an integer or the dot decimal_point)) { // a sign must be followed by an integer or the dot
@ -328,8 +328,8 @@ parse_number_string(UC const *p, UC const *pend,
} }
UC const *const end_of_integer_part = p; UC const *const end_of_integer_part = p;
int64_t digit_count = int64_t(end_of_integer_part - start_digits); int64_t digit_count = int64_t(end_of_integer_part - start_digits);
answer.integer = span<UC const>(start_digits, size_t(digit_count)); answer.integer = span<const UC>(start_digits, size_t(digit_count));
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { if (fmt & FASTFLOAT_JSONFMT) {
// at least 1 digit in integer part, without leading zeros // at least 1 digit in integer part, without leading zeros
if (digit_count == 0) { if (digit_count == 0) {
return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part); return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
@ -341,7 +341,7 @@ parse_number_string(UC const *p, UC const *pend,
} }
int64_t exponent = 0; int64_t exponent = 0;
bool const has_decimal_point = (p != pend) && (*p == decimal_point); const bool has_decimal_point = (p != pend) && (*p == decimal_point);
if (has_decimal_point) { if (has_decimal_point) {
++p; ++p;
UC const *before = p; UC const *before = p;
@ -355,23 +355,23 @@ parse_number_string(UC const *p, UC const *pend,
i = i * 10 + digit; // in rare cases, this will overflow, but that's ok i = i * 10 + digit; // in rare cases, this will overflow, but that's ok
} }
exponent = before - p; exponent = before - p;
answer.fraction = span<UC const>(before, size_t(p - before)); answer.fraction = span<const UC>(before, size_t(p - before));
digit_count -= exponent; digit_count -= exponent;
} }
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) { if (fmt & FASTFLOAT_JSONFMT) {
// at least 1 digit in fractional part // at least 1 digit in fractional part
if (has_decimal_point && exponent == 0) { if (has_decimal_point && exponent == 0) {
return report_parse_error<UC>(p, return report_parse_error<UC>(p,
parse_error::no_digits_in_fractional_part); parse_error::no_digits_in_fractional_part);
} }
} } else if (digit_count ==
else if (digit_count == 0) { // we must have encountered at least one integer! 0) { // we must have encountered at least one integer!
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa); return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
} }
int64_t exp_number = 0; // explicit exponential part int64_t exp_number = 0; // explicit exponential part
if ((uint64_t(fmt & chars_format::scientific) && (p != pend) && if (((fmt & chars_format::scientific) && (p != pend) &&
((UC('e') == *p) || (UC('E') == *p))) || ((UC('e') == *p) || (UC('E') == *p))) ||
(uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) && ((fmt & FASTFLOAT_FORTRANFMT) && (p != pend) &&
((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) ||
(UC('D') == *p)))) { (UC('D') == *p)))) {
UC const *location_of_e = p; UC const *location_of_e = p;
@ -389,7 +389,7 @@ parse_number_string(UC const *p, UC const *pend,
++p; ++p;
} }
if ((p == pend) || !is_integer(*p)) { if ((p == pend) || !is_integer(*p)) {
if (!uint64_t(fmt & chars_format::fixed)) { if (!(fmt & chars_format::fixed)) {
// The exponential part is invalid for scientific notation, so it must // The exponential part is invalid for scientific notation, so it must
// be a trailing token for fixed notation. However, fixed notation is // be a trailing token for fixed notation. However, fixed notation is
// disabled, so report a scientific notation error. // disabled, so report a scientific notation error.
@ -412,8 +412,7 @@ parse_number_string(UC const *p, UC const *pend,
} }
} else { } else {
// If it scientific and not fixed, we have to bail out. // If it scientific and not fixed, we have to bail out.
if (uint64_t(fmt & chars_format::scientific) && if ((fmt & chars_format::scientific) && !(fmt & chars_format::fixed)) {
!uint64_t(fmt & chars_format::fixed)) {
return report_parse_error<UC>(p, parse_error::missing_exponential_part); return report_parse_error<UC>(p, parse_error::missing_exponential_part);
} }
} }
@ -441,17 +440,17 @@ parse_number_string(UC const *p, UC const *pend,
if (digit_count > 19) { if (digit_count > 19) {
answer.too_many_digits = true; answer.too_many_digits = true;
// Let us start again, this time, avoiding overflows. // Let us start again, this time, avoiding overflows.
// We don't need to call if is_integer, since we use the // We don't need to check if is_integer, since we use the
// pre-tokenized spans from above. // pre-tokenized spans from above.
i = 0; i = 0;
p = answer.integer.ptr; p = answer.integer.ptr;
UC const *int_end = p + answer.integer.len(); UC const *int_end = p + answer.integer.len();
uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; const uint64_t minimal_nineteen_digit_integer{1000000000000000000};
while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { while ((i < minimal_nineteen_digit_integer) && (p != int_end)) {
i = i * 10 + uint64_t(*p - UC('0')); i = i * 10 + uint64_t(*p - UC('0'));
++p; ++p;
} }
if (i >= minimal_nineteen_digit_integer) { // We have a big integer if (i >= minimal_nineteen_digit_integer) { // We have a big integers
exponent = end_of_integer_part - p + exp_number; exponent = end_of_integer_part - p + exp_number;
} else { // We have a value with a fractional component. } else { // We have a value with a fractional component.
p = answer.fraction.ptr; p = answer.fraction.ptr;
@ -472,30 +471,22 @@ parse_number_string(UC const *p, UC const *pend,
template <typename T, typename UC> template <typename T, typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
parse_int_string(UC const *p, UC const *pend, T &value, parse_int_string(UC const *p, UC const *pend, T &value, int base) {
parse_options_t<UC> options) {
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
UC const *const first = p; UC const *const first = p;
bool const negative = (*p == UC('-')); bool negative = (*p == UC('-'));
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(push)
#pragma warning(disable : 4127)
#endif
if (!std::is_signed<T>::value && negative) { if (!std::is_signed<T>::value && negative) {
#ifdef FASTFLOAT_VISUAL_STUDIO
#pragma warning(pop)
#endif
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} }
if ((*p == UC('-')) || #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
(uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { if ((*p == UC('-')) || (*p == UC('+'))) {
#else
if (*p == UC('-')) {
#endif
++p; ++p;
} }
@ -505,7 +496,7 @@ parse_int_string(UC const *p, UC const *pend, T &value,
++p; ++p;
} }
bool const has_leading_zeros = p > start_num; const bool has_leading_zeros = p > start_num;
UC const *const start_digits = p; UC const *const start_digits = p;

View File

@ -43,8 +43,8 @@ template <uint16_t size> struct stackvec {
uint16_t length{0}; uint16_t length{0};
stackvec() = default; stackvec() = default;
stackvec(stackvec const &) = delete; stackvec(const stackvec &) = delete;
stackvec &operator=(stackvec const &) = delete; stackvec &operator=(const stackvec &) = delete;
stackvec(stackvec &&) = delete; stackvec(stackvec &&) = delete;
stackvec &operator=(stackvec &&other) = delete; stackvec &operator=(stackvec &&other) = delete;
@ -57,12 +57,10 @@ template <uint16_t size> struct stackvec {
FASTFLOAT_DEBUG_ASSERT(index < length); FASTFLOAT_DEBUG_ASSERT(index < length);
return data[index]; return data[index];
} }
FASTFLOAT_CONSTEXPR14 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
FASTFLOAT_CONSTEXPR14 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);
@ -74,19 +72,14 @@ template <uint16_t size> struct stackvec {
FASTFLOAT_CONSTEXPR14 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 { return length; } constexpr size_t len() const noexcept { return length; }
constexpr bool is_empty() const noexcept { return length == 0; } constexpr bool is_empty() const noexcept { return length == 0; }
constexpr size_t capacity() const noexcept { return size; } constexpr size_t capacity() const noexcept { return size; }
// append item to vector, without bounds checking // append item to vector, without bounds checking
FASTFLOAT_CONSTEXPR14 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
FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept { FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept {
if (len() < capacity()) { if (len() < capacity()) {
@ -96,14 +89,12 @@ template <uint16_t size> struct stackvec {
return false; return false;
} }
} }
// add items to the vector, from a span, without bounds checking // add items to the vector, from a span, without bounds checking
FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept {
limb *ptr = data + length; limb *ptr = data + length;
std::copy_n(s.ptr, s.len(), ptr); std::copy_n(s.ptr, s.len(), ptr);
set_len(len() + s.len()); set_len(len() + s.len());
} }
// try to add items to the vector, returning if items were added // try to add items to the vector, returning if items were added
FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept { FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept {
if (len() + s.len() <= capacity()) { if (len() + s.len() <= capacity()) {
@ -113,7 +104,6 @@ template <uint16_t size> struct stackvec {
return false; return false;
} }
} }
// resize the vector, without bounds checking // resize the vector, without bounds checking
// if the new size is longer than the vector, assign value to each // if the new size is longer than the vector, assign value to each
// appended item. // appended item.
@ -129,7 +119,6 @@ template <uint16_t size> struct stackvec {
set_len(new_len); set_len(new_len);
} }
} }
// try to resize the vector, returning if the vector was resized. // try to resize the vector, returning if the vector was resized.
FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept { FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept {
if (new_len > capacity()) { if (new_len > capacity()) {
@ -139,7 +128,6 @@ template <uint16_t size> struct stackvec {
return true; return true;
} }
} }
// 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.
@ -152,7 +140,6 @@ template <uint16_t size> 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.
FASTFLOAT_CONSTEXPR14 void normalize() noexcept { FASTFLOAT_CONSTEXPR14 void normalize() noexcept {
while (len() > 0 && rindex(0) == 0) { while (len() > 0 && rindex(0) == 0) {
@ -436,9 +423,8 @@ struct bigint : pow5_tables<> {
stackvec<bigint_limbs> vec; stackvec<bigint_limbs> vec;
FASTFLOAT_CONSTEXPR20 bigint() : vec() {} FASTFLOAT_CONSTEXPR20 bigint() : vec() {}
bigint(const bigint &) = delete;
bigint(bigint const &) = delete; bigint &operator=(const bigint &) = delete;
bigint &operator=(bigint const &) = delete;
bigint(bigint &&) = delete; bigint(bigint &&) = delete;
bigint &operator=(bigint &&other) = delete; bigint &operator=(bigint &&other) = delete;
@ -487,7 +473,7 @@ struct bigint : pow5_tables<> {
// positive, this is larger, otherwise they are equal. // positive, this is larger, otherwise they are equal.
// the limbs are stored in little-endian order, so we // the limbs are stored in little-endian order, so we
// must compare the limbs in ever order. // must compare the limbs in ever order.
FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept { FASTFLOAT_CONSTEXPR20 int compare(const bigint &other) const noexcept {
if (vec.len() > other.vec.len()) { if (vec.len() > other.vec.len()) {
return 1; return 1;
} else if (vec.len() < other.vec.len()) { } else if (vec.len() < other.vec.len()) {
@ -541,7 +527,7 @@ struct bigint : pow5_tables<> {
} else if (!vec.is_empty()) { } else if (!vec.is_empty()) {
// move limbs // move limbs
limb *dst = vec.data + n; limb *dst = vec.data + n;
limb const *src = vec.data; const limb *src = vec.data;
std::copy_backward(src, src + vec.len(), dst + vec.len()); std::copy_backward(src, src + vec.len(), dst + vec.len());
// fill in empty limbs // fill in empty limbs
limb *first = vec.data; limb *first = vec.data;

View File

@ -8,7 +8,7 @@
#endif #endif
// Testing for https://wg21.link/N3652, adopted in C++14 // Testing for https://wg21.link/N3652, adopted in C++14
#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 #if __cpp_constexpr >= 201304
#define FASTFLOAT_CONSTEXPR14 constexpr #define FASTFLOAT_CONSTEXPR14 constexpr
#else #else
#define FASTFLOAT_CONSTEXPR14 #define FASTFLOAT_CONSTEXPR14
@ -27,15 +27,8 @@
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
#endif #endif
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
#define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x)
#else
#define FASTFLOAT_IF_CONSTEXPR17(x) if (x)
#endif
// Testing for relevant C++20 constexpr library features // Testing for relevant C++20 constexpr library features
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \ #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \
defined(__cpp_lib_constexpr_algorithms) && \
__cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/ __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
#define FASTFLOAT_CONSTEXPR20 constexpr #define FASTFLOAT_CONSTEXPR20 constexpr
#define FASTFLOAT_IS_CONSTEXPR 1 #define FASTFLOAT_IS_CONSTEXPR 1

View File

@ -20,7 +20,7 @@ namespace fast_float {
template <int bit_precision> template <int bit_precision>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128
compute_product_approximation(int64_t q, uint64_t w) { compute_product_approximation(int64_t q, uint64_t w) {
int const index = 2 * int(q - powers::smallest_power_of_five); const int index = 2 * int(q - powers::smallest_power_of_five);
// For small values of q, e.g., q in [0,27], the answer is always exact // For small values of q, e.g., q in [0,27], the answer is always exact
// because The line value128 firstproduct = full_multiplication(w, // because The line value128 firstproduct = full_multiplication(w,
// power_of_five_128[index]); gives the exact answer. // power_of_five_128[index]); gives the exact answer.
@ -93,8 +93,8 @@ compute_error(int64_t q, uint64_t w) noexcept {
return compute_error_scaled<binary>(q, product.high, lz); return compute_error_scaled<binary>(q, product.high, lz);
} }
// Computers w * 10 ** q. // w * 10 ** q
// The returned value should be a valid number that simply needs to be // The returned value should be a valid ieee64 number that simply need to be
// packed. However, in some very rare cases, the computation will fail. In such // packed. However, in some very rare cases, the computation will fail. In such
// cases, we return an adjusted_mantissa with a negative power of 2: the caller // cases, we return an adjusted_mantissa with a negative power of 2: the caller
// should recompute in such cases. // should recompute in such cases.
@ -158,8 +158,7 @@ compute_float(int64_t q, uint64_t w) noexcept {
// next line is safe because -answer.power2 + 1 < 64 // next line is safe because -answer.power2 + 1 < 64
answer.mantissa >>= -answer.power2 + 1; answer.mantissa >>= -answer.power2 + 1;
// Thankfully, we can't have both "round-to-even" and subnormals because // Thankfully, we can't have both "round-to-even" and subnormals because
// "round-to-even" only occurs for powers close to 0 in the 32-bit and // "round-to-even" only occurs for powers close to 0.
// and 64-bit case (with no more than 19 digits).
answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa += (answer.mantissa & 1); // round up
answer.mantissa >>= 1; answer.mantissa >>= 1;
// There is a weird scenario where we don't have a subnormal but just. // There is a weird scenario where we don't have a subnormal but just.

View File

@ -38,8 +38,11 @@ constexpr static uint64_t powers_of_ten_uint64[] = {1UL,
// 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.
template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept { scientific_exponent(parsed_number_string_t<UC> &num) noexcept {
uint64_t mantissa = num.mantissa;
int32_t exponent = int32_t(num.exponent);
while (mantissa >= 10000) { while (mantissa >= 10000) {
mantissa /= 10000; mantissa /= 10000;
exponent += 4; exponent += 4;
@ -59,7 +62,7 @@ scientific_exponent(uint64_t mantissa, int32_t exponent) noexcept {
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa
to_extended(T value) noexcept { to_extended(T value) noexcept {
using equiv_uint = equiv_uint_t<T>; using equiv_uint = typename binary_format<T>::equiv_uint;
constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask(); constexpr equiv_uint exponent_mask = binary_format<T>::exponent_mask();
constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask(); constexpr equiv_uint mantissa_mask = binary_format<T>::mantissa_mask();
constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask(); constexpr equiv_uint hidden_bit_mask = binary_format<T>::hidden_bit_mask();
@ -140,8 +143,8 @@ template <typename callback>
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void
round_nearest_tie_even(adjusted_mantissa &am, int32_t shift, round_nearest_tie_even(adjusted_mantissa &am, int32_t shift,
callback cb) noexcept { callback cb) noexcept {
uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1; const uint64_t mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1;
uint64_t const halfway = (shift == 0) ? 0 : 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 truncated_bits = am.mantissa & mask;
bool is_above = truncated_bits > halfway; bool is_above = truncated_bits > halfway;
bool is_halfway = truncated_bits == halfway; bool is_halfway = truncated_bits == halfway;
@ -167,7 +170,6 @@ round_down(adjusted_mantissa &am, int32_t shift) noexcept {
} }
am.power2 += shift; am.power2 += shift;
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
skip_zeros(UC const *&first, UC const *last) noexcept { skip_zeros(UC const *&first, UC const *last) noexcept {
@ -211,16 +213,15 @@ is_truncated(UC const *first, UC const *last) noexcept {
} }
return false; return false;
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
is_truncated(span<UC const> s) noexcept { is_truncated(span<const UC> s) noexcept {
return is_truncated(s.ptr, s.ptr + s.len()); return is_truncated(s.ptr, s.ptr + s.len());
} }
template <typename UC> template <typename UC>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
parse_eight_digits(UC const *&p, limb &value, size_t &counter, parse_eight_digits(const UC *&p, limb &value, size_t &counter,
size_t &count) noexcept { size_t &count) noexcept {
value = value * 100000000 + parse_eight_digits_unrolled(p); value = value * 100000000 + parse_eight_digits_unrolled(p);
p += 8; p += 8;
@ -395,7 +396,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp)));
} }
// compare digits, and use it to direct rounding // compare digits, and use it to director rounding
int ord = real_digits.compare(theor_digits); int ord = real_digits.compare(theor_digits);
adjusted_mantissa answer = am; adjusted_mantissa answer = am;
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) { round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
@ -416,7 +417,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
return answer; return answer;
} }
// parse the significant digits as a big integer to unambiguously round // parse the significant digits as a big integer to unambiguously round the
// the significant digits. here, we are trying to determine how to round // the significant digits. here, we are trying to determine how to round
// an extended float representation close to `b+h`, halfway between `b` // an extended float representation close to `b+h`, halfway between `b`
// (the float rounded-down) and `b+u`, the next positive float. this // (the float rounded-down) and `b+u`, the next positive float. this
@ -435,8 +436,7 @@ digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
// remove the invalid exponent bias // remove the invalid exponent bias
am.power2 -= invalid_am_bias; am.power2 -= invalid_am_bias;
int32_t sci_exp = int32_t sci_exp = scientific_exponent(num);
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
size_t max_digits = binary_format<T>::max_digits(); size_t max_digits = binary_format<T>::max_digits();
size_t digits = 0; size_t digits = 0;
bigint bigmant; bigint bigmant;

View File

@ -31,61 +31,26 @@ namespace fast_float {
* `scientific`. * `scientific`.
*/ */
template <typename T, typename UC = char, template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>::value)> typename = FASTFLOAT_ENABLE_IF(is_supported_float_type<T>())>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, from_chars(UC const *first, UC const *last, T &value,
chars_format fmt = chars_format::general) noexcept; chars_format fmt = chars_format::general) noexcept;
/** /**
* Like from_chars, but accepts an `options` argument to govern number parsing. * Like from_chars, but accepts an `options` argument to govern number parsing.
* Both for floating-point types and integer types.
*/ */
template <typename T, typename UC = char> template <typename T, typename UC = char>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(UC const *first, UC const *last, T &value, from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept; parse_options_t<UC> options) noexcept;
/**
* This function multiplies an integer number by a power of 10 and returns
* the result as a double precision floating-point value that is correctly
* rounded. The resulting floating-point value is the closest floating-point
* value, using the "round to nearest, tie to even" convention for values that
* would otherwise fall right in-between two values. That is, we provide exact
* conversion according to the IEEE standard.
*
* On overflow infinity is returned, on underflow 0 is returned.
*
* The implementation does not throw and does not allocate memory (e.g., with
* `new` or `malloc`).
*/
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
/**
* This function is a template overload of `integer_times_pow10()`
* that returns a floating-point value of type `T` that is one of
* supported floating-point types (e.g. `double`, `float`).
*/
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
/** /**
* from_chars for integer types. * from_chars for integer types.
*/ */
template <typename T, typename UC = char, template <typename T, typename UC = char,
typename = FASTFLOAT_ENABLE_IF(is_supported_integer_type<T>::value)> typename = FASTFLOAT_ENABLE_IF(!is_supported_float_type<T>())>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept; from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept;
} // namespace fast_float } // namespace fast_float
#include "parse_number.h" #include "parse_number.h"
#endif // FASTFLOAT_FAST_FLOAT_H #endif // FASTFLOAT_FAST_FLOAT_H

View File

@ -5,81 +5,49 @@
#include <cstdint> #include <cstdint>
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <limits>
#include <type_traits> #include <type_traits>
#include <system_error> #include <system_error>
#ifdef __has_include #ifdef __has_include
#if __has_include(<stdfloat>) && (__cplusplus > 202002L || (defined(_MSVC_LANG) && (_MSVC_LANG > 202002L))) #if __has_include(<stdfloat>) && (__cplusplus > 202002L || _MSVC_LANG > 202002L)
#include <stdfloat> #include <stdfloat>
#endif #endif
#endif #endif
#include "constexpr_feature_detect.h" #include "constexpr_feature_detect.h"
#define FASTFLOAT_VERSION_MAJOR 8
#define FASTFLOAT_VERSION_MINOR 1
#define FASTFLOAT_VERSION_PATCH 0
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
#define FASTFLOAT_STRINGIZE(x) FASTFLOAT_STRINGIZE_IMPL(x)
#define FASTFLOAT_VERSION_STR \
FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MAJOR) \
"." FASTFLOAT_STRINGIZE(FASTFLOAT_VERSION_MINOR) "." FASTFLOAT_STRINGIZE( \
FASTFLOAT_VERSION_PATCH)
#define FASTFLOAT_VERSION \
(FASTFLOAT_VERSION_MAJOR * 10000 + FASTFLOAT_VERSION_MINOR * 100 + \
FASTFLOAT_VERSION_PATCH)
namespace fast_float { namespace fast_float {
enum class chars_format : uint64_t; #define FASTFLOAT_JSONFMT (1 << 5)
#define FASTFLOAT_FORTRANFMT (1 << 6)
namespace detail { enum chars_format {
constexpr chars_format basic_json_fmt = chars_format(1 << 5);
constexpr chars_format basic_fortran_fmt = chars_format(1 << 6);
} // namespace detail
enum class chars_format : uint64_t {
scientific = 1 << 0, scientific = 1 << 0,
fixed = 1 << 2, fixed = 1 << 2,
hex = 1 << 3, hex = 1 << 3,
no_infnan = 1 << 4, no_infnan = 1 << 4,
// RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6 // RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6
json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan, json = FASTFLOAT_JSONFMT | fixed | scientific | no_infnan,
// Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed. // Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed.
json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, json_or_infnan = FASTFLOAT_JSONFMT | fixed | scientific,
fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, fortran = FASTFLOAT_FORTRANFMT | fixed | scientific,
general = fixed | scientific, general = fixed | scientific
allow_leading_plus = 1 << 7,
skip_white_space = 1 << 8,
}; };
template <typename UC> struct from_chars_result_t { template <typename UC> struct from_chars_result_t {
UC const *ptr; UC const *ptr;
std::errc ec; std::errc ec;
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2497r0.html
constexpr explicit operator bool() const noexcept {
return ec == std::errc();
}
}; };
using from_chars_result = from_chars_result_t<char>; using from_chars_result = from_chars_result_t<char>;
template <typename UC> struct parse_options_t { template <typename UC> struct parse_options_t {
constexpr explicit parse_options_t(chars_format fmt = chars_format::general, constexpr explicit parse_options_t(chars_format fmt = chars_format::general,
UC dot = UC('.'), int b = 10) UC dot = UC('.'))
: format(fmt), decimal_point(dot), base(b) {} : format(fmt), decimal_point(dot) {}
/** Which number formats are accepted */ /** Which number formats are accepted */
chars_format format; chars_format format;
/** The character used as decimal point */ /** The character used as decimal point */
UC decimal_point; UC decimal_point;
/** The base used for integers */
int base;
}; };
using parse_options = parse_options_t<char>; using parse_options = parse_options_t<char>;
} // namespace fast_float } // namespace fast_float
@ -93,12 +61,11 @@ using parse_options = parse_options_t<char>;
defined(__MINGW64__) || defined(__s390x__) || \ defined(__MINGW64__) || defined(__s390x__) || \
(defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
defined(__PPC64LE__)) || \ defined(__PPC64LE__)) || \
defined(__loongarch64) || (defined(__riscv) && __riscv_xlen == 64)) defined(__loongarch64))
#define FASTFLOAT_64BIT 1 #define FASTFLOAT_64BIT 1
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \ defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \
defined(__MINGW32__) || defined(__EMSCRIPTEN__) || \ defined(__MINGW32__) || defined(__EMSCRIPTEN__))
(defined(__riscv) && __riscv_xlen == 32))
#define FASTFLOAT_32BIT 1 #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.
@ -227,58 +194,32 @@ fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() {
} }
template <typename T> template <typename T>
struct is_supported_float_type fastfloat_really_inline constexpr bool is_supported_float_type() {
: std::integral_constant< return std::is_same<T, float>::value || std::is_same<T, double>::value
bool, std::is_same<T, double>::value || std::is_same<T, float>::value #if __STDCPP_FLOAT32_T__
#ifdef __STDCPP_FLOAT64_T__ || std::is_same<T, std::float32_t>::value
|| std::is_same<T, std::float64_t>::value
#endif #endif
#ifdef __STDCPP_FLOAT32_T__ #if __STDCPP_FLOAT64_T__
|| std::is_same<T, std::float32_t>::value || std::is_same<T, std::float64_t>::value
#endif #endif
#ifdef __STDCPP_FLOAT16_T__ ;
|| std::is_same<T, std::float16_t>::value }
#endif
#ifdef __STDCPP_BFLOAT16_T__
|| std::is_same<T, std::bfloat16_t>::value
#endif
> {
};
template <typename T>
using equiv_uint_t = typename std::conditional<
sizeof(T) == 1, uint8_t,
typename std::conditional<
sizeof(T) == 2, uint16_t,
typename std::conditional<sizeof(T) == 4, uint32_t,
uint64_t>::type>::type>::type;
template <typename T> struct is_supported_integer_type : std::is_integral<T> {};
template <typename UC> template <typename UC>
struct is_supported_char_type fastfloat_really_inline constexpr bool is_supported_char_type() {
: std::integral_constant<bool, std::is_same<UC, char>::value || return std::is_same<UC, char>::value || std::is_same<UC, wchar_t>::value ||
std::is_same<UC, wchar_t>::value || std::is_same<UC, char16_t>::value || std::is_same<UC, char32_t>::value;
std::is_same<UC, char16_t>::value || }
std::is_same<UC, char32_t>::value
#ifdef __cpp_char8_t
|| std::is_same<UC, char8_t>::value
#endif
> {
};
// Compares two ASCII strings in a case insensitive manner. // Compares two ASCII strings in a case insensitive manner.
template <typename UC> template <typename UC>
inline FASTFLOAT_CONSTEXPR14 bool inline FASTFLOAT_CONSTEXPR14 bool
fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase, fastfloat_strncasecmp(UC const *input1, UC const *input2, size_t length) {
size_t length) { char running_diff{0};
for (size_t i = 0; i < length; ++i) { for (size_t i = 0; i < length; ++i) {
UC const actual = actual_mixedcase[i]; running_diff |= (char(input1[i]) ^ char(input2[i]));
if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) {
return false;
}
} }
return true; return (running_diff == 0) || (running_diff == 32);
} }
#ifndef FLT_EVAL_METHOD #ifndef FLT_EVAL_METHOD
@ -287,11 +228,9 @@ fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase,
// a pointer and a length to a contiguous block of memory // a pointer and a length to a contiguous block of memory
template <typename T> struct span { template <typename T> struct span {
T const *ptr; const T *ptr;
size_t length; size_t length;
constexpr span(const T *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {}
constexpr span() : ptr(nullptr), length(0) {} constexpr span() : ptr(nullptr), length(0) {}
constexpr size_t len() const noexcept { return length; } constexpr size_t len() const noexcept { return length; }
@ -305,9 +244,7 @@ template <typename T> struct span {
struct value128 { struct value128 {
uint64_t low; uint64_t low;
uint64_t high; uint64_t high;
constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {}
constexpr value128() : low(0), high(0) {} constexpr value128() : low(0), high(0) {}
}; };
@ -406,8 +343,8 @@ full_multiplication(uint64_t a, uint64_t b) {
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications // 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(_M_ARM64) && !defined(__GNUC__)) (defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64))
answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64 answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__) #elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
__uint128_t r = ((__uint128_t)a) * b; __uint128_t r = ((__uint128_t)a) * b;
@ -423,12 +360,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;
constexpr bool operator==(const adjusted_mantissa &o) const {
constexpr bool operator==(adjusted_mantissa const &o) const {
return mantissa == o.mantissa && power2 == o.power2; return mantissa == o.mantissa && power2 == o.power2;
} }
constexpr bool operator!=(const adjusted_mantissa &o) const {
constexpr bool operator!=(adjusted_mantissa const &o) const {
return mantissa != o.mantissa || power2 != o.power2; return mantissa != o.mantissa || power2 != o.power2;
} }
}; };
@ -442,27 +377,28 @@ constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5;
template <typename T, typename U = void> struct binary_format_lookup_tables; template <typename T, typename U = void> struct binary_format_lookup_tables;
template <typename T> struct binary_format : binary_format_lookup_tables<T> { template <typename T> struct binary_format : binary_format_lookup_tables<T> {
using equiv_uint = equiv_uint_t<T>; using equiv_uint =
typename std::conditional<sizeof(T) == 4, uint32_t, uint64_t>::type;
static constexpr int mantissa_explicit_bits(); static inline constexpr int mantissa_explicit_bits();
static constexpr int minimum_exponent(); static inline constexpr int minimum_exponent();
static constexpr int infinite_power(); static inline constexpr int infinite_power();
static constexpr int sign_index(); static inline constexpr int sign_index();
static constexpr int static inline constexpr int
min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int max_exponent_fast_path(); static inline constexpr int max_exponent_fast_path();
static constexpr int max_exponent_round_to_even(); static inline constexpr int max_exponent_round_to_even();
static constexpr int min_exponent_round_to_even(); static inline constexpr int min_exponent_round_to_even();
static constexpr uint64_t max_mantissa_fast_path(int64_t power); static inline constexpr uint64_t max_mantissa_fast_path(int64_t power);
static constexpr uint64_t static inline constexpr uint64_t
max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST
static constexpr int largest_power_of_ten(); static inline constexpr int largest_power_of_ten();
static constexpr int smallest_power_of_ten(); static inline constexpr int smallest_power_of_ten();
static constexpr T exact_power_of_ten(int64_t power); static inline constexpr T exact_power_of_ten(int64_t power);
static constexpr size_t max_digits(); static inline constexpr size_t max_digits();
static constexpr equiv_uint exponent_mask(); static inline constexpr equiv_uint exponent_mask();
static constexpr equiv_uint mantissa_mask(); static inline constexpr equiv_uint mantissa_mask();
static constexpr equiv_uint hidden_bit_mask(); static inline constexpr equiv_uint hidden_bit_mask();
}; };
template <typename U> struct binary_format_lookup_tables<double, U> { template <typename U> struct binary_format_lookup_tables<double, U> {
@ -570,7 +506,6 @@ template <>
inline constexpr int binary_format<double>::mantissa_explicit_bits() { inline constexpr int binary_format<double>::mantissa_explicit_bits() {
return 52; return 52;
} }
template <> template <>
inline constexpr int binary_format<float>::mantissa_explicit_bits() { inline constexpr int binary_format<float>::mantissa_explicit_bits() {
return 23; return 23;
@ -599,7 +534,6 @@ inline constexpr int binary_format<float>::min_exponent_round_to_even() {
template <> inline constexpr int binary_format<double>::minimum_exponent() { template <> inline constexpr int binary_format<double>::minimum_exponent() {
return -1023; return -1023;
} }
template <> inline constexpr int binary_format<float>::minimum_exponent() { template <> inline constexpr int binary_format<float>::minimum_exponent() {
return -127; return -127;
} }
@ -607,7 +541,6 @@ template <> inline constexpr int binary_format<float>::minimum_exponent() {
template <> inline constexpr int binary_format<double>::infinite_power() { template <> inline constexpr int binary_format<double>::infinite_power() {
return 0x7FF; return 0x7FF;
} }
template <> inline constexpr int binary_format<float>::infinite_power() { template <> inline constexpr int binary_format<float>::infinite_power() {
return 0xFF; return 0xFF;
} }
@ -615,7 +548,6 @@ template <> inline constexpr int binary_format<float>::infinite_power() {
template <> inline constexpr int binary_format<double>::sign_index() { template <> inline constexpr int binary_format<double>::sign_index() {
return 63; return 63;
} }
template <> inline constexpr int binary_format<float>::sign_index() { template <> inline constexpr int binary_format<float>::sign_index() {
return 31; return 31;
} }
@ -624,7 +556,6 @@ template <>
inline constexpr int binary_format<double>::max_exponent_fast_path() { inline constexpr int binary_format<double>::max_exponent_fast_path() {
return 22; return 22;
} }
template <> template <>
inline constexpr int binary_format<float>::max_exponent_fast_path() { inline constexpr int binary_format<float>::max_exponent_fast_path() {
return 10; return 10;
@ -634,261 +565,6 @@ template <>
inline constexpr uint64_t binary_format<double>::max_mantissa_fast_path() { 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<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
// credit: Jakub Jelínek
#ifdef __STDCPP_FLOAT16_T__
template <typename U> struct binary_format_lookup_tables<std::float16_t, U> {
static constexpr std::float16_t powers_of_ten[] = {1e0f16, 1e1f16, 1e2f16,
1e3f16, 1e4f16};
// Largest integer value v so that (5**index * v) <= 1<<11.
// 0x800 == 1<<11
static constexpr uint64_t max_mantissa[] = {0x800,
0x800 / 5,
0x800 / (5 * 5),
0x800 / (5 * 5 * 5),
0x800 / (5 * 5 * 5 * 5),
0x800 / (constant_55555)};
};
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <typename U>
constexpr std::float16_t
binary_format_lookup_tables<std::float16_t, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t
binary_format_lookup_tables<std::float16_t, U>::max_mantissa[];
#endif
template <>
inline constexpr std::float16_t
binary_format<std::float16_t>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::exponent_mask() {
return 0x7C00;
}
template <>
inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::mantissa_mask() {
return 0x03FF;
}
template <>
inline constexpr binary_format<std::float16_t>::equiv_uint
binary_format<std::float16_t>::hidden_bit_mask() {
return 0x0400;
}
template <>
inline constexpr int binary_format<std::float16_t>::max_exponent_fast_path() {
return 4;
}
template <>
inline constexpr int binary_format<std::float16_t>::mantissa_explicit_bits() {
return 10;
}
template <>
inline constexpr uint64_t
binary_format<std::float16_t>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint64_t
binary_format<std::float16_t>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 4
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <>
inline constexpr int binary_format<std::float16_t>::min_exponent_fast_path() {
return 0;
}
template <>
inline constexpr int
binary_format<std::float16_t>::max_exponent_round_to_even() {
return 5;
}
template <>
inline constexpr int
binary_format<std::float16_t>::min_exponent_round_to_even() {
return -22;
}
template <>
inline constexpr int binary_format<std::float16_t>::minimum_exponent() {
return -15;
}
template <>
inline constexpr int binary_format<std::float16_t>::infinite_power() {
return 0x1F;
}
template <> inline constexpr int binary_format<std::float16_t>::sign_index() {
return 15;
}
template <>
inline constexpr int binary_format<std::float16_t>::largest_power_of_ten() {
return 4;
}
template <>
inline constexpr int binary_format<std::float16_t>::smallest_power_of_ten() {
return -27;
}
template <>
inline constexpr size_t binary_format<std::float16_t>::max_digits() {
return 22;
}
#endif // __STDCPP_FLOAT16_T__
// credit: Jakub Jelínek
#ifdef __STDCPP_BFLOAT16_T__
template <typename U> struct binary_format_lookup_tables<std::bfloat16_t, U> {
static constexpr std::bfloat16_t powers_of_ten[] = {1e0bf16, 1e1bf16, 1e2bf16,
1e3bf16};
// Largest integer value v so that (5**index * v) <= 1<<8.
// 0x100 == 1<<8
static constexpr uint64_t max_mantissa[] = {0x100, 0x100 / 5, 0x100 / (5 * 5),
0x100 / (5 * 5 * 5),
0x100 / (5 * 5 * 5 * 5)};
};
#if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE
template <typename U>
constexpr std::bfloat16_t
binary_format_lookup_tables<std::bfloat16_t, U>::powers_of_ten[];
template <typename U>
constexpr uint64_t
binary_format_lookup_tables<std::bfloat16_t, U>::max_mantissa[];
#endif
template <>
inline constexpr std::bfloat16_t
binary_format<std::bfloat16_t>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power];
}
template <>
inline constexpr int binary_format<std::bfloat16_t>::max_exponent_fast_path() {
return 3;
}
template <>
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::exponent_mask() {
return 0x7F80;
}
template <>
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::mantissa_mask() {
return 0x007F;
}
template <>
inline constexpr binary_format<std::bfloat16_t>::equiv_uint
binary_format<std::bfloat16_t>::hidden_bit_mask() {
return 0x0080;
}
template <>
inline constexpr int binary_format<std::bfloat16_t>::mantissa_explicit_bits() {
return 7;
}
template <>
inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <>
inline constexpr uint64_t
binary_format<std::bfloat16_t>::max_mantissa_fast_path(int64_t power) {
// caller is responsible to ensure that
// power >= 0 && power <= 3
//
// Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power];
}
template <>
inline constexpr int binary_format<std::bfloat16_t>::min_exponent_fast_path() {
return 0;
}
template <>
inline constexpr int
binary_format<std::bfloat16_t>::max_exponent_round_to_even() {
return 3;
}
template <>
inline constexpr int
binary_format<std::bfloat16_t>::min_exponent_round_to_even() {
return -24;
}
template <>
inline constexpr int binary_format<std::bfloat16_t>::minimum_exponent() {
return -127;
}
template <>
inline constexpr int binary_format<std::bfloat16_t>::infinite_power() {
return 0xFF;
}
template <> inline constexpr int binary_format<std::bfloat16_t>::sign_index() {
return 15;
}
template <>
inline constexpr int binary_format<std::bfloat16_t>::largest_power_of_ten() {
return 38;
}
template <>
inline constexpr int binary_format<std::bfloat16_t>::smallest_power_of_ten() {
return -60;
}
template <>
inline constexpr size_t binary_format<std::bfloat16_t>::max_digits() {
return 98;
}
#endif // __STDCPP_BFLOAT16_T__
template <> template <>
inline constexpr uint64_t inline constexpr uint64_t
binary_format<double>::max_mantissa_fast_path(int64_t power) { binary_format<double>::max_mantissa_fast_path(int64_t power) {
@ -898,7 +574,10 @@ binary_format<double>::max_mantissa_fast_path(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)max_mantissa[0], max_mantissa[power]; return (void)max_mantissa[0], max_mantissa[power];
} }
template <>
inline constexpr uint64_t binary_format<float>::max_mantissa_fast_path() {
return uint64_t(2) << mantissa_explicit_bits();
}
template <> template <>
inline constexpr uint64_t inline constexpr uint64_t
binary_format<float>::max_mantissa_fast_path(int64_t power) { binary_format<float>::max_mantissa_fast_path(int64_t power) {
@ -915,7 +594,6 @@ binary_format<double>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
return (void)powers_of_ten[0], powers_of_ten[power]; return (void)powers_of_ten[0], powers_of_ten[power];
} }
template <> template <>
inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) { inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
// Work around clang bug https://godbolt.org/z/zedh7rrhc // Work around clang bug https://godbolt.org/z/zedh7rrhc
@ -925,7 +603,6 @@ inline constexpr float binary_format<float>::exact_power_of_ten(int64_t power) {
template <> inline constexpr int binary_format<double>::largest_power_of_ten() { template <> inline constexpr int binary_format<double>::largest_power_of_ten() {
return 308; return 308;
} }
template <> inline constexpr int binary_format<float>::largest_power_of_ten() { template <> inline constexpr int binary_format<float>::largest_power_of_ten() {
return 38; return 38;
} }
@ -934,7 +611,6 @@ template <>
inline constexpr int binary_format<double>::smallest_power_of_ten() { inline constexpr int binary_format<double>::smallest_power_of_ten() {
return -342; return -342;
} }
template <> inline constexpr int binary_format<float>::smallest_power_of_ten() { template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
return -64; return -64;
} }
@ -942,7 +618,6 @@ template <> inline constexpr int binary_format<float>::smallest_power_of_ten() {
template <> inline constexpr size_t binary_format<double>::max_digits() { template <> inline constexpr size_t binary_format<double>::max_digits() {
return 769; return 769;
} }
template <> inline constexpr size_t binary_format<float>::max_digits() { template <> inline constexpr size_t binary_format<float>::max_digits() {
return 114; return 114;
} }
@ -952,7 +627,6 @@ inline constexpr binary_format<float>::equiv_uint
binary_format<float>::exponent_mask() { binary_format<float>::exponent_mask() {
return 0x7F800000; return 0x7F800000;
} }
template <> template <>
inline constexpr binary_format<double>::equiv_uint inline constexpr binary_format<double>::equiv_uint
binary_format<double>::exponent_mask() { binary_format<double>::exponent_mask() {
@ -964,7 +638,6 @@ inline constexpr binary_format<float>::equiv_uint
binary_format<float>::mantissa_mask() { binary_format<float>::mantissa_mask() {
return 0x007FFFFF; return 0x007FFFFF;
} }
template <> template <>
inline constexpr binary_format<double>::equiv_uint inline constexpr binary_format<double>::equiv_uint
binary_format<double>::mantissa_mask() { binary_format<double>::mantissa_mask() {
@ -976,7 +649,6 @@ inline constexpr binary_format<float>::equiv_uint
binary_format<float>::hidden_bit_mask() { binary_format<float>::hidden_bit_mask() {
return 0x00800000; return 0x00800000;
} }
template <> template <>
inline constexpr binary_format<double>::equiv_uint inline constexpr binary_format<double>::equiv_uint
binary_format<double>::hidden_bit_mask() { binary_format<double>::hidden_bit_mask() {
@ -986,12 +658,11 @@ binary_format<double>::hidden_bit_mask() {
template <typename T> template <typename T>
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void
to_float(bool negative, adjusted_mantissa am, T &value) { to_float(bool negative, adjusted_mantissa am, T &value) {
using equiv_uint = equiv_uint_t<T>; using fastfloat_uint = typename binary_format<T>::equiv_uint;
equiv_uint word = equiv_uint(am.mantissa); fastfloat_uint word = (fastfloat_uint)am.mantissa;
word = equiv_uint(word | equiv_uint(am.power2) word |= fastfloat_uint(am.power2)
<< binary_format<T>::mantissa_explicit_bits()); << binary_format<T>::mantissa_explicit_bits();
word = word |= fastfloat_uint(negative) << binary_format<T>::sign_index();
equiv_uint(word | equiv_uint(negative) << binary_format<T>::sign_index());
#if FASTFLOAT_HAS_BIT_CAST #if FASTFLOAT_HAS_BIT_CAST
value = std::bit_cast<T>(word); value = std::bit_cast<T>(word);
#else #else
@ -999,6 +670,7 @@ to_float(bool negative, adjusted_mantissa am, T &value) {
#endif #endif
} }
#ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
template <typename = void> struct space_lut { template <typename = void> struct space_lut {
static constexpr bool value[] = { 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, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -1020,9 +692,8 @@ template <typename T> constexpr bool space_lut<T>::value[];
#endif #endif
template <typename UC> constexpr bool is_space(UC c) { inline constexpr bool is_space(uint8_t c) { return space_lut<>::value[c]; }
return c < 256 && space_lut<>::value[uint8_t(c)]; #endif
}
template <typename UC> static constexpr uint64_t int_cmp_zeros() { template <typename UC> static constexpr uint64_t int_cmp_zeros() {
static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4),
@ -1033,53 +704,34 @@ template <typename UC> static constexpr uint64_t int_cmp_zeros() {
uint64_t(UC('0')) << 16 | UC('0')) uint64_t(UC('0')) << 16 | UC('0'))
: (uint64_t(UC('0')) << 32 | UC('0')); : (uint64_t(UC('0')) << 32 | UC('0'));
} }
template <typename UC> static constexpr int int_cmp_len() { template <typename UC> static constexpr int int_cmp_len() {
return sizeof(uint64_t) / sizeof(UC); return sizeof(uint64_t) / sizeof(UC);
} }
template <typename UC> static constexpr UC const *str_const_nan() {
template <typename UC> constexpr UC const *str_const_nan(); return nullptr;
}
template <> constexpr char const *str_const_nan<char>() { return "nan"; } template <> constexpr char const *str_const_nan<char>() { return "nan"; }
template <> constexpr wchar_t const *str_const_nan<wchar_t>() { return L"nan"; } template <> constexpr wchar_t const *str_const_nan<wchar_t>() { return L"nan"; }
template <> constexpr char16_t const *str_const_nan<char16_t>() { template <> constexpr char16_t const *str_const_nan<char16_t>() {
return u"nan"; return u"nan";
} }
template <> constexpr char32_t const *str_const_nan<char32_t>() { template <> constexpr char32_t const *str_const_nan<char32_t>() {
return U"nan"; return U"nan";
} }
template <typename UC> static constexpr UC const *str_const_inf() {
#ifdef __cpp_char8_t return nullptr;
template <> constexpr char8_t const *str_const_nan<char8_t>() {
return u8"nan";
} }
#endif
template <typename UC> constexpr UC const *str_const_inf();
template <> constexpr char const *str_const_inf<char>() { return "infinity"; } template <> constexpr char const *str_const_inf<char>() { return "infinity"; }
template <> constexpr wchar_t const *str_const_inf<wchar_t>() { template <> constexpr wchar_t const *str_const_inf<wchar_t>() {
return L"infinity"; return L"infinity";
} }
template <> constexpr char16_t const *str_const_inf<char16_t>() { template <> constexpr char16_t const *str_const_inf<char16_t>() {
return u"infinity"; return u"infinity";
} }
template <> constexpr char32_t const *str_const_inf<char32_t>() { template <> constexpr char32_t const *str_const_inf<char32_t>() {
return U"infinity"; return U"infinity";
} }
#ifdef __cpp_char8_t
template <> constexpr char8_t const *str_const_inf<char8_t>() {
return u8"infinity";
}
#endif
template <typename = void> struct int_luts { template <typename = void> struct int_luts {
static constexpr uint8_t chdigit[] = { static constexpr uint8_t chdigit[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
@ -1132,12 +784,7 @@ template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
template <typename UC> template <typename UC>
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) { fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
// wchar_t and char can be signed, so we need to be careful. return int_luts<>::chdigit[static_cast<unsigned char>(c)];
using UnsignedUC = typename std::make_unsigned<UC>::type;
return int_luts<>::chdigit[static_cast<unsigned char>(
static_cast<UnsignedUC>(c) &
static_cast<UnsignedUC>(
-((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))];
} }
fastfloat_really_inline constexpr size_t max_digits_u64(int base) { fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
@ -1150,107 +797,6 @@ fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) {
return int_luts<>::min_safe_u64[base - 2]; return int_luts<>::min_safe_u64[base - 2];
} }
static_assert(std::is_same<equiv_uint_t<double>, uint64_t>::value,
"equiv_uint should be uint64_t for double");
static_assert(std::numeric_limits<double>::is_iec559,
"double must fulfill the requirements of IEC 559 (IEEE 754)");
static_assert(std::is_same<equiv_uint_t<float>, uint32_t>::value,
"equiv_uint should be uint32_t for float");
static_assert(std::numeric_limits<float>::is_iec559,
"float must fulfill the requirements of IEC 559 (IEEE 754)");
#ifdef __STDCPP_FLOAT64_T__
static_assert(std::is_same<equiv_uint_t<std::float64_t>, uint64_t>::value,
"equiv_uint should be uint64_t for std::float64_t");
static_assert(
std::numeric_limits<std::float64_t>::is_iec559,
"std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)");
template <>
struct binary_format<std::float64_t> : public binary_format<double> {};
#endif // __STDCPP_FLOAT64_T__
#ifdef __STDCPP_FLOAT32_T__
static_assert(std::is_same<equiv_uint_t<std::float32_t>, uint32_t>::value,
"equiv_uint should be uint32_t for std::float32_t");
static_assert(
std::numeric_limits<std::float32_t>::is_iec559,
"std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)");
template <>
struct binary_format<std::float32_t> : public binary_format<float> {};
#endif // __STDCPP_FLOAT32_T__
#ifdef __STDCPP_FLOAT16_T__
static_assert(
std::is_same<binary_format<std::float16_t>::equiv_uint, uint16_t>::value,
"equiv_uint should be uint16_t for std::float16_t");
static_assert(
std::numeric_limits<std::float16_t>::is_iec559,
"std::float16_t must fulfill the requirements of IEC 559 (IEEE 754)");
#endif // __STDCPP_FLOAT16_T__
#ifdef __STDCPP_BFLOAT16_T__
static_assert(
std::is_same<binary_format<std::bfloat16_t>::equiv_uint, uint16_t>::value,
"equiv_uint should be uint16_t for std::bfloat16_t");
static_assert(
std::numeric_limits<std::bfloat16_t>::is_iec559,
"std::bfloat16_t must fulfill the requirements of IEC 559 (IEEE 754)");
#endif // __STDCPP_BFLOAT16_T__
constexpr chars_format operator~(chars_format rhs) noexcept {
using int_type = std::underlying_type<chars_format>::type;
return static_cast<chars_format>(~static_cast<int_type>(rhs));
}
constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept {
using int_type = std::underlying_type<chars_format>::type;
return static_cast<chars_format>(static_cast<int_type>(lhs) &
static_cast<int_type>(rhs));
}
constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept {
using int_type = std::underlying_type<chars_format>::type;
return static_cast<chars_format>(static_cast<int_type>(lhs) |
static_cast<int_type>(rhs));
}
constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept {
using int_type = std::underlying_type<chars_format>::type;
return static_cast<chars_format>(static_cast<int_type>(lhs) ^
static_cast<int_type>(rhs));
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
operator&=(chars_format &lhs, chars_format rhs) noexcept {
return lhs = (lhs & rhs);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
operator|=(chars_format &lhs, chars_format rhs) noexcept {
return lhs = (lhs | rhs);
}
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format &
operator^=(chars_format &lhs, chars_format rhs) noexcept {
return lhs = (lhs ^ rhs);
}
namespace detail {
// adjust for deprecated feature macros
constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
return fmt
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS
| chars_format::allow_leading_plus
#endif
#ifdef FASTFLOAT_SKIP_WHITE_SPACE
| chars_format::skip_white_space
#endif
;
}
} // namespace detail
} // namespace fast_float } // namespace fast_float
#endif #endif

View File

@ -10,7 +10,6 @@
#include <cstring> #include <cstring>
#include <limits> #include <limits>
#include <system_error> #include <system_error>
namespace fast_float { namespace fast_float {
namespace detail { namespace detail {
@ -20,20 +19,24 @@ namespace detail {
* strings a null-free and fixed. * strings a null-free and fixed.
**/ **/
template <typename T, typename UC> template <typename T, typename UC>
from_chars_result_t<UC> from_chars_result_t<UC> FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first,
FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, UC const *last,
T &value, chars_format fmt) noexcept { T &value) noexcept {
from_chars_result_t<UC> answer{}; from_chars_result_t<UC> answer{};
answer.ptr = first; answer.ptr = first;
answer.ec = std::errc(); // be optimistic answer.ec = std::errc(); // be optimistic
// assume first < last, so dereference without checks; bool minusSign = false;
bool const minusSign = (*first == UC('-')); if (*first ==
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here UC('-')) { // assume first < last, so dereference without checks;
if ((*first == UC('-')) || // C++17 20.19.3.(7.1) explicitly forbids '+' here
(uint64_t(fmt & chars_format::allow_leading_plus) && minusSign = true;
(*first == UC('+')))) {
++first; ++first;
} }
#ifdef FASTFLOAT_ALLOWS_LEADING_PLUS // disabled by default
if (*first == UC('+')) {
++first;
}
#endif
if (last - first >= 3) { if (last - first >= 3) {
if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) { if (fastfloat_strncasecmp(first, str_const_nan<UC>(), 3)) {
answer.ptr = (first += 3); answer.ptr = (first += 3);
@ -90,13 +93,13 @@ fastfloat_really_inline bool rounds_to_nearest() noexcept {
// However, it is expected to be much faster than the fegetround() // However, it is expected to be much faster than the fegetround()
// function call. // function call.
// //
// The volatile keyword prevents the compiler from computing the function // The volatile keywoard prevents the compiler from computing the function
// at compile-time. // at compile-time.
// There might be other ways to prevent compile-time optimizations (e.g., // 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 // 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 // small value so that 1 + x should round to 1 would do (after accounting for
// excess precision, as in 387 instructions). // excess precision, as in 387 instructions).
static float volatile fmin = std::numeric_limits<float>::min(); static volatile float fmin = std::numeric_limits<float>::min();
float fmini = fmin; // we copy it so that it gets loaded at most once. float fmini = fmin; // we copy it so that it gets loaded at most once.
// //
// Explanation: // Explanation:
@ -146,7 +149,7 @@ template <typename T> struct from_chars_caller {
} }
}; };
#ifdef __STDCPP_FLOAT32_T__ #if __STDCPP_FLOAT32_T__ == 1
template <> struct from_chars_caller<std::float32_t> { template <> struct from_chars_caller<std::float32_t> {
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
@ -163,7 +166,7 @@ template <> struct from_chars_caller<std::float32_t> {
}; };
#endif #endif
#ifdef __STDCPP_FLOAT64_T__ #if __STDCPP_FLOAT64_T__ == 1
template <> struct from_chars_caller<std::float64_t> { template <> struct from_chars_caller<std::float64_t> {
template <typename UC> template <typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
@ -188,17 +191,32 @@ from_chars(UC const *first, UC const *last, T &value,
parse_options_t<UC>(fmt)); parse_options_t<UC>(fmt));
} }
template <typename T> /**
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool * This function overload takes parsed_number_string_t structure that is created
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative, * and populated either by from_chars_advanced function taking chars range and
T &value) noexcept { * parsing options or other parsing custom function implemented by user.
*/
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
static_assert(is_supported_float_type<T>(),
"only some floating-point types are supported");
static_assert(is_supported_char_type<UC>(),
"only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
// The implementation of the Clinger's fast path is convoluted because // The implementation of the Clinger's fast path is convoluted because
// we want round-to-nearest in all cases, irrespective of the rounding mode // we want round-to-nearest in all cases, irrespective of the rounding mode
// selected on the thread. // selected on the thread.
// We proceed optimistically, assuming that detail::rounds_to_nearest() // We proceed optimistically, assuming that detail::rounds_to_nearest()
// returns true. // returns true.
if (binary_format<T>::min_exponent_fast_path() <= exponent && if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
exponent <= binary_format<T>::max_exponent_fast_path()) { pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
!pns.too_many_digits) {
// Unfortunately, the conventional Clinger's fast path is only possible // Unfortunately, the conventional Clinger's fast path is only possible
// when the system rounds to the nearest float. // when the system rounds to the nearest float.
// //
@ -209,64 +227,41 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
// We have that fegetround() == FE_TONEAREST. // We have that fegetround() == FE_TONEAREST.
// Next is Clinger's fast path. // Next is Clinger's fast path.
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) { if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
value = T(mantissa); value = T(pns.mantissa);
if (exponent < 0) { if (pns.exponent < 0) {
value = value / binary_format<T>::exact_power_of_ten(-exponent); value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
} else { } else {
value = value * binary_format<T>::exact_power_of_ten(exponent); value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
} }
if (is_negative) { if (pns.negative) {
value = -value; value = -value;
} }
return true; return answer;
} }
} else { } else {
// We do not have that fegetround() == FE_TONEAREST. // We do not have that fegetround() == FE_TONEAREST.
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
// proposal // proposal
if (exponent >= 0 && if (pns.exponent >= 0 &&
mantissa <= binary_format<T>::max_mantissa_fast_path(exponent)) { pns.mantissa <=
binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
#if defined(__clang__) || defined(FASTFLOAT_32BIT) #if defined(__clang__) || defined(FASTFLOAT_32BIT)
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
if (mantissa == 0) { if (pns.mantissa == 0) {
value = is_negative ? T(-0.) : T(0.); value = pns.negative ? T(-0.) : T(0.);
return true; return answer;
} }
#endif #endif
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent); value = T(pns.mantissa) *
if (is_negative) { binary_format<T>::exact_power_of_ten(pns.exponent);
if (pns.negative) {
value = -value; value = -value;
} }
return true; return answer;
} }
} }
} }
return false;
}
/**
* This function overload takes parsed_number_string_t structure that is created
* and populated either by from_chars_advanced function taking chars range and
* parsing options or other parsing custom function implemented by user.
*/
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
static_assert(is_supported_float_type<T>::value,
"only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
from_chars_result_t<UC> answer;
answer.ec = std::errc(); // be optimistic
answer.ptr = pns.lastmatch;
if (!pns.too_many_digits &&
clinger_fast_path_impl(pns.mantissa, pns.exponent, pns.negative, value))
return answer;
adjusted_mantissa am = adjusted_mantissa am =
compute_float<binary_format<T>>(pns.exponent, pns.mantissa); 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) {
@ -291,38 +286,34 @@ from_chars_advanced(parsed_number_string_t<UC> &pns, T &value) noexcept {
template <typename T, typename UC> template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_float_advanced(UC const *first, UC const *last, T &value, from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept { parse_options_t<UC> options) noexcept {
static_assert(is_supported_float_type<T>::value, static_assert(is_supported_float_type<T>(),
"only some floating-point types are supported"); "only some floating-point types are supported");
static_assert(is_supported_char_type<UC>::value, static_assert(is_supported_char_type<UC>(),
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
if (uint64_t(fmt & chars_format::skip_white_space)) { #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++; first++;
}
} }
#endif
if (first == last) { if (first == last) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} }
parsed_number_string_t<UC> pns = parsed_number_string_t<UC> pns =
uint64_t(fmt & detail::basic_json_fmt) parse_number_string<UC>(first, last, options);
? parse_number_string<true, UC>(first, last, options)
: parse_number_string<false, UC>(first, last, options);
if (!pns.valid) { if (!pns.valid) {
if (uint64_t(fmt & chars_format::no_infnan)) { if (options.format & chars_format::no_infnan) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} else { } else {
return detail::parse_infnan(first, last, value, fmt); return detail::parse_infnan(first, last, value);
} }
} }
@ -333,153 +324,21 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
template <typename T, typename UC, typename> template <typename T, typename UC, typename>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC> FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars(UC const *first, UC const *last, T &value, int base) noexcept { from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
static_assert(is_supported_char_type<UC>(),
static_assert(is_supported_integer_type<T>::value,
"only integer types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported"); "only char, wchar_t, char16_t and char32_t are supported");
parse_options_t<UC> options;
options.base = base;
return from_chars_advanced(first, last, value, options);
}
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
T value;
if (clinger_fast_path_impl(mantissa, decimal_exponent, false, value))
return value;
adjusted_mantissa am =
compute_float<binary_format<T>>(decimal_exponent, mantissa);
to_float(false, am, value);
return value;
}
template <typename T>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value, T>::type
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
const bool is_negative = mantissa < 0;
const uint64_t m = static_cast<uint64_t>(is_negative ? -mantissa : mantissa);
T value;
if (clinger_fast_path_impl(m, decimal_exponent, is_negative, value))
return value;
adjusted_mantissa am = compute_float<binary_format<T>>(decimal_exponent, m);
to_float(is_negative, am, value);
return value;
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
FASTFLOAT_CONSTEXPR20 inline double
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<double>(mantissa, decimal_exponent);
}
// the following overloads are here to avoid surprising ambiguity for int,
// unsigned, etc.
template <typename T, typename Int>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value &&
std::is_integral<Int>::value &&
!std::is_signed<Int>::value,
T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<uint64_t>(mantissa),
decimal_exponent);
}
template <typename T, typename Int>
FASTFLOAT_CONSTEXPR20
typename std::enable_if<is_supported_float_type<T>::value &&
std::is_integral<Int>::value &&
std::is_signed<Int>::value,
T>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10<T>(static_cast<int64_t>(mantissa),
decimal_exponent);
}
template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && !std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<uint64_t>(mantissa), decimal_exponent);
}
template <typename Int>
FASTFLOAT_CONSTEXPR20 typename std::enable_if<
std::is_integral<Int>::value && std::is_signed<Int>::value, double>::type
integer_times_pow10(Int mantissa, int decimal_exponent) noexcept {
return integer_times_pow10(static_cast<int64_t>(mantissa), decimal_exponent);
}
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_int_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
static_assert(is_supported_integer_type<T>::value,
"only integer types are supported");
static_assert(is_supported_char_type<UC>::value,
"only char, wchar_t, char16_t and char32_t are supported");
chars_format const fmt = detail::adjust_for_feature_macros(options.format);
int const base = options.base;
from_chars_result_t<UC> answer; from_chars_result_t<UC> answer;
if (uint64_t(fmt & chars_format::skip_white_space)) { #ifdef FASTFLOAT_SKIP_WHITE_SPACE // disabled by default
while ((first != last) && fast_float::is_space(*first)) { while ((first != last) && fast_float::is_space(uint8_t(*first))) {
first++; first++;
}
} }
#endif
if (first == last || base < 2 || base > 36) { if (first == last || base < 2 || base > 36) {
answer.ec = std::errc::invalid_argument; answer.ec = std::errc::invalid_argument;
answer.ptr = first; answer.ptr = first;
return answer; return answer;
} }
return parse_int_string(first, last, value, base);
return parse_int_string(first, last, value, options);
}
template <size_t TypeIx> struct from_chars_advanced_caller {
static_assert(TypeIx > 0, "unsupported type");
};
template <> struct from_chars_advanced_caller<1> {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
return from_chars_float_advanced(first, last, value, options);
}
};
template <> struct from_chars_advanced_caller<2> {
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 static from_chars_result_t<UC>
call(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
return from_chars_int_advanced(first, last, value, options);
}
};
template <typename T, typename UC>
FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
from_chars_advanced(UC const *first, UC const *last, T &value,
parse_options_t<UC> options) noexcept {
return from_chars_advanced_caller<
size_t(is_supported_float_type<T>::value) +
2 * size_t(is_supported_integer_type<T>::value)>::call(first, last, value,
options);
} }
} // namespace fast_float } // namespace fast_float

View File

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

@ -1,38 +1,36 @@
import sys
from math import floor from math import floor
def log2(x): def log2(x):
"""returns ceil(log2(x)))""" """returns ceil(log2(x)))"""
y = 0 y = 0
while (1 << y) < x: while((1<<y) < x):
y = y + 1 y = y + 1
return y return y
for q in range(1, 17 + 1): for q in range(1,17+1):
d = 5 ** q d = 5**q
b = 127 + log2(d) b = 127 + log2(d)
t = 2 ** b t = 2** b
c = t // d + 1 c = t//d + 1
assert c < 2 ** 128 assert c < 2**128
assert c >= 2 ** 127 assert c >= 2**127
K = 2 ** 127 K = 2**127
if not (c * K * d <= (K + 1) * t): if(not(c * K * d<=( K + 1) * t)):
print(q) print(q)
top = floor(t / (c * d - t)) top = floor(t/(c * d - t))
sys.exit(-1) sys.exit(-1)
for q in range(18, 344 + 1): for q in range(18, 344+1):
d = 5 ** q d = 5**q
b = 64 + 2 * log2(d) b = 64 + 2*log2(d)
t = 2 ** b t = 2**b
c = t // d + 1 c = t//d + 1
assert c > 2 ** (64 + log2(d)) assert c > 2**(64 +log2(d))
K = 2 ** 64 K = 2**64
if not (c * K * d <= (K + 1) * t): if(not(c * K * d<=( K + 1) * t)):
print(q) print(q)
top = floor(t / (c * d - t)) top = floor(t/(c * d - t))
sys.exit(-1) sys.exit(-1)
print("all good") print("all good")

View File

@ -9,25 +9,25 @@ all_tqs = []
# Appendix B of Number parsing at a gigabyte per second. # Appendix B of Number parsing at a gigabyte per second.
# Software: Practice and Experience 2021;51(8):17001727. # Software: Practice and Experience 2021;51(8):17001727.
for q in range(-342, -27): for q in range(-342, -27):
power5 = 5 ** -q power5 = 5**-q
z = 0 z = 0
while (1 << z) < power5: while (1 << z) < power5:
z += 1 z += 1
b = 2 * z + 2 * 64 b = 2 * z + 2 * 64
c = 2 ** b // power5 + 1 c = 2**b // power5 + 1
while c >= (1 << 128): while c >= (1 << 128):
c //= 2 c //= 2
all_tqs.append(c) all_tqs.append(c)
for q in range(-27, 0): for q in range(-27, 0):
power5 = 5 ** -q power5 = 5**-q
z = 0 z = 0
while (1 << z) < power5: while (1 << z) < power5:
z += 1 z += 1
b = z + 127 b = z + 127
c = 2 ** b // power5 + 1 c = 2**b // power5 + 1
all_tqs.append(c) all_tqs.append(c)
for q in range(0, 308 + 1): for q in range(0, 308 + 1):
power5 = 5 ** q power5 = 5**q
while power5 < (1 << 127): while power5 < (1 << 127):
power5 *= 2 power5 *= 2
while power5 >= (1 << 128): while power5 >= (1 << 128):
@ -44,7 +44,6 @@ def continued_fraction(numer, denom):
numer, denom = denom, rem numer, denom = denom, rem
return cf return cf
# Given a continued fraction [a0; a1, a2, ..., an], returns # Given a continued fraction [a0; a1, a2, ..., an], returns
# all the convergents of that continued fraction # all the convergents of that continued fraction
# as pairs of the form (numer, denom), where numer/denom is # as pairs of the form (numer, denom), where numer/denom is
@ -59,22 +58,17 @@ def convergents(cf):
p_n = a_n * p_n_minus_1 + p_n_minus_2 p_n = a_n * p_n_minus_1 + p_n_minus_2
q_n = a_n * q_n_minus_1 + q_n_minus_2 q_n = a_n * q_n_minus_1 + q_n_minus_2
convergents.append((p_n, q_n)) convergents.append((p_n, q_n))
p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = ( p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = p_n_minus_1, q_n_minus_1, p_n, q_n
p_n_minus_1,
q_n_minus_1,
p_n,
q_n,
)
return convergents return convergents
# Enumerate through all the convergents of T[q] / 2^137 with denominators < 2^64 # Enumerate through all the convergents of T[q] / 2^137 with denominators < 2^64
found_solution = False found_solution = False
for j, tq in enumerate(all_tqs): for j, tq in enumerate(all_tqs):
for _, w in convergents(continued_fraction(tq, 2 ** 137)): for _, w in convergents(continued_fraction(tq, 2**137)):
if w >= 2 ** 64: if w >= 2**64:
break break
if (tq * w) % 2 ** 137 > 2 ** 137 - 2 ** 64: if (tq*w) % 2**137 > 2**137 - 2**64:
print(f"SOLUTION: q={j-342} T[q]={tq} w={w}") print(f"SOLUTION: q={j-342} T[q]={tq} w={w}")
found_solution = True found_solution = True
if not found_solution: if not found_solution:

View File

@ -8,176 +8,116 @@ import subprocess
import io import io
import os import os
import fileinput import fileinput
if sys.version_info < (3, 0): if sys.version_info < (3, 0):
sys.stdout.write("Sorry, requires Python 3.x or better\n") sys.stdout.write("Sorry, requires Python 3.x or better\n")
sys.exit(1) sys.exit(1)
def colored(r, g, b, text): def colored(r, g, b, text):
return f"\033[38;2;{r};{g};{b}m{text} \033[38;2;255;255;255m" return "\033[38;2;{};{};{}m{} \033[38;2;255;255;255m".format(r, g, b, text)
def extractnumbers(s): def extractnumbers(s):
return tuple(map(int, re.findall(r"(\d+)\.(\d+)\.(\d+)", str(s))[0])) return tuple(map(int,re.findall(r"(\d+)\.(\d+)\.(\d+)",str(s))[0]))
def toversionstring(major, minor, rev): def toversionstring(major, minor, rev):
return f"{major}.{minor}.{rev}" return str(major)+"."+str(minor)+"."+str(rev)
def topaddedversionstring(major, minor, rev):
return str(major)+str(minor).zfill(3)+str(rev).zfill(3)
print("Calling git rev-parse --abbrev-ref HEAD") print("Calling git rev-parse --abbrev-ref HEAD")
pipe = subprocess.Popen( pipe = subprocess.Popen(["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
["git", "rev-parse", "--abbrev-ref", "HEAD"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
branchresult = pipe.communicate()[0].decode().strip() branchresult = pipe.communicate()[0].decode().strip()
if branchresult != "main": if(branchresult != "main"):
print( print(colored(255, 0, 0, "We recommend that you release on main, you are on '"+branchresult+"'"))
colored(
255,
0,
0,
f"We recommend that you release on main, you are on '{branchresult}'",
)
)
ret = subprocess.call(["git", "remote", "update"]) ret = subprocess.call(["git", "remote", "update"])
if ret != 0: if(ret != 0):
sys.exit(ret) sys.exit(ret)
print("Calling git log HEAD.. --oneline") print("Calling git log HEAD.. --oneline")
pipe = subprocess.Popen( pipe = subprocess.Popen(["git", "log", "HEAD..", "--oneline"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
["git", "log", "HEAD..", "--oneline"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
uptodateresult = pipe.communicate()[0].decode().strip() uptodateresult = pipe.communicate()[0].decode().strip()
if len(uptodateresult) != 0: if(len(uptodateresult) != 0):
print(uptodateresult) print(uptodateresult)
sys.exit(-1) sys.exit(-1)
pipe = subprocess.Popen( pipe = subprocess.Popen(["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
["git", "rev-parse", "--show-toplevel"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
maindir = pipe.communicate()[0].decode().strip() maindir = pipe.communicate()[0].decode().strip()
scriptlocation = os.path.dirname(os.path.abspath(__file__)) scriptlocation = os.path.dirname(os.path.abspath(__file__))
print(f"repository: {maindir}") print("repository: "+maindir)
pipe = subprocess.Popen( pipe = subprocess.Popen(["git", "describe", "--abbrev=0", "--tags"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
["git", "describe", "--abbrev=0", "--tags"],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
versionresult = pipe.communicate()[0].decode().strip() versionresult = pipe.communicate()[0].decode().strip()
print(f"last version: {versionresult}") print("last version: "+versionresult )
try: try:
currentv = extractnumbers(versionresult) currentv = extractnumbers(versionresult)
except: except:
currentv = [0, 0, 0] currentv = [0,0,0]
if len(sys.argv) != 2: if(len(sys.argv) != 2):
nextv = (currentv[0], currentv[1], currentv[2] + 1) nextv = (currentv[0],currentv[1], currentv[2]+1)
print(f"please specify version number, e.g. {toversionstring(*nextv)}") print ("please specify version number, e.g. "+toversionstring(*nextv))
sys.exit(-1) sys.exit(-1)
try: try:
newversion = extractnumbers(sys.argv[1]) newversion = extractnumbers(sys.argv[1])
print(newversion) print(newversion)
except: except:
print(f"can't parse version number {sys.argv[1]}") print("can't parse version number "+sys.argv[1])
sys.exit(-1) sys.exit(-1)
print("checking that new version is valid") print("checking that new version is valid")
if newversion[0] != currentv[0]: if(newversion[0] != currentv[0]):
assert newversion[0] == currentv[0] + 1 assert newversion[0] == currentv[0] + 1
assert newversion[1] == 0 assert newversion[1] == 0
assert newversion[2] == 0 assert newversion[2] == 0
elif newversion[1] != currentv[1]: elif (newversion[1] != currentv[1]):
assert newversion[1] == currentv[1] + 1 assert newversion[1] == currentv[1] + 1
assert newversion[2] == 0 assert newversion[2] == 0
else: else :
assert newversion[2] == currentv[2] + 1 assert newversion[2] == currentv[2] + 1
atleastminor= (currentv[0] != newversion[0]) or (currentv[1] != newversion[1])
atleastminor = (currentv[0] != newversion[0]) or (currentv[1] != newversion[1])
newmajorversionstring = str(newversion[0]) newmajorversionstring = str(newversion[0])
newminorversionstring = str(newversion[1]) mewminorversionstring = str(newversion[1])
newpatchversionstring = str(newversion[2]) newrevversionstring = str(newversion[2])
newversionstring = f"{newversion[0]}.{newversion[1]}.{newversion[2]}" newversionstring = str(newversion[0]) + "." + str(newversion[1]) + "." + str(newversion[2])
cmakefile = f"{maindir}{os.sep}CMakeLists.txt" cmakefile = maindir + os.sep + "CMakeLists.txt"
for line in fileinput.input(cmakefile, inplace=1, backup=".bak"):
line = re.sub( for line in fileinput.input(cmakefile, inplace=1, backup='.bak'):
r"project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)", line = re.sub(r'project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)','project(fast_float VERSION '+newmajorversionstring+'.'+mewminorversionstring+'.'+newrevversionstring+" LANGUAGES CXX)", line.rstrip())
f"project(fast_float VERSION {newversionstring} LANGUAGES CXX)",
line.rstrip(),
)
print(line) print(line)
print(f"modified {cmakefile}, a backup was made") print("modified "+cmakefile+", a backup was made")
versionfilerel = f"{os.sep}include{os.sep}fast_float{os.sep}float_common.h"
versionfile = f"{maindir}{versionfilerel}"
for line in fileinput.input(versionfile, inplace=1, backup=".bak"): readmefile = maindir + os.sep + "README.md"
line = re.sub(
r"#define FASTFLOAT_VERSION_MAJOR \d+",
f"#define FASTFLOAT_VERSION_MAJOR {newmajorversionstring}", for line in fileinput.input(readmefile, inplace=1, backup='.bak'):
line.rstrip(), line = re.sub(r'https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h','https://github.com/fastfloat/fast_float/releases/download/v'+newmajorversionstring+'.'+mewminorversionstring+'.'+newrevversionstring+'/fast_float.h', line.rstrip())
)
line = re.sub(
r"#define FASTFLOAT_VERSION_MINOR \d+",
f"#define FASTFLOAT_VERSION_MINOR {newminorversionstring}",
line.rstrip(),
)
line = re.sub(
r"#define FASTFLOAT_VERSION_PATCH \d+",
f"#define FASTFLOAT_VERSION_PATCH {newpatchversionstring}",
line.rstrip(),
)
print(line) print(line)
print(f"{versionfile} modified") print("modified "+readmefile+", a backup was made")
readmefile = f"{maindir}{os.sep}README.md"
for line in fileinput.input(readmefile, inplace=1, backup=".bak"):
line = re.sub(
r"https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h",
f"https://github.com/fastfloat/fast_float/releases/download/v{newversionstring}/fast_float.h",
line.rstrip(),
)
line = re.sub(
r"GIT_TAG tags/v(\d+\.\d+\.\d+)",
f"GIT_TAG tags/v{newversionstring}",
line.rstrip(),
)
line = re.sub(
r"GIT_TAG v(\d+\.\d+\.\d+)\)", f"GIT_TAG v{newversionstring})", line.rstrip()
)
print(line)
print(f"modified {readmefile}, a backup was made")
print("running amalgamate.py") print("running amalgamate.py")
with open(f"{maindir}{os.sep}fast_float.h", "w") as outfile: with open(maindir+ os.sep + 'fast_float.h', "w") as outfile:
cp = subprocess.run( cp = subprocess.run(["python3", maindir+ os.sep + "script/amalgamate.py"], stdout=outfile)
[f"python3", f"{maindir}{os.sep}script{os.sep}amalgamate.py"], stdout=outfile
)
if cp.returncode != 0: if(cp.returncode != 0):
print("Failed to run amalgamate") print("Failed to run amalgamate")
else: else:
print("amalgamate.py ran successfully") print("amalgamate.py ran successfully")
print(f"You should upload {maindir}{os.sep}fast_float.h") print("You should upload "+ maindir+ os.sep + 'fast_float.h')
print("Please run the tests before issuing a release. \n")
print("to issue release, enter \n git commit -a && git push && git tag -a v"+toversionstring(*newversion)+" -m \"version "+toversionstring(*newversion)+"\" && git push --tags \n")
print("Please run the tests before issuing a release.\n")
print(
f'to issue release, enter\n git commit -a && git push && git tag -a v{toversionstring(*newversion)} -m "version {toversionstring(*newversion)}" && git push --tags\n'
)

View File

@ -1,15 +1,14 @@
def format(number): def format(number):
upper = number // (1 << 64) upper = number // (1<<64)
lower = number % (1 << 64) lower = number % (1<<64)
print("" + hex(upper) + "," + hex(lower) + ",") print(""+hex(upper)+","+hex(lower)+",")
for q in range(-342,0):
for q in range(-342, 0):
power5 = 5 ** -q power5 = 5 ** -q
z = 0 z = 0
while (1 << z) < power5: while( (1<<z) < power5) :
z += 1 z += 1
if q >= -27: if(q >= -27):
b = z + 127 b = z + 127
c = 2 ** b // power5 + 1 c = 2 ** b // power5 + 1
format(c) format(c)
@ -17,16 +16,16 @@ for q in range(-342, 0):
b = 2 * z + 2 * 64 b = 2 * z + 2 * 64
c = 2 ** b // power5 + 1 c = 2 ** b // power5 + 1
# truncate # truncate
while c >= (1 << 128): while(c >= (1<<128)):
c //= 2 c //= 2
format(c) format(c)
for q in range(0, 308 + 1): for q in range(0,308+1):
power5 = 5 ** q power5 = 5 ** q
# move the most significant bit in position # move the most significant bit in position
while power5 < (1 << 127): while(power5 < (1<<127)):
power5 *= 2 power5 *= 2
# *truncate* # *truncate*
while power5 >= (1 << 128): while(power5 >= (1<<128)):
power5 //= 2 power5 //= 2
format(power5) format(power5)

View File

@ -1,116 +0,0 @@
cc_test(
name = "basictest",
srcs = ["basictest.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "example_test",
srcs = ["example_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "example_comma_test",
srcs = ["example_comma_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "fast_int",
srcs = ["fast_int.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "fixedwidthtest",
srcs = ["fixedwidthtest.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "fortran",
srcs = ["fortran.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "json_fmt",
srcs = ["json_fmt.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "long_test",
srcs = ["long_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "powersoffive_hardround",
srcs = ["powersoffive_hardround.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "rcppfastfloat_test",
srcs = ["rcppfastfloat_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "wide_char_test",
srcs = ["wide_char_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "supported_chars_test",
srcs = ["supported_chars_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)
cc_test(
name = "string_test",
srcs = ["string_test.cpp"],
deps = [
"//:fast_float",
"@doctest//doctest",
],
)

View File

@ -9,9 +9,8 @@ option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON)
if (NOT SYSTEM_DOCTEST) if (NOT SYSTEM_DOCTEST)
FetchContent_Declare(doctest FetchContent_Declare(doctest
GIT_REPOSITORY https://github.com/lemire/doctest.git) GIT_REPOSITORY https://github.com/onqtam/doctest.git
else () GIT_TAG v2.4.11)
find_package(doctest REQUIRED)
endif() endif()
if (FASTFLOAT_SUPPLEMENTAL_TESTS) if (FASTFLOAT_SUPPLEMENTAL_TESTS)
FetchContent_Declare(supplemental_test_files FetchContent_Declare(supplemental_test_files
@ -22,15 +21,24 @@ endif()
# FetchContent_MakeAvailable() was only introduced in 3.14 # FetchContent_MakeAvailable() was only introduced in 3.14
# https://cmake.org/cmake/help/v3.14/release/3.14.html#modules # https://cmake.org/cmake/help/v3.14/release/3.14.html#modules
# FetchContent_MakeAvailable(doctest)
if (NOT SYSTEM_DOCTEST) if (NOT SYSTEM_DOCTEST)
FetchContent_MakeAvailable(doctest) FetchContent_GetProperties(doctest)
if(NOT doctest_POPULATED)
FetchContent_Populate(doctest)
add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR})
endif()
endif() endif()
add_library(supplemental-data INTERFACE) add_library(supplemental-data INTERFACE)
if (FASTFLOAT_SUPPLEMENTAL_TESTS) if (FASTFLOAT_SUPPLEMENTAL_TESTS)
message(STATUS "Supplemental tests enabled. Retrieving test files.") FetchContent_GetProperties(supplemental_test_files)
FetchContent_MakeAvailable(supplemental_test_files) if(NOT supplemental_test_files_POPULATED)
message(STATUS "Supplemental test files retrieved.") message(STATUS "Supplemental tests enabled. Retrieving test files.")
FetchContent_Populate(supplemental_test_files)
message(STATUS "Supplemental test files retrieved.")
add_subdirectory(${supplemental_test_files_SOURCE_DIR} ${supplemental_test_files_BINARY_DIR})
endif()
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")
endif() endif()
@ -51,17 +59,12 @@ function(fast_float_add_cpp_test TEST_NAME)
target_link_libraries(${TEST_NAME} PUBLIC fast_float supplemental-data) target_link_libraries(${TEST_NAME} PUBLIC fast_float supplemental-data)
if (NOT SYSTEM_DOCTEST) if (NOT SYSTEM_DOCTEST)
target_link_libraries(${TEST_NAME} PUBLIC doctest) target_link_libraries(${TEST_NAME} PUBLIC doctest)
else ()
target_link_libraries(${TEST_NAME} PUBLIC doctest::doctest)
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(rcppfastfloat_test)
fast_float_add_cpp_test(wide_char_test)
fast_float_add_cpp_test(supported_chars_test)
fast_float_add_cpp_test(example_test) fast_float_add_cpp_test(example_test)
fast_float_add_cpp_test(example_comma_test) fast_float_add_cpp_test(example_comma_test)
fast_float_add_cpp_test(example_integer_times_pow10)
fast_float_add_cpp_test(basictest) fast_float_add_cpp_test(basictest)
option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF) option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF)
if (FASTFLOAT_CONSTEXPR_TESTS) if (FASTFLOAT_CONSTEXPR_TESTS)
@ -73,7 +76,7 @@ endif()
if (FASTFLOAT_SUPPLEMENTAL_TESTS) if (FASTFLOAT_SUPPLEMENTAL_TESTS)
target_compile_definitions(basictest PRIVATE FASTFLOAT_SUPPLEMENTAL_TESTS) target_compile_definitions(basictest PRIVATE FASTFLOAT_SUPPLEMENTAL_TESTS)
endif() endif()
fast_float_add_cpp_test(p2497)
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)

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get1(char const *input) { double get1(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get10(char const *input) { double get10(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get2(char const *input) { double get2(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get3(char const *input) { double get3(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get4(char const *input) { double get4(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get5(char const *input) { double get5(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get6(char const *input) { double get6(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get7(char const *input) { double get7(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get8(char const *input) { double get8(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get9(char const *input) { double get9(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,15 +1,15 @@
double get1(char const *input); double get1(const char *input);
double get2(char const *input); double get2(const char *input);
double get3(char const *input); double get3(const char *input);
double get4(char const *input); double get4(const char *input);
double get5(char const *input); double get5(const char *input);
double get6(char const *input); double get6(const char *input);
double get7(char const *input); double get7(const char *input);
double get8(char const *input); double get8(const char *input);
double get9(char const *input); double get9(const char *input);
double get10(char const *input); double get10(const char *input);
int main(int arg, char **argv) { int main(int arg, char **argv) {
double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) + double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) +

View File

@ -1,6 +1,6 @@
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
double get(char const *input) { double get(const char *input) {
double result_value; double result_value;
auto result = auto result =
fast_float::from_chars(input, input + strlen(input), result_value); fast_float::from_chars(input, input + strlen(input), result_value);

View File

@ -1,3 +1,2 @@
#include "test.h" #include "test.h"
void foo() {} void foo() {}

View File

@ -1,3 +1,2 @@
#include "test.h" #include "test.h"
int main() { return 0; } int main() { return 0; }

View File

@ -5,7 +5,7 @@
#include <system_error> #include <system_error>
int main() { int main() {
std::string const input = "3,1416 xyz "; const std::string input = "3,1416 xyz ";
double result; double result;
fast_float::parse_options options{fast_float::chars_format::general, ','}; fast_float::parse_options options{fast_float::chars_format::general, ','};
auto answer = fast_float::from_chars_advanced( auto answer = fast_float::from_chars_advanced(

View File

@ -1,36 +0,0 @@
#include "fast_float/fast_float.h"
#include <iostream>
void default_overload() {
const uint64_t W = 12345678901234567;
const int Q = 23;
const double result = fast_float::integer_times_pow10(W, Q);
std::cout.precision(17);
std::cout << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
}
void double_specialization() {
const uint64_t W = 12345678901234567;
const int Q = 23;
const double result = fast_float::integer_times_pow10<double>(W, Q);
std::cout.precision(17);
std::cout << "double: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
}
void float_specialization() {
const uint64_t W = 12345678;
const int Q = 23;
const float result = fast_float::integer_times_pow10<float>(W, Q);
std::cout.precision(9);
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
}
int main() {
default_overload();
double_specialization();
float_specialization();
}

View File

@ -5,7 +5,7 @@
#include <system_error> #include <system_error>
bool many() { bool many() {
std::string const input = "234532.3426362,7869234.9823,324562.645"; const std::string input = "234532.3426362,7869234.9823,324562.645";
double result; double result;
auto answer = auto answer =
fast_float::from_chars(input.data(), input.data() + input.size(), result); fast_float::from_chars(input.data(), input.data() + input.size(), result);
@ -41,10 +41,10 @@ bool many() {
} }
void many_loop() { void many_loop() {
std::string const input = "234532.3426362,7869234.9823,324562.645"; const std::string input = "234532.3426362,7869234.9823,324562.645";
double result; double result;
char const *pointer = input.data(); const char *pointer = input.data();
char const *end_pointer = input.data() + input.size(); const char *end_pointer = input.data() + input.size();
while (pointer < end_pointer) { while (pointer < end_pointer) {
auto answer = fast_float::from_chars(pointer, end_pointer, result); auto answer = fast_float::from_chars(pointer, end_pointer, result);
@ -112,7 +112,7 @@ bool large() {
} }
int main() { int main() {
std::string input = "3.1416 xyz "; const std::string input = "3.1416 xyz ";
double result; double result;
auto answer = auto answer =
fast_float::from_chars(input.data(), input.data() + input.size(), result); fast_float::from_chars(input.data(), input.data() + input.size(), result);
@ -121,20 +121,6 @@ int main() {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
std::cout << "parsed the number " << result << std::endl; std::cout << "parsed the number " << result << std::endl;
#ifdef __STDCPP_FLOAT16_T__
printf("16-bit float\n");
// Parse as 16-bit float
std::float16_t parsed_16{};
input = "10000e-1452";
auto fast_float_r16 = fast_float::from_chars(
input.data(), input.data() + input.size(), parsed_16);
if (fast_float_r16.ec != std::errc() &&
fast_float_r16.ec != std::errc::result_out_of_range) {
std::cerr << "16-bit fast_float parsing failure for: " + input + "\n";
return false;
}
std::cout << "parsed the 16-bit value " << float(parsed_16) << std::endl;
#endif
if (!small()) { if (!small()) {
printf("Bug\n"); printf("Bug\n");
return EXIT_FAILURE; return EXIT_FAILURE;

View File

@ -27,7 +27,7 @@ void allvalues() {
memcpy(&v, &word, sizeof(v)); memcpy(&v, &word, sizeof(v));
{ {
char const *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
float result_value; float result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value); auto result = fast_float::from_chars(buffer, string_end, result_value);
// Starting with version 4.0 for fast_float, we return result_out_of_range // Starting with version 4.0 for fast_float, we return result_out_of_range

View File

@ -58,7 +58,7 @@ void all_32bit_values() {
double v = v32; double v = v32;
{ {
char const *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
std::string s(buffer, size_t(string_end - buffer)); std::string s(buffer, size_t(string_end - buffer));
if (!basic_test_64bit(s, v)) { if (!basic_test_64bit(s, v)) {
return; return;

View File

@ -14,9 +14,8 @@
// gcc. // gcc.
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(char const *start, char **end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -32,8 +31,7 @@ double cygwin_strtod_l(char const *start, char **end) {
*end = const_cast<char *>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char *start, char **end) {
float cygwin_strtof_l(char const *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -57,7 +55,7 @@ template <typename T> char *to_string(T d, char *buffer) {
return buffer + written; return buffer + written;
} }
void strtof_from_string(char const *st, float &d) { void strtof_from_string(const char *st, float &d) {
char *pr = (char *)st; char *pr = (char *)st;
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun) defined(sun) || defined(__sun)
@ -99,7 +97,7 @@ bool allvalues() {
double midv{v1 + (v2 - v1) / 2}; double midv{v1 + (v2 - v1) / 2};
float expected_midv = float(midv); float expected_midv = float(midv);
char const *string_end = to_string(midv, buffer); const char *string_end = to_string(midv, buffer);
float str_answer; float str_answer;
strtof_from_string(buffer, str_answer); strtof_from_string(buffer, str_answer);
@ -166,7 +164,6 @@ inline void Assert(bool Assertion) {
} }
#endif #endif
} }
int main() { int main() {
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun) defined(sun) || defined(__sun)

View File

@ -46,12 +46,12 @@ ignores all zeroes in front of valid number after converted from base
int main() { int main() {
// int basic test // int basic test
std::vector<int> const int_basic_test_expected{0, 10, -40, 1001, 9}; const std::vector<int> int_basic_test_expected{0, 10, -40, 1001, 9};
std::vector<std::string_view> const int_basic_test{"0", "10 ", "-40", const std::vector<std::string_view> int_basic_test{"0", "10 ", "-40",
"1001 with text", "9.999"}; "1001 with text", "9.999"};
for (std::size_t i = 0; i < int_basic_test.size(); ++i) { for (std::size_t i = 0; i < int_basic_test.size(); ++i) {
auto const f = int_basic_test[i]; const auto f = int_basic_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
@ -75,12 +75,12 @@ int main() {
} }
// unsigned basic test // unsigned basic test
std::vector<unsigned> const unsigned_basic_test_expected{0, 10, 1001, 9}; const std::vector<unsigned> unsigned_basic_test_expected{0, 10, 1001, 9};
std::vector<std::string_view> const unsigned_basic_test{ const std::vector<std::string_view> unsigned_basic_test{
"0", "10 ", "1001 with text", "9.999"}; "0", "10 ", "1001 with text", "9.999"};
for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) {
auto const &f = unsigned_basic_test[i]; const auto &f = unsigned_basic_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
@ -96,11 +96,11 @@ int main() {
} }
// int invalid error test // int invalid error test
std::vector<std::string_view> const int_invalid_argument_test{ const std::vector<std::string_view> int_invalid_argument_test{
"text", "text with 1002", "+50", " 50"}; "text", "text with 1002", "+50", " 50"};
for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) { for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) {
auto const &f = int_invalid_argument_test[i]; const auto &f = int_invalid_argument_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
@ -111,11 +111,11 @@ int main() {
} }
// unsigned invalid error test // unsigned invalid error test
std::vector<std::string_view> const unsigned_invalid_argument_test{ const std::vector<std::string_view> unsigned_invalid_argument_test{
"text", "text with 1002", "+50", " 50", "-50"}; "text", "text with 1002", "+50", " 50", "-50"};
for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) {
auto const &f = unsigned_invalid_argument_test[i]; const auto &f = unsigned_invalid_argument_test[i];
unsigned result; unsigned result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::invalid_argument) { if (answer.ec != std::errc::invalid_argument) {
@ -126,11 +126,11 @@ int main() {
} }
// int out of range error test #1 (8 bit) // int out of range error test #1 (8 bit)
std::vector<std::string_view> const int_out_of_range_test_1{ const std::vector<std::string_view> int_out_of_range_test_1{
"2000000000000000000000", "128", "-129"}; "2000000000000000000000", "128", "-129"};
for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) {
auto const &f = int_out_of_range_test_1[i]; const auto &f = int_out_of_range_test_1[i];
int8_t result; int8_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -141,11 +141,11 @@ int main() {
} }
// int out of range error test #2 (16 bit) // int out of range error test #2 (16 bit)
std::vector<std::string_view> const int_out_of_range_test_2{ const std::vector<std::string_view> int_out_of_range_test_2{
"2000000000000000000000", "32768", "-32769"}; "2000000000000000000000", "32768", "-32769"};
for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) {
auto const &f = int_out_of_range_test_2[i]; const auto &f = int_out_of_range_test_2[i];
int16_t result; int16_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -156,11 +156,11 @@ int main() {
} }
// int out of range error test #3 (32 bit) // int out of range error test #3 (32 bit)
std::vector<std::string_view> const int_out_of_range_test_3{ const std::vector<std::string_view> int_out_of_range_test_3{
"2000000000000000000000", "2147483648", "-2147483649"}; "2000000000000000000000", "2147483648", "-2147483649"};
for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) {
auto const &f = int_out_of_range_test_3[i]; const auto &f = int_out_of_range_test_3[i];
int32_t result; int32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -171,11 +171,11 @@ int main() {
} }
// int out of range error test #4 (64 bit) // int out of range error test #4 (64 bit)
std::vector<std::string_view> const int_out_of_range_test_4{ const std::vector<std::string_view> int_out_of_range_test_4{
"2000000000000000000000", "9223372036854775808", "-9223372036854775809"}; "2000000000000000000000", "9223372036854775808", "-9223372036854775809"};
for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) {
auto const &f = int_out_of_range_test_4[i]; const auto &f = int_out_of_range_test_4[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -186,11 +186,11 @@ int main() {
} }
// unsigned out of range error test #1 (8 bit) // unsigned out of range error test #1 (8 bit)
std::vector<std::string_view> const unsigned_out_of_range_test_1{ const std::vector<std::string_view> unsigned_out_of_range_test_1{
"2000000000000000000000", "256"}; "2000000000000000000000", "256"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_1.size(); ++i) {
auto const &f = unsigned_out_of_range_test_1[i]; const auto &f = unsigned_out_of_range_test_1[i];
uint8_t result; uint8_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -201,11 +201,11 @@ int main() {
} }
// unsigned out of range error test #2 (16 bit) // unsigned out of range error test #2 (16 bit)
std::vector<std::string_view> const unsigned_out_of_range_test_2{ const std::vector<std::string_view> unsigned_out_of_range_test_2{
"2000000000000000000000", "65536"}; "2000000000000000000000", "65536"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_2.size(); ++i) {
auto const &f = unsigned_out_of_range_test_2[i]; const auto &f = unsigned_out_of_range_test_2[i];
uint16_t result; uint16_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -216,11 +216,11 @@ int main() {
} }
// unsigned out of range error test #3 (32 bit) // unsigned out of range error test #3 (32 bit)
std::vector<std::string_view> const unsigned_out_of_range_test_3{ const std::vector<std::string_view> unsigned_out_of_range_test_3{
"2000000000000000000000", "4294967296"}; "2000000000000000000000", "4294967296"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_3.size(); ++i) {
auto const &f = unsigned_out_of_range_test_3[i]; const auto &f = unsigned_out_of_range_test_3[i];
uint32_t result; uint32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -231,11 +231,11 @@ int main() {
} }
// unsigned out of range error test #4 (64 bit) // unsigned out of range error test #4 (64 bit)
std::vector<std::string_view> const unsigned_out_of_range_test_4{ const std::vector<std::string_view> unsigned_out_of_range_test_4{
"2000000000000000000000", "18446744073709551616"}; "2000000000000000000000", "18446744073709551616"};
for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_test_4.size(); ++i) {
auto const &f = unsigned_out_of_range_test_4[i]; const auto &f = unsigned_out_of_range_test_4[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc::result_out_of_range) { if (answer.ec != std::errc::result_out_of_range) {
@ -246,10 +246,10 @@ int main() {
} }
// int pointer test #1 (only numbers) // int pointer test #1 (only numbers)
std::vector<std::string_view> const int_pointer_test_1{"0", "010", "-40"}; const std::vector<std::string_view> int_pointer_test_1{"0", "010", "-40"};
for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) { for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) {
auto const &f = int_pointer_test_1[i]; const auto &f = int_pointer_test_1[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
@ -264,9 +264,9 @@ int main() {
} }
// int pointer test #2 (string behind numbers) // int pointer test #2 (string behind numbers)
std::string_view const int_pointer_test_2 = "1001 with text"; const std::string_view int_pointer_test_2 = "1001 with text";
auto const &f2 = int_pointer_test_2; const auto &f2 = int_pointer_test_2;
int result2; int result2;
auto answer2 = auto answer2 =
fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2); fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2);
@ -277,9 +277,9 @@ int main() {
} }
// int pointer test #3 (string with newline behind numbers) // int pointer test #3 (string with newline behind numbers)
std::string_view const int_pointer_test_3 = "1001 with text\n"; const std::string_view int_pointer_test_3 = "1001 with text\n";
auto const &f3 = int_pointer_test_3; const auto &f3 = int_pointer_test_3;
int result3; int result3;
auto answer3 = auto answer3 =
fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3); fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3);
@ -290,9 +290,9 @@ int main() {
} }
// int pointer test #4 (float) // int pointer test #4 (float)
std::string_view const int_pointer_test_4 = "9.999"; const std::string_view int_pointer_test_4 = "9.999";
auto const &f4 = int_pointer_test_4; const auto &f4 = int_pointer_test_4;
int result4; int result4;
auto answer4 = auto answer4 =
fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4); fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4);
@ -303,9 +303,9 @@ int main() {
} }
// int pointer test #5 (invalid int) // int pointer test #5 (invalid int)
std::string_view const int_pointer_test_5 = "+50"; const std::string_view int_pointer_test_5 = "+50";
auto const &f5 = int_pointer_test_5; const auto &f5 = int_pointer_test_5;
int result5; int result5;
auto answer5 = auto answer5 =
fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5); fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5);
@ -316,9 +316,9 @@ int main() {
} }
// unsigned pointer test #2 (string behind numbers) // unsigned pointer test #2 (string behind numbers)
std::string_view const unsigned_pointer_test_1 = "1001 with text"; const std::string_view unsigned_pointer_test_1 = "1001 with text";
auto const &f6 = unsigned_pointer_test_1; const auto &f6 = unsigned_pointer_test_1;
unsigned result6; unsigned result6;
auto answer6 = auto answer6 =
fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6); fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6);
@ -329,9 +329,9 @@ int main() {
} }
// unsigned pointer test #2 (invalid unsigned) // unsigned pointer test #2 (invalid unsigned)
std::string_view const unsigned_pointer_test_2 = "-50"; const std::string_view unsigned_pointer_test_2 = "-50";
auto const &f7 = unsigned_pointer_test_2; const auto &f7 = unsigned_pointer_test_2;
unsigned result7; unsigned result7;
auto answer7 = auto answer7 =
fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7); fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7);
@ -342,12 +342,12 @@ int main() {
} }
// int base 2 test // int base 2 test
std::vector<int> const int_base_2_test_expected{0, 1, 4, 2, -1}; const std::vector<int> int_base_2_test_expected{0, 1, 4, 2, -1};
std::vector<std::string_view> const int_base_2_test{"0", "1", "100", "010", const std::vector<std::string_view> int_base_2_test{"0", "1", "100", "010",
"-1"}; "-1"};
for (std::size_t i = 0; i < int_base_2_test.size(); ++i) { for (std::size_t i = 0; i < int_base_2_test.size(); ++i) {
auto const f = int_base_2_test[i]; const auto f = int_base_2_test[i];
int result; int result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
@ -363,12 +363,12 @@ int main() {
} }
// unsigned base 2 test // unsigned base 2 test
std::vector<unsigned> const unsigned_base_2_test_expected{0, 1, 4, 2}; const std::vector<unsigned> unsigned_base_2_test_expected{0, 1, 4, 2};
std::vector<std::string_view> const unsigned_base_2_test{"0", "1", "100", const std::vector<std::string_view> unsigned_base_2_test{"0", "1", "100",
"010"}; "010"};
for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) {
auto const &f = unsigned_base_2_test[i]; const auto &f = unsigned_base_2_test[i];
unsigned result; unsigned result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
@ -384,11 +384,11 @@ int main() {
} }
// int invalid error base 2 test // int invalid error base 2 test
std::vector<std::string_view> const int_invalid_argument_base_2_test{"2", "A", const std::vector<std::string_view> int_invalid_argument_base_2_test{"2", "A",
"-2"}; "-2"};
for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) { for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) {
auto const &f = int_invalid_argument_base_2_test[i]; const auto &f = int_invalid_argument_base_2_test[i];
int result; int result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
@ -400,12 +400,12 @@ int main() {
} }
// unsigned invalid error base 2 test // unsigned invalid error base 2 test
std::vector<std::string_view> const unsigned_invalid_argument_base_2_test{ const std::vector<std::string_view> unsigned_invalid_argument_base_2_test{
"2", "A", "-1", "-2"}; "2", "A", "-1", "-2"};
for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size(); for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size();
++i) { ++i) {
auto const &f = unsigned_invalid_argument_base_2_test[i]; const auto &f = unsigned_invalid_argument_base_2_test[i];
unsigned result; unsigned result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); fast_float::from_chars(f.data(), f.data() + f.size(), result, 2);
@ -417,12 +417,12 @@ int main() {
} }
// octal test // octal test
std::vector<int> const base_octal_test_expected{0, 1, 7, 8, 9}; const std::vector<int> base_octal_test_expected{0, 1, 7, 8, 9};
std::vector<std::string_view> const base_octal_test{"0", "1", "07", "010", const std::vector<std::string_view> base_octal_test{"0", "1", "07", "010",
"0011"}; "0011"};
for (std::size_t i = 0; i < base_octal_test.size(); ++i) { for (std::size_t i = 0; i < base_octal_test.size(); ++i) {
auto const &f = base_octal_test[i]; const auto &f = base_octal_test[i];
int result; int result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 8); fast_float::from_chars(f.data(), f.data() + f.size(), result, 8);
@ -438,12 +438,12 @@ int main() {
} }
// hex test // hex test
std::vector<int> const base_hex_test_expected{0, 1, 15, 31, 0, 16}; const std::vector<int> base_hex_test_expected{0, 1, 15, 31, 0, 16};
std::vector<std::string_view> const base_hex_test{"0", "1", "F", const std::vector<std::string_view> base_hex_test{"0", "1", "F",
"01f", "0x11", "10X11"}; "01f", "0x11", "10X11"};
for (std::size_t i = 0; i < base_hex_test.size(); ++i) { for (std::size_t i = 0; i < base_hex_test.size(); ++i) {
auto const &f = base_hex_test[i]; const auto &f = base_hex_test[i];
int result; int result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 16); fast_float::from_chars(f.data(), f.data() + f.size(), result, 16);
@ -459,11 +459,11 @@ int main() {
} }
// invalid base test #1 (-1) // invalid base test #1 (-1)
std::vector<std::string_view> const invalid_base_test_1{"0", "1", "-1", "F", const std::vector<std::string_view> invalid_base_test_1{"0", "1", "-1", "F",
"10Z"}; "10Z"};
for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) { for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) {
auto const &f = invalid_base_test_1[i]; const auto &f = invalid_base_test_1[i];
int result; int result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, -1); fast_float::from_chars(f.data(), f.data() + f.size(), result, -1);
@ -475,11 +475,11 @@ int main() {
} }
// invalid base test #2 (37) // invalid base test #2 (37)
std::vector<std::string_view> const invalid_base_test_2{"0", "1", "F", "Z", const std::vector<std::string_view> invalid_base_test_2{"0", "1", "F", "Z",
"10Z"}; "10Z"};
for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) { for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) {
auto const &f = invalid_base_test_2[i]; const auto &f = invalid_base_test_2[i];
int result; int result;
auto answer = auto answer =
fast_float::from_chars(f.data(), f.data() + f.size(), result, 37); fast_float::from_chars(f.data(), f.data() + f.size(), result, 37);
@ -491,7 +491,7 @@ int main() {
} }
// int out of range error base test (64 bit) // int out of range error base test (64 bit)
std::vector<std::string_view> const int_out_of_range_base_test{ const std::vector<std::string_view> int_out_of_range_base_test{
"1000000000000000000000000000000000000000000000000000000000000000", "1000000000000000000000000000000000000000000000000000000000000000",
"-1000000000000000000000000000000000000000000000000000000000000001", "-1000000000000000000000000000000000000000000000000000000000000001",
"2021110011022210012102010021220101220222", "2021110011022210012102010021220101220222",
@ -564,7 +564,7 @@ int main() {
"-1Y2P0IJ32E8E9"}; "-1Y2P0IJ32E8E9"};
for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) { for (std::size_t i = 0; i < int_out_of_range_base_test.size(); ++i) {
auto const &f = int_out_of_range_base_test[i]; const auto &f = int_out_of_range_base_test[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
int(2 + (i / 2))); int(2 + (i / 2)));
@ -576,7 +576,7 @@ int main() {
} }
// unsigned out of range error base test (64 bit) // unsigned out of range error base test (64 bit)
std::vector<std::string_view> const unsigned_out_of_range_base_test{ const std::vector<std::string_view> unsigned_out_of_range_base_test{
"10000000000000000000000000000000000000000000000000000000000000000", "10000000000000000000000000000000000000000000000000000000000000000",
"11112220022122120101211020120210210211221", "11112220022122120101211020120210210211221",
"100000000000000000000000000000000", "100000000000000000000000000000000",
@ -614,7 +614,7 @@ int main() {
"3W5E11264SGSG"}; "3W5E11264SGSG"};
int base_unsigned = 2; int base_unsigned = 2;
for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_out_of_range_base_test.size(); ++i) {
auto const &f = unsigned_out_of_range_base_test[i]; const auto &f = unsigned_out_of_range_base_test[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
base_unsigned); base_unsigned);
@ -627,7 +627,7 @@ int main() {
} }
// just within range base test (64 bit) // just within range base test (64 bit)
std::vector<std::string_view> const int_within_range_base_test{ const std::vector<std::string_view> int_within_range_base_test{
"111111111111111111111111111111111111111111111111111111111111111", "111111111111111111111111111111111111111111111111111111111111111",
"-1000000000000000000000000000000000000000000000000000000000000000", "-1000000000000000000000000000000000000000000000000000000000000000",
"2021110011022210012102010021220101220221", "2021110011022210012102010021220101220221",
@ -700,7 +700,7 @@ int main() {
"-1Y2P0IJ32E8E8"}; "-1Y2P0IJ32E8E8"};
for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) { for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) {
auto const &f = int_within_range_base_test[i]; const auto &f = int_within_range_base_test[i];
int64_t result; int64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
int(2 + (i / 2))); int(2 + (i / 2)));
@ -712,7 +712,7 @@ int main() {
} }
// unsigned within range base test (64 bit) // unsigned within range base test (64 bit)
std::vector<std::string_view> const unsigned_within_range_base_test{ const std::vector<std::string_view> unsigned_within_range_base_test{
"1111111111111111111111111111111111111111111111111111111111111111", "1111111111111111111111111111111111111111111111111111111111111111",
"11112220022122120101211020120210210211220", "11112220022122120101211020120210210211220",
"33333333333333333333333333333333", "33333333333333333333333333333333",
@ -750,7 +750,7 @@ int main() {
"3W5E11264SGSF"}; "3W5E11264SGSF"};
int base_unsigned2 = 2; int base_unsigned2 = 2;
for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) { for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) {
auto const &f = unsigned_within_range_base_test[i]; const auto &f = unsigned_within_range_base_test[i];
uint64_t result; uint64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
base_unsigned2); base_unsigned2);
@ -764,7 +764,7 @@ int main() {
} }
// int leading zeros test // int leading zeros test
std::vector<std::string_view> const int_leading_zeros_test{ const std::vector<std::string_view> int_leading_zeros_test{
"000000000000000000000000000000000000000000000000000000000000000000000011" "000000000000000000000000000000000000000000000000000000000000000000000011"
"11110111", "11110111",
"000000000000000000000000000000000000000000000000001101121", "000000000000000000000000000000000000000000000000001101121",
@ -803,7 +803,7 @@ int main() {
"00000000000000000000S7"}; "00000000000000000000S7"};
for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) { for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) {
auto const &f = int_leading_zeros_test[i]; const auto &f = int_leading_zeros_test[i];
int result; int result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result,
int(i + 2)); int(i + 2));
@ -831,275 +831,6 @@ int main() {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
// dont parse UTF-16 code units of emojis as int if low byte is ascii digit
{
const std::u16string emojis[] = {
u"", u"", u"", u"☸️", u"", u"☹️", u"", u"✳️",
u"", u"✴️", u"", u"⤴️", u"", u"⤵️", u"", u"〰️",
};
bool failed = false;
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
for (size_t i = 0; i < array_size; i++) {
auto e = emojis[i];
int foo;
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
if (answer.ec == std::errc()) {
failed = true;
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
<< "." << std::endl;
}
}
if (failed) {
return EXIT_FAILURE;
}
}
// dont parse UTF-32 code points of emojis as int if low byte is ascii digit
{
const std::u32string emojis[] = {
U"",
U"",
U"",
U"☸️",
U"",
U"☹️",
U"",
U"✳️",
U"",
U"✴️",
U"",
U"⤴️",
U"",
U"⤵️",
U"",
U"〰️",
U"🈲",
U"🈳",
U"🈴",
U"🈵",
U"🈶",
U"🈷",
U"🈷️",
U"🈸",
U"🈹",
U"🌰",
U"🌱",
U"🌲",
U"🌳",
U"🌴",
U"🌵",
U"🌶",
U"🌶️",
U"🌷",
U"🌸",
U"🌹",
U"🐰",
U"🐱",
U"🐲",
U"🐳",
U"🐴",
U"🐵",
U"🐶",
U"🐷",
U"🐸",
U"🐹",
U"🔰",
U"🔱",
U"🔲",
U"🔳",
U"🔴",
U"🔵",
U"🔶",
U"🔷",
U"🔸",
U"🔹",
U"😰",
U"😱",
U"😲",
U"😳",
U"😴",
U"😵",
U"😵‍💫",
U"😶",
U"😶‍🌫",
U"😶‍🌫️",
U"😷",
U"😸",
U"😹",
U"🤰",
U"🤰🏻",
U"🤰🏼",
U"🤰🏽",
U"🤰🏾",
U"🤰🏿",
U"🤱",
U"🤱🏻",
U"🤱🏼",
U"🤱🏽",
U"🤱🏾",
U"🤱🏿",
U"🤲",
U"🤲🏻",
U"🤲🏼",
U"🤲🏽",
U"🤲🏾",
U"🤲🏿",
U"🤳",
U"🤳🏻",
U"🤳🏼",
U"🤳🏽",
U"🤳🏾",
U"🤳🏿",
U"🤴",
U"🤴🏻",
U"🤴🏼",
U"🤴🏽",
U"🤴🏾",
U"🤴🏿",
U"🤵",
U"🤵‍♀",
U"🤵‍♀️",
U"🤵‍♂",
U"🤵‍♂️",
U"🤵🏻",
U"🤵🏻‍♀",
U"🤵🏻‍♀️",
U"🤵🏻‍♂",
U"🤵🏻‍♂️",
U"🤵🏼",
U"🤵🏼‍♀",
U"🤵🏼‍♀️",
U"🤵🏼‍♂",
U"🤵🏼‍♂️",
U"🤵🏽",
U"🤵🏽‍♀",
U"🤵🏽‍♀️",
U"🤵🏽‍♂",
U"🤵🏽‍♂️",
U"🤵🏾",
U"🤵🏾‍♀",
U"🤵🏾‍♀️",
U"🤵🏾‍♂",
U"🤵🏾‍♂️",
U"🤵🏿",
U"🤵🏿‍♀",
U"🤵🏿‍♀️",
U"🤵🏿‍♂",
U"🤵🏿‍♂️",
U"🤶",
U"🤶🏻",
U"🤶🏼",
U"🤶🏽",
U"🤶🏾",
U"🤶🏿",
U"🤷",
U"🤷‍♀",
U"🤷‍♀️",
U"🤷‍♂",
U"🤷‍♂️",
U"🤷🏻",
U"🤷🏻‍♀",
U"🤷🏻‍♀️",
U"🤷🏻‍♂",
U"🤷🏻‍♂️",
U"🤷🏼",
U"🤷🏼‍♀",
U"🤷🏼‍♀️",
U"🤷🏼‍♂",
U"🤷🏼‍♂️",
U"🤷🏽",
U"🤷🏽‍♀",
U"🤷🏽‍♀️",
U"🤷🏽‍♂",
U"🤷🏽‍♂️",
U"🤷🏾",
U"🤷🏾‍♀",
U"🤷🏾‍♀️",
U"🤷🏾‍♂",
U"🤷🏾‍♂️",
U"🤷🏿",
U"🤷🏿‍♀",
U"🤷🏿‍♀️",
U"🤷🏿‍♂",
U"🤷🏿‍♂️",
U"🤸",
U"🤸‍♀",
U"🤸‍♀️",
U"🤸‍♂",
U"🤸‍♂️",
U"🤸🏻",
U"🤸🏻‍♀",
U"🤸🏻‍♀️",
U"🤸🏻‍♂",
U"🤸🏻‍♂️",
U"🤸🏼",
U"🤸🏼‍♀",
U"🤸🏼‍♀️",
U"🤸🏼‍♂",
U"🤸🏼‍♂️",
U"🤸🏽",
U"🤸🏽‍♀",
U"🤸🏽‍♀️",
U"🤸🏽‍♂",
U"🤸🏽‍♂️",
U"🤸🏾",
U"🤸🏾‍♀",
U"🤸🏾‍♀️",
U"🤸🏾‍♂",
U"🤸🏾‍♂️",
U"🤸🏿",
U"🤸🏿‍♀",
U"🤸🏿‍♀️",
U"🤸🏿‍♂",
U"🤸🏿‍♂️",
U"🤹",
U"🤹‍♀",
U"🤹‍♀️",
U"🤹‍♂",
U"🤹‍♂️",
U"🤹🏻",
U"🤹🏻‍♀",
U"🤹🏻‍♀️",
U"🤹🏻‍♂",
U"🤹🏻‍♂️",
U"🤹🏼",
U"🤹🏼‍♀",
U"🤹🏼‍♀️",
U"🤹🏼‍♂",
U"🤹🏼‍♂️",
U"🤹🏽",
U"🤹🏽‍♀",
U"🤹🏽‍♀️",
U"🤹🏽‍♂",
U"🤹🏽‍♂️",
U"🤹🏾",
U"🤹🏾‍♀",
U"🤹🏾‍♀️",
U"🤹🏾‍♂",
U"🤹🏾‍♂️",
U"🤹🏿",
U"🤹🏿‍♀",
U"🤹🏿‍♀️",
U"🤹🏿‍♂",
U"🤹🏿‍♂️",
};
bool failed = false;
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
for (size_t i = 0; i < array_size; i++) {
auto e = emojis[i];
int foo;
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
if (answer.ec == std::errc()) {
failed = true;
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
<< "." << std::endl;
}
}
if (failed) {
return EXIT_FAILURE;
}
}
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -4,22 +4,19 @@
#include <cstring> #include <cstring>
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
#include <cstdint> #include <cstdint>
#if __cplusplus >= 202300L
#include <stdfloat> #include <stdfloat>
#endif
int main() { int main() {
// Write some testcases for the parsing of floating point numbers in the // Write some testcases for the parsing of floating point numbers in the
// float32_t type. We use the from_chars function defined in this library. // float32_t type. We use the from_chars function defined in this library.
#ifdef __STDCPP_FLOAT32_T__ #if __STDCPP_FLOAT32_T__
std::vector<std::float32_t> const float32_test_expected{ const std::vector<std::float32_t> float32_test_expected{
123.456f, -78.9f, 0.0001f, 3.40282e+038f}; 123.456f, -78.9f, 0.0001f, 3.40282e+038f};
std::vector<std::string_view> const float32_test{"123.456", "-78.9", "0.0001", const std::vector<std::string_view> float32_test{"123.456", "-78.9", "0.0001",
"3.40282e+038"}; "3.40282e+038"};
std::cout << "runing float32 test" << std::endl; std::cout << "runing float32 test" << std::endl;
for (std::size_t i = 0; i < float32_test.size(); ++i) { for (std::size_t i = 0; i < float32_test.size(); ++i) {
auto const &f = float32_test[i]; const auto &f = float32_test[i];
std::float32_t result; std::float32_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);
@ -37,16 +34,16 @@ int main() {
std::cout << "No std::float32_t type available." << std::endl; std::cout << "No std::float32_t type available." << std::endl;
#endif #endif
#ifdef __STDCPP_FLOAT64_T__ #if __STDCPP_FLOAT64_T__
// Test cases for std::float64_t // Test cases for std::float64_t
std::vector<std::float64_t> const float64_test_expected{ const std::vector<std::float64_t> float64_test_expected{
1.23e4, -5.67e-8, 1.7976931348623157e+308, -1.7976931348623157e+308}; 1.23e4, -5.67e-8, 1.7976931348623157e+308, -1.7976931348623157e+308};
std::vector<std::string_view> const float64_test{"1.23e4", "-5.67e-8", const std::vector<std::string_view> float64_test{"1.23e4", "-5.67e-8",
"1.7976931348623157e+308", "1.7976931348623157e+308",
"-1.7976931348623157e+308"}; "-1.7976931348623157e+308"};
std::cout << "runing float64 test" << std::endl; std::cout << "runing float64 test" << std::endl;
for (std::size_t i = 0; i < float64_test.size(); ++i) { for (std::size_t i = 0; i < float64_test.size(); ++i) {
auto const &f = float64_test[i]; const auto &f = float64_test[i];
std::float64_t result; std::float64_t result;
auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result);

View File

@ -4,14 +4,15 @@
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#define FASTFLOAT_ALLOWS_LEADING_PLUS
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
int main_readme() { int main_readme() {
std::string const input = "1d+4"; const std::string input = "1d+4";
double result; double result;
fast_float::parse_options options{ fast_float::parse_options options{fast_float::chars_format::fortran};
fast_float::chars_format::fortran |
fast_float::chars_format::allow_leading_plus};
auto answer = fast_float::from_chars_advanced( auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options); input.data(), input.data() + input.size(), result, options);
if ((answer.ec != std::errc()) || ((result != 10000))) { if ((answer.ec != std::errc()) || ((result != 10000))) {
@ -23,17 +24,15 @@ int main_readme() {
} }
int main() { int main() {
std::vector<double> const expected{10000, 1000, 100, 10, 1, const std::vector<double> expected{10000, 1000, 100, 10, 1,
.1, .01, .001, .0001}; .1, .01, .001, .0001};
std::vector<std::string> const fmt1{"1+4", "1+3", "1+2", "1+1", "1+0", const std::vector<std::string> fmt1{"1+4", "1+3", "1+2", "1+1", "1+0",
"1-1", "1-2", "1-3", "1-4"}; "1-1", "1-2", "1-3", "1-4"};
std::vector<std::string> const fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0", const std::vector<std::string> fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0",
"1d-1", "1d-2", "1d-3", "1d-4"}; "1d-1", "1d-2", "1d-3", "1d-4"};
std::vector<std::string> const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0", const std::vector<std::string> fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0",
"+1-1", "+1-2", "+1-3", "+1-4"}; "+1-1", "+1-2", "+1-3", "+1-4"};
fast_float::parse_options const options{ const fast_float::parse_options options{fast_float::chars_format::fortran};
fast_float::chars_format::fortran |
fast_float::chars_format::allow_leading_plus};
for (auto const &f : fmt1) { for (auto const &f : fmt1) {
auto d{std::distance(&fmt1[0], &f)}; auto d{std::distance(&fmt1[0], &f)};

View File

@ -17,10 +17,10 @@ file(WRITE main.cpp "
#include <iostream> #include <iostream>
int main() { int main() {
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()) { std::cerr << \"parsing failure\\n\"; return EXIT_FAILURE; } if(answer.ec != std::errc()) { std::cerr << \"parsing failure\\n\"; return EXIT_FAILURE; }
std::cout << \"parsed the number \" << result << std::endl; std::cout << \"parsed the number \" << result << std::endl;
return EXIT_SUCCESS; return EXIT_SUCCESS;
}") }")

View File

@ -2,14 +2,16 @@
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
// test that this option is ignored
#define FASTFLOAT_ALLOWS_LEADING_PLUS
#include "fast_float/fast_float.h" #include "fast_float/fast_float.h"
int main_readme() { int main_readme() {
std::string const input = "+.1"; // not valid const std::string input = "+.1"; // not valid
double result; double result;
fast_float::parse_options options{ fast_float::parse_options options{fast_float::chars_format::json};
fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced( auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options); input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) { if (answer.ec == std::errc()) {
@ -20,11 +22,9 @@ int main_readme() {
} }
int main_readme2() { int main_readme2() {
std::string const input = "inf"; // not valid in JSON const std::string input = "inf"; // not valid in JSON
double result; double result;
fast_float::parse_options options{ fast_float::parse_options options{fast_float::chars_format::json};
fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced( auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options); input.data(), input.data() + input.size(), result, options);
if (answer.ec == std::errc()) { if (answer.ec == std::errc()) {
@ -35,12 +35,10 @@ int main_readme2() {
} }
int main_readme3() { int main_readme3() {
std::string const input = const std::string input =
"inf"; // not valid in JSON but we allow it with json_or_infnan "inf"; // not valid in JSON but we allow it with json_or_infnan
double result; double result;
fast_float::parse_options options{ fast_float::parse_options options{fast_float::chars_format::json_or_infnan};
fast_float::chars_format::json_or_infnan |
fast_float::chars_format::allow_leading_plus}; // should be ignored
auto answer = fast_float::from_chars_advanced( auto answer = fast_float::from_chars_advanced(
input.data(), input.data() + input.size(), result, options); input.data(), input.data() + input.size(), result, options);
if (answer.ec != std::errc() || (!std::isinf(result))) { if (answer.ec != std::errc() || (!std::isinf(result))) {
@ -54,7 +52,6 @@ struct ExpectedResult {
double value; double value;
std::string junk_chars; std::string junk_chars;
}; };
struct AcceptedValue { struct AcceptedValue {
std::string input; std::string input;
ExpectedResult expected; ExpectedResult expected;
@ -64,14 +61,13 @@ struct RejectReason {
fast_float::parse_error error; fast_float::parse_error error;
intptr_t location_offset; intptr_t location_offset;
}; };
struct RejectedValue { struct RejectedValue {
std::string input; std::string input;
RejectReason reason; RejectReason reason;
}; };
int main() { int main() {
std::vector<AcceptedValue> const accept{ const std::vector<AcceptedValue> accept{
{"-0.2", {-0.2, ""}}, {"-0.2", {-0.2, ""}},
{"0.02", {0.02, ""}}, {"0.02", {0.02, ""}},
{"0.002", {0.002, ""}}, {"0.002", {0.002, ""}},
@ -80,7 +76,7 @@ int main() {
{"1e", {1., "e"}}, {"1e", {1., "e"}},
{"1e+", {1., "e+"}}, {"1e+", {1., "e+"}},
{"inf", {std::numeric_limits<double>::infinity(), ""}}}; {"inf", {std::numeric_limits<double>::infinity(), ""}}};
std::vector<RejectedValue> const reject{ const std::vector<RejectedValue> reject{
{"-.2", {fast_float::parse_error::missing_integer_after_sign, 1}}, {"-.2", {fast_float::parse_error::missing_integer_after_sign, 1}},
{"00.02", {fast_float::parse_error::leading_zeros_in_integer_part, 0}}, {"00.02", {fast_float::parse_error::leading_zeros_in_integer_part, 0}},
{"0.e+1", {fast_float::parse_error::no_digits_in_fractional_part, 2}}, {"0.e+1", {fast_float::parse_error::no_digits_in_fractional_part, 2}},
@ -94,8 +90,8 @@ int main() {
{"nan(snan)", {fast_float::parse_error::no_digits_in_integer_part, 0}}}; {"nan(snan)", {fast_float::parse_error::no_digits_in_integer_part, 0}}};
for (std::size_t i = 0; i < accept.size(); ++i) { for (std::size_t i = 0; i < accept.size(); ++i) {
auto const &s = accept[i].input; const auto &s = accept[i].input;
auto const &expected = accept[i].expected; const auto &expected = accept[i].expected;
double result; double result;
auto answer = auto answer =
fast_float::from_chars(s.data(), s.data() + s.size(), result, fast_float::from_chars(s.data(), s.data() + s.size(), result,
@ -118,7 +114,7 @@ int main() {
} }
for (std::size_t i = 0; i < reject.size(); ++i) { for (std::size_t i = 0; i < reject.size(); ++i) {
auto const &s = reject[i].input; const auto &s = reject[i].input;
double result; double result;
auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result,
fast_float::chars_format::json); fast_float::chars_format::json);
@ -129,13 +125,11 @@ int main() {
} }
for (std::size_t i = 0; i < reject.size(); ++i) { for (std::size_t i = 0; i < reject.size(); ++i) {
auto const &f = reject[i].input; const auto &f = reject[i].input;
auto const &expected_reason = reject[i].reason; const auto &expected_reason = reject[i].reason;
auto answer = fast_float::parse_number_string<true>( auto answer = fast_float::parse_number_string(
f.data(), f.data() + f.size(), f.data(), f.data() + f.size(),
fast_float::parse_options( fast_float::parse_options(fast_float::chars_format::json));
fast_float::chars_format::json |
fast_float::chars_format::allow_leading_plus)); // should be ignored
if (answer.valid) { if (answer.valid) {
std::cerr << "json parse accepted invalid json " << f << std::endl; std::cerr << "json parse accepted invalid json " << f << std::endl;
return EXIT_FAILURE; return EXIT_FAILURE;
@ -160,11 +154,6 @@ int main() {
if (main_readme2() != EXIT_SUCCESS) { if (main_readme2() != EXIT_SUCCESS) {
return EXIT_FAILURE; return EXIT_FAILURE;
} }
#ifndef __FAST_MATH__
if (main_readme3() != EXIT_SUCCESS) {
return EXIT_FAILURE;
}
#endif
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -25,7 +25,7 @@ void allvalues() {
memcpy(&v, &word, sizeof(v)); memcpy(&v, &word, sizeof(v));
{ {
char const *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
float result_value; float result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value); auto result = fast_float::from_chars(buffer, string_end, result_value);
// Starting with version 4.0 for fast_float, we return result_out_of_range // Starting with version 4.0 for fast_float, we return result_out_of_range

View File

@ -24,7 +24,7 @@ void all_32bit_values() {
double v = v32; double v = v32;
{ {
char const *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
double result_value; double result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value); auto result = fast_float::from_chars(buffer, string_end, result_value);
// Starting with version 4.0 for fast_float, we return result_out_of_range // Starting with version 4.0 for fast_float, we return result_out_of_range

View File

@ -53,7 +53,7 @@ void random_values(size_t N) {
double v; double v;
memcpy(&v, &word, sizeof(v)); memcpy(&v, &word, sizeof(v));
{ {
char const *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
double result_value; double result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value); auto result = fast_float::from_chars(buffer, string_end, result_value);
// Starting with version 4.0 for fast_float, we return result_out_of_range // Starting with version 4.0 for fast_float, we return result_out_of_range

View File

@ -19,8 +19,8 @@ template <typename T> bool test() {
"2.71828182845904523536028747135266249775724709369995"; "2.71828182845904523536028747135266249775724709369995";
std::vector<T> answers = {T(0.15625), T(3.141592653589793), std::vector<T> answers = {T(0.15625), T(3.141592653589793),
T(2.718281828459045)}; T(2.718281828459045)};
char const *begin = input.data(); const char *begin = input.data();
char const *end = input.data() + input.size(); const char *end = input.data() + input.size();
for (size_t i = 0; i < answers.size(); i++) { for (size_t i = 0; i < answers.size(); i++) {
T result_value; T result_value;
while ((begin < end) && (std::isspace(*begin))) { while ((begin < end) && (std::isspace(*begin))) {

View File

@ -1,16 +0,0 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
int main() {
std::string input = "3.1416 xyz ";
double result;
if (auto answer = fast_float::from_chars(
input.data(), input.data() + input.size(), result)) {
std::cout << "parsed the number " << result << std::endl;
return EXIT_SUCCESS;
}
std::cerr << "failed to parse " << result << std::endl;
return EXIT_FAILURE;
}

View File

@ -15,9 +15,8 @@
// always use this fallback because we cannot rely on it behaving as normal // always use this fallback because we cannot rely on it behaving as normal
// gcc. // gcc.
#include <locale> #include <locale>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(char const *start, char **end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -33,8 +32,7 @@ double cygwin_strtod_l(char const *start, char **end) {
*end = const_cast<char *>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char *start, char **end) {
float cygwin_strtof_l(char const *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -52,7 +50,7 @@ float cygwin_strtof_l(char const *start, char **end) {
} }
#endif #endif
std::pair<double, bool> strtod_from_string(char const *st) { std::pair<double, bool> strtod_from_string(const char *st) {
double d; double d;
char *pr; char *pr;
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \

View File

@ -56,7 +56,7 @@ void random_values(size_t N) {
memcpy(&v, &word, sizeof(v)); memcpy(&v, &word, sizeof(v));
// if (!std::isnormal(v)) // if (!std::isnormal(v))
{ {
char const *string_end = to_string(v, buffer); const char *string_end = to_string(v, buffer);
double result_value; double result_value;
auto result = fast_float::from_chars(buffer, string_end, result_value); auto result = fast_float::from_chars(buffer, string_end, result_value);
// Starting with version 4.0 for fast_float, we return result_out_of_range // Starting with version 4.0 for fast_float, we return result_out_of_range

View File

@ -14,9 +14,8 @@
// gcc. // gcc.
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(char const *start, char **end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -32,8 +31,7 @@ double cygwin_strtod_l(char const *start, char **end) {
*end = const_cast<char *>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char *start, char **end) {
float cygwin_strtof_l(char const *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -55,7 +53,6 @@ class RandomEngine {
public: public:
RandomEngine() = delete; RandomEngine() = delete;
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){}; RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
uint64_t next() { uint64_t next() {
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
// Inspired from // Inspired from
@ -68,13 +65,9 @@ public:
uint64_t m2 = (tmp.high) ^ tmp.low; uint64_t m2 = (tmp.high) ^ tmp.low;
return m2; return m2;
} }
bool next_bool() { return (next() & 1) == 1; } bool next_bool() { return (next() & 1) == 1; }
int next_int() { return static_cast<int>(next()); } int next_int() { return static_cast<int>(next()); }
char next_char() { return static_cast<char>(next()); } char next_char() { return static_cast<char>(next()); }
double next_double() { return static_cast<double>(next()); } double next_double() { return static_cast<double>(next()); }
int next_ranged_int(int min, int max) { // min and max are included int next_ranged_int(int min, int max) { // min and max are included
@ -97,7 +90,6 @@ public:
} }
return int(m.high) + min; return int(m.high) + min;
} }
int next_digit() { return next_ranged_int(0, 9); } int next_digit() { return next_ranged_int(0, 9); }
private: private:

View File

@ -2,70 +2,91 @@
* See https://github.com/eddelbuettel/rcppfastfloat/issues/4 * 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 "fast_float/fast_float.h"
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
struct test_data {
std::string input;
bool expected_success;
double expected_result;
};
bool eddelbuettel() { bool eddelbuettel() {
std::vector<test_data> const test_datas = { std::vector<std::string> inputs = {"infinity",
{"infinity", true, std::numeric_limits<double>::infinity()}, " \r\n\t\f\v3.16227766016838 \r\n\t\f\v",
{" \r\n\t\f\v3.16227766016838 \r\n\t\f\v", true, 3.16227766016838}, " \r\n\t\f\v3 \r\n\t\f\v",
{" \r\n\t\f\v3 \r\n\t\f\v", true, 3.0}, " 1970-01-01",
{" 1970-01-01", false, 0.0}, "-NaN",
{"-NaN", true, std::numeric_limits<double>::quiet_NaN()}, "-inf",
{"-inf", true, -std::numeric_limits<double>::infinity()}, " \r\n\t\f\v2.82842712474619 \r\n\t\f\v",
{" \r\n\t\f\v2.82842712474619 \r\n\t\f\v", true, 2.82842712474619}, "nan",
{"nan", true, std::numeric_limits<double>::quiet_NaN()}, " \r\n\t\f\v2.44948974278318 \r\n\t\f\v",
{" \r\n\t\f\v2.44948974278318 \r\n\t\f\v", true, 2.44948974278318}, "Inf",
{"Inf", true, std::numeric_limits<double>::infinity()}, " \r\n\t\f\v2 \r\n\t\f\v",
{" \r\n\t\f\v2 \r\n\t\f\v", true, 2.0}, "-infinity",
{"-infinity", true, -std::numeric_limits<double>::infinity()}, " \r\n\t\f\v0 \r\n\t\f\v",
{" \r\n\t\f\v0 \r\n\t\f\v", true, 0.0}, " \r\n\t\f\v1.73205080756888 \r\n\t\f\v",
{" \r\n\t\f\v1.73205080756888 \r\n\t\f\v", true, 1.73205080756888}, " \r\n\t\f\v1 \r\n\t\f\v",
{" \r\n\t\f\v1 \r\n\t\f\v", true, 1.0}, " \r\n\t\f\v1.4142135623731 \r\n\t\f\v",
{" \r\n\t\f\v1.4142135623731 \r\n\t\f\v", true, 1.4142135623731}, " \r\n\t\f\v2.23606797749979 \r\n\t\f\v",
{" \r\n\t\f\v2.23606797749979 \r\n\t\f\v", true, 2.23606797749979}, "1970-01-02 ",
{"1970-01-02 ", false, 0.0}, " \r\n\t\f\v2.64575131106459 \r\n\t\f\v",
{" \r\n\t\f\v2.64575131106459 \r\n\t\f\v", true, 2.64575131106459}, "inf",
{"inf", true, std::numeric_limits<double>::infinity()}, "-nan",
{"-nan", true, std::numeric_limits<double>::quiet_NaN()}, "NaN",
{"NaN", true, std::numeric_limits<double>::quiet_NaN()}, "",
{"", false, 0.0}, "-Inf",
{"-Inf", true, -std::numeric_limits<double>::infinity()}, "+2.2",
{"+2.2", true, 2.2}, "1d+4",
{"1d+4", false, 0.0}, "1d-1",
{"1d-1", false, 0.0}, "0.",
{"0.", true, 0.0}, "-.1",
{"-.1", true, -0.1}, "+.1",
{"+.1", true, 0.1}, "1e+1",
{"1e+1", true, 10.0}, "+1e1"};
{"+1e1", true, 10.0}, std::vector<std::pair<bool, double>> expected_results = {
{"-+0", false, 0.0}, {true, std::numeric_limits<double>::infinity()},
{"-+inf", false, 0.0}, {true, 3.16227766016838},
{"-+nan", false, 0.0}, {true, 3},
{false, -1},
{true, std::numeric_limits<double>::quiet_NaN()},
{true, -std::numeric_limits<double>::infinity()},
{true, 2.82842712474619},
{true, std::numeric_limits<double>::quiet_NaN()},
{true, 2.44948974278318},
{true, std::numeric_limits<double>::infinity()},
{true, 2},
{true, -std::numeric_limits<double>::infinity()},
{true, 0},
{true, 1.73205080756888},
{true, 1},
{true, 1.4142135623731},
{true, 2.23606797749979},
{false, -1},
{true, 2.64575131106459},
{true, std::numeric_limits<double>::infinity()},
{true, std::numeric_limits<double>::quiet_NaN()},
{true, std::numeric_limits<double>::quiet_NaN()},
{false, -1},
{true, -std::numeric_limits<double>::infinity()},
{true, 2.2},
{false, -1},
{false, -1},
{true, 0},
{true, -0.1},
{true, 0.1},
{true, 10},
{true, 10},
}; };
for (size_t i = 0; i < test_datas.size(); i++) { for (size_t i = 0; i < inputs.size(); i++) {
auto const &input = test_datas[i].input; const std::string &input = inputs[i];
auto const expected_success = test_datas[i].expected_success; std::pair<bool, double> expected = expected_results[i];
auto const expected_result = test_datas[i].expected_result;
double result; double result;
// answer contains a error code and a pointer to the end of the // answer contains a error code and a pointer to the end of the
// parsed region (on success). // parsed region (on success).
auto const answer = fast_float::from_chars( auto answer = fast_float::from_chars(input.data(),
input.data(), input.data() + input.size(), result, input.data() + input.size(), result);
fast_float::chars_format::general |
fast_float::chars_format::allow_leading_plus |
fast_float::chars_format::skip_white_space);
if (answer.ec != std::errc()) { if (answer.ec != std::errc()) {
std::cout << "could not parse" << std::endl; std::cout << "could not parse" << std::endl;
if (expected_success) { if (expected.first) {
return false; return false;
} }
continue; continue;
@ -73,9 +94,9 @@ bool eddelbuettel() {
bool non_space_trailing_content = false; bool non_space_trailing_content = false;
if (answer.ptr != input.data() + input.size()) { if (answer.ptr != input.data() + input.size()) {
// check that there is no content left // check that there is no content left
for (char const *leftover = answer.ptr; for (const char *leftover = answer.ptr;
leftover != input.data() + input.size(); leftover++) { leftover != input.data() + input.size(); leftover++) {
if (!fast_float::is_space(*leftover)) { if (!fast_float::is_space(uint8_t(*leftover))) {
non_space_trailing_content = true; non_space_trailing_content = true;
break; break;
} }
@ -83,19 +104,24 @@ bool eddelbuettel() {
} }
if (non_space_trailing_content) { if (non_space_trailing_content) {
std::cout << "found trailing content " << std::endl; std::cout << "found trailing content " << std::endl;
if (!expected_success) { }
if (non_space_trailing_content) {
if (!expected.first) {
continue; continue;
} else { } else {
return false; return false;
} }
} }
std::cout << "parsed " << result << std::endl; std::cout << "parsed " << result << std::endl;
if (!expected_success) { if (!expected.first) {
return false; return false;
} }
if (result != expected_result && if (result != expected.second) {
!(std::isnan(result) && std::isnan(expected_result))) { if (std::isnan(result) && std::isnan(expected.second)) {
std::cout << "results do not match. Expected " << expected_result continue;
}
std::cout << "results do not match. Expected " << expected.second
<< std::endl; << std::endl;
return false; return false;
} }

View File

@ -14,9 +14,8 @@
// gcc. // gcc.
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(char const *start, char **end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -32,8 +31,7 @@ double cygwin_strtod_l(char const *start, char **end) {
*end = const_cast<char *>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char *start, char **end) {
float cygwin_strtof_l(char const *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -55,7 +53,6 @@ class RandomEngine {
public: public:
RandomEngine() = delete; RandomEngine() = delete;
RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){}; RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){};
uint64_t next() { uint64_t next() {
// Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h
// Inspired from // Inspired from
@ -68,13 +65,9 @@ public:
uint64_t m2 = (tmp.high) ^ tmp.low; uint64_t m2 = (tmp.high) ^ tmp.low;
return m2; return m2;
} }
bool next_bool() { return (next() & 1) == 1; } bool next_bool() { return (next() & 1) == 1; }
int next_int() { return static_cast<int>(next()); } int next_int() { return static_cast<int>(next()); }
char next_char() { return static_cast<char>(next()); } char next_char() { return static_cast<char>(next()); }
double next_double() { return static_cast<double>(next()); } double next_double() { return static_cast<double>(next()); }
int next_ranged_int(int min, int max) { // min and max are included int next_ranged_int(int min, int max) { // min and max are included
@ -97,7 +90,6 @@ public:
} }
return int(m.high) + min; return int(m.high) + min;
} }
int next_digit() { return next_ranged_int(0, 9); } int next_digit() { return next_ranged_int(0, 9); }
private: private:

View File

@ -16,9 +16,8 @@
// gcc. // gcc.
#include <locale> #include <locale>
#include <sstream> #include <sstream>
// workaround for CYGWIN // workaround for CYGWIN
double cygwin_strtod_l(char const *start, char **end) { double cygwin_strtod_l(const char *start, char **end) {
double d; double d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -34,8 +33,7 @@ double cygwin_strtod_l(char const *start, char **end) {
*end = const_cast<char *>(start) + nread; *end = const_cast<char *>(start) + nread;
return d; return d;
} }
float cygwin_strtof_l(const char *start, char **end) {
float cygwin_strtof_l(char const *start, char **end) {
float d; float d;
std::stringstream ss; std::stringstream ss;
ss.imbue(std::locale::classic()); ss.imbue(std::locale::classic());
@ -95,8 +93,8 @@ template <typename T> bool test() {
std::errc(), std::errc(),
std::errc(), std::errc(),
std::errc()}; std::errc()};
char const *begin = input.data(); const char *begin = input.data();
char const *end = input.data() + input.size(); const char *end = input.data() + input.size();
for (size_t i = 0; i < answers.size(); i++) { for (size_t i = 0; i < answers.size(); i++) {
T result_value; T result_value;
while ((begin < end) && (std::isspace(*begin))) { while ((begin < end) && (std::isspace(*begin))) {
@ -122,9 +120,9 @@ template <typename T> bool test() {
return true; return true;
} }
template <typename T> void strtod_from_string(std::string const &st, T &d); template <typename T> void strtod_from_string(const std::string &st, T &d);
template <> void strtod_from_string(std::string const &st, double &d) { template <> void strtod_from_string(const std::string &st, double &d) {
char *pr = (char *)st.c_str(); char *pr = (char *)st.c_str();
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun) defined(sun) || defined(__sun)
@ -141,7 +139,7 @@ template <> void strtod_from_string(std::string const &st, double &d) {
} }
} }
template <> void strtod_from_string(std::string const &st, float &d) { template <> void strtod_from_string(const std::string &st, float &d) {
char *pr = (char *)st.c_str(); char *pr = (char *)st.c_str();
#if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \
defined(sun) || defined(__sun) defined(sun) || defined(__sun)
@ -162,7 +160,7 @@ template <typename T> bool partow_test() {
// credit: // credit:
// https://github.com/ArashPartow/strtk/blob/master/strtk_tokenizer_cmp.cpp#L568 // https://github.com/ArashPartow/strtk/blob/master/strtk_tokenizer_cmp.cpp#L568
// MIT license // MIT license
std::string const strint_list[] = { const std::string strint_list[] = {
"9007199254740993", "9007199254740993",
"9007199254740994", "9007199254740994",
"9007199254740995", "9007199254740995",
@ -1087,7 +1085,7 @@ template <typename T> bool partow_test() {
"1234567890", "1234567890",
"-1234567890", "-1234567890",
}; };
for (std::string const &st : strint_list) { for (const std::string &st : strint_list) {
T expected_value; T expected_value;
strtod_from_string(st, expected_value); strtod_from_string(st, expected_value);
T result_value; T result_value;

View File

@ -1,53 +0,0 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
#include <system_error>
template <typename UC> bool test(std::string s, double expected) {
std::basic_string<UC> input(s.begin(), s.end());
double result;
auto answer =
fast_float::from_chars(input.data(), input.data() + input.size(), result);
if (answer.ec != std::errc()) {
std::cerr << "parsing of \"" << s << "\" should succeed\n";
return false;
}
if (result != expected && !(std::isnan(result) && std::isnan(expected))) {
std::cerr << "parsing of \"" << s << "\" succeeded, expected " << expected
<< " got " << result << "\n";
return false;
}
return true;
}
int main() {
if (!test<char>("4.2", 4.2)) {
std::cout << "test failure for char" << std::endl;
return EXIT_FAILURE;
}
if (!test<wchar_t>("4.2", 4.2)) {
std::cout << "test failure for wchar_t" << std::endl;
return EXIT_FAILURE;
}
#ifdef __cpp_char8_t
if (!test<char8_t>("4.2", 4.2)) {
std::cout << "test failure for char8_t" << std::endl;
return EXIT_FAILURE;
}
#endif
if (!test<char16_t>("4.2", 4.2)) {
std::cout << "test failure for char16_t" << std::endl;
return EXIT_FAILURE;
}
if (!test<char32_t>("4.2", 4.2)) {
std::cout << "test failure for char32_t" << std::endl;
return EXIT_FAILURE;
}
std::cout << "all ok" << std::endl;
return EXIT_SUCCESS;
}

View File

@ -1,57 +0,0 @@
#include "fast_float/fast_float.h"
#include <iostream>
#include <string>
#include <system_error>
bool tester(std::string s, double expected,
fast_float::chars_format fmt = fast_float::chars_format::general) {
std::wstring input(s.begin(), s.end());
double result;
auto answer = fast_float::from_chars(
input.data(), input.data() + input.size(), result, fmt);
if (answer.ec != std::errc()) {
std::cerr << "parsing of \"" << s << "\" should succeed\n";
return false;
}
if (result != expected && !(std::isnan(result) && std::isnan(expected))) {
std::cerr << "parsing of \"" << s << "\" succeeded, expected " << expected
<< " got " << result << "\n";
return false;
}
input[0] += 256;
answer = fast_float::from_chars(input.data(), input.data() + input.size(),
result, fmt);
if (answer.ec == std::errc()) {
std::cerr << "parsing of altered \"" << s << "\" should fail\n";
return false;
}
return true;
}
bool test_minus() { return tester("-42", -42); }
bool test_plus() {
return tester("+42", 42,
fast_float::chars_format::general |
fast_float::chars_format::allow_leading_plus);
}
bool test_space() {
return tester(" 42", 42,
fast_float::chars_format::general |
fast_float::chars_format::skip_white_space);
}
bool test_nan() {
return tester("nan", std::numeric_limits<double>::quiet_NaN());
}
int main() {
if (test_minus() && test_plus() && test_space() && test_nan()) {
std::cout << "all ok" << std::endl;
return EXIT_SUCCESS;
}
std::cout << "test failure" << std::endl;
return EXIT_FAILURE;
}