mirror of
https://github.com/fastfloat/fast_float.git
synced 2025-12-07 01:06:48 +08:00
Compare commits
No commits in common. "main" and "v8.0.0" have entirely different histories.
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
@ -1,5 +1,4 @@
|
||||
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.
|
||||
|
||||
We have benchmarks, please consider running them: see our README for details.
|
||||
|
||||
Our CI tests check formatting automating. If such a test fails, please consider running the bash script:
|
||||
|
||||
|
||||
2
.github/workflows/alpine.yml
vendored
2
.github/workflows/alpine.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
||||
- riscv64
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install latest Alpine Linux for ${{ matrix.arch }}
|
||||
uses: jirutka/setup-alpine@v1
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
name: Amalgamate Ubuntu 24.04 CI
|
||||
name: Amalgamate Ubuntu 20.04 CI (GCC 9)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Compile with amalgamation
|
||||
run: |
|
||||
mkdir build &&
|
||||
4
.github/workflows/cifuzz.yml
vendored
4
.github/workflows/cifuzz.yml
vendored
@ -20,14 +20,14 @@ jobs:
|
||||
fuzz-seconds: 300
|
||||
output-sarif: true
|
||||
- name: Upload Crash
|
||||
uses: actions/upload-artifact@v5
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
with:
|
||||
name: artifacts
|
||||
path: ./out/artifacts
|
||||
- name: Upload Sarif
|
||||
if: always() && steps.build.outcome == 'success'
|
||||
uses: github/codeql-action/upload-sarif@v4
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: cifuzz-sarif/results.sarif
|
||||
|
||||
17
.github/workflows/emscripten.yml
vendored
17
.github/workflows/emscripten.yml
vendored
@ -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
|
||||
4
.github/workflows/lint_and_format_check.yml
vendored
4
.github/workflows/lint_and_format_check.yml
vendored
@ -24,10 +24,10 @@ jobs:
|
||||
lint-and-format:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@ff7abcd0c3c05ccf6adc123a8cd1fd4fb30fb493 # v4.1.7
|
||||
- uses: actions/checkout@9a9194f87191a7e9055e3e9b95b8cfb13023bb08 # v4.1.7
|
||||
|
||||
- name: Run clang-format
|
||||
uses: jidicula/clang-format-action@4726374d1aa3c6aecf132e5197e498979588ebc8 # v4.15.0
|
||||
uses: jidicula/clang-format-action@d05cecd4a1a5b7e64c22f5a468456135a43f13f6 # v4.14.0
|
||||
with:
|
||||
clang-format-version: '17'
|
||||
|
||||
|
||||
2
.github/workflows/msys2-clang.yml
vendored
2
.github/workflows/msys2-clang.yml
vendored
@ -23,7 +23,7 @@ jobs:
|
||||
CMAKE_GENERATOR: Ninja
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
|
||||
2
.github/workflows/msys2.yml
vendored
2
.github/workflows/msys2.yml
vendored
@ -29,7 +29,7 @@ jobs:
|
||||
CMAKE_GENERATOR: Ninja
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
update: true
|
||||
|
||||
2
.github/workflows/on-release.yml
vendored
2
.github/workflows/on-release.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Amalgamate fast_float.h
|
||||
run: |
|
||||
|
||||
23
.github/workflows/risc.yml
vendored
23
.github/workflows/risc.yml
vendored
@ -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)
|
||||
4
.github/workflows/s390x.yml
vendored
4
.github/workflows/s390x.yml
vendored
@ -12,8 +12,8 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: uraimo/run-on-arch-action@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: uraimo/run-on-arch-action@v2
|
||||
name: Test
|
||||
id: runcmd
|
||||
with:
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
name: Ubuntu 24.04 CI
|
||||
name: Ubuntu 20.04 CI (C++20)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-24.04
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
fail-fast: false
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
16
.github/workflows/ubuntu20-fastmath.yml
vendored
Normal file
16
.github/workflows/ubuntu20-fastmath.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
name: Ubuntu 20.04 CI (GCC 9, fast-math)
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@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
21
.github/workflows/ubuntu20.yml
vendored
Normal 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 .
|
||||
2
.github/workflows/ubuntu22-clang.yml
vendored
2
.github/workflows/ubuntu22-clang.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Install clang++-14
|
||||
run: sudo apt-get install -y clang++-14
|
||||
- name: Use cmake
|
||||
|
||||
2
.github/workflows/ubuntu22-gcc12.yml
vendored
2
.github/workflows/ubuntu22-gcc12.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
|
||||
2
.github/workflows/ubuntu22-sanitize.yml
vendored
2
.github/workflows/ubuntu22-sanitize.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
|
||||
2
.github/workflows/ubuntu22.yml
vendored
2
.github/workflows/ubuntu22.yml
vendored
@ -6,7 +6,7 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
mkdir build &&
|
||||
|
||||
28
.github/workflows/ubuntu24.yml
vendored
28
.github/workflows/ubuntu24.yml
vendored
@ -6,24 +6,18 @@ jobs:
|
||||
ubuntu-build:
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use cmake
|
||||
run: |
|
||||
set -xe
|
||||
cmake -B build \
|
||||
-DFASTFLOAT_TEST=ON \
|
||||
-DFASTFLOAT_BENCHMARKS=ON \
|
||||
-DCMAKE_CXX_FLAGS=' -Werror -Wundef '
|
||||
cmake --build build --parallel
|
||||
( cd build ; ctest --output-on-failure )
|
||||
mkdir build &&
|
||||
cd build &&
|
||||
CXXFLAGS=-Werror cmake -DFASTFLOAT_TEST=ON -D FASTFLOAT_BENCHMARKS=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure
|
||||
- name: Use cmake CXX23
|
||||
run: |
|
||||
set -xe
|
||||
cmake -B build20 \
|
||||
-DFASTFLOAT_TEST=ON \
|
||||
-DFASTFLOAT_CONSTEXPR_TESTS=ON \
|
||||
-DFASTFLOAT_FIXEDWIDTH_TESTS=ON \
|
||||
-DFASTFLOAT_CXX_STANDARD=23 \
|
||||
-DCMAKE_CXX_FLAGS=' -Werror -Wundef '
|
||||
cmake --build build20 --parallel
|
||||
( cd build20 ; ctest --output-on-failure )
|
||||
mkdir build20 &&
|
||||
cd build20 &&
|
||||
CXXFLAGS=-Werror cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_FIXEDWIDTH_TESTS=ON -DFASTFLOAT_CXX_STANDARD=23 -DFASTFLOAT_TEST=ON .. &&
|
||||
cmake --build . &&
|
||||
ctest --output-on-failure
|
||||
2
.github/workflows/vs17-arm-ci.yml
vendored
2
.github/workflows/vs17-arm-ci.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
||||
- {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: configure
|
||||
run: |
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON
|
||||
|
||||
4
.github/workflows/vs17-ci.yml
vendored
4
.github/workflows/vs17-ci.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: configure
|
||||
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
|
||||
run: |
|
||||
cmake --build build --verbose --config ${{matrix.cfg}} --parallel
|
||||
|
||||
4
.github/workflows/vs17-clang-ci.yml
vendored
4
.github/workflows/vs17-clang-ci.yml
vendored
@ -16,10 +16,10 @@ jobs:
|
||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: Configure
|
||||
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
|
||||
run: cmake --build build --config ${{matrix.cfg}} --parallel --verbose
|
||||
- name: Run basic tests
|
||||
|
||||
2
.github/workflows/vs17-cxx20.yml
vendored
2
.github/workflows/vs17-cxx20.yml
vendored
@ -16,7 +16,7 @@ jobs:
|
||||
- {gen: Visual Studio 17 2022, arch: x64, cfg: Debug}
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@v4
|
||||
- name: configure
|
||||
run: >-
|
||||
cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
cmake_minimum_required(VERSION 3.9)
|
||||
|
||||
|
||||
project(fast_float VERSION 8.1.0 LANGUAGES CXX)
|
||||
project(fast_float VERSION 8.0.0 LANGUAGES CXX)
|
||||
set(FASTFLOAT_CXX_STANDARD 11 CACHE STRING "the C++ standard to use for fastfloat")
|
||||
set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD})
|
||||
option(FASTFLOAT_TEST "Enable tests" OFF)
|
||||
@ -56,15 +55,11 @@ if(FASTFLOAT_SANITIZE)
|
||||
target_link_libraries(fast_float INTERFACE -fuse-ld=gold)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
include(CheckCXXCompilerFlag)
|
||||
unset(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
|
||||
CHECK_CXX_COMPILER_FLAG(/permissive- FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
|
||||
|
||||
if(FASTFLOAT_COMPILER_SUPPORTS_PERMISSIVE)
|
||||
if(MSVC_VERSION GREATER 1910)
|
||||
target_compile_options(fast_float INTERFACE /permissive-)
|
||||
endif()
|
||||
|
||||
|
||||
if(FASTFLOAT_INSTALL)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
|
||||
102
README.md
102
README.md
@ -57,7 +57,6 @@ Example:
|
||||
```C++
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
std::string input = "3.1416 xyz ";
|
||||
@ -69,25 +68,6 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
Though the C++17 standard has you do a comparison with `std::errc()` to check whether the conversion worked, you can avoid it by casting the result to a `bool` like so:
|
||||
|
||||
```cpp
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
|
||||
int main() {
|
||||
std::string input = "3.1416 xyz ";
|
||||
double result;
|
||||
if(auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result)) {
|
||||
std::cout << "parsed the number " << result << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
std::cerr << "failed to parse " << result << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
```
|
||||
|
||||
You can parse delimited numbers:
|
||||
|
||||
```C++
|
||||
@ -377,51 +357,6 @@ int main() {
|
||||
}
|
||||
```
|
||||
|
||||
## Multiplication of an integer by a power of 10
|
||||
An integer `W` can be multiplied by a power of ten `10^Q` and
|
||||
converted to `double` with correctly rounded value
|
||||
(in "round to nearest, tie to even" fashion) using
|
||||
`fast_float::integer_times_pow10()`, e.g.:
|
||||
```C++
|
||||
const uint64_t W = 12345678901234567;
|
||||
const int Q = 23;
|
||||
const double result = fast_float::integer_times_pow10(W, Q);
|
||||
std::cout.precision(17);
|
||||
std::cout << W << " * 10^" << Q << " = " << result << " ("
|
||||
<< (result == 12345678901234567e23 ? "==" : "!=") << "expected)\n";
|
||||
```
|
||||
outputs
|
||||
```
|
||||
12345678901234567 * 10^23 = 1.2345678901234567e+39 (==expected)
|
||||
```
|
||||
`fast_float::integer_times_pow10()` gives the same result as
|
||||
using `fast_float::from_chars()` when parsing the string `"WeQ"`
|
||||
(in this example `"12345678901234567e23"`),
|
||||
except `fast_float::integer_times_pow10()` does not report out-of-range errors, and
|
||||
underflows to zero or overflows to infinity when the resulting value is
|
||||
out of range.
|
||||
|
||||
You can use template overloads to get the result converted to different
|
||||
supported floating-point types: `float`, `double`, etc.
|
||||
For example, to get result as `float` use
|
||||
`fast_float::integer_times_pow10<float>()` specialization:
|
||||
```C++
|
||||
const uint64_t W = 12345678;
|
||||
const int Q = 23;
|
||||
const float result = fast_float::integer_times_pow10<float>(W, Q);
|
||||
std::cout.precision(9);
|
||||
std::cout << "float: " << W << " * 10^" << Q << " = " << result << " ("
|
||||
<< (result == 12345678e23f ? "==" : "!=") << "expected)\n";
|
||||
```
|
||||
outputs
|
||||
```
|
||||
float: 12345678 * 10^23 = 1.23456782e+30 (==expected)
|
||||
```
|
||||
|
||||
Overloads of `fast_float::integer_times_pow10()` are provided for
|
||||
signed and unsigned integer types: `int64_t`, `uint64_t`, etc.
|
||||
|
||||
|
||||
## Users and Related Work
|
||||
|
||||
The fast_float library is part of:
|
||||
@ -429,8 +364,6 @@ The fast_float library is part of:
|
||||
* GCC (as of version 12): the `from_chars` function in GCC relies on fast_float,
|
||||
* [Chromium](https://github.com/Chromium/Chromium), the engine behind Google
|
||||
Chrome, Microsoft Edge, and Opera,
|
||||
* Boost JSON, MySQL, etc.
|
||||
* Blender
|
||||
* [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's
|
||||
web browser),
|
||||
* [DuckDB](https://duckdb.org),
|
||||
@ -443,10 +376,7 @@ The fast_float library is part of:
|
||||
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).
|
||||
AdaCore](https://github.com/AdaCore/VSS).
|
||||
|
||||
The fast_float library provides a performance similar to that of the
|
||||
[fast_double_parser](https://github.com/lemire/fast_double_parser) library but
|
||||
@ -455,14 +385,6 @@ API more in line with the expectations of C++ programmers. The
|
||||
fast_double_parser library is part of the [Microsoft LightGBM machine-learning
|
||||
framework](https://github.com/microsoft/LightGBM).
|
||||
|
||||
|
||||
|
||||
Packages
|
||||
------
|
||||
|
||||
[](https://repology.org/project/fast-float/versions)
|
||||
|
||||
|
||||
## References
|
||||
|
||||
* Daniel Lemire, [Number Parsing at a Gigabyte per
|
||||
@ -507,7 +429,7 @@ abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfl
|
||||
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 the [Benchmarking](#benchmarking) Section for instructions on how to run our benchmarks.
|
||||
|
||||
## Video
|
||||
|
||||
@ -533,7 +455,7 @@ sufficiently recent version of CMake (3.11 or better at least):
|
||||
FetchContent_Declare(
|
||||
fast_float
|
||||
GIT_REPOSITORY https://github.com/fastfloat/fast_float.git
|
||||
GIT_TAG tags/v8.1.0
|
||||
GIT_TAG tags/v8.0.0
|
||||
GIT_SHALLOW TRUE)
|
||||
|
||||
FetchContent_MakeAvailable(fast_float)
|
||||
@ -549,7 +471,7 @@ You may also use [CPM](https://github.com/cpm-cmake/CPM.cmake), like so:
|
||||
CPMAddPackage(
|
||||
NAME fast_float
|
||||
GITHUB_REPOSITORY "fastfloat/fast_float"
|
||||
GIT_TAG v8.1.0)
|
||||
GIT_TAG v8.0.0)
|
||||
```
|
||||
|
||||
## Using as single header
|
||||
@ -561,7 +483,7 @@ if desired as described in the command line help.
|
||||
|
||||
You may directly download automatically generated single-header files:
|
||||
|
||||
<https://github.com/fastfloat/fast_float/releases/download/v8.1.0/fast_float.h>
|
||||
<https://github.com/fastfloat/fast_float/releases/download/v8.0.0/fast_float.h>
|
||||
|
||||
## Benchmarking
|
||||
|
||||
@ -586,21 +508,12 @@ in some cases:
|
||||
sudo ./build/benchmarks/realbenchmark
|
||||
```
|
||||
|
||||
If you have a text file containing one number per line (`myfile.txt`), you can run a benchmark over it like so:
|
||||
```
|
||||
cmake -B build -D FASTFLOAT_BENCHMARKS=ON
|
||||
cmake --build build
|
||||
./build/benchmarks/realbenchmark myfile.txt
|
||||
```
|
||||
|
||||
|
||||
## Packages
|
||||
|
||||
* The fast_float library is part of the [Conan package
|
||||
manager](https://conan.io/center/recipes/fast_float).
|
||||
* It is part of the [brew package
|
||||
manager](https://formulae.brew.sh/formula/fast_float).
|
||||
* fast_float is available on [xmake](https://xmake.io) repository.
|
||||
* Some Linux distribution like Fedora include fast_float (e.g., as
|
||||
`fast_float-devel`).
|
||||
|
||||
@ -615,11 +528,6 @@ long digits.
|
||||
The library includes code adapted from Google Wuffs (written by Nigel Tao) which
|
||||
was originally published under the Apache 2.0 license.
|
||||
|
||||
## Stars
|
||||
|
||||
|
||||
[](https://www.star-history.com/#fastfloat/fast_float&Date)
|
||||
|
||||
## License
|
||||
|
||||
<sup>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#if defined(__linux__) || (__APPLE__ && __aarch64__)
|
||||
#define USING_COUNTERS
|
||||
#endif
|
||||
#include "event_counter.h"
|
||||
#endif
|
||||
#include <algorithm>
|
||||
#include "fast_float/fast_float.h"
|
||||
#include <chrono>
|
||||
@ -236,11 +236,6 @@ int main(int argc, char **argv) {
|
||||
<< 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;
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
#if (defined(__APPLE__) && __APPLE__) && (defined(__aarch64__) && __aarch64__)
|
||||
#if __APPLE__ && __aarch64__
|
||||
#include "apple_arm_events.h"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
set(CMAKE_SYSTEM_NAME Linux)
|
||||
set(CMAKE_SYSTEM_PROCESSOR riscv64)
|
||||
|
||||
set(CMAKE_CROSSCOMPILING_EMULATOR "qemu-riscv64-static")
|
||||
@ -279,7 +279,7 @@ report_parse_error(UC const *p, parse_error error) {
|
||||
|
||||
// Assuming that you use no more than 19 digits, this will
|
||||
// parse an ASCII string.
|
||||
template <bool basic_json_fmt, typename UC>
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t<UC>
|
||||
parse_number_string(UC const *p, UC const *pend,
|
||||
parse_options_t<UC> options) noexcept {
|
||||
@ -292,20 +292,20 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
// assume p < pend, so dereference without checks;
|
||||
answer.negative = (*p == UC('-'));
|
||||
// C++17 20.19.3.(7.1) explicitly forbids '+' sign here
|
||||
if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) &&
|
||||
!basic_json_fmt && *p == UC('+'))) {
|
||||
if ((*p == UC('-')) ||
|
||||
(uint64_t(fmt & chars_format::allow_leading_plus) &&
|
||||
!uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) {
|
||||
++p;
|
||||
if (p == pend) {
|
||||
return report_parse_error<UC>(
|
||||
p, parse_error::missing_integer_or_dot_after_sign);
|
||||
}
|
||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||
if (uint64_t(fmt & detail::basic_json_fmt)) {
|
||||
if (!is_integer(*p)) { // a sign must be followed by an integer
|
||||
return report_parse_error<UC>(p,
|
||||
parse_error::missing_integer_after_sign);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
if (!is_integer(*p) &&
|
||||
(*p !=
|
||||
decimal_point)) { // a sign must be followed by an integer or the dot
|
||||
@ -329,7 +329,7 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
UC const *const end_of_integer_part = p;
|
||||
int64_t digit_count = int64_t(end_of_integer_part - start_digits);
|
||||
answer.integer = span<UC const>(start_digits, size_t(digit_count));
|
||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||
if (uint64_t(fmt & detail::basic_json_fmt)) {
|
||||
// at least 1 digit in integer part, without leading zeros
|
||||
if (digit_count == 0) {
|
||||
return report_parse_error<UC>(p, parse_error::no_digits_in_integer_part);
|
||||
@ -358,14 +358,14 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
answer.fraction = span<UC const>(before, size_t(p - before));
|
||||
digit_count -= exponent;
|
||||
}
|
||||
FASTFLOAT_IF_CONSTEXPR17(basic_json_fmt) {
|
||||
if (uint64_t(fmt & detail::basic_json_fmt)) {
|
||||
// at least 1 digit in fractional part
|
||||
if (has_decimal_point && exponent == 0) {
|
||||
return report_parse_error<UC>(p,
|
||||
parse_error::no_digits_in_fractional_part);
|
||||
}
|
||||
}
|
||||
else if (digit_count == 0) { // we must have encountered at least one integer!
|
||||
} else if (digit_count ==
|
||||
0) { // we must have encountered at least one integer!
|
||||
return report_parse_error<UC>(p, parse_error::no_digits_in_mantissa);
|
||||
}
|
||||
int64_t exp_number = 0; // explicit exponential part
|
||||
@ -441,7 +441,7 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
if (digit_count > 19) {
|
||||
answer.too_many_digits = true;
|
||||
// Let us start again, this time, avoiding overflows.
|
||||
// We don't need to 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.
|
||||
i = 0;
|
||||
p = answer.integer.ptr;
|
||||
@ -451,7 +451,7 @@ parse_number_string(UC const *p, UC const *pend,
|
||||
i = i * 10 + uint64_t(*p - UC('0'));
|
||||
++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;
|
||||
} else { // We have a value with a fractional component.
|
||||
p = answer.fraction.ptr;
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#endif
|
||||
|
||||
// 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
|
||||
#else
|
||||
#define FASTFLOAT_CONSTEXPR14
|
||||
@ -27,15 +27,8 @@
|
||||
#define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0
|
||||
#endif
|
||||
|
||||
#if defined(__cpp_if_constexpr) && __cpp_if_constexpr >= 201606L
|
||||
#define FASTFLOAT_IF_CONSTEXPR17(x) if constexpr (x)
|
||||
#else
|
||||
#define FASTFLOAT_IF_CONSTEXPR17(x) if (x)
|
||||
#endif
|
||||
|
||||
// Testing for relevant C++20 constexpr library features
|
||||
#if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \
|
||||
defined(__cpp_lib_constexpr_algorithms) && \
|
||||
__cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/
|
||||
#define FASTFLOAT_CONSTEXPR20 constexpr
|
||||
#define FASTFLOAT_IS_CONSTEXPR 1
|
||||
|
||||
@ -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
|
||||
// effect on performance: in order to have a faster algorithm, we'd need
|
||||
// to slow down performance for faster algorithms, and this is still fast.
|
||||
template <typename UC>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t
|
||||
scientific_exponent(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) {
|
||||
mantissa /= 10000;
|
||||
exponent += 4;
|
||||
@ -395,7 +398,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
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);
|
||||
adjusted_mantissa answer = am;
|
||||
round<T>(answer, [ord](adjusted_mantissa &a, int32_t shift) {
|
||||
@ -416,7 +419,7 @@ inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp(
|
||||
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
|
||||
// an extended float representation close to `b+h`, halfway between `b`
|
||||
// (the float rounded-down) and `b+u`, the next positive float. this
|
||||
@ -435,8 +438,7 @@ digit_comp(parsed_number_string_t<UC> &num, adjusted_mantissa am) noexcept {
|
||||
// remove the invalid exponent bias
|
||||
am.power2 -= invalid_am_bias;
|
||||
|
||||
int32_t sci_exp =
|
||||
scientific_exponent(num.mantissa, static_cast<int32_t>(num.exponent));
|
||||
int32_t sci_exp = scientific_exponent(num);
|
||||
size_t max_digits = binary_format<T>::max_digits();
|
||||
size_t digits = 0;
|
||||
bigint bigmant;
|
||||
|
||||
@ -45,38 +45,6 @@ FASTFLOAT_CONSTEXPR20 from_chars_result_t<UC>
|
||||
from_chars_advanced(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC> options) noexcept;
|
||||
|
||||
/**
|
||||
* This function multiplies an integer number by a power of 10 and returns
|
||||
* the result as a double precision floating-point value that is correctly
|
||||
* rounded. The resulting floating-point value is the closest floating-point
|
||||
* value, using the "round to nearest, tie to even" convention for values that
|
||||
* would otherwise fall right in-between two values. That is, we provide exact
|
||||
* conversion according to the IEEE standard.
|
||||
*
|
||||
* On overflow infinity is returned, on underflow 0 is returned.
|
||||
*
|
||||
* The implementation does not throw and does not allocate memory (e.g., with
|
||||
* `new` or `malloc`).
|
||||
*/
|
||||
FASTFLOAT_CONSTEXPR20 inline double
|
||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
|
||||
FASTFLOAT_CONSTEXPR20 inline double
|
||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
|
||||
|
||||
/**
|
||||
* This function is a template overload of `integer_times_pow10()`
|
||||
* that returns a floating-point value of type `T` that is one of
|
||||
* supported floating-point types (e.g. `double`, `float`).
|
||||
*/
|
||||
template <typename T>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||
integer_times_pow10(uint64_t mantissa, int decimal_exponent) noexcept;
|
||||
template <typename T>
|
||||
FASTFLOAT_CONSTEXPR20
|
||||
typename std::enable_if<is_supported_float_type<T>::value, T>::type
|
||||
integer_times_pow10(int64_t mantissa, int decimal_exponent) noexcept;
|
||||
|
||||
/**
|
||||
* from_chars for integer types.
|
||||
*/
|
||||
|
||||
@ -9,14 +9,14 @@
|
||||
#include <type_traits>
|
||||
#include <system_error>
|
||||
#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>
|
||||
#endif
|
||||
#endif
|
||||
#include "constexpr_feature_detect.h"
|
||||
|
||||
#define FASTFLOAT_VERSION_MAJOR 8
|
||||
#define FASTFLOAT_VERSION_MINOR 1
|
||||
#define FASTFLOAT_VERSION_MINOR 0
|
||||
#define FASTFLOAT_VERSION_PATCH 0
|
||||
|
||||
#define FASTFLOAT_STRINGIZE_IMPL(x) #x
|
||||
@ -58,11 +58,6 @@ enum class chars_format : uint64_t {
|
||||
template <typename UC> struct from_chars_result_t {
|
||||
UC const *ptr;
|
||||
std::errc ec;
|
||||
|
||||
// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2497r0.html
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
return ec == std::errc();
|
||||
}
|
||||
};
|
||||
|
||||
using from_chars_result = from_chars_result_t<char>;
|
||||
@ -93,12 +88,11 @@ using parse_options = parse_options_t<char>;
|
||||
defined(__MINGW64__) || defined(__s390x__) || \
|
||||
(defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \
|
||||
defined(__PPC64LE__)) || \
|
||||
defined(__loongarch64) || (defined(__riscv) && __riscv_xlen == 64))
|
||||
defined(__loongarch64))
|
||||
#define FASTFLOAT_64BIT 1
|
||||
#elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \
|
||||
defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \
|
||||
defined(__MINGW32__) || defined(__EMSCRIPTEN__) || \
|
||||
(defined(__riscv) && __riscv_xlen == 32))
|
||||
defined(__MINGW32__) || defined(__EMSCRIPTEN__))
|
||||
#define FASTFLOAT_32BIT 1
|
||||
#else
|
||||
// Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow.
|
||||
@ -406,8 +400,8 @@ full_multiplication(uint64_t a, uint64_t b) {
|
||||
// But MinGW on ARM64 doesn't have native support for 64-bit multiplications
|
||||
answer.high = __umulh(a, b);
|
||||
answer.low = a * b;
|
||||
#elif defined(FASTFLOAT_32BIT) || (defined(_WIN64) && !defined(__clang__) && \
|
||||
!defined(_M_ARM64) && !defined(__GNUC__))
|
||||
#elif defined(FASTFLOAT_32BIT) || \
|
||||
(defined(_WIN64) && !defined(__clang__) && !defined(_M_ARM64))
|
||||
answer.low = _umul128(a, b, &answer.high); // _umul128 not available on ARM64
|
||||
#elif defined(FASTFLOAT_64BIT) && defined(__SIZEOF_INT128__)
|
||||
__uint128_t r = ((__uint128_t)a) * b;
|
||||
@ -1132,12 +1126,7 @@ template <typename T> constexpr uint64_t int_luts<T>::min_safe_u64[];
|
||||
|
||||
template <typename UC>
|
||||
fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) {
|
||||
// wchar_t and char can be signed, so we need to be careful.
|
||||
using UnsignedUC = typename std::make_unsigned<UC>::type;
|
||||
return int_luts<>::chdigit[static_cast<unsigned char>(
|
||||
static_cast<UnsignedUC>(c) &
|
||||
static_cast<UnsignedUC>(
|
||||
-((static_cast<UnsignedUC>(c) & ~0xFFull) == 0)))];
|
||||
return int_luts<>::chdigit[static_cast<unsigned char>(c)];
|
||||
}
|
||||
|
||||
fastfloat_really_inline constexpr size_t max_digits_u64(int base) {
|
||||
@ -1166,9 +1155,6 @@ static_assert(std::is_same<equiv_uint_t<std::float64_t>, uint64_t>::value,
|
||||
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__
|
||||
@ -1177,9 +1163,6 @@ static_assert(std::is_same<equiv_uint_t<std::float32_t>, uint32_t>::value,
|
||||
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__
|
||||
@ -1251,6 +1234,7 @@ constexpr chars_format adjust_for_feature_macros(chars_format fmt) {
|
||||
;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
} // namespace fast_float
|
||||
|
||||
#endif
|
||||
|
||||
@ -188,17 +188,32 @@ from_chars(UC const *first, UC const *last, T &value,
|
||||
parse_options_t<UC>(fmt));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool
|
||||
clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
||||
T &value) noexcept {
|
||||
/**
|
||||
* 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;
|
||||
// The implementation of the Clinger's fast path is convoluted because
|
||||
// we want round-to-nearest in all cases, irrespective of the rounding mode
|
||||
// selected on the thread.
|
||||
// We proceed optimistically, assuming that detail::rounds_to_nearest()
|
||||
// returns true.
|
||||
if (binary_format<T>::min_exponent_fast_path() <= exponent &&
|
||||
exponent <= binary_format<T>::max_exponent_fast_path()) {
|
||||
if (binary_format<T>::min_exponent_fast_path() <= pns.exponent &&
|
||||
pns.exponent <= binary_format<T>::max_exponent_fast_path() &&
|
||||
!pns.too_many_digits) {
|
||||
// Unfortunately, the conventional Clinger's fast path is only possible
|
||||
// when the system rounds to the nearest float.
|
||||
//
|
||||
@ -209,64 +224,41 @@ clinger_fast_path_impl(uint64_t mantissa, int64_t exponent, bool is_negative,
|
||||
if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) {
|
||||
// We have that fegetround() == FE_TONEAREST.
|
||||
// Next is Clinger's fast path.
|
||||
if (mantissa <= binary_format<T>::max_mantissa_fast_path()) {
|
||||
value = T(mantissa);
|
||||
if (exponent < 0) {
|
||||
value = value / binary_format<T>::exact_power_of_ten(-exponent);
|
||||
if (pns.mantissa <= binary_format<T>::max_mantissa_fast_path()) {
|
||||
value = T(pns.mantissa);
|
||||
if (pns.exponent < 0) {
|
||||
value = value / binary_format<T>::exact_power_of_ten(-pns.exponent);
|
||||
} else {
|
||||
value = value * binary_format<T>::exact_power_of_ten(exponent);
|
||||
value = value * binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
}
|
||||
if (is_negative) {
|
||||
if (pns.negative) {
|
||||
value = -value;
|
||||
}
|
||||
return true;
|
||||
return answer;
|
||||
}
|
||||
} else {
|
||||
// We do not have that fegetround() == FE_TONEAREST.
|
||||
// Next is a modified Clinger's fast path, inspired by Jakub Jelínek's
|
||||
// proposal
|
||||
if (exponent >= 0 &&
|
||||
mantissa <= binary_format<T>::max_mantissa_fast_path(exponent)) {
|
||||
if (pns.exponent >= 0 &&
|
||||
pns.mantissa <=
|
||||
binary_format<T>::max_mantissa_fast_path(pns.exponent)) {
|
||||
#if defined(__clang__) || defined(FASTFLOAT_32BIT)
|
||||
// Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD
|
||||
if (mantissa == 0) {
|
||||
value = is_negative ? T(-0.) : T(0.);
|
||||
return true;
|
||||
if (pns.mantissa == 0) {
|
||||
value = pns.negative ? T(-0.) : T(0.);
|
||||
return answer;
|
||||
}
|
||||
#endif
|
||||
value = T(mantissa) * binary_format<T>::exact_power_of_ten(exponent);
|
||||
if (is_negative) {
|
||||
value = T(pns.mantissa) *
|
||||
binary_format<T>::exact_power_of_ten(pns.exponent);
|
||||
if (pns.negative) {
|
||||
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 =
|
||||
compute_float<binary_format<T>>(pns.exponent, pns.mantissa);
|
||||
if (pns.too_many_digits && am.power2 >= 0) {
|
||||
@ -313,9 +305,7 @@ from_chars_float_advanced(UC const *first, UC const *last, T &value,
|
||||
return answer;
|
||||
}
|
||||
parsed_number_string_t<UC> pns =
|
||||
uint64_t(fmt & detail::basic_json_fmt)
|
||||
? parse_number_string<true, UC>(first, last, options)
|
||||
: parse_number_string<false, UC>(first, last, options);
|
||||
parse_number_string<UC>(first, last, options);
|
||||
if (!pns.valid) {
|
||||
if (uint64_t(fmt & chars_format::no_infnan)) {
|
||||
answer.ec = std::errc::invalid_argument;
|
||||
@ -344,84 +334,6 @@ from_chars(UC const *first, UC const *last, T &value, int base) noexcept {
|
||||
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,
|
||||
|
||||
@ -9,7 +9,8 @@ option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON)
|
||||
|
||||
if (NOT SYSTEM_DOCTEST)
|
||||
FetchContent_Declare(doctest
|
||||
GIT_REPOSITORY https://github.com/lemire/doctest.git)
|
||||
GIT_REPOSITORY https://github.com/onqtam/doctest.git
|
||||
GIT_TAG v2.4.11)
|
||||
else ()
|
||||
find_package(doctest REQUIRED)
|
||||
endif()
|
||||
@ -22,15 +23,24 @@ endif()
|
||||
|
||||
# FetchContent_MakeAvailable() was only introduced in 3.14
|
||||
# https://cmake.org/cmake/help/v3.14/release/3.14.html#modules
|
||||
# FetchContent_MakeAvailable(doctest)
|
||||
if (NOT SYSTEM_DOCTEST)
|
||||
FetchContent_MakeAvailable(doctest)
|
||||
FetchContent_GetProperties(doctest)
|
||||
if(NOT doctest_POPULATED)
|
||||
FetchContent_Populate(doctest)
|
||||
add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(supplemental-data INTERFACE)
|
||||
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
|
||||
message(STATUS "Supplemental tests enabled. Retrieving test files.")
|
||||
FetchContent_MakeAvailable(supplemental_test_files)
|
||||
message(STATUS "Supplemental test files retrieved.")
|
||||
FetchContent_GetProperties(supplemental_test_files)
|
||||
if(NOT supplemental_test_files_POPULATED)
|
||||
message(STATUS "Supplemental tests enabled. Retrieving test files.")
|
||||
FetchContent_Populate(supplemental_test_files)
|
||||
message(STATUS "Supplemental test files retrieved.")
|
||||
add_subdirectory(${supplemental_test_files_SOURCE_DIR} ${supplemental_test_files_BINARY_DIR})
|
||||
endif()
|
||||
target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data")
|
||||
endif()
|
||||
|
||||
@ -61,7 +71,6 @@ fast_float_add_cpp_test(wide_char_test)
|
||||
fast_float_add_cpp_test(supported_chars_test)
|
||||
fast_float_add_cpp_test(example_test)
|
||||
fast_float_add_cpp_test(example_comma_test)
|
||||
fast_float_add_cpp_test(example_integer_times_pow10)
|
||||
fast_float_add_cpp_test(basictest)
|
||||
option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF)
|
||||
if (FASTFLOAT_CONSTEXPR_TESTS)
|
||||
@ -73,7 +82,7 @@ endif()
|
||||
if (FASTFLOAT_SUPPLEMENTAL_TESTS)
|
||||
target_compile_definitions(basictest PRIVATE FASTFLOAT_SUPPLEMENTAL_TESTS)
|
||||
endif()
|
||||
fast_float_add_cpp_test(p2497)
|
||||
|
||||
fast_float_add_cpp_test(long_test)
|
||||
fast_float_add_cpp_test(powersoffive_hardround)
|
||||
fast_float_add_cpp_test(string_test)
|
||||
|
||||
@ -1134,23 +1134,6 @@ TEST_CASE("double.inf") {
|
||||
std::errc::result_out_of_range);
|
||||
verify("1.9e308", std::numeric_limits<double>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
|
||||
// DBL_MAX + 0.00000000000000001e308
|
||||
verify("1.79769313486231581e308", std::numeric_limits<double>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
|
||||
// DBL_MAX + 0.0000000000000001e308
|
||||
verify("1.7976931348623159e308", std::numeric_limits<double>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
|
||||
// ( (2 - 0.5*2^(−52)) * 2^1023 ) smallest number that overflows to infinity
|
||||
verify("179769313486231580793728971405303415079934132710037826936173778980444"
|
||||
"968292764750946649017977587207096330286416692887910946555547851940402"
|
||||
"630657488671505820681908902000708383676273854845817711531764475730270"
|
||||
"069855571366959622842914819860834936475292719074168444365510704342711"
|
||||
"559699508093042880177904174497792",
|
||||
std::numeric_limits<double>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
}
|
||||
|
||||
TEST_CASE("double.general") {
|
||||
@ -1160,13 +1143,6 @@ TEST_CASE("double.general") {
|
||||
verify("-22250738585072012e-324",
|
||||
-0x1p-1022); /* limit between normal and subnormal*/
|
||||
verify("-1e-999", -0.0, std::errc::result_out_of_range);
|
||||
|
||||
// DBL_TRUE_MIN / 2
|
||||
verify("2.4703282292062327e-324", 0.0, std::errc::result_out_of_range);
|
||||
|
||||
// DBL_TRUE_MIN / 2 + 0.0000000000000001e-324
|
||||
verify("2.4703282292062328e-324", 0x0.0000000000001p-1022);
|
||||
|
||||
verify("-2.2222222222223e-322", -0x1.68p-1069);
|
||||
verify("9007199254740993.0", 0x1p+53);
|
||||
verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328);
|
||||
@ -1342,15 +1318,6 @@ TEST_CASE("double.general") {
|
||||
std::numeric_limits<double>::infinity(), std::errc::result_out_of_range);
|
||||
verify("-2240084132271013504.131248280843119943687942846658579428",
|
||||
-0x1.f1660a65b00bfp+60);
|
||||
|
||||
// ( (2 - 0.5*2^(−52)) * 2^1023 - 1 ) largest 309 decimal digit number
|
||||
// that rounds to DBL_MAX
|
||||
verify("179769313486231580793728971405303415079934132710037826936173778980444"
|
||||
"968292764750946649017977587207096330286416692887910946555547851940402"
|
||||
"630657488671505820681908902000708383676273854845817711531764475730270"
|
||||
"069855571366959622842914819860834936475292719074168444365510704342711"
|
||||
"559699508093042880177904174497791",
|
||||
std::numeric_limits<double>::max());
|
||||
}
|
||||
|
||||
TEST_CASE("double.decimal_point") {
|
||||
@ -1525,35 +1492,14 @@ TEST_CASE("float.inf") {
|
||||
std::errc::result_out_of_range);
|
||||
verify("3.5028234666e38", std::numeric_limits<float>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
// FLT_MAX + 0.00000007e38
|
||||
verify("3.40282357e38", std::numeric_limits<float>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
// FLT_MAX + 0.0000001e38
|
||||
verify("3.4028236e38", std::numeric_limits<float>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
|
||||
// ( (2 - 0.5*2^(-23)) * 2^127 ) smallest number that overflows to infinity
|
||||
verify("340282356779733661637539395458142568448",
|
||||
std::numeric_limits<float>::infinity(),
|
||||
std::errc::result_out_of_range);
|
||||
}
|
||||
|
||||
TEST_CASE("float.general") {
|
||||
// FLT_TRUE_MIN / 2
|
||||
verify("0.7006492e-45", 0.f, std::errc::result_out_of_range);
|
||||
// FLT_TRUE_MIN / 2 + 0.0000001e-45
|
||||
verify("0.7006493e-45", 0x1p-149f);
|
||||
|
||||
// max
|
||||
verify("340282346638528859811704183484516925440", 0x1.fffffep+127f);
|
||||
// -max
|
||||
verify("-340282346638528859811704183484516925440", -0x1.fffffep+127f);
|
||||
|
||||
// ( (2 - 0.5*2^(-23)) * 2^127 - 1 ) largest 39 decimal digits number
|
||||
// that rounds to FLT_MAX
|
||||
verify("340282356779733661637539395458142568447",
|
||||
std::numeric_limits<float>::max());
|
||||
|
||||
verify("-1e-999", -0.0f, std::errc::result_out_of_range);
|
||||
verify("1."
|
||||
"175494140627517859246175898662808184331245864732796240031385942718174"
|
||||
@ -2124,317 +2070,3 @@ TEST_CASE("bfloat16.general") {
|
||||
// 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875bf16);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename Int, typename T, typename U>
|
||||
void verify_integer_times_pow10_result(Int mantissa, int decimal_exponent,
|
||||
T actual, U expected) {
|
||||
static_assert(std::is_same<T, U>::value,
|
||||
"expected and actual types must match");
|
||||
|
||||
INFO("m * 10^e=" << mantissa << " * 10^" << decimal_exponent
|
||||
<< "\n"
|
||||
" expected="
|
||||
<< fHexAndDec(expected) << "\n"
|
||||
<< " ..actual=" << fHexAndDec(actual) << "\n"
|
||||
<< " expected mantissa="
|
||||
<< iHexAndDec(get_mantissa(expected)) << "\n"
|
||||
<< " ..actual mantissa=" << iHexAndDec(get_mantissa(actual))
|
||||
<< "\n");
|
||||
CHECK_EQ(actual, expected);
|
||||
}
|
||||
|
||||
template <typename T, typename Int>
|
||||
T calculate_integer_times_pow10_expected_result(Int mantissa,
|
||||
int decimal_exponent) {
|
||||
std::string constructed_string =
|
||||
std::to_string(mantissa) + "e" + std::to_string(decimal_exponent);
|
||||
T expected_result;
|
||||
const auto result = fast_float::from_chars(
|
||||
constructed_string.data(),
|
||||
constructed_string.data() + constructed_string.size(), expected_result);
|
||||
if (result.ec != std::errc())
|
||||
INFO("Failed to parse: " << constructed_string);
|
||||
return expected_result;
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent,
|
||||
double expected) {
|
||||
static_assert(std::is_integral<Int>::value);
|
||||
|
||||
// the "default" overload
|
||||
const double actual =
|
||||
fast_float::integer_times_pow10(mantissa, decimal_exponent);
|
||||
|
||||
verify_integer_times_pow10_result(mantissa, decimal_exponent, actual,
|
||||
expected);
|
||||
}
|
||||
|
||||
template <typename Int>
|
||||
void verify_integer_times_pow10_dflt(Int mantissa, int decimal_exponent) {
|
||||
static_assert(std::is_integral<Int>::value);
|
||||
|
||||
const auto expected_result =
|
||||
calculate_integer_times_pow10_expected_result<double>(mantissa,
|
||||
decimal_exponent);
|
||||
|
||||
verify_integer_times_pow10_dflt(mantissa, decimal_exponent, expected_result);
|
||||
}
|
||||
|
||||
template <typename T, typename Int>
|
||||
void verify_integer_times_pow10(Int mantissa, int decimal_exponent,
|
||||
T expected) {
|
||||
static_assert(std::is_floating_point<T>::value);
|
||||
static_assert(std::is_integral<Int>::value);
|
||||
|
||||
// explicit specialization
|
||||
const auto actual =
|
||||
fast_float::integer_times_pow10<T>(mantissa, decimal_exponent);
|
||||
|
||||
verify_integer_times_pow10_result(mantissa, decimal_exponent, actual,
|
||||
expected);
|
||||
}
|
||||
|
||||
template <typename T, typename Int>
|
||||
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
|
||||
static_assert(std::is_floating_point<T>::value);
|
||||
static_assert(std::is_integral<Int>::value);
|
||||
|
||||
const auto expected_result = calculate_integer_times_pow10_expected_result<T>(
|
||||
mantissa, decimal_exponent);
|
||||
|
||||
verify_integer_times_pow10(mantissa, decimal_exponent, expected_result);
|
||||
}
|
||||
|
||||
namespace all_supported_types {
|
||||
template <typename Int>
|
||||
void verify_integer_times_pow10(Int mantissa, int decimal_exponent) {
|
||||
static_assert(std::is_integral<Int>::value);
|
||||
|
||||
// verify the "default" overload
|
||||
verify_integer_times_pow10_dflt(mantissa, decimal_exponent);
|
||||
|
||||
// verify explicit specializations
|
||||
::verify_integer_times_pow10<double>(mantissa, decimal_exponent);
|
||||
::verify_integer_times_pow10<float>(mantissa, decimal_exponent);
|
||||
#if defined(__STDCPP_FLOAT64_T__)
|
||||
::verify_integer_times_pow10<std::float64_t>(mantissa, decimal_exponent);
|
||||
#endif
|
||||
#if defined(__STDCPP_FLOAT32_T__)
|
||||
::verify_integer_times_pow10<std::float32_t>(mantissa, decimal_exponent);
|
||||
#endif
|
||||
#if defined(__STDCPP_FLOAT16_T__)
|
||||
::verify_integer_times_pow10<std::float16_t>(mantissa, decimal_exponent);
|
||||
#endif
|
||||
#if defined(__STDCPP_BFLOAT16_T__)
|
||||
::verify_integer_times_pow10<std::bfloat16_t>(mantissa, decimal_exponent);
|
||||
#endif
|
||||
}
|
||||
} // namespace all_supported_types
|
||||
|
||||
TEST_CASE("integer_times_pow10") {
|
||||
/* explicitly verifying API with different types of integers */
|
||||
// double (the "default" overload)
|
||||
verify_integer_times_pow10_dflt<int8_t>(31, -1, 3.1);
|
||||
verify_integer_times_pow10_dflt<int8_t>(-31, -1, -3.1);
|
||||
verify_integer_times_pow10_dflt<uint8_t>(31, -1, 3.1);
|
||||
verify_integer_times_pow10_dflt<int16_t>(31415, -4, 3.1415);
|
||||
verify_integer_times_pow10_dflt<int16_t>(-31415, -4, -3.1415);
|
||||
verify_integer_times_pow10_dflt<uint16_t>(31415, -4, 3.1415);
|
||||
verify_integer_times_pow10_dflt<int32_t>(314159265, -8, 3.14159265);
|
||||
verify_integer_times_pow10_dflt<int32_t>(-314159265, -8, -3.14159265);
|
||||
verify_integer_times_pow10_dflt<uint32_t>(3141592653, -9, 3.141592653);
|
||||
verify_integer_times_pow10_dflt<long>(314159265, -8, 3.14159265);
|
||||
verify_integer_times_pow10_dflt<long>(-314159265, -8, -3.14159265);
|
||||
verify_integer_times_pow10_dflt<unsigned long>(3141592653, -9, 3.141592653);
|
||||
verify_integer_times_pow10_dflt<int64_t>(3141592653589793238, -18,
|
||||
3.141592653589793238);
|
||||
verify_integer_times_pow10_dflt<int64_t>(-3141592653589793238, -18,
|
||||
-3.141592653589793238);
|
||||
verify_integer_times_pow10_dflt<uint64_t>(3141592653589793238, -18,
|
||||
3.141592653589793238);
|
||||
verify_integer_times_pow10_dflt<long long>(3141592653589793238, -18,
|
||||
3.141592653589793238);
|
||||
verify_integer_times_pow10_dflt<long long>(-3141592653589793238, -18,
|
||||
-3.141592653589793238);
|
||||
verify_integer_times_pow10_dflt<unsigned long long>(3141592653589793238, -18,
|
||||
3.141592653589793238);
|
||||
// double (explicit specialization)
|
||||
verify_integer_times_pow10<double, int8_t>(31, -1, 3.1);
|
||||
verify_integer_times_pow10<double, int8_t>(-31, -1, -3.1);
|
||||
verify_integer_times_pow10<double, uint8_t>(31, -1, 3.1);
|
||||
verify_integer_times_pow10<double, int16_t>(31415, -4, 3.1415);
|
||||
verify_integer_times_pow10<double, int16_t>(-31415, -4, -3.1415);
|
||||
verify_integer_times_pow10<double, uint16_t>(31415, -4, 3.1415);
|
||||
verify_integer_times_pow10<double, int32_t>(314159265, -8, 3.14159265);
|
||||
verify_integer_times_pow10<double, int32_t>(-314159265, -8, -3.14159265);
|
||||
verify_integer_times_pow10<double, uint32_t>(3141592653, -9, 3.141592653);
|
||||
verify_integer_times_pow10<double, long>(314159265, -8, 3.14159265);
|
||||
verify_integer_times_pow10<double, long>(-314159265, -8, -3.14159265);
|
||||
verify_integer_times_pow10<double, unsigned long>(3141592653, -9,
|
||||
3.141592653);
|
||||
verify_integer_times_pow10<double, int64_t>(3141592653589793238, -18,
|
||||
3.141592653589793238);
|
||||
verify_integer_times_pow10<double, int64_t>(-3141592653589793238, -18,
|
||||
-3.141592653589793238);
|
||||
verify_integer_times_pow10<double, uint64_t>(3141592653589793238, -18,
|
||||
3.141592653589793238);
|
||||
verify_integer_times_pow10<double, long long>(3141592653589793238, -18,
|
||||
3.141592653589793238);
|
||||
verify_integer_times_pow10<double, long long>(-3141592653589793238, -18,
|
||||
-3.141592653589793238);
|
||||
verify_integer_times_pow10<double, unsigned long long>(
|
||||
3141592653589793238, -18, 3.141592653589793238);
|
||||
// float (explicit specialization)
|
||||
verify_integer_times_pow10<float, int8_t>(31, -1, 3.1f);
|
||||
verify_integer_times_pow10<float, int8_t>(-31, -1, -3.1f);
|
||||
verify_integer_times_pow10<float, uint8_t>(31, -1, 3.1f);
|
||||
verify_integer_times_pow10<float, int16_t>(31415, -4, 3.1415f);
|
||||
verify_integer_times_pow10<float, int16_t>(-31415, -4, -3.1415f);
|
||||
verify_integer_times_pow10<float, uint16_t>(31415, -4, 3.1415f);
|
||||
verify_integer_times_pow10<float, int32_t>(314159265, -8, 3.14159265f);
|
||||
verify_integer_times_pow10<float, int32_t>(-314159265, -8, -3.14159265f);
|
||||
verify_integer_times_pow10<float, uint32_t>(3141592653, -9, 3.14159265f);
|
||||
verify_integer_times_pow10<float, long>(314159265, -8, 3.14159265f);
|
||||
verify_integer_times_pow10<float, long>(-314159265, -8, -3.14159265f);
|
||||
verify_integer_times_pow10<float, unsigned long>(3141592653, -9, 3.14159265f);
|
||||
verify_integer_times_pow10<float, int64_t>(3141592653589793238, -18,
|
||||
3.141592653589793238f);
|
||||
verify_integer_times_pow10<float, int64_t>(-3141592653589793238, -18,
|
||||
-3.141592653589793238f);
|
||||
verify_integer_times_pow10<float, uint64_t>(3141592653589793238, -18,
|
||||
3.141592653589793238f);
|
||||
verify_integer_times_pow10<float, long long>(3141592653589793238, -18,
|
||||
3.141592653589793238f);
|
||||
verify_integer_times_pow10<float, long long>(-3141592653589793238, -18,
|
||||
-3.141592653589793238f);
|
||||
verify_integer_times_pow10<float, unsigned long long>(
|
||||
3141592653589793238, -18, 3.141592653589793238f);
|
||||
|
||||
for (int mode : {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}) {
|
||||
fesetround(mode);
|
||||
INFO("fesetround(): " << std::string{round_name(mode)});
|
||||
|
||||
struct Guard {
|
||||
~Guard() { fesetround(FE_TONEAREST); }
|
||||
} guard;
|
||||
|
||||
namespace all = all_supported_types;
|
||||
|
||||
all::verify_integer_times_pow10(0, 0);
|
||||
all::verify_integer_times_pow10(1, 0);
|
||||
all::verify_integer_times_pow10(0, 1);
|
||||
all::verify_integer_times_pow10(1, 1);
|
||||
all::verify_integer_times_pow10(-1, 0);
|
||||
all::verify_integer_times_pow10(0, -1);
|
||||
all::verify_integer_times_pow10(-1, -1);
|
||||
all::verify_integer_times_pow10(-1, 1);
|
||||
all::verify_integer_times_pow10(1, -1);
|
||||
|
||||
/* denormal min */
|
||||
verify_integer_times_pow10_dflt(49406564584124654, -340,
|
||||
std::numeric_limits<double>::denorm_min());
|
||||
verify_integer_times_pow10<double>(
|
||||
49406564584124654, -340, std::numeric_limits<double>::denorm_min());
|
||||
verify_integer_times_pow10<float>(14012984, -52,
|
||||
std::numeric_limits<float>::denorm_min());
|
||||
|
||||
/* normal min */
|
||||
verify_integer_times_pow10_dflt(22250738585072014, -324,
|
||||
std::numeric_limits<double>::min());
|
||||
verify_integer_times_pow10<double>(22250738585072014, -324,
|
||||
std::numeric_limits<double>::min());
|
||||
verify_integer_times_pow10<float>(11754944, -45,
|
||||
std::numeric_limits<float>::min());
|
||||
|
||||
/* max */
|
||||
verify_integer_times_pow10_dflt(17976931348623158, 292,
|
||||
std::numeric_limits<double>::max());
|
||||
verify_integer_times_pow10<double>(17976931348623158, 292,
|
||||
std::numeric_limits<double>::max());
|
||||
verify_integer_times_pow10<float>(34028235, 31,
|
||||
std::numeric_limits<float>::max());
|
||||
|
||||
/* underflow */
|
||||
// (DBL_TRUE_MIN / 2) underflows to 0
|
||||
verify_integer_times_pow10_dflt(49406564584124654 / 2, -340, 0.);
|
||||
verify_integer_times_pow10<double>(49406564584124654 / 2, -340, 0.);
|
||||
// (FLT_TRUE_MIN / 2) underflows to 0
|
||||
verify_integer_times_pow10<float>(14012984 / 2, -52, 0.f);
|
||||
|
||||
/* rounding to denormal min */
|
||||
// (DBL_TRUE_MIN / 2 + 0.0000000000000001e-324) rounds to DBL_TRUE_MIN
|
||||
verify_integer_times_pow10_dflt(49406564584124654 / 2 + 1, -340,
|
||||
std::numeric_limits<double>::denorm_min());
|
||||
verify_integer_times_pow10<double>(
|
||||
49406564584124654 / 2 + 1, -340,
|
||||
std::numeric_limits<double>::denorm_min());
|
||||
// (FLT_TRUE_MIN / 2 + 0.0000001e-45) rounds to FLT_TRUE_MIN
|
||||
verify_integer_times_pow10<float>(14012984 / 2 + 1, -52,
|
||||
std::numeric_limits<float>::denorm_min());
|
||||
|
||||
/* overflow */
|
||||
// (DBL_MAX + 0.0000000000000001e308) overflows to infinity
|
||||
verify_integer_times_pow10_dflt(17976931348623158 + 1, 292,
|
||||
std::numeric_limits<double>::infinity());
|
||||
verify_integer_times_pow10<double>(17976931348623158 + 1, 292,
|
||||
std::numeric_limits<double>::infinity());
|
||||
// (DBL_MAX + 0.00000000000000001e308) overflows to infinity
|
||||
verify_integer_times_pow10_dflt(179769313486231580 + 1, 291,
|
||||
std::numeric_limits<double>::infinity());
|
||||
verify_integer_times_pow10<double>(179769313486231580 + 1, 291,
|
||||
std::numeric_limits<double>::infinity());
|
||||
// (FLT_MAX + 0.0000001e38) overflows to infinity
|
||||
verify_integer_times_pow10<float>(34028235 + 1, 31,
|
||||
std::numeric_limits<float>::infinity());
|
||||
// (FLT_MAX + 0.00000007e38) overflows to infinity
|
||||
verify_integer_times_pow10<float>(340282350 + 7, 30,
|
||||
std::numeric_limits<float>::infinity());
|
||||
|
||||
// loosely verifying correct rounding of 1 to 64 bits
|
||||
// worth of significant digits
|
||||
all::verify_integer_times_pow10(1, 42);
|
||||
all::verify_integer_times_pow10(1, -42);
|
||||
all::verify_integer_times_pow10(12, 42);
|
||||
all::verify_integer_times_pow10(12, -42);
|
||||
all::verify_integer_times_pow10(123, 42);
|
||||
all::verify_integer_times_pow10(123, -42);
|
||||
all::verify_integer_times_pow10(1234, 42);
|
||||
all::verify_integer_times_pow10(1234, -42);
|
||||
all::verify_integer_times_pow10(12345, 42);
|
||||
all::verify_integer_times_pow10(12345, -42);
|
||||
all::verify_integer_times_pow10(123456, 42);
|
||||
all::verify_integer_times_pow10(123456, -42);
|
||||
all::verify_integer_times_pow10(1234567, 42);
|
||||
all::verify_integer_times_pow10(1234567, -42);
|
||||
all::verify_integer_times_pow10(12345678, 42);
|
||||
all::verify_integer_times_pow10(12345678, -42);
|
||||
all::verify_integer_times_pow10(123456789, 42);
|
||||
all::verify_integer_times_pow10(1234567890, 42);
|
||||
all::verify_integer_times_pow10(1234567890, -42);
|
||||
all::verify_integer_times_pow10(12345678901, 42);
|
||||
all::verify_integer_times_pow10(12345678901, -42);
|
||||
all::verify_integer_times_pow10(123456789012, 42);
|
||||
all::verify_integer_times_pow10(123456789012, -42);
|
||||
all::verify_integer_times_pow10(1234567890123, 42);
|
||||
all::verify_integer_times_pow10(1234567890123, -42);
|
||||
all::verify_integer_times_pow10(12345678901234, 42);
|
||||
all::verify_integer_times_pow10(12345678901234, -42);
|
||||
all::verify_integer_times_pow10(123456789012345, 42);
|
||||
all::verify_integer_times_pow10(123456789012345, -42);
|
||||
all::verify_integer_times_pow10(1234567890123456, 42);
|
||||
all::verify_integer_times_pow10(1234567890123456, -42);
|
||||
all::verify_integer_times_pow10(12345678901234567, 42);
|
||||
all::verify_integer_times_pow10(12345678901234567, -42);
|
||||
all::verify_integer_times_pow10(123456789012345678, 42);
|
||||
all::verify_integer_times_pow10(123456789012345678, -42);
|
||||
all::verify_integer_times_pow10(1234567890123456789, 42);
|
||||
all::verify_integer_times_pow10(1234567890123456789, -42);
|
||||
all::verify_integer_times_pow10(12345678901234567890ull, 42);
|
||||
all::verify_integer_times_pow10(12345678901234567890ull, -42);
|
||||
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), 42);
|
||||
all::verify_integer_times_pow10(std::numeric_limits<int64_t>::max(), -42);
|
||||
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), 42);
|
||||
all::verify_integer_times_pow10(std::numeric_limits<uint64_t>::max(), -42);
|
||||
}
|
||||
}
|
||||
@ -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();
|
||||
}
|
||||
@ -831,275 +831,6 @@ int main() {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
// dont parse UTF-16 code units of emojis as int if low byte is ascii digit
|
||||
{
|
||||
const std::u16string emojis[] = {
|
||||
u"ℹ", u"ℹ️", u"☸", u"☸️", u"☹", u"☹️", u"✳", u"✳️",
|
||||
u"✴", u"✴️", u"⤴", u"⤴️", u"⤵", u"⤵️", u"〰", u"〰️",
|
||||
};
|
||||
bool failed = false;
|
||||
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
|
||||
for (size_t i = 0; i < array_size; i++) {
|
||||
auto e = emojis[i];
|
||||
int foo;
|
||||
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
|
||||
if (answer.ec == std::errc()) {
|
||||
failed = true;
|
||||
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
|
||||
<< "." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
// dont parse UTF-32 code points of emojis as int if low byte is ascii digit
|
||||
{
|
||||
const std::u32string emojis[] = {
|
||||
U"ℹ",
|
||||
U"ℹ️",
|
||||
U"☸",
|
||||
U"☸️",
|
||||
U"☹",
|
||||
U"☹️",
|
||||
U"✳",
|
||||
U"✳️",
|
||||
U"✴",
|
||||
U"✴️",
|
||||
U"⤴",
|
||||
U"⤴️",
|
||||
U"⤵",
|
||||
U"⤵️",
|
||||
U"〰",
|
||||
U"〰️",
|
||||
U"🈲",
|
||||
U"🈳",
|
||||
U"🈴",
|
||||
U"🈵",
|
||||
U"🈶",
|
||||
U"🈷",
|
||||
U"🈷️",
|
||||
U"🈸",
|
||||
U"🈹",
|
||||
U"🌰",
|
||||
U"🌱",
|
||||
U"🌲",
|
||||
U"🌳",
|
||||
U"🌴",
|
||||
U"🌵",
|
||||
U"🌶",
|
||||
U"🌶️",
|
||||
U"🌷",
|
||||
U"🌸",
|
||||
U"🌹",
|
||||
U"🐰",
|
||||
U"🐱",
|
||||
U"🐲",
|
||||
U"🐳",
|
||||
U"🐴",
|
||||
U"🐵",
|
||||
U"🐶",
|
||||
U"🐷",
|
||||
U"🐸",
|
||||
U"🐹",
|
||||
U"🔰",
|
||||
U"🔱",
|
||||
U"🔲",
|
||||
U"🔳",
|
||||
U"🔴",
|
||||
U"🔵",
|
||||
U"🔶",
|
||||
U"🔷",
|
||||
U"🔸",
|
||||
U"🔹",
|
||||
U"😰",
|
||||
U"😱",
|
||||
U"😲",
|
||||
U"😳",
|
||||
U"😴",
|
||||
U"😵",
|
||||
U"😵💫",
|
||||
U"😶",
|
||||
U"😶🌫",
|
||||
U"😶🌫️",
|
||||
U"😷",
|
||||
U"😸",
|
||||
U"😹",
|
||||
U"🤰",
|
||||
U"🤰🏻",
|
||||
U"🤰🏼",
|
||||
U"🤰🏽",
|
||||
U"🤰🏾",
|
||||
U"🤰🏿",
|
||||
U"🤱",
|
||||
U"🤱🏻",
|
||||
U"🤱🏼",
|
||||
U"🤱🏽",
|
||||
U"🤱🏾",
|
||||
U"🤱🏿",
|
||||
U"🤲",
|
||||
U"🤲🏻",
|
||||
U"🤲🏼",
|
||||
U"🤲🏽",
|
||||
U"🤲🏾",
|
||||
U"🤲🏿",
|
||||
U"🤳",
|
||||
U"🤳🏻",
|
||||
U"🤳🏼",
|
||||
U"🤳🏽",
|
||||
U"🤳🏾",
|
||||
U"🤳🏿",
|
||||
U"🤴",
|
||||
U"🤴🏻",
|
||||
U"🤴🏼",
|
||||
U"🤴🏽",
|
||||
U"🤴🏾",
|
||||
U"🤴🏿",
|
||||
U"🤵",
|
||||
U"🤵♀",
|
||||
U"🤵♀️",
|
||||
U"🤵♂",
|
||||
U"🤵♂️",
|
||||
U"🤵🏻",
|
||||
U"🤵🏻♀",
|
||||
U"🤵🏻♀️",
|
||||
U"🤵🏻♂",
|
||||
U"🤵🏻♂️",
|
||||
U"🤵🏼",
|
||||
U"🤵🏼♀",
|
||||
U"🤵🏼♀️",
|
||||
U"🤵🏼♂",
|
||||
U"🤵🏼♂️",
|
||||
U"🤵🏽",
|
||||
U"🤵🏽♀",
|
||||
U"🤵🏽♀️",
|
||||
U"🤵🏽♂",
|
||||
U"🤵🏽♂️",
|
||||
U"🤵🏾",
|
||||
U"🤵🏾♀",
|
||||
U"🤵🏾♀️",
|
||||
U"🤵🏾♂",
|
||||
U"🤵🏾♂️",
|
||||
U"🤵🏿",
|
||||
U"🤵🏿♀",
|
||||
U"🤵🏿♀️",
|
||||
U"🤵🏿♂",
|
||||
U"🤵🏿♂️",
|
||||
U"🤶",
|
||||
U"🤶🏻",
|
||||
U"🤶🏼",
|
||||
U"🤶🏽",
|
||||
U"🤶🏾",
|
||||
U"🤶🏿",
|
||||
U"🤷",
|
||||
U"🤷♀",
|
||||
U"🤷♀️",
|
||||
U"🤷♂",
|
||||
U"🤷♂️",
|
||||
U"🤷🏻",
|
||||
U"🤷🏻♀",
|
||||
U"🤷🏻♀️",
|
||||
U"🤷🏻♂",
|
||||
U"🤷🏻♂️",
|
||||
U"🤷🏼",
|
||||
U"🤷🏼♀",
|
||||
U"🤷🏼♀️",
|
||||
U"🤷🏼♂",
|
||||
U"🤷🏼♂️",
|
||||
U"🤷🏽",
|
||||
U"🤷🏽♀",
|
||||
U"🤷🏽♀️",
|
||||
U"🤷🏽♂",
|
||||
U"🤷🏽♂️",
|
||||
U"🤷🏾",
|
||||
U"🤷🏾♀",
|
||||
U"🤷🏾♀️",
|
||||
U"🤷🏾♂",
|
||||
U"🤷🏾♂️",
|
||||
U"🤷🏿",
|
||||
U"🤷🏿♀",
|
||||
U"🤷🏿♀️",
|
||||
U"🤷🏿♂",
|
||||
U"🤷🏿♂️",
|
||||
U"🤸",
|
||||
U"🤸♀",
|
||||
U"🤸♀️",
|
||||
U"🤸♂",
|
||||
U"🤸♂️",
|
||||
U"🤸🏻",
|
||||
U"🤸🏻♀",
|
||||
U"🤸🏻♀️",
|
||||
U"🤸🏻♂",
|
||||
U"🤸🏻♂️",
|
||||
U"🤸🏼",
|
||||
U"🤸🏼♀",
|
||||
U"🤸🏼♀️",
|
||||
U"🤸🏼♂",
|
||||
U"🤸🏼♂️",
|
||||
U"🤸🏽",
|
||||
U"🤸🏽♀",
|
||||
U"🤸🏽♀️",
|
||||
U"🤸🏽♂",
|
||||
U"🤸🏽♂️",
|
||||
U"🤸🏾",
|
||||
U"🤸🏾♀",
|
||||
U"🤸🏾♀️",
|
||||
U"🤸🏾♂",
|
||||
U"🤸🏾♂️",
|
||||
U"🤸🏿",
|
||||
U"🤸🏿♀",
|
||||
U"🤸🏿♀️",
|
||||
U"🤸🏿♂",
|
||||
U"🤸🏿♂️",
|
||||
U"🤹",
|
||||
U"🤹♀",
|
||||
U"🤹♀️",
|
||||
U"🤹♂",
|
||||
U"🤹♂️",
|
||||
U"🤹🏻",
|
||||
U"🤹🏻♀",
|
||||
U"🤹🏻♀️",
|
||||
U"🤹🏻♂",
|
||||
U"🤹🏻♂️",
|
||||
U"🤹🏼",
|
||||
U"🤹🏼♀",
|
||||
U"🤹🏼♀️",
|
||||
U"🤹🏼♂",
|
||||
U"🤹🏼♂️",
|
||||
U"🤹🏽",
|
||||
U"🤹🏽♀",
|
||||
U"🤹🏽♀️",
|
||||
U"🤹🏽♂",
|
||||
U"🤹🏽♂️",
|
||||
U"🤹🏾",
|
||||
U"🤹🏾♀",
|
||||
U"🤹🏾♀️",
|
||||
U"🤹🏾♂",
|
||||
U"🤹🏾♂️",
|
||||
U"🤹🏿",
|
||||
U"🤹🏿♀",
|
||||
U"🤹🏿♀️",
|
||||
U"🤹🏿♂",
|
||||
U"🤹🏿♂️",
|
||||
};
|
||||
bool failed = false;
|
||||
auto array_size = sizeof(emojis) / sizeof(emojis[0]);
|
||||
for (size_t i = 0; i < array_size; i++) {
|
||||
auto e = emojis[i];
|
||||
int foo;
|
||||
auto answer = fast_float::from_chars(e.data(), e.data() + e.size(), foo);
|
||||
if (answer.ec == std::errc()) {
|
||||
failed = true;
|
||||
std::cerr << "Incorrectly parsed emoji #" << i << " as integer " << foo
|
||||
<< "." << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (failed) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
@ -1111,4 +842,4 @@ int main() {
|
||||
std::cerr << "The test requires C++17." << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@ -131,7 +131,7 @@ int main() {
|
||||
for (std::size_t i = 0; i < reject.size(); ++i) {
|
||||
auto const &f = reject[i].input;
|
||||
auto const &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(),
|
||||
fast_float::parse_options(
|
||||
fast_float::chars_format::json |
|
||||
|
||||
@ -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;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user