pax_global_header00006660000000000000000000000064147517054660014531gustar00rootroot0000000000000052 comment=77cc847c842c49e7e3477c1e95da2b6540166d66 fast_float-8.0.0/000077500000000000000000000000001475170546600136605ustar00rootroot00000000000000fast_float-8.0.0/.bazelrc000066400000000000000000000000351475170546600153010ustar00rootroot00000000000000build --cxxopt="--std=c++17" fast_float-8.0.0/.cirrus.yml000066400000000000000000000006321475170546600157710ustar00rootroot00000000000000 task: timeout_in: 120m freebsd_instance: matrix: - image_family: freebsd-13-0-snap env: ASSUME_ALWAYS_YES: YES setup_script: - pkg update -f - pkg install bash - pkg install cmake - pkg install git build_script: - mkdir build - cd build - cmake -DFASTFLOAT_TEST=ON .. - make test_script: - cd build - ctest --output-on-failure -R basictest fast_float-8.0.0/.clang-format000066400000000000000000000001371475170546600162340ustar00rootroot00000000000000BasedOnStyle: LLVM SortIncludes: false SeparateDefinitionBlocks: Always MaxEmptyLinesToKeep: 1 fast_float-8.0.0/.github/000077500000000000000000000000001475170546600152205ustar00rootroot00000000000000fast_float-8.0.0/.github/dependabot.yml000066400000000000000000000011031475170546600200430ustar00rootroot00000000000000# Keep GitHub Actions up to date with GitHub's Dependabot... # https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot # https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem version: 2 updates: - package-ecosystem: github-actions directory: / groups: github-actions: patterns: - "*" # Group all Actions updates into a single larger pull request schedule: interval: weekly fast_float-8.0.0/.github/pull_request_template.md000066400000000000000000000012501475170546600221570ustar00rootroot00000000000000We 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: ```bash bash script/run-clangcldocker.sh ``` Make sure that you have [docker installed and running](https://docs.docker.com/engine/install/) on your system. Most Linux distributions support docker though some (like RedHat) have the equivalent (Podman). Users of Apple systems may want to [consider OrbStack](https://orbstack.dev). You do not need to familiar with docker, you just need to make sure that you are have it running. If you are unable to format the code, we may format it for you. fast_float-8.0.0/.github/workflows/000077500000000000000000000000001475170546600172555ustar00rootroot00000000000000fast_float-8.0.0/.github/workflows/alpine.yml000066400000000000000000000020661475170546600212540ustar00rootroot00000000000000name: Alpine Linux on: - push - pull_request jobs: build: name: Build on Alpine ${{ matrix.arch }} runs-on: ubuntu-latest strategy: matrix: arch: - x86_64 - x86 - aarch64 - armv7 - ppc64le - riscv64 steps: - name: Checkout repository uses: actions/checkout@v4 - name: Install latest Alpine Linux for ${{ matrix.arch }} uses: jirutka/setup-alpine@v1 with: arch: ${{ matrix.arch }} branch: ${{ matrix.arch == 'riscv64' && 'edge' || 'latest-stable' }} packages: > build-base cmake g++ linux-headers git bash build-base - name: Prepare run: | cmake -DFASTFLOAT_TEST=ON -B build shell: alpine.sh {0} - name: Build run: | cmake --build build shell: alpine.sh {0} - name: Test run: | ctest --test-dir build -R basictest shell: alpine.sh {0} fast_float-8.0.0/.github/workflows/amalgamate-ubuntu20.yml000066400000000000000000000007021475170546600235520ustar00rootroot00000000000000name: Amalgamate Ubuntu 20.04 CI (GCC 9) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v4 - name: Compile with amalgamation run: | mkdir build && mkdir build/fast_float && python3 ./script/amalgamate.py > build/fast_float/fast_float.h && cp tests/string_test.cpp build/ && cd build && g++ string_test.cpp fast_float-8.0.0/.github/workflows/cifuzz.yml000066400000000000000000000017651475170546600213230ustar00rootroot00000000000000name: CIFuzz on: [pull_request] jobs: Fuzzing: runs-on: ubuntu-latest permissions: security-events: write steps: - name: Build Fuzzers id: build uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master with: oss-fuzz-project-name: 'fast_float' language: c++ - name: Run Fuzzers uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master with: oss-fuzz-project-name: 'fast_float' language: c++ fuzz-seconds: 300 output-sarif: true - name: Upload Crash 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@v3 with: # Path to SARIF file relative to the root of the repository sarif_file: cifuzz-sarif/results.sarif checkout_path: cifuzz-sarif category: CIFuzz fast_float-8.0.0/.github/workflows/lint_and_format_check.yml000066400000000000000000000012601475170546600242740ustar00rootroot00000000000000name: Lint and format on: pull_request: types: [opened, synchronize, reopened, ready_for_review] paths-ignore: - '**.md' - 'docs/**' push: branches: - main paths-ignore: - '**.md' - 'docs/**' permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: lint-and-format: runs-on: ubuntu-latest steps: - uses: actions/checkout@9a9194f87191a7e9055e3e9b95b8cfb13023bb08 # v4.1.7 - name: Run clang-format uses: jidicula/clang-format-action@d05cecd4a1a5b7e64c22f5a468456135a43f13f6 # v4.14.0 with: clang-format-version: '17' fast_float-8.0.0/.github/workflows/msys2-clang.yml000066400000000000000000000022241475170546600221370ustar00rootroot00000000000000name: MSYS2-CLANG-CI on: [push, pull_request] jobs: windows-mingw: name: ${{ matrix.msystem }} runs-on: windows-latest defaults: run: shell: msys2 {0} strategy: fail-fast: false matrix: include: - msystem: "MINGW64" install: mingw-w64-x86_64-libxml2 mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang type: Release - msystem: "MINGW32" install: mingw-w64-i686-libxml2 mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang type: Release env: CMAKE_GENERATOR: Ninja steps: - uses: actions/checkout@v4 - uses: msys2/setup-msys2@v2 with: update: true msystem: ${{ matrix.msystem }} install: ${{ matrix.install }} - name: Prepare build dir run: mkdir build - name: Configure run: cd build && cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON .. - name: Build run: cmake --build build - name: Run basic tests run: cd build && ctest --output-on-failure -R basictest fast_float-8.0.0/.github/workflows/msys2.yml000066400000000000000000000025251475170546600210610ustar00rootroot00000000000000name: MSYS2-CI on: [push, pull_request] jobs: windows-mingw: name: ${{ matrix.msystem }} runs-on: windows-latest defaults: run: shell: msys2 {0} strategy: fail-fast: false matrix: include: - msystem: "MINGW64" install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc type: Release - msystem: "MINGW32" install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc type: Release - msystem: "MINGW64" install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc type: Debug - msystem: "MINGW32" install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc type: Debug env: CMAKE_GENERATOR: Ninja steps: - uses: actions/checkout@v4 - uses: msys2/setup-msys2@v2 with: update: true msystem: ${{ matrix.msystem }} install: ${{ matrix.install }} - name: Prepare build dir run: mkdir build - name: Configure run: cd build && cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} -DFASTFLOAT_TEST=ON .. - name: Build run: cmake --build build - name: Run basic tests run: cd build && ctest --output-on-failure -R basictest fast_float-8.0.0/.github/workflows/on-release.yml000066400000000000000000000017411475170546600220350ustar00rootroot00000000000000name: On Release # By default, a workflow only has read permissions. # Add the needed permission to write release assets permissions: contents: write on: release: types: - published jobs: build: name: Add Release Assets runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Amalgamate fast_float.h run: | mkdir build mkdir build/fast_float python3 ./script/amalgamate.py > build/fast_float/fast_float.h - name: Test Amalgamation run: | cp tests/string_test.cpp build/ cd build g++ string_test.cpp - name: Upload Release Asset uses: actions/upload-release-asset@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: upload_url: ${{ github.event.release.upload_url }} asset_path: build/fast_float/fast_float.h asset_name: fast_float.h asset_content_type: text/plain fast_float-8.0.0/.github/workflows/s390x.yml000066400000000000000000000012231475170546600206640ustar00rootroot00000000000000name: Ubuntu s390x (GCC 11) on: push: branches: - master pull_request: branches: - master jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: uraimo/run-on-arch-action@v2 name: Test id: runcmd with: arch: s390x githubToken: ${{ github.token }} distro: ubuntu_latest install: | apt-get update -q -y apt-get install -y cmake make g++ run: | cmake -DCMAKE_BUILD_TYPE=Release -B build cmake --build build -j=2 ctest --output-on-failure --test-dir build fast_float-8.0.0/.github/workflows/ubuntu20-cxx20.yml000066400000000000000000000007431475170546600224320ustar00rootroot00000000000000name: Ubuntu 20.04 CI (C++20) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-20.04 strategy: fail-fast: false steps: - uses: actions/checkout@v4 - name: Use cmake run: | mkdir build && cd build && cmake -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination .. && cmake --build . && ctest --output-on-failure && cmake --install . fast_float-8.0.0/.github/workflows/ubuntu20-fastmath.yml000066400000000000000000000006051475170546600232720ustar00rootroot00000000000000name: 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 fast_float-8.0.0/.github/workflows/ubuntu20.yml000066400000000000000000000014311475170546600214630ustar00rootroot00000000000000name: 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 . fast_float-8.0.0/.github/workflows/ubuntu22-clang.yml000066400000000000000000000013261475170546600225520ustar00rootroot00000000000000name: Ubuntu 22.04 CI (clang 14) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Install clang++-14 run: sudo apt-get install -y clang++-14 - name: Use cmake run: | mkdir build && cd build && CXX=clang++-14 cmake -DFASTFLOAT_TEST=ON .. && cmake --build . && ctest --output-on-failure - name: Use cmake CXX20 run: | mkdir build20 && cd build20 && CXX=clang++-14 cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. && cmake --build . && ctest --output-on-failurefast_float-8.0.0/.github/workflows/ubuntu22-gcc12.yml000066400000000000000000000012351475170546600223640ustar00rootroot00000000000000name: Ubuntu 22.04 CI (GCC 12) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Use cmake run: | mkdir build && cd build && CXX=g++-12 CXXFLAGS=-Werror cmake -DFASTFLOAT_TEST=ON .. && cmake --build . && ctest --output-on-failure - name: Use cmake CXX20 run: | mkdir build20 && cd build20 && CXX=g++-12 CXXFLAGS=-Werror cmake -DFASTFLOAT_CONSTEXPR_TESTS=ON -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON .. && cmake --build . && ctest --output-on-failurefast_float-8.0.0/.github/workflows/ubuntu22-sanitize.yml000066400000000000000000000005761475170546600233220ustar00rootroot00000000000000name: Ubuntu 22.04 CI Sanitized (GCC 11) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Use cmake run: | mkdir build && cd build && cmake -DFASTFLOAT_TEST=ON -D FASTFLOAT_SANITIZE=ON .. && cmake --build . && ctest --output-on-failurefast_float-8.0.0/.github/workflows/ubuntu22.yml000066400000000000000000000005331475170546600214670ustar00rootroot00000000000000name: Ubuntu 22.04 CI (GCC 11) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - name: Use cmake run: | mkdir build && cd build && cmake -DFASTFLOAT_TEST=ON .. && cmake --build . && ctest --output-on-failurefast_float-8.0.0/.github/workflows/ubuntu24.yml000066400000000000000000000013011475170546600214630ustar00rootroot00000000000000name: Ubuntu 24.04 CI (GCC 13) on: [push, pull_request] jobs: ubuntu-build: runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v4 - name: Use cmake run: | 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: | 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-failurefast_float-8.0.0/.github/workflows/vs17-arm-ci.yml000066400000000000000000000015011475170546600217430ustar00rootroot00000000000000name: VS17-ARM-CI on: [push, pull_request] jobs: ci: name: vs17/${{matrix.arch}}/${{matrix.cfg}} runs-on: windows-latest strategy: fail-fast: false matrix: include: - {gen: Visual Studio 17 2022, arch: ARM64, cfg: Release} - {gen: Visual Studio 17 2022, arch: ARM64, cfg: Debug} steps: - name: checkout uses: actions/checkout@v4 - name: configure run: | cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_CROSSCOMPILING=1 -DFASTFLOAT_TEST=ON - name: build run: | cmake --build build --verbose --config ${{matrix.cfg}} --parallel # disabled because it requires a toolchain #- name: test # run: | # cd build && # ctest --output-on-failure -C ${{matrix.cfg}} fast_float-8.0.0/.github/workflows/vs17-ci.yml000066400000000000000000000031371475170546600211750ustar00rootroot00000000000000name: VS17-CI on: [push, pull_request] jobs: ci: name: vs17/${{matrix.arch}}/${{matrix.cfg}} runs-on: windows-latest strategy: fail-fast: false matrix: include: - {gen: Visual Studio 17 2022, arch: Win32, cfg: Release} #- {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Release} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug} steps: - name: checkout uses: actions/checkout@v4 - name: configure run: | cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_TEST=ON -DCMAKE_INSTALL_PREFIX:PATH=destination - name: build run: | cmake --build build --verbose --config ${{matrix.cfg}} --parallel - name: test run: | cd build && ctest --output-on-failure -C ${{matrix.cfg}} - name: install run: | cd build && cmake --install . - name: test install (find) run: | cd tests/installation_tests/find && cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination && cmake --build build --verbose --config ${{matrix.cfg}} --parallel - name: test install (issue 72) run: | cd tests/installation_tests/issue72_installation && cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DCMAKE_INSTALL_PREFIX:PATH=../../../build/destination && cmake --build build --verbose --config ${{matrix.cfg}} --parallel fast_float-8.0.0/.github/workflows/vs17-clang-ci.yml000066400000000000000000000016131475170546600222540ustar00rootroot00000000000000name: VS17-CLANG-CI on: [push, pull_request] jobs: ci: name: vs17/${{matrix.arch}}/${{matrix.cfg}} runs-on: windows-latest strategy: fail-fast: false matrix: include: - {gen: Visual Studio 17 2022, arch: Win32, cfg: Release} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Release} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug} steps: - name: checkout uses: actions/checkout@v4 - name: Configure run: | cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -DFASTFLOAT_TEST=ON - name: Build run: cmake --build build --config ${{matrix.cfg}} --parallel --verbose - name: Run basic tests run: | cd build ctest -C ${{matrix.cfg}} --output-on-failure -R basictest fast_float-8.0.0/.github/workflows/vs17-cxx20.yml000066400000000000000000000020031475170546600215350ustar00rootroot00000000000000name: VS17-CI C++20 on: [push, pull_request] jobs: ci: name: vs17/${{matrix.arch}}/${{matrix.cfg}} runs-on: windows-latest strategy: fail-fast: false matrix: include: - {gen: Visual Studio 17 2022, arch: Win32, cfg: Release} - {gen: Visual Studio 17 2022, arch: Win32, cfg: Debug} - {gen: Visual Studio 17 2022, arch: x64, cfg: Release} - {gen: Visual Studio 17 2022, arch: x64, cfg: Debug} steps: - name: checkout uses: actions/checkout@v4 - name: configure run: >- cmake -S . -B build -G "${{matrix.gen}}" -A ${{matrix.arch}} -DFASTFLOAT_CXX_STANDARD=20 -DFASTFLOAT_TEST=ON -DFASTFLOAT_CONSTEXPR_TESTS=ON -DCMAKE_INSTALL_PREFIX:PATH=destination - name: build run: | cmake --build build --verbose --config ${{matrix.cfg}} --parallel - name: test run: | cd build && ctest --output-on-failure -C ${{matrix.cfg}} fast_float-8.0.0/.gitignore000066400000000000000000000003451475170546600156520ustar00rootroot00000000000000build/* Testing/* .cache/ compile_commands.json bazel-* # Visual studio .vs/ Debug/ Release/ /out/ *.sln *.vcxproj *.vcxproj.filters *.vcxproj.user *.psess *.vspx *.vsp *.diagsession *.hint # VS CMake /out/ /CMakeSettings.json fast_float-8.0.0/.travis.yml000066400000000000000000000124241475170546600157740ustar00rootroot00000000000000language: cpp dist: bionic cache: directories: - $HOME/.dep_cache env: global: - fastfloat_DEPENDENCY_CACHE_DIR=$HOME/.dep_cache services: - docker # the ppc64le and s390x images use cmake 3.10, but fast_float requires 3.11. # so we compile cmake from source in those images. # - tried the kitware ppa but that is using 3.10 as well # - tried also using snap to get a more recent version but that failed with # udev errors. matrix: include: - arch: ppc64le os: linux env: - CMAKE_SRC="https://github.com/Kitware/CMake/releases/download/v3.11.4/cmake-3.11.4.tar.gz" - arch: s390x os: linux env: - CMAKE_SRC="https://github.com/Kitware/CMake/releases/download/v3.11.4/cmake-3.11.4.tar.gz" - arch: amd64 os: linux - arch: amd64 os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-8 env: - COMPILER="CC=gcc-8 && CXX=g++-8" compiler: gcc-8 - arch: amd64 os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-9 env: - COMPILER="CC=gcc-9 && CXX=g++-9" compiler: gcc-9 - arch: amd64 os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-10 env: - COMPILER="CC=gcc-10 && CXX=g++-10" compiler: gcc-10 - arch: amd64 os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-10 env: - COMPILER="CC=gcc-10 && CXX=g++-10" - SANITIZE="on" compiler: gcc-10-sanitize - arch: amd64 os: linux addons: apt: sources: - ubuntu-toolchain-r-test packages: - g++-10 env: - COMPILER="CC=gcc-10 && CXX=g++-10" - STATIC="on" acompiler: gcc-10-static - arch: amd64 os: linux addons: apt: sources: - llvm-toolchain-bionic-6.0 packages: - clang-6.0 env: - COMPILER="CC=clang-6.0 && CXX=clang++-6.0" compiler: clang-6 - arch: amd64 os: linux addons: apt: sources: - llvm-toolchain-bionic-7 packages: - clang-7 env: - COMPILER="CC=clang-7 && CXX=clang++-7" compiler: clang-7 - arch: amd64 os: linux addons: apt: sources: - llvm-toolchain-bionic-8 packages: - clang-8 env: - COMPILER="CC=clang-8 && CXX=clang++-8" compiler: clang-8 - arch: amd64 os: linux addons: apt: sources: - llvm-toolchain-bionic-9 packages: - clang-9 env: - COMPILER="CC=clang-9 && CXX=clang++-9" compiler: clang-9 - arch: amd64 os: linux addons: apt: packages: - clang-10 sources: - ubuntu-toolchain-r-test - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' env: - COMPILER="CC=clang-10 && CXX=clang++-10" compiler: clang-10 - arch: amd64 os: linux addons: apt: packages: - clang-10 sources: - ubuntu-toolchain-r-test - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' env: - COMPILER="CC=clang-10 && CXX=clang++-10" - STATIC="on" compiler: clang-10-static - arch: amd64 os: linux addons: apt: packages: - clang-10 sources: - ubuntu-toolchain-r-test - sourceline: 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main' key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key' env: - COMPILER="CC=clang-10 && CXX=clang++-10" - SANITIZE="on" compiler: clang-10-sanitize - arch: amd64 os: linux env: - TOOLCHAIN="mips64" - arch: amd64 os: linux env: - TOOLCHAIN="riscv64" before_install: - eval "${COMPILER}" - | if [ "$TOOLCHAIN" != "" ] ; then docker pull ahuszagh/cross:"$TOOLCHAIN" fi install: - | if [ "$CMAKE_SRC" != "" ] ; then set -x set -e sudo -E apt remove --purge cmake sudo -E apt-get update sudo -E apt-get install -y build-essential libssl-dev mkdir cmake_src pushd cmake_src wget "$CMAKE_SRC" tar xfz $(basename "$CMAKE_SRC") pushd $(basename "$CMAKE_SRC" | sed "s:.tar.gz::") ./bootstrap make -j2 sudo make install popd popd set +x fi - echo ${PATH} - which cmake - cmake --version - which ${CC} - ${CC} --version - which ${CXX} - ${CXX} --version script: - | if [ "$TOOLCHAIN" != "" ] ; then docker run -v "$(pwd)":/ff ahuszagh/cross:"$TOOLCHAIN" /bin/bash -c "cd ff && ci/script.sh $TOOLCHAIN" else ci/script.sh fi fast_float-8.0.0/AUTHORS000066400000000000000000000000441475170546600147260ustar00rootroot00000000000000Daniel Lemire João Paulo Magalhaes fast_float-8.0.0/BUILD.bazel000066400000000000000000000002441475170546600155360ustar00rootroot00000000000000cc_library( name = "fast_float", hdrs = glob(["include/fast_float/*.h"]), strip_include_prefix = "include", visibility = ["//visibility:public"], ) fast_float-8.0.0/CMakeLists.txt000066400000000000000000000066001475170546600164220ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.9) 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) if(FASTFLOAT_TEST) enable_testing() add_subdirectory(tests) else(FASTFLOAT_TEST) message(STATUS "Tests are disabled. Set FASTFLOAT_TEST to ON to run tests.") endif(FASTFLOAT_TEST) option(FASTFLOAT_SANITIZE "Sanitize addresses" OFF) if (NOT CMAKE_BUILD_TYPE) if(FASTFLOAT_SANITIZE) set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) else() message(STATUS "No build type selected, default to Release") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) endif() endif() option(FASTFLOAT_INSTALL "Enable install" ON) if(FASTFLOAT_INSTALL) include(GNUInstallDirs) endif() add_library(fast_float INTERFACE) option(FASTFLOAT_BENCHMARKS "Enable benchmarks" OFF) if(FASTFLOAT_BENCHMARKS) add_subdirectory(benchmarks) else(FASTFLOAT_BENCHMARKS) message(STATUS "Benchmarks are disabled. Set FASTFLOAT_BENCHMARKS to ON to build benchmarks (assumes C++17).") endif(FASTFLOAT_BENCHMARKS) add_library(FastFloat::fast_float ALIAS fast_float) target_include_directories( fast_float INTERFACE $ $ ) target_compile_features(fast_float INTERFACE cxx_std_11) if(FASTFLOAT_SANITIZE) target_compile_options(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all) target_link_libraries(fast_float INTERFACE -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all) if (CMAKE_COMPILER_IS_GNUCC) target_link_libraries(fast_float INTERFACE -fuse-ld=gold) endif() endif() if(MSVC_VERSION GREATER 1910) target_compile_options(fast_float INTERFACE /permissive-) endif() if(FASTFLOAT_INSTALL) include(CMakePackageConfigHelpers) set(FASTFLOAT_VERSION_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfigVersion.cmake") set(FASTFLOAT_PROJECT_CONFIG "${CMAKE_CURRENT_BINARY_DIR}/module/FastFloatConfig.cmake") set(FASTFLOAT_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_DATAROOTDIR}/cmake/FastFloat") if(${CMAKE_VERSION} VERSION_LESS "3.14") write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion) else() write_basic_package_version_file("${FASTFLOAT_VERSION_CONFIG}" VERSION ${PROJECT_VERSION} COMPATIBILITY SameMajorVersion ARCH_INDEPENDENT) endif() configure_package_config_file("cmake/config.cmake.in" "${FASTFLOAT_PROJECT_CONFIG}" INSTALL_DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}") install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/fast_float" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") install(FILES "${FASTFLOAT_PROJECT_CONFIG}" "${FASTFLOAT_VERSION_CONFIG}" DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}") install(EXPORT ${PROJECT_NAME}-targets NAMESPACE FastFloat:: DESTINATION "${FASTFLOAT_CONFIG_INSTALL_DIR}") install(TARGETS fast_float EXPORT ${PROJECT_NAME}-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) endif() fast_float-8.0.0/CONTRIBUTORS000066400000000000000000000002361475170546600155410ustar00rootroot00000000000000Eugene Golushkov Maksim Kita Marcin Wojdyr Neal Richardson Tim Paine Fabio Pellacini Lénárd Szolnoki Jan Pharago Maya Warrier Taha Khokhar Anders Dalvander fast_float-8.0.0/LICENSE-APACHE000066400000000000000000000250171475170546600156110ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2021 The fast_float authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. fast_float-8.0.0/LICENSE-BOOST000066400000000000000000000024711475170546600155550ustar00rootroot00000000000000Boost Software License - Version 1.0 - August 17th, 2003 Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following: The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.fast_float-8.0.0/LICENSE-MIT000066400000000000000000000020671475170546600153210ustar00rootroot00000000000000MIT License Copyright (c) 2021 The fast_float authors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. fast_float-8.0.0/MODULE.bazel000066400000000000000000000003371475170546600156670ustar00rootroot00000000000000"""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) fast_float-8.0.0/README.md000066400000000000000000000464421475170546600151510ustar00rootroot00000000000000 ## fast_float number parsing library: 4x faster than strtod [![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++ from_chars functions for `float` and `double` types as well as integer types. These functions convert ASCII strings representing decimal values (e.g., `1.3e10`) into binary types. We provide exact rounding (including round to even). In our experience, these `fast_float` functions many times faster than comparable number-parsing functions from existing C++ standard libraries. Specifically, `fast_float` provides the following two functions to parse floating-point numbers with a C++17-like syntax (the library itself only requires C++11): ```C++ from_chars_result from_chars(char const *first, char const *last, float &value, ...); from_chars_result from_chars(char const *first, char const *last, double &value, ...); ``` You can also parse integer types: ```C++ from_chars_result from_chars(char const *first, char const *last, int &value, ...); from_chars_result from_chars(char const *first, char const *last, unsigned &value, ...); ``` The return type (`from_chars_result`) is defined as the struct: ```C++ struct from_chars_result { char const *ptr; std::errc ec; }; ``` It parses the character sequence `[first, last)` for a number. It parses floating-point numbers expecting a locale-independent format equivalent to the C++17 from_chars function. The resulting floating-point value is the closest floating-point values (using either `float` or `double`), using the "round to even" convention for values that would otherwise fall right in-between two values. That is, we provide exact parsing according to the IEEE standard. Given a successful parse, the pointer (`ptr`) in the returned value is set to point right after the parsed number, and the `value` referenced is set to the parsed value. In case of error, the returned `ec` contains a representative error, otherwise the default (`std::errc()`) value is stored. The implementation does not throw and does not allocate memory (e.g., with `new` or `malloc`). It will parse infinity and nan values. Example: ```C++ #include "fast_float/fast_float.h" #include int main() { std::string input = "3.1416 xyz "; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; return EXIT_SUCCESS; } ``` You can parse delimited numbers: ```C++ std::string input = "234532.3426362,7869234.9823,324562.645"; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); if (answer.ec != std::errc()) { // check error } // we have result == 234532.3426362. if (answer.ptr[0] != ',') { // unexpected delimiter } answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); if (answer.ec != std::errc()) { // check error } // we have result == 7869234.9823. if (answer.ptr[0] != ',') { // unexpected delimiter } answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); if (answer.ec != std::errc()) { // check error } // we have result == 324562.645. ``` 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 `fast_float::chars_format::skip_white_space` is set). * [A leading `+` sign](https://en.cppreference.com/w/cpp/utility/from_chars) is forbidden (unless `fast_float::chars_format::allow_leading_plus` is set). * It is generally impossible to represent a decimal value exactly as binary floating-point number (`float` and `double` types). We seek the nearest value. We round to an even mantissa when we are in-between two binary floating-point numbers. Furthermore, we have the following restrictions: * We support `float` and `double`, but not `long double`. We also support fixed-width floating-point types such as `std::float64_t`, `std::float32_t`, `std::float16_t`, and `std::bfloat16_t`. * We only support the decimal format: we do not support hexadecimal strings. * For values that are either very large or very small (e.g., `1e9999`), we represent it using the infinity or negative infinity value and the returned `ec` is set to `std::errc::result_out_of_range`. We support Visual Studio, macOS, Linux, freeBSD. We support big and little endian. We support 32-bit and 64-bit systems. We assume that the rounding mode is set to nearest (`std::fegetround() == FE_TONEAREST`). ## Integer types You can also parse integer types using different bases (e.g., 2, 10, 16). The following code will print the number 22250738585072012 three times: ```C++ #include "fast_float/fast_float.h" #include int main() { uint64_t i; std::string str = "22250738585072012"; auto answer = fast_float::from_chars(str.data(), str.data() + str.size(), i); if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << i << std::endl; std::string binstr = "1001111000011001110110111001001010110100111000110001100"; answer = fast_float::from_chars(binstr.data(), binstr.data() + binstr.size(), i, 2); if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << i << std::endl; std::string hexstr = "4f0cedc95a718c"; answer = fast_float::from_chars(hexstr.data(), hexstr.data() + hexstr.size(), i, 16); if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << i << std::endl; return EXIT_SUCCESS; } ``` ## Behavior of result_out_of_range When parsing floating-point values, the numbers can sometimes be too small (e.g., `1e-1000`) or too large (e.g., `1e1000`). The C language established the precedent that these small values are out of range. In such cases, it is customary to parse small values to zero and large values to infinity. That is the behaviour of the C language (e.g., `stdtod`). That is the behaviour followed by the fast_float library. Specifically, we follow Jonathan Wakely's interpretation of the standard: > In any case, the resulting value is one of at most two floating-point values > closest to the value of the string matching the pattern. It is also the approach taken by the [Microsoft C++ library](https://github.com/microsoft/STL/blob/62205ab155d093e71dd9588a78f02c5396c3c14b/tests/std/tests/P0067R5_charconv/test.cpp#L943-L946). Hence, we have the following examples: ```cpp double result = -1; std::string str = "3e-1000"; auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); // r.ec == std::errc::result_out_of_range // r.ptr == str.data() + 7 // result == 0 ``` ```cpp double result = -1; std::string str = "3e1000"; auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); // r.ec == std::errc::result_out_of_range // r.ptr == str.data() + 6 // result == std::numeric_limits::infinity() ``` Users who wish for the value to be left unmodified given `std::errc::result_out_of_range` may do so by adding two lines of code: ```cpp double old_result = result; // make copy auto r = fast_float::from_chars(start, end, result); if (r.ec == std::errc::result_out_of_range) { result = old_result; } ``` ## C++20: compile-time evaluation (constexpr) In C++20, you may use `fast_float::from_chars` to parse strings at compile-time, as in the following example: ```C++ // consteval forces compile-time evaluation of the function in C++20. consteval double parse(std::string_view input) { double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); if (answer.ec != std::errc()) { return -1.0; } return result; } // This function should compile to a function which // merely returns 3.1415. constexpr double constexptest() { return parse("3.1415 input"); } ``` ## C++23: Fixed width floating-point types The library also supports fixed-width floating-point types such as `std::float64_t`, `std::float32_t`, `std::float16_t`, and `std::bfloat16_t`. E.g., you can write: ```C++ std::float32_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); ``` ## Non-ASCII Inputs We also support UTF-16 and UTF-32 inputs, as well as ASCII/UTF-8, as in the following example: ```C++ #include "fast_float/fast_float.h" #include int main() { std::u16string input = u"3.1416 xyz "; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); if (answer.ec != std::errc()) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; return EXIT_SUCCESS; } ``` ## Advanced options: using commas as decimal separator, JSON and Fortran The C++ standard stipulate that `from_chars` has to be locale-independent. In particular, the decimal separator has to be the period (`.`). However, some users still want to use the `fast_float` library with in a locale-dependent manner. Using a separate function called `from_chars_advanced`, we allow the users to pass a `parse_options` instance which contains a custom decimal separator (e.g., the comma). You may use it as follows. ```C++ #include "fast_float/fast_float.h" #include int main() { std::string input = "3,1416 xyz "; double result; fast_float::parse_options options{fast_float::chars_format::general, ','}; auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); if ((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; return EXIT_SUCCESS; } ``` ### You can also parse Fortran-like inputs ```C++ #include "fast_float/fast_float.h" #include int main() { std::string input = "1d+4"; double result; fast_float::parse_options options{fast_float::chars_format::fortran}; auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); if ((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; return EXIT_SUCCESS; } ``` ### You may also enforce the JSON format ([RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259#section-6)) ```C++ #include "fast_float/fast_float.h" #include int main() { std::string input = "+.1"; // not valid double result; fast_float::parse_options options{fast_float::chars_format::json}; auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` By default the JSON format does not allow `inf`: ```C++ #include "fast_float/fast_float.h" #include int main() { std::string input = "inf"; // not valid in JSON double result; fast_float::parse_options options{fast_float::chars_format::json}; auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` You can allow it with a non-standard `json_or_infnan` variant: ```C++ #include "fast_float/fast_float.h" #include int main() { std::string input = "inf"; // not valid in JSON but we allow it with json_or_infnan double result; fast_float::parse_options options{fast_float::chars_format::json_or_infnan}; auto answer = fast_float::from_chars_advanced(input.data(), input.data() + input.size(), result, options); if (answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` ## Users and Related Work The fast_float library is part of: * GCC (as of version 12): the `from_chars` function in GCC relies on fast_float, * [Chromium](https://github.com/Chromium/Chromium), the engine behind Google Chrome, Microsoft Edge, and Opera, * [WebKit](https://github.com/WebKit/WebKit), the engine behind Safari (Apple's web browser), * [DuckDB](https://duckdb.org), * [Redis](https://github.com/redis/redis) and [Valkey](https://github.com/valkey-io/valkey), * [Apache Arrow](https://github.com/apache/arrow/pull/8494) where it multiplied the number parsing speed by two or three times, * [Google Jsonnet](https://github.com/google/jsonnet), * [ClickHouse](https://github.com/ClickHouse/ClickHouse). The fastfloat algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). There is a [derived implementation part of AdaCore](https://github.com/AdaCore/VSS). The fast_float library provides a performance similar to that of the [fast_double_parser](https://github.com/lemire/fast_double_parser) library but using an updated algorithm reworked from the ground up, and while offering an API more in line with the expectations of C++ programmers. The fast_double_parser library is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM). ## References * Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021. * Noble Mushtak, Daniel Lemire, [Fast Number Parsing Without Fallback](https://arxiv.org/abs/2212.06644), Software: Practice and Experience 53 (7), 2023. ## Other programming languages * [There is an R binding](https://github.com/eddelbuettel/rcppfastfloat) called `rcppfastfloat`. * [There is a Rust port of the fast_float library](https://github.com/aldanor/fast-float-rust/) called `fast-float-rust`. * [There is a Java port of the fast_float library](https://github.com/wrandelshofer/FastDoubleParser) called `FastDoubleParser`. It used for important systems such as [Jackson](https://github.com/FasterXML/jackson-core). * [There is a C# port of the fast_float library](https://github.com/CarlVerret/csFastFloat) called `csFastFloat`. ## How fast is it? It can parse random floating-point numbers at a speed of 1 GB/s on some systems. We find that it is often twice as fast as the best available competitor, and many times faster than many standard-library implementations. fast_float is many times faster than many standard-library
implementations ```bash $ ./build/benchmarks/benchmark # parsing random integers in the range [0,1) volume = 2.09808 MB netlib : 271.18 MB/s (+/- 1.2 %) 12.93 Mfloat/s doubleconversion : 225.35 MB/s (+/- 1.2 %) 10.74 Mfloat/s strtod : 190.94 MB/s (+/- 1.6 %) 9.10 Mfloat/s abseil : 430.45 MB/s (+/- 2.2 %) 20.52 Mfloat/s fastfloat : 1042.38 MB/s (+/- 9.9 %) 49.68 Mfloat/s ``` See the [Benchmarking](#benchmarking) Section for instructions on how to run our benchmarks. ## Video [![Go Systems 2020](https://img.youtube.com/vi/AVXgvlMeIm4/0.jpg)](https://www.youtube.com/watch?v=AVXgvlMeIm4) ## Using as a CMake dependency This library is header-only by design. The CMake file provides the `fast_float` target which is merely a pointer to the `include` directory. If you drop the `fast_float` repository in your CMake project, you should be able to use it in this manner: ```cmake add_subdirectory(fast_float) target_link_libraries(myprogram PUBLIC fast_float) ``` Or you may want to retrieve the dependency automatically if you have a sufficiently recent version of CMake (3.11 or better at least): ```cmake FetchContent_Declare( fast_float GIT_REPOSITORY https://github.com/fastfloat/fast_float.git GIT_TAG tags/v8.0.0 GIT_SHALLOW TRUE) FetchContent_MakeAvailable(fast_float) target_link_libraries(myprogram PUBLIC fast_float) ``` You should change the `GIT_TAG` line so that you recover the version you wish to use. You 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.0.0) ``` ## Using as single header The script `script/amalgamate.py` may be used to generate a single header version of the library if so desired. Just run the script from the root directory of this repository. You can customize the license type and output file if desired as described in the command line help. You may directly download automatically generated single-header files: ## 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 ``` ## Packages * The fast_float library is part of the [Conan package manager](https://conan.io/center/recipes/fast_float). * It is part of the [brew package manager](https://formulae.brew.sh/formula/fast_float). * Some Linux distribution like Fedora include fast_float (e.g., as `fast_float-devel`). ## Credit Though this work is inspired by many different people, this work benefited especially from exchanges with Michael Eisel, who motivated the original research with his key insights, and with Nigel Tao who provided invaluable feedback. Rémy Oudompheng first implemented a fast path we use in the case of long digits. The library includes code adapted from Google Wuffs (written by Nigel Tao) which was originally published under the Apache 2.0 license. ## License Licensed under either of Apache License, Version 2.0 or MIT license or BOOST license.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this repository by you, as defined in the Apache-2.0 license, shall be triple licensed as above, without any additional terms or conditions. fast_float-8.0.0/SECURITY.md000066400000000000000000000002761475170546600154560ustar00rootroot00000000000000# Security Policy ## Reporting a Vulnerability Please use the following contact information for reporting a vulnerability: - [Daniel Lemire](https://github.com/lemire) - daniel@lemire.me fast_float-8.0.0/benchmarks/000077500000000000000000000000001475170546600157755ustar00rootroot00000000000000fast_float-8.0.0/benchmarks/CMakeLists.txt000066400000000000000000000017531475170546600205430ustar00rootroot00000000000000add_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") fast_float-8.0.0/benchmarks/apple_arm_events.h000066400000000000000000001152651475170546600215040ustar00rootroot00000000000000// Original design from: // ============================================================================= // XNU kperf/kpc // Available for 64-bit Intel/Apple Silicon, macOS/iOS, with root privileges // // References: // // XNU source (since xnu 2422.1.72): // https://github.com/apple/darwin-xnu/blob/main/osfmk/kern/kpc.h // https://github.com/apple/darwin-xnu/blob/main/bsd/kern/kern_kpc.c // // Lightweight PET (Profile Every Thread, since xnu 3789.1.32): // https://github.com/apple/darwin-xnu/blob/main/osfmk/kperf/pet.c // https://github.com/apple/darwin-xnu/blob/main/osfmk/kperf/kperf_kpc.c // // System Private frameworks (since macOS 10.11, iOS 8.0): // /System/Library/PrivateFrameworks/kperf.framework // /System/Library/PrivateFrameworks/kperfdata.framework // // Xcode framework (since Xcode 7.0): // /Applications/Xcode.app/Contents/SharedFrameworks/DVTInstrumentsFoundation.framework // // CPU database (plist files) // macOS (since macOS 10.11): // /usr/share/kpep/.plist // iOS (copied from Xcode, since iOS 10.0, Xcode 8.0): // /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform // /DeviceSupport//DeveloperDiskImage.dmg/usr/share/kpep/.plist // // // Created by YaoYuan on 2021. // Released into the public domain (unlicense.org). // ============================================================================= #ifndef M1CYCLES_H #define M1CYCLES_H #include #include #include #include #include #include // for dlopen() and dlsym() #include // for mach_absolute_time() #include // for kdebug trace decode #include // for sysctl() #include // for usleep() struct performance_counters { double cycles; double branches; double missed_branches; double instructions; performance_counters(uint64_t c, uint64_t b, uint64_t m, uint64_t i) : cycles(c), branches(b), missed_branches(m), instructions(i) {} performance_counters(double c, double b, double m, double i) : cycles(c), branches(b), missed_branches(m), instructions(i) {} performance_counters(double init) : cycles(init), branches(init), missed_branches(init), instructions(init) {} inline performance_counters &operator-=(const performance_counters &other) { cycles -= other.cycles; branches -= other.branches; missed_branches -= other.missed_branches; instructions -= other.instructions; return *this; } inline performance_counters &min(const performance_counters &other) { cycles = other.cycles < cycles ? other.cycles : cycles; branches = other.branches < branches ? other.branches : branches; missed_branches = other.missed_branches < missed_branches ? other.missed_branches : missed_branches; instructions = other.instructions < instructions ? other.instructions : instructions; return *this; } inline performance_counters &operator+=(const performance_counters &other) { cycles += other.cycles; branches += other.branches; missed_branches += other.missed_branches; instructions += other.instructions; return *this; } inline performance_counters &operator/=(double numerator) { cycles /= numerator; branches /= numerator; missed_branches /= numerator; instructions /= numerator; return *this; } }; inline performance_counters operator-(const performance_counters &a, const performance_counters &b) { return performance_counters(a.cycles - b.cycles, a.branches - b.branches, a.missed_branches - b.missed_branches, a.instructions - b.instructions); } typedef float f32; typedef double f64; typedef int8_t i8; typedef uint8_t u8; typedef int16_t i16; typedef uint16_t u16; typedef int32_t i32; typedef uint32_t u32; typedef int64_t i64; typedef uint64_t u64; typedef size_t usize; // ----------------------------------------------------------------------------- // header (reverse engineered) // This framework wraps some sysctl calls to communicate with the kpc in kernel. // Most functions requires root privileges, or process is "blessed". // ----------------------------------------------------------------------------- // Cross-platform class constants. #define KPC_CLASS_FIXED (0) #define KPC_CLASS_CONFIGURABLE (1) #define KPC_CLASS_POWER (2) #define KPC_CLASS_RAWPMU (3) // Cross-platform class mask constants. #define KPC_CLASS_FIXED_MASK (1u << KPC_CLASS_FIXED) // 1 #define KPC_CLASS_CONFIGURABLE_MASK (1u << KPC_CLASS_CONFIGURABLE) // 2 #define KPC_CLASS_POWER_MASK (1u << KPC_CLASS_POWER) // 4 #define KPC_CLASS_RAWPMU_MASK (1u << KPC_CLASS_RAWPMU) // 8 // PMU version constants. #define KPC_PMU_ERROR (0) // Error #define KPC_PMU_INTEL_V3 (1) // Intel #define KPC_PMU_ARM_APPLE (2) // ARM64 #define KPC_PMU_INTEL_V2 (3) // Old Intel #define KPC_PMU_ARM_V2 (4) // Old ARM // The maximum number of counters we could read from every class in one go. // ARMV7: FIXED: 1, CONFIGURABLE: 4 // ARM32: FIXED: 2, CONFIGURABLE: 6 // ARM64: FIXED: 2, CONFIGURABLE: CORE_NCTRS - FIXED (6 or 8) // x86: 32 #define KPC_MAX_COUNTERS 32 // Bits for defining what to do on an action. // Defined in https://github.com/apple/darwin-xnu/blob/main/osfmk/kperf/action.h #define KPERF_SAMPLER_TH_INFO (1U << 0) #define KPERF_SAMPLER_TH_SNAPSHOT (1U << 1) #define KPERF_SAMPLER_KSTACK (1U << 2) #define KPERF_SAMPLER_USTACK (1U << 3) #define KPERF_SAMPLER_PMC_THREAD (1U << 4) #define KPERF_SAMPLER_PMC_CPU (1U << 5) #define KPERF_SAMPLER_PMC_CONFIG (1U << 6) #define KPERF_SAMPLER_MEMINFO (1U << 7) #define KPERF_SAMPLER_TH_SCHEDULING (1U << 8) #define KPERF_SAMPLER_TH_DISPATCH (1U << 9) #define KPERF_SAMPLER_TK_SNAPSHOT (1U << 10) #define KPERF_SAMPLER_SYS_MEM (1U << 11) #define KPERF_SAMPLER_TH_INSCYC (1U << 12) #define KPERF_SAMPLER_TK_INFO (1U << 13) // Maximum number of kperf action ids. #define KPERF_ACTION_MAX (32) // Maximum number of kperf timer ids. #define KPERF_TIMER_MAX (8) // x86/arm config registers are 64-bit typedef u64 kpc_config_t; /// Print current CPU identification string to the buffer (same as snprintf), /// such as "cpu_7_8_10b282dc_46". This string can be used to locate the PMC /// database in /usr/share/kpep. /// @return string's length, or negative value if error occurs. /// @note This method does not requires root privileges. /// @details sysctl get(hw.cputype), get(hw.cpusubtype), /// get(hw.cpufamily), get(machdep.cpu.model) static int (*kpc_cpu_string)(char *buf, usize buf_size); /// Get the version of KPC that's being run. /// @return See `PMU version constants` above. /// @details sysctl get(kpc.pmu_version) static u32 (*kpc_pmu_version)(void); /// Get running PMC classes. /// @return See `class mask constants` above, /// 0 if error occurs or no class is set. /// @details sysctl get(kpc.counting) static u32 (*kpc_get_counting)(void); /// Set PMC classes to enable counting. /// @param classes See `class mask constants` above, set 0 to shutdown counting. /// @return 0 for success. /// @details sysctl set(kpc.counting) static int (*kpc_set_counting)(u32 classes); /// Get running PMC classes for current thread. /// @return See `class mask constants` above, /// 0 if error occurs or no class is set. /// @details sysctl get(kpc.thread_counting) static u32 (*kpc_get_thread_counting)(void); /// Set PMC classes to enable counting for current thread. /// @param classes See `class mask constants` above, set 0 to shutdown counting. /// @return 0 for success. /// @details sysctl set(kpc.thread_counting) static int (*kpc_set_thread_counting)(u32 classes); /// Get how many config registers there are for a given mask. /// For example: Intel may returns 1 for `KPC_CLASS_FIXED_MASK`, /// returns 4 for `KPC_CLASS_CONFIGURABLE_MASK`. /// @param classes See `class mask constants` above. /// @return 0 if error occurs or no class is set. /// @note This method does not requires root privileges. /// @details sysctl get(kpc.config_count) static u32 (*kpc_get_config_count)(u32 classes); /// Get config registers. /// @param classes see `class mask constants` above. /// @param config Config buffer to receive values, should not smaller than /// kpc_get_config_count(classes) * sizeof(kpc_config_t). /// @return 0 for success. /// @details sysctl get(kpc.config_count), get(kpc.config) static int (*kpc_get_config)(u32 classes, kpc_config_t *config); /// Set config registers. /// @param classes see `class mask constants` above. /// @param config Config buffer, should not smaller than /// kpc_get_config_count(classes) * sizeof(kpc_config_t). /// @return 0 for success. /// @details sysctl get(kpc.config_count), set(kpc.config) static int (*kpc_set_config)(u32 classes, kpc_config_t *config); /// Get how many counters there are for a given mask. /// For example: Intel may returns 3 for `KPC_CLASS_FIXED_MASK`, /// returns 4 for `KPC_CLASS_CONFIGURABLE_MASK`. /// @param classes See `class mask constants` above. /// @note This method does not requires root privileges. /// @details sysctl get(kpc.counter_count) static u32 (*kpc_get_counter_count)(u32 classes); /// Get counter accumulations. /// If `all_cpus` is true, the buffer count should not smaller than /// (cpu_count * counter_count). Otherwize, the buffer count should not smaller /// than (counter_count). /// @see kpc_get_counter_count(), kpc_cpu_count(). /// @param all_cpus true for all CPUs, false for current cpu. /// @param classes See `class mask constants` above. /// @param curcpu A pointer to receive current cpu id, can be NULL. /// @param buf Buffer to receive counter's value. /// @return 0 for success. /// @details sysctl get(hw.ncpu), get(kpc.counter_count), get(kpc.counters) static int (*kpc_get_cpu_counters)(bool all_cpus, u32 classes, int *curcpu, u64 *buf); /// Get counter accumulations for current thread. /// @param tid Thread id, should be 0. /// @param buf_count The number of buf's elements (not bytes), /// should not smaller than kpc_get_counter_count(). /// @param buf Buffer to receive counter's value. /// @return 0 for success. /// @details sysctl get(kpc.thread_counters) static int (*kpc_get_thread_counters)(u32 tid, u32 buf_count, u64 *buf); /// Acquire/release the counters used by the Power Manager. /// @param val 1:acquire, 0:release /// @return 0 for success. /// @details sysctl set(kpc.force_all_ctrs) static int (*kpc_force_all_ctrs_set)(int val); /// Get the state of all_ctrs. /// @return 0 for success. /// @details sysctl get(kpc.force_all_ctrs) static int (*kpc_force_all_ctrs_get)(int *val_out); /// Set number of actions, should be `KPERF_ACTION_MAX`. /// @details sysctl set(kperf.action.count) static int (*kperf_action_count_set)(u32 count); /// Get number of actions. /// @details sysctl get(kperf.action.count) static int (*kperf_action_count_get)(u32 *count); /// Set what to sample when a trigger fires an action, e.g. /// `KPERF_SAMPLER_PMC_CPU`. /// @details sysctl set(kperf.action.samplers) static int (*kperf_action_samplers_set)(u32 actionid, u32 sample); /// Get what to sample when a trigger fires an action. /// @details sysctl get(kperf.action.samplers) static int (*kperf_action_samplers_get)(u32 actionid, u32 *sample); /// Apply a task filter to the action, -1 to disable filter. /// @details sysctl set(kperf.action.filter_by_task) static int (*kperf_action_filter_set_by_task)(u32 actionid, i32 port); /// Apply a pid filter to the action, -1 to disable filter. /// @details sysctl set(kperf.action.filter_by_pid) static int (*kperf_action_filter_set_by_pid)(u32 actionid, i32 pid); /// Set number of time triggers, should be `KPERF_TIMER_MAX`. /// @details sysctl set(kperf.timer.count) static int (*kperf_timer_count_set)(u32 count); /// Get number of time triggers. /// @details sysctl get(kperf.timer.count) static int (*kperf_timer_count_get)(u32 *count); /// Set timer number and period. /// @details sysctl set(kperf.timer.period) static int (*kperf_timer_period_set)(u32 actionid, u64 tick); /// Get timer number and period. /// @details sysctl get(kperf.timer.period) static int (*kperf_timer_period_get)(u32 actionid, u64 *tick); /// Set timer number and actionid. /// @details sysctl set(kperf.timer.action) static int (*kperf_timer_action_set)(u32 actionid, u32 timerid); /// Get timer number and actionid. /// @details sysctl get(kperf.timer.action) static int (*kperf_timer_action_get)(u32 actionid, u32 *timerid); /// Set which timer ID does PET (Profile Every Thread). /// @details sysctl set(kperf.timer.pet_timer) static int (*kperf_timer_pet_set)(u32 timerid); /// Get which timer ID does PET (Profile Every Thread). /// @details sysctl get(kperf.timer.pet_timer) static int (*kperf_timer_pet_get)(u32 *timerid); /// Enable or disable sampling. /// @details sysctl set(kperf.sampling) static int (*kperf_sample_set)(u32 enabled); /// Get is currently sampling. /// @details sysctl get(kperf.sampling) static int (*kperf_sample_get)(u32 *enabled); /// Reset kperf: stop sampling, kdebug, timers and actions. /// @return 0 for success. static int (*kperf_reset)(void); /// Nanoseconds to CPU ticks. static u64 (*kperf_ns_to_ticks)(u64 ns); /// CPU ticks to nanoseconds. static u64 (*kperf_ticks_to_ns)(u64 ticks); /// CPU ticks frequency (mach_absolute_time). static u64 (*kperf_tick_frequency)(void); /// Get lightweight PET mode (not in kperf.framework). static int kperf_lightweight_pet_get(u32 *enabled) { if (!enabled) return -1; usize size = 4; return sysctlbyname("kperf.lightweight_pet", enabled, &size, NULL, 0); } /// Set lightweight PET mode (not in kperf.framework). static int kperf_lightweight_pet_set(u32 enabled) { return sysctlbyname("kperf.lightweight_pet", NULL, NULL, &enabled, 4); } // ----------------------------------------------------------------------------- // header (reverse engineered) // This framework provides some functions to access the local CPU database. // These functions do not require root privileges. // ----------------------------------------------------------------------------- // KPEP CPU archtecture constants. #define KPEP_ARCH_I386 0 #define KPEP_ARCH_X86_64 1 #define KPEP_ARCH_ARM 2 #define KPEP_ARCH_ARM64 3 /// KPEP event (size: 48/28 bytes on 64/32 bit OS) typedef struct kpep_event { const char *name; ///< Unique name of a event, such as "INST_RETIRED.ANY". const char *description; ///< Description for this event. const char *errata; ///< Errata, currently NULL. const char *alias; ///< Alias name, such as "Instructions", "Cycles". const char *fallback; ///< Fallback event name for fixed counter. u32 mask; u8 number; u8 umask; u8 reserved; u8 is_fixed; } kpep_event; /// KPEP database (size: 144/80 bytes on 64/32 bit OS) typedef struct kpep_db { const char *name; ///< Database name, such as "haswell". const char *cpu_id; ///< Plist name, such as "cpu_7_8_10b282dc". const char *marketing_name; ///< Marketing name, such as "Intel Haswell". void *plist_data; ///< Plist data (CFDataRef), currently NULL. void *event_map; ///< All events (CFDict). kpep_event *event_arr; ///< Event struct buffer (sizeof(kpep_event) * events_count). kpep_event **fixed_event_arr; ///< Fixed counter events (sizeof(kpep_event *) ///< * fixed_counter_count) void *alias_map; ///< All aliases (CFDict). usize reserved_1; usize reserved_2; usize reserved_3; usize event_count; ///< All events count. usize alias_count; usize fixed_counter_count; usize config_counter_count; usize power_counter_count; u32 archtecture; ///< see `KPEP CPU archtecture constants` above. u32 fixed_counter_bits; u32 config_counter_bits; u32 power_counter_bits; } kpep_db; /// KPEP config (size: 80/44 bytes on 64/32 bit OS) typedef struct kpep_config { kpep_db *db; kpep_event **ev_arr; ///< (sizeof(kpep_event *) * counter_count), init NULL usize *ev_map; ///< (sizeof(usize *) * counter_count), init 0 usize *ev_idx; ///< (sizeof(usize *) * counter_count), init -1 u32 *flags; ///< (sizeof(u32 *) * counter_count), init 0 u64 *kpc_periods; ///< (sizeof(u64 *) * counter_count), init 0 usize event_count; /// kpep_config_events_count() usize counter_count; u32 classes; ///< See `class mask constants` above. u32 config_counter; u32 power_counter; u32 reserved; } kpep_config; /// Error code for kpep_config_xxx() and kpep_db_xxx() functions. typedef enum { KPEP_CONFIG_ERROR_NONE = 0, KPEP_CONFIG_ERROR_INVALID_ARGUMENT = 1, KPEP_CONFIG_ERROR_OUT_OF_MEMORY = 2, KPEP_CONFIG_ERROR_IO = 3, KPEP_CONFIG_ERROR_BUFFER_TOO_SMALL = 4, KPEP_CONFIG_ERROR_CUR_SYSTEM_UNKNOWN = 5, KPEP_CONFIG_ERROR_DB_PATH_INVALID = 6, KPEP_CONFIG_ERROR_DB_NOT_FOUND = 7, KPEP_CONFIG_ERROR_DB_ARCH_UNSUPPORTED = 8, KPEP_CONFIG_ERROR_DB_VERSION_UNSUPPORTED = 9, KPEP_CONFIG_ERROR_DB_CORRUPT = 10, KPEP_CONFIG_ERROR_EVENT_NOT_FOUND = 11, KPEP_CONFIG_ERROR_CONFLICTING_EVENTS = 12, KPEP_CONFIG_ERROR_COUNTERS_NOT_FORCED = 13, KPEP_CONFIG_ERROR_EVENT_UNAVAILABLE = 14, KPEP_CONFIG_ERROR_ERRNO = 15, KPEP_CONFIG_ERROR_MAX } kpep_config_error_code; /// Error description for kpep_config_error_code. static const char *kpep_config_error_names[KPEP_CONFIG_ERROR_MAX] = { "none", "invalid argument", "out of memory", "I/O", "buffer too small", "current system unknown", "database path invalid", "database not found", "database architecture unsupported", "database version unsupported", "database corrupt", "event not found", "conflicting events", "all counters must be forced", "event unavailable", "check errno"}; /// Error description. static const char *kpep_config_error_desc(int code) { if (0 <= code && code < KPEP_CONFIG_ERROR_MAX) { return kpep_config_error_names[code]; } return "unknown error"; } /// Create a config. /// @param db A kpep db, see kpep_db_create() /// @param cfg_ptr A pointer to receive the new config. /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_create)(kpep_db *db, kpep_config **cfg_ptr); /// Free the config. static void (*kpep_config_free)(kpep_config *cfg); /// Add an event to config. /// @param cfg The config. /// @param ev_ptr A event pointer. /// @param flag 0: all, 1: user space only /// @param err Error bitmap pointer, can be NULL. /// If return value is `CONFLICTING_EVENTS`, this bitmap contains /// the conflicted event indices, e.g. "1 << 2" means index 2. /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_add_event)(kpep_config *cfg, kpep_event **ev_ptr, u32 flag, u32 *err); /// Remove event at index. /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_remove_event)(kpep_config *cfg, usize idx); /// Force all counters. /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_force_counters)(kpep_config *cfg); /// Get events count. /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_events_count)(kpep_config *cfg, usize *count_ptr); /// Get all event pointers. /// @param buf A buffer to receive event pointers. /// @param buf_size The buffer's size in bytes, should not smaller than /// kpep_config_events_count() * sizeof(void *). /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_events)(kpep_config *cfg, kpep_event **buf, usize buf_size); /// Get kpc register configs. /// @param buf A buffer to receive kpc register configs. /// @param buf_size The buffer's size in bytes, should not smaller than /// kpep_config_kpc_count() * sizeof(kpc_config_t). /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_kpc)(kpep_config *cfg, kpc_config_t *buf, usize buf_size); /// Get kpc register config count. /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_kpc_count)(kpep_config *cfg, usize *count_ptr); /// Get kpc classes. /// @param classes See `class mask constants` above. /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_kpc_classes)(kpep_config *cfg, u32 *classes_ptr); /// Get the index mapping from event to counter. /// @param buf A buffer to receive indexes. /// @param buf_size The buffer's size in bytes, should not smaller than /// kpep_config_events_count() * sizeof(kpc_config_t). /// @return kpep_config_error_code, 0 for success. static int (*kpep_config_kpc_map)(kpep_config *cfg, usize *buf, usize buf_size); /// Open a kpep database file in "/usr/share/kpep/" or "/usr/local/share/kpep/". /// @param name File name, for example "haswell", "cpu_100000c_1_92fb37c8". /// Pass NULL for current CPU. /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_create)(const char *name, kpep_db **db_ptr); /// Free the kpep database. static void (*kpep_db_free)(kpep_db *db); /// Get the database's name. /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_name)(kpep_db *db, const char **name); /// Get the event alias count. /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_aliases_count)(kpep_db *db, usize *count); /// Get all alias. /// @param buf A buffer to receive all alias strings. /// @param buf_size The buffer's size in bytes, /// should not smaller than kpep_db_aliases_count() * sizeof(void *). /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_aliases)(kpep_db *db, const char **buf, usize buf_size); /// Get counters count for given classes. /// @param classes 1: Fixed, 2: Configurable. /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_counters_count)(kpep_db *db, u8 classes, usize *count); /// Get all event count. /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_events_count)(kpep_db *db, usize *count); /// Get all events. /// @param buf A buffer to receive all event pointers. /// @param buf_size The buffer's size in bytes, /// should not smaller than kpep_db_events_count() * sizeof(void *). /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_events)(kpep_db *db, kpep_event **buf, usize buf_size); /// Get one event by name. /// @return kpep_config_error_code, 0 for success. static int (*kpep_db_event)(kpep_db *db, const char *name, kpep_event **ev_ptr); /// Get event's name. /// @return kpep_config_error_code, 0 for success. static int (*kpep_event_name)(kpep_event *ev, const char **name_ptr); /// Get event's alias. /// @return kpep_config_error_code, 0 for success. static int (*kpep_event_alias)(kpep_event *ev, const char **alias_ptr); /// Get event's description. /// @return kpep_config_error_code, 0 for success. static int (*kpep_event_description)(kpep_event *ev, const char **str_ptr); // ----------------------------------------------------------------------------- // load kperf/kperfdata dynamic library // ----------------------------------------------------------------------------- typedef struct { const char *name; void **impl; } lib_symbol; #define lib_nelems(x) (sizeof(x) / sizeof((x)[0])) #define lib_symbol_def(name) \ { #name, (void **)&name } static const lib_symbol lib_symbols_kperf[] = { lib_symbol_def(kpc_pmu_version), lib_symbol_def(kpc_cpu_string), lib_symbol_def(kpc_set_counting), lib_symbol_def(kpc_get_counting), lib_symbol_def(kpc_set_thread_counting), lib_symbol_def(kpc_get_thread_counting), lib_symbol_def(kpc_get_config_count), lib_symbol_def(kpc_get_counter_count), lib_symbol_def(kpc_set_config), lib_symbol_def(kpc_get_config), lib_symbol_def(kpc_get_cpu_counters), lib_symbol_def(kpc_get_thread_counters), lib_symbol_def(kpc_force_all_ctrs_set), lib_symbol_def(kpc_force_all_ctrs_get), lib_symbol_def(kperf_action_count_set), lib_symbol_def(kperf_action_count_get), lib_symbol_def(kperf_action_samplers_set), lib_symbol_def(kperf_action_samplers_get), lib_symbol_def(kperf_action_filter_set_by_task), lib_symbol_def(kperf_action_filter_set_by_pid), lib_symbol_def(kperf_timer_count_set), lib_symbol_def(kperf_timer_count_get), lib_symbol_def(kperf_timer_period_set), lib_symbol_def(kperf_timer_period_get), lib_symbol_def(kperf_timer_action_set), lib_symbol_def(kperf_timer_action_get), lib_symbol_def(kperf_sample_set), lib_symbol_def(kperf_sample_get), lib_symbol_def(kperf_reset), lib_symbol_def(kperf_timer_pet_set), lib_symbol_def(kperf_timer_pet_get), lib_symbol_def(kperf_ns_to_ticks), lib_symbol_def(kperf_ticks_to_ns), lib_symbol_def(kperf_tick_frequency), }; static const lib_symbol lib_symbols_kperfdata[] = { lib_symbol_def(kpep_config_create), lib_symbol_def(kpep_config_free), lib_symbol_def(kpep_config_add_event), lib_symbol_def(kpep_config_remove_event), lib_symbol_def(kpep_config_force_counters), lib_symbol_def(kpep_config_events_count), lib_symbol_def(kpep_config_events), lib_symbol_def(kpep_config_kpc), lib_symbol_def(kpep_config_kpc_count), lib_symbol_def(kpep_config_kpc_classes), lib_symbol_def(kpep_config_kpc_map), lib_symbol_def(kpep_db_create), lib_symbol_def(kpep_db_free), lib_symbol_def(kpep_db_name), lib_symbol_def(kpep_db_aliases_count), lib_symbol_def(kpep_db_aliases), lib_symbol_def(kpep_db_counters_count), lib_symbol_def(kpep_db_events_count), lib_symbol_def(kpep_db_events), lib_symbol_def(kpep_db_event), lib_symbol_def(kpep_event_name), lib_symbol_def(kpep_event_alias), lib_symbol_def(kpep_event_description), }; #define lib_path_kperf "/System/Library/PrivateFrameworks/kperf.framework/kperf" #define lib_path_kperfdata \ "/System/Library/PrivateFrameworks/kperfdata.framework/kperfdata" static bool lib_inited = false; static bool lib_has_err = false; static char lib_err_msg[256]; static void *lib_handle_kperf = NULL; static void *lib_handle_kperfdata = NULL; static void lib_deinit(void) { lib_inited = false; lib_has_err = false; if (lib_handle_kperf) dlclose(lib_handle_kperf); if (lib_handle_kperfdata) dlclose(lib_handle_kperfdata); lib_handle_kperf = NULL; lib_handle_kperfdata = NULL; for (usize i = 0; i < lib_nelems(lib_symbols_kperf); i++) { const lib_symbol *symbol = &lib_symbols_kperf[i]; *symbol->impl = NULL; } for (usize i = 0; i < lib_nelems(lib_symbols_kperfdata); i++) { const lib_symbol *symbol = &lib_symbols_kperfdata[i]; *symbol->impl = NULL; } } static bool lib_init(void) { #define return_err() \ do { \ lib_deinit(); \ lib_inited = true; \ lib_has_err = true; \ return false; \ } while (false) if (lib_inited) return !lib_has_err; // load dynamic library lib_handle_kperf = dlopen(lib_path_kperf, RTLD_LAZY); if (!lib_handle_kperf) { snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperf.framework, message: %s.", dlerror()); return_err(); } lib_handle_kperfdata = dlopen(lib_path_kperfdata, RTLD_LAZY); if (!lib_handle_kperfdata) { snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperfdata.framework, message: %s.", dlerror()); return_err(); } // load symbol address from dynamic library for (usize i = 0; i < lib_nelems(lib_symbols_kperf); i++) { const lib_symbol *symbol = &lib_symbols_kperf[i]; *symbol->impl = dlsym(lib_handle_kperf, symbol->name); if (!*symbol->impl) { snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperf function: %s.", symbol->name); return_err(); } } for (usize i = 0; i < lib_nelems(lib_symbols_kperfdata); i++) { const lib_symbol *symbol = &lib_symbols_kperfdata[i]; *symbol->impl = dlsym(lib_handle_kperfdata, symbol->name); if (!*symbol->impl) { snprintf(lib_err_msg, sizeof(lib_err_msg), "Failed to load kperfdata function: %s.", symbol->name); return_err(); } } lib_inited = true; lib_has_err = false; return true; #undef return_err } // ----------------------------------------------------------------------------- // kdebug private structs // https://github.com/apple/darwin-xnu/blob/main/bsd/sys_private/kdebug_private.h // ----------------------------------------------------------------------------- /* * Ensure that both LP32 and LP64 variants of arm64 use the same kd_buf * structure. */ #if defined(__arm64__) typedef uint64_t kd_buf_argtype; #else typedef uintptr_t kd_buf_argtype; #endif typedef struct { uint64_t timestamp; kd_buf_argtype arg1; kd_buf_argtype arg2; kd_buf_argtype arg3; kd_buf_argtype arg4; kd_buf_argtype arg5; /* the thread ID */ uint32_t debugid; /* see */ /* * Ensure that both LP32 and LP64 variants of arm64 use the same kd_buf * structure. */ #if defined(__LP64__) || defined(__arm64__) uint32_t cpuid; /* cpu index, from 0 */ kd_buf_argtype unused; #endif } kd_buf; /* bits for the type field of kd_regtype */ #define KDBG_CLASSTYPE 0x10000 #define KDBG_SUBCLSTYPE 0x20000 #define KDBG_RANGETYPE 0x40000 #define KDBG_TYPENONE 0x80000 #define KDBG_CKTYPES 0xF0000 /* only trace at most 4 types of events, at the code granularity */ #define KDBG_VALCHECK 0x00200000U typedef struct { unsigned int type; unsigned int value1; unsigned int value2; unsigned int value3; unsigned int value4; } kd_regtype; typedef struct { /* number of events that can fit in the buffers */ int nkdbufs; /* set if trace is disabled */ int nolog; /* kd_ctrl_page.flags */ unsigned int flags; /* number of threads in thread map */ int nkdthreads; /* the owning pid */ int bufid; } kbufinfo_t; // ----------------------------------------------------------------------------- // kdebug utils // ----------------------------------------------------------------------------- /// Clean up trace buffers and reset ktrace/kdebug/kperf. /// @return 0 on success. static int kdebug_reset(void) { int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDREMOVE}; return sysctl(mib, 3, NULL, NULL, NULL, 0); } /// Disable and reinitialize the trace buffers. /// @return 0 on success. static int kdebug_reinit(void) { int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETUP}; return sysctl(mib, 3, NULL, NULL, NULL, 0); } /// Set debug filter. static int kdebug_setreg(kd_regtype *kdr) { int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETREG}; usize size = sizeof(kd_regtype); return sysctl(mib, 3, kdr, &size, NULL, 0); } /// Set maximum number of trace entries (kd_buf). /// Only allow allocation up to half the available memory (sane_size). /// @return 0 on success. static int kdebug_trace_setbuf(int nbufs) { int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDSETBUF, nbufs}; return sysctl(mib, 4, NULL, NULL, NULL, 0); } /// Enable or disable kdebug trace. /// Trace buffer must already be initialized. /// @return 0 on success. static int kdebug_trace_enable(bool enable) { int mib[4] = {CTL_KERN, KERN_KDEBUG, KERN_KDENABLE, enable}; return sysctl(mib, 4, NULL, 0, NULL, 0); } /// Retrieve trace buffer information from kernel. /// @return 0 on success. static int kdebug_get_bufinfo(kbufinfo_t *info) { if (!info) return -1; int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDGETBUF}; size_t needed = sizeof(kbufinfo_t); return sysctl(mib, 3, info, &needed, NULL, 0); } /// Retrieve trace buffers from kernel. /// @param buf Memory to receive buffer data, array of `kd_buf`. /// @param len Length of `buf` in bytes. /// @param count Number of trace entries (kd_buf) obtained. /// @return 0 on success. static int kdebug_trace_read(void *buf, usize len, usize *count) { if (count) *count = 0; if (!buf || !len) return -1; // Note: the input and output units are not the same. // input: bytes // output: number of kd_buf int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDREADTR}; int ret = sysctl(mib, 3, buf, &len, NULL, 0); if (ret != 0) return ret; *count = len; return 0; } /// Block until there are new buffers filled or `timeout_ms` have passed. /// @param timeout_ms timeout milliseconds, 0 means wait forever. /// @param suc set true if new buffers filled. /// @return 0 on success. static int kdebug_wait(usize timeout_ms, bool *suc) { if (timeout_ms == 0) return -1; int mib[3] = {CTL_KERN, KERN_KDEBUG, KERN_KDBUFWAIT}; usize val = timeout_ms; int ret = sysctl(mib, 3, NULL, &val, NULL, 0); if (suc) *suc = !!val; return ret; } // ----------------------------------------------------------------------------- // Demo // ----------------------------------------------------------------------------- #define EVENT_NAME_MAX 8 typedef struct { const char *alias; /// name for print const char *names[EVENT_NAME_MAX]; /// name from pmc db } event_alias; /// Event names from /usr/share/kpep/.plist static const event_alias profile_events[] = { {"cycles", { "FIXED_CYCLES", // Apple A7-A15//CORE_ACTIVE_CYCLE "CPU_CLK_UNHALTED.THREAD", // Intel Core 1th-10th "CPU_CLK_UNHALTED.CORE", // Intel Yonah, Merom }}, {"instructions", { "FIXED_INSTRUCTIONS", // Apple A7-A15 "INST_RETIRED.ANY" // Intel Yonah, Merom, Core 1th-10th }}, {"branches", { "INST_BRANCH", // Apple A7-A15 "BR_INST_RETIRED.ALL_BRANCHES", // Intel Core 1th-10th "INST_RETIRED.ANY", // Intel Yonah, Merom }}, {"branch-misses", { "BRANCH_MISPRED_NONSPEC", // Apple A7-A15, since iOS 15, macOS 12 "BRANCH_MISPREDICT", // Apple A7-A14 "BR_MISP_RETIRED.ALL_BRANCHES", // Intel Core 2th-10th "BR_INST_RETIRED.MISPRED", // Intel Yonah, Merom }}, }; static kpep_event *get_event(kpep_db *db, const event_alias *alias) { for (usize j = 0; j < EVENT_NAME_MAX; j++) { const char *name = alias->names[j]; if (!name) break; kpep_event *ev = NULL; if (kpep_db_event(db, name, &ev) == 0) { return ev; } } return NULL; } kpc_config_t regs[KPC_MAX_COUNTERS] = {0}; usize counter_map[KPC_MAX_COUNTERS] = {0}; u64 counters_0[KPC_MAX_COUNTERS] = {0}; u64 counters_1[KPC_MAX_COUNTERS] = {0}; const usize ev_count = sizeof(profile_events) / sizeof(profile_events[0]); bool setup_performance_counters() { static bool init = false; static bool worked = false; if (init) { return worked; } init = true; // load dylib if (!lib_init()) { printf("Error: %s\n", lib_err_msg); return (worked = false); } // check permission int force_ctrs = 0; if (kpc_force_all_ctrs_get(&force_ctrs)) { // printf("Permission denied, xnu/kpc requires root privileges.\n"); return (worked = false); } int ret; // load pmc db kpep_db *db = NULL; if ((ret = kpep_db_create(NULL, &db))) { printf("Error: cannot load pmc database: %d.\n", ret); return (worked = false); } printf("loaded db: %s (%s)\n", db->name, db->marketing_name); // create a config kpep_config *cfg = NULL; if ((ret = kpep_config_create(db, &cfg))) { printf("Failed to create kpep config: %d (%s).\n", ret, kpep_config_error_desc(ret)); return (worked = false); } if ((ret = kpep_config_force_counters(cfg))) { printf("Failed to force counters: %d (%s).\n", ret, kpep_config_error_desc(ret)); return (worked = false); } // get events kpep_event *ev_arr[ev_count] = {0}; for (usize i = 0; i < ev_count; i++) { const event_alias *alias = profile_events + i; ev_arr[i] = get_event(db, alias); if (!ev_arr[i]) { printf("Cannot find event: %s.\n", alias->alias); return (worked = false); } } // add event to config for (usize i = 0; i < ev_count; i++) { kpep_event *ev = ev_arr[i]; if ((ret = kpep_config_add_event(cfg, &ev, 0, NULL))) { printf("Failed to add event: %d (%s).\n", ret, kpep_config_error_desc(ret)); return (worked = false); } } // prepare buffer and config u32 classes = 0; usize reg_count = 0; if ((ret = kpep_config_kpc_classes(cfg, &classes))) { printf("Failed get kpc classes: %d (%s).\n", ret, kpep_config_error_desc(ret)); return (worked = false); } if ((ret = kpep_config_kpc_count(cfg, ®_count))) { printf("Failed get kpc count: %d (%s).\n", ret, kpep_config_error_desc(ret)); return (worked = false); } if ((ret = kpep_config_kpc_map(cfg, counter_map, sizeof(counter_map)))) { printf("Failed get kpc map: %d (%s).\n", ret, kpep_config_error_desc(ret)); return (worked = false); } if ((ret = kpep_config_kpc(cfg, regs, sizeof(regs)))) { printf("Failed get kpc registers: %d (%s).\n", ret, kpep_config_error_desc(ret)); return (worked = false); } // set config to kernel if ((ret = kpc_force_all_ctrs_set(1))) { printf("Failed force all ctrs: %d.\n", ret); return (worked = false); } if ((classes & KPC_CLASS_CONFIGURABLE_MASK) && reg_count) { if ((ret = kpc_set_config(classes, regs))) { printf("Failed set kpc config: %d.\n", ret); return (worked = false); } } // start counting if ((ret = kpc_set_counting(classes))) { printf("Failed set counting: %d.\n", ret); return (worked = false); } if ((ret = kpc_set_thread_counting(classes))) { printf("Failed set thread counting: %d.\n", ret); return (worked = false); } return (worked = true); } inline performance_counters get_counters() { static bool warned = false; int ret; // get counters before if ((ret = kpc_get_thread_counters(0, KPC_MAX_COUNTERS, counters_0))) { if (!warned) { printf("Failed get thread counters before: %d.\n", ret); warned = true; } return 1; } /*printf("counters value:\n"); for (usize i = 0; i < ev_count; i++) { const event_alias *alias = profile_events + i; usize idx = counter_map[i]; u64 val = counters_1[idx] - counters_0[idx]; printf("%14s: %llu\n", alias->alias, val); }*/ return performance_counters{ counters_0[counter_map[0]], counters_0[counter_map[2]], counters_0[counter_map[3]], counters_0[counter_map[1]]}; } #endif fast_float-8.0.0/benchmarks/benchmark.cpp000066400000000000000000000172221475170546600204370ustar00rootroot00000000000000#if defined(__linux__) || (__APPLE__ && __aarch64__) #define USING_COUNTERS #include "event_counter.h" #endif #include #include "fast_float/fast_float.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include template double findmax_fastfloat64(std::vector> &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 double findmax_fastfloat32(std::vector> &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 std::vector time_it_ns(std::vector> &lines, T const &function, size_t repeat) { std::vector 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 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 std::pair time_it_ns(std::vector> &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(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 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 widen(const std::vector &lines) { std::vector u16lines; u16lines.reserve(lines.size()); for (auto const &line : lines) { u16lines.push_back(widen(line)); } return u16lines; } void process(std::vector &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, repeat)); pretty_print(volume, lines.size(), "fastfloat (32)", time_it_ns(lines, findmax_fastfloat32, repeat)); std::vector 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, repeat)); pretty_print(volume, lines.size(), "fastfloat (32)", time_it_ns(lines16, findmax_fastfloat32, 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 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 } fileload(std::string(BENCHMARK_DATA_DIR) + "/canada.txt"); fileload(std::string(BENCHMARK_DATA_DIR) + "/mesh.txt"); } fast_float-8.0.0/benchmarks/event_counter.h000066400000000000000000000112301475170546600210230ustar00rootroot00000000000000#ifndef __EVENT_COUNTER_H #define __EVENT_COUNTER_H #include #ifndef _MSC_VER #include #endif #include #include #include #include #include "linux-perf-events.h" #ifdef __linux__ #include #endif #if __APPLE__ && __aarch64__ #include "apple_arm_events.h" #endif struct event_count { std::chrono::duration elapsed; std::vector event_counts; event_count() : elapsed(0), event_counts{0, 0, 0, 0, 0} {} event_count(const std::chrono::duration _elapsed, const std::vector _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(elapsed).count(); } double elapsed_ns() const { return std::chrono::duration(elapsed).count(); } double cycles() const { return static_cast(event_counts[CPU_CYCLES]); } double instructions() const { return static_cast(event_counts[INSTRUCTIONS]); } double branches() const { return static_cast(event_counts[BRANCHES]); } double missed_branches() const { return static_cast(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 start_clock{}; #if defined(__linux__) LinuxEvents linux_events; event_collector() : linux_events(std::vector{ 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 fast_float-8.0.0/benchmarks/linux-perf-events.h000066400000000000000000000054101475170546600215410ustar00rootroot00000000000000#pragma once #ifdef __linux__ #include // for __NR_perf_event_open #include // for perf event constants #include // for ioctl #include // for syscall #include // for errno #include // for memset #include #include #include template class LinuxEvents { int fd; bool working; perf_event_attr attribs{}; size_t num_events{}; std::vector temp_result_vec{}; std::vector ids{}; public: explicit LinuxEvents(std::vector 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( 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 &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; } }; #endiffast_float-8.0.0/ci/000077500000000000000000000000001475170546600142535ustar00rootroot00000000000000fast_float-8.0.0/ci/script.sh000077500000000000000000000005261475170546600161210ustar00rootroot00000000000000#!/bin/bash TOOLCHAIN="$1" mkdir build cd build if [ "$TOOLCHAIN" != "" ] ; then cmake -DFASTFLOAT_TEST=ON .. -DCMAKE_TOOLCHAIN_FILE=/toolchains/"$TOOLCHAIN".cmake else cmake -DFASTFLOAT_TEST=ON .. fi make -j 2 if [ "$TOOLCHAIN" != "" ] ; then qemu-"$TOOLCHAIN" tests/basictest else ctest --output-on-failure -R basictest fi fast_float-8.0.0/clang-format-ignore.txt000066400000000000000000000000001475170546600202420ustar00rootroot00000000000000fast_float-8.0.0/cmake/000077500000000000000000000000001475170546600147405ustar00rootroot00000000000000fast_float-8.0.0/cmake/config.cmake.in000066400000000000000000000001761475170546600176200ustar00rootroot00000000000000@PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") check_required_components("@PROJECT_NAME@") fast_float-8.0.0/fuzz/000077500000000000000000000000001475170546600146565ustar00rootroot00000000000000fast_float-8.0.0/fuzz/build.sh000066400000000000000000000003251475170546600163110ustar00rootroot00000000000000#!/bin/bash $CXX $CFLAGS $CXXFLAGS \ -I $SRC/fast_float/include \ -c $SRC/fast_float/fuzz/from_chars.cc -o from_chars.o $CXX $CFLAGS $CXXFLAGS $LIB_FUZZING_ENGINE from_chars.o \ -o $OUT/from_charsfast_float-8.0.0/fuzz/from_chars.cc000066400000000000000000000023521475170546600173120ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include fast_float::chars_format arbitrary_format(FuzzedDataProvider &fdp) { using fast_float::chars_format; switch (fdp.ConsumeIntegralInRange(0, 3)) { case 0: return chars_format::scientific; break; case 1: return chars_format::fixed; break; case 2: return chars_format::fixed; break; } return chars_format::general; } extern "C" int LLVMFuzzerTestOneInput(uint8_t const *data, size_t size) { FuzzedDataProvider fdp(data, size); fast_float::chars_format format = arbitrary_format(fdp); double result_d = 0.0; std::string input_d = fdp.ConsumeRandomLengthString(128); auto answer = fast_float::from_chars( input_d.data(), input_d.data() + input_d.size(), result_d, format); std::string input_f = fdp.ConsumeRandomLengthString(128); float result_f = 0.0; answer = fast_float::from_chars( input_f.data(), input_f.data() + input_f.size(), result_f, format); int result_i = 0; std::string input_i = fdp.ConsumeRandomLengthString(128); answer = fast_float::from_chars(input_i.data(), input_i.data() + input_i.size(), result_i); return 0; }fast_float-8.0.0/include/000077500000000000000000000000001475170546600153035ustar00rootroot00000000000000fast_float-8.0.0/include/fast_float/000077500000000000000000000000001475170546600174255ustar00rootroot00000000000000fast_float-8.0.0/include/fast_float/ascii_number.h000066400000000000000000000447351475170546600222530ustar00rootroot00000000000000#ifndef FASTFLOAT_ASCII_NUMBER_H #define FASTFLOAT_ASCII_NUMBER_H #include #include #include #include #include #include #include "float_common.h" #ifdef FASTFLOAT_SSE2 #include #endif #ifdef FASTFLOAT_NEON #include #endif namespace fast_float { template fastfloat_really_inline constexpr bool has_simd_opt() { #ifdef FASTFLOAT_HAS_SIMD return std::is_same::value; #else return false; #endif } // Next function can be micro-optimized, but compilers are entirely // able to optimize it well. template fastfloat_really_inline constexpr bool is_integer(UC c) noexcept { return !(c > UC('9') || c < UC('0')); } fastfloat_really_inline constexpr uint64_t byteswap(uint64_t val) { return (val & 0xFF00000000000000) >> 56 | (val & 0x00FF000000000000) >> 40 | (val & 0x0000FF0000000000) >> 24 | (val & 0x000000FF00000000) >> 8 | (val & 0x00000000FF000000) << 8 | (val & 0x0000000000FF0000) << 24 | (val & 0x000000000000FF00) << 40 | (val & 0x00000000000000FF) << 56; } // Read 8 UC into a u64. Truncates UC if not char. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t read8_to_u64(UC const *chars) { if (cpp20_and_in_constexpr() || !std::is_same::value) { uint64_t val = 0; for (int i = 0; i < 8; ++i) { val |= uint64_t(uint8_t(*chars)) << (i * 8); ++chars; } return val; } uint64_t val; ::memcpy(&val, chars, sizeof(uint64_t)); #if FASTFLOAT_IS_BIG_ENDIAN == 1 // Need to read as-if the number was in little-endian order. val = byteswap(val); #endif return val; } #ifdef FASTFLOAT_SSE2 fastfloat_really_inline uint64_t simd_read8_to_u64(__m128i const data) { FASTFLOAT_SIMD_DISABLE_WARNINGS __m128i const packed = _mm_packus_epi16(data, data); #ifdef FASTFLOAT_64BIT return uint64_t(_mm_cvtsi128_si64(packed)); #else uint64_t value; // Visual Studio + older versions of GCC don't support _mm_storeu_si64 _mm_storel_epi64(reinterpret_cast<__m128i *>(&value), packed); return value; #endif FASTFLOAT_SIMD_RESTORE_WARNINGS } fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { FASTFLOAT_SIMD_DISABLE_WARNINGS return simd_read8_to_u64( _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars))); FASTFLOAT_SIMD_RESTORE_WARNINGS } #elif defined(FASTFLOAT_NEON) fastfloat_really_inline uint64_t simd_read8_to_u64(uint16x8_t const data) { FASTFLOAT_SIMD_DISABLE_WARNINGS uint8x8_t utf8_packed = vmovn_u16(data); return vget_lane_u64(vreinterpret_u64_u8(utf8_packed), 0); FASTFLOAT_SIMD_RESTORE_WARNINGS } fastfloat_really_inline uint64_t simd_read8_to_u64(char16_t const *chars) { FASTFLOAT_SIMD_DISABLE_WARNINGS return simd_read8_to_u64( vld1q_u16(reinterpret_cast(chars))); FASTFLOAT_SIMD_RESTORE_WARNINGS } #endif // FASTFLOAT_SSE2 // MSVC SFINAE is broken pre-VS2017 #if defined(_MSC_VER) && _MSC_VER <= 1900 template #else template ()) = 0> #endif // dummy for compile uint64_t simd_read8_to_u64(UC const *) { return 0; } // credit @aqrit fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint32_t parse_eight_digits_unrolled(uint64_t val) { uint64_t const mask = 0x000000FF000000FF; uint64_t const mul1 = 0x000F424000000064; // 100 + (1000000ULL << 32) uint64_t const mul2 = 0x0000271000000001; // 1 + (10000ULL << 32) val -= 0x3030303030303030; val = (val * 10) + (val >> 8); // val = (val * 2561) >> 8; val = (((val & mask) * mul1) + (((val >> 16) & mask) * mul2)) >> 32; return uint32_t(val); } // Call this if chars are definitely 8 digits. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint32_t parse_eight_digits_unrolled(UC const *chars) noexcept { if (cpp20_and_in_constexpr() || !has_simd_opt()) { return parse_eight_digits_unrolled(read8_to_u64(chars)); // truncation okay } return parse_eight_digits_unrolled(simd_read8_to_u64(chars)); } // credit @aqrit fastfloat_really_inline constexpr bool is_made_of_eight_digits_fast(uint64_t val) noexcept { return !((((val + 0x4646464646464646) | (val - 0x3030303030303030)) & 0x8080808080808080)); } #ifdef FASTFLOAT_HAS_SIMD // Call this if chars might not be 8 digits. // Using this style (instead of is_made_of_eight_digits_fast() then // parse_eight_digits_unrolled()) ensures we don't load SIMD registers twice. fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool simd_parse_if_eight_digits_unrolled(char16_t const *chars, uint64_t &i) noexcept { if (cpp20_and_in_constexpr()) { return false; } #ifdef FASTFLOAT_SSE2 FASTFLOAT_SIMD_DISABLE_WARNINGS __m128i const data = _mm_loadu_si128(reinterpret_cast<__m128i const *>(chars)); // (x - '0') <= 9 // http://0x80.pl/articles/simd-parsing-int-sequences.html __m128i const t0 = _mm_add_epi16(data, _mm_set1_epi16(32720)); __m128i const t1 = _mm_cmpgt_epi16(t0, _mm_set1_epi16(-32759)); if (_mm_movemask_epi8(t1) == 0) { i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); return true; } else return false; FASTFLOAT_SIMD_RESTORE_WARNINGS #elif defined(FASTFLOAT_NEON) FASTFLOAT_SIMD_DISABLE_WARNINGS uint16x8_t const data = vld1q_u16(reinterpret_cast(chars)); // (x - '0') <= 9 // http://0x80.pl/articles/simd-parsing-int-sequences.html uint16x8_t const t0 = vsubq_u16(data, vmovq_n_u16('0')); uint16x8_t const mask = vcltq_u16(t0, vmovq_n_u16('9' - '0' + 1)); if (vminvq_u16(mask) == 0xFFFF) { i = i * 100000000 + parse_eight_digits_unrolled(simd_read8_to_u64(data)); return true; } else return false; FASTFLOAT_SIMD_RESTORE_WARNINGS #else (void)chars; (void)i; return false; #endif // FASTFLOAT_SSE2 } #endif // FASTFLOAT_HAS_SIMD // MSVC SFINAE is broken pre-VS2017 #if defined(_MSC_VER) && _MSC_VER <= 1900 template #else template ()) = 0> #endif // dummy for compile bool simd_parse_if_eight_digits_unrolled(UC const *, uint64_t &) { return 0; } template ::value) = 0> fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void loop_parse_if_eight_digits(UC const *&p, UC const *const pend, uint64_t &i) { if (!has_simd_opt()) { return; } while ((std::distance(p, pend) >= 8) && simd_parse_if_eight_digits_unrolled( p, i)) { // in rare cases, this will overflow, but that's ok p += 8; } } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void loop_parse_if_eight_digits(char const *&p, char const *const pend, uint64_t &i) { // optimizes better than parse_if_eight_digits_unrolled() for UC = char. while ((std::distance(p, pend) >= 8) && is_made_of_eight_digits_fast(read8_to_u64(p))) { i = i * 100000000 + parse_eight_digits_unrolled(read8_to_u64( p)); // in rare cases, this will overflow, but that's ok p += 8; } } enum class parse_error { no_error, // [JSON-only] The minus sign must be followed by an integer. missing_integer_after_sign, // A sign must be followed by an integer or dot. missing_integer_or_dot_after_sign, // [JSON-only] The integer part must not have leading zeros. leading_zeros_in_integer_part, // [JSON-only] The integer part must have at least one digit. no_digits_in_integer_part, // [JSON-only] If there is a decimal point, there must be digits in the // fractional part. no_digits_in_fractional_part, // The mantissa must have at least one digit. no_digits_in_mantissa, // Scientific notation requires an exponential part. missing_exponential_part, }; template struct parsed_number_string_t { int64_t exponent{0}; uint64_t mantissa{0}; UC const *lastmatch{nullptr}; bool negative{false}; bool valid{false}; bool too_many_digits{false}; // contains the range of the significant digits span integer{}; // non-nullable span fraction{}; // nullable parse_error error{parse_error::no_error}; }; using byte_span = span; using parsed_number_string = parsed_number_string_t; template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t report_parse_error(UC const *p, parse_error error) { parsed_number_string_t answer; answer.valid = false; answer.lastmatch = p; answer.error = error; return answer; } // Assuming that you use no more than 19 digits, this will // parse an ASCII string. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 parsed_number_string_t parse_number_string(UC const *p, UC const *pend, parse_options_t options) noexcept { chars_format const fmt = detail::adjust_for_feature_macros(options.format); UC const decimal_point = options.decimal_point; parsed_number_string_t answer; answer.valid = false; answer.too_many_digits = false; // 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) && !uint64_t(fmt & detail::basic_json_fmt) && *p == UC('+'))) { ++p; if (p == pend) { return report_parse_error( p, parse_error::missing_integer_or_dot_after_sign); } if (uint64_t(fmt & detail::basic_json_fmt)) { if (!is_integer(*p)) { // a sign must be followed by an integer return report_parse_error(p, parse_error::missing_integer_after_sign); } } else { if (!is_integer(*p) && (*p != decimal_point)) { // a sign must be followed by an integer or the dot return report_parse_error( p, parse_error::missing_integer_or_dot_after_sign); } } } UC const *const start_digits = p; uint64_t i = 0; // an unsigned int avoids signed overflows (which are bad) while ((p != pend) && is_integer(*p)) { // a multiplication by 10 is cheaper than an arbitrary integer // multiplication i = 10 * i + uint64_t(*p - UC('0')); // might overflow, we will handle the overflow later ++p; } UC const *const end_of_integer_part = p; int64_t digit_count = int64_t(end_of_integer_part - start_digits); answer.integer = span(start_digits, size_t(digit_count)); 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(p, parse_error::no_digits_in_integer_part); } if ((start_digits[0] == UC('0') && digit_count > 1)) { return report_parse_error(start_digits, parse_error::leading_zeros_in_integer_part); } } int64_t exponent = 0; bool const has_decimal_point = (p != pend) && (*p == decimal_point); if (has_decimal_point) { ++p; UC const *before = p; // can occur at most twice without overflowing, but let it occur more, since // for integers with many digits, digit parsing is the primary bottleneck. loop_parse_if_eight_digits(p, pend, i); while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - UC('0')); ++p; i = i * 10 + digit; // in rare cases, this will overflow, but that's ok } exponent = before - p; answer.fraction = span(before, size_t(p - before)); digit_count -= exponent; } 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(p, parse_error::no_digits_in_fractional_part); } } else if (digit_count == 0) { // we must have encountered at least one integer! return report_parse_error(p, parse_error::no_digits_in_mantissa); } int64_t exp_number = 0; // explicit exponential part if ((uint64_t(fmt & chars_format::scientific) && (p != pend) && ((UC('e') == *p) || (UC('E') == *p))) || (uint64_t(fmt & detail::basic_fortran_fmt) && (p != pend) && ((UC('+') == *p) || (UC('-') == *p) || (UC('d') == *p) || (UC('D') == *p)))) { UC const *location_of_e = p; if ((UC('e') == *p) || (UC('E') == *p) || (UC('d') == *p) || (UC('D') == *p)) { ++p; } bool neg_exp = false; if ((p != pend) && (UC('-') == *p)) { neg_exp = true; ++p; } else if ((p != pend) && (UC('+') == *p)) { // '+' on exponent is allowed by C++17 20.19.3.(7.1) ++p; } if ((p == pend) || !is_integer(*p)) { if (!uint64_t(fmt & chars_format::fixed)) { // The exponential part is invalid for scientific notation, so it must // be a trailing token for fixed notation. However, fixed notation is // disabled, so report a scientific notation error. return report_parse_error(p, parse_error::missing_exponential_part); } // Otherwise, we will be ignoring the 'e'. p = location_of_e; } else { while ((p != pend) && is_integer(*p)) { uint8_t digit = uint8_t(*p - UC('0')); if (exp_number < 0x10000000) { exp_number = 10 * exp_number + digit; } ++p; } if (neg_exp) { exp_number = -exp_number; } exponent += exp_number; } } else { // If it scientific and not fixed, we have to bail out. if (uint64_t(fmt & chars_format::scientific) && !uint64_t(fmt & chars_format::fixed)) { return report_parse_error(p, parse_error::missing_exponential_part); } } answer.lastmatch = p; answer.valid = true; // If we frequently had to deal with long strings of digits, // we could extend our code by using a 128-bit integer instead // of a 64-bit integer. However, this is uncommon. // // We can deal with up to 19 digits. if (digit_count > 19) { // this is uncommon // It is possible that the integer had an overflow. // We have to handle the case where we have 0.0000somenumber. // We need to be mindful of the case where we only have zeroes... // E.g., 0.000000000...000. UC const *start = start_digits; while ((start != pend) && (*start == UC('0') || *start == decimal_point)) { if (*start == UC('0')) { digit_count--; } start++; } if (digit_count > 19) { answer.too_many_digits = true; // Let us start again, this time, avoiding overflows. // We don't need to check if is_integer, since we use the // pre-tokenized spans from above. i = 0; p = answer.integer.ptr; UC const *int_end = p + answer.integer.len(); uint64_t const minimal_nineteen_digit_integer{1000000000000000000}; while ((i < minimal_nineteen_digit_integer) && (p != int_end)) { i = i * 10 + uint64_t(*p - UC('0')); ++p; } if (i >= minimal_nineteen_digit_integer) { // We have a big integers exponent = end_of_integer_part - p + exp_number; } else { // We have a value with a fractional component. p = answer.fraction.ptr; UC const *frac_end = p + answer.fraction.len(); while ((i < minimal_nineteen_digit_integer) && (p != frac_end)) { i = i * 10 + uint64_t(*p - UC('0')); ++p; } exponent = answer.fraction.ptr - p + exp_number; } // We have now corrected both exponent and i, to a truncated value } } answer.exponent = exponent; answer.mantissa = i; return answer; } template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 from_chars_result_t parse_int_string(UC const *p, UC const *pend, T &value, parse_options_t options) { chars_format const fmt = detail::adjust_for_feature_macros(options.format); int const base = options.base; from_chars_result_t answer; UC const *const first = p; bool const negative = (*p == UC('-')); #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(push) #pragma warning(disable : 4127) #endif if (!std::is_signed::value && negative) { #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(pop) #endif answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } if ((*p == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) && (*p == UC('+')))) { ++p; } UC const *const start_num = p; while (p != pend && *p == UC('0')) { ++p; } bool const has_leading_zeros = p > start_num; UC const *const start_digits = p; uint64_t i = 0; if (base == 10) { loop_parse_if_eight_digits(p, pend, i); // use SIMD if possible } while (p != pend) { uint8_t digit = ch_to_digit(*p); if (digit >= base) { break; } i = uint64_t(base) * i + digit; // might overflow, check this later p++; } size_t digit_count = size_t(p - start_digits); if (digit_count == 0) { if (has_leading_zeros) { value = 0; answer.ec = std::errc(); answer.ptr = p; } else { answer.ec = std::errc::invalid_argument; answer.ptr = first; } return answer; } answer.ptr = p; // check u64 overflow size_t max_digits = max_digits_u64(base); if (digit_count > max_digits) { answer.ec = std::errc::result_out_of_range; return answer; } // this check can be eliminated for all other types, but they will all require // a max_digits(base) equivalent if (digit_count == max_digits && i < min_safe_u64(base)) { answer.ec = std::errc::result_out_of_range; return answer; } // check other types overflow if (!std::is_same::value) { if (i > uint64_t(std::numeric_limits::max()) + uint64_t(negative)) { answer.ec = std::errc::result_out_of_range; return answer; } } if (negative) { #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(push) #pragma warning(disable : 4146) #endif // this weird workaround is required because: // - converting unsigned to signed when its value is greater than signed max // is UB pre-C++23. // - reinterpret_casting (~i + 1) would work, but it is not constexpr // this is always optimized into a neg instruction (note: T is an integer // type) value = T(-std::numeric_limits::max() - T(i - uint64_t(std::numeric_limits::max()))); #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(pop) #endif } else { value = T(i); } answer.ec = std::errc(); return answer; } } // namespace fast_float #endif fast_float-8.0.0/include/fast_float/bigint.h000066400000000000000000000443071475170546600210620ustar00rootroot00000000000000#ifndef FASTFLOAT_BIGINT_H #define FASTFLOAT_BIGINT_H #include #include #include #include #include "float_common.h" namespace fast_float { // the limb width: we want efficient multiplication of double the bits in // limb, or for 64-bit limbs, at least 64-bit multiplication where we can // extract the high and low parts efficiently. this is every 64-bit // architecture except for sparc, which emulates 128-bit multiplication. // we might have platforms where `CHAR_BIT` is not 8, so let's avoid // doing `8 * sizeof(limb)`. #if defined(FASTFLOAT_64BIT) && !defined(__sparc) #define FASTFLOAT_64BIT_LIMB 1 typedef uint64_t limb; constexpr size_t limb_bits = 64; #else #define FASTFLOAT_32BIT_LIMB typedef uint32_t limb; constexpr size_t limb_bits = 32; #endif typedef span limb_span; // number of bits in a bigint. this needs to be at least the number // of bits required to store the largest bigint, which is // `log2(10**(digits + max_exp))`, or `log2(10**(767 + 342))`, or // ~3600 bits, so we round to 4000. constexpr size_t bigint_bits = 4000; constexpr size_t bigint_limbs = bigint_bits / limb_bits; // vector-like type that is allocated on the stack. the entire // buffer is pre-allocated, and only the length changes. template struct stackvec { limb data[size]; // we never need more than 150 limbs uint16_t length{0}; stackvec() = default; stackvec(stackvec const &) = delete; stackvec &operator=(stackvec const &) = delete; stackvec(stackvec &&) = delete; stackvec &operator=(stackvec &&other) = delete; // create stack vector from existing limb span. FASTFLOAT_CONSTEXPR20 stackvec(limb_span s) { FASTFLOAT_ASSERT(try_extend(s)); } FASTFLOAT_CONSTEXPR14 limb &operator[](size_t index) noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); return data[index]; } FASTFLOAT_CONSTEXPR14 const limb &operator[](size_t index) const noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); return data[index]; } // index from the end of the container FASTFLOAT_CONSTEXPR14 const limb &rindex(size_t index) const noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); size_t rindex = length - index - 1; return data[rindex]; } // set the length, without bounds checking. FASTFLOAT_CONSTEXPR14 void set_len(size_t len) noexcept { length = uint16_t(len); } constexpr size_t len() const noexcept { return length; } constexpr bool is_empty() const noexcept { return length == 0; } constexpr size_t capacity() const noexcept { return size; } // append item to vector, without bounds checking FASTFLOAT_CONSTEXPR14 void push_unchecked(limb value) noexcept { data[length] = value; length++; } // append item to vector, returning if item was added FASTFLOAT_CONSTEXPR14 bool try_push(limb value) noexcept { if (len() < capacity()) { push_unchecked(value); return true; } else { return false; } } // add items to the vector, from a span, without bounds checking FASTFLOAT_CONSTEXPR20 void extend_unchecked(limb_span s) noexcept { limb *ptr = data + length; std::copy_n(s.ptr, s.len(), ptr); set_len(len() + s.len()); } // try to add items to the vector, returning if items were added FASTFLOAT_CONSTEXPR20 bool try_extend(limb_span s) noexcept { if (len() + s.len() <= capacity()) { extend_unchecked(s); return true; } else { return false; } } // resize the vector, without bounds checking // if the new size is longer than the vector, assign value to each // appended item. FASTFLOAT_CONSTEXPR20 void resize_unchecked(size_t new_len, limb value) noexcept { if (new_len > len()) { size_t count = new_len - len(); limb *first = data + len(); limb *last = first + count; ::std::fill(first, last, value); set_len(new_len); } else { set_len(new_len); } } // try to resize the vector, returning if the vector was resized. FASTFLOAT_CONSTEXPR20 bool try_resize(size_t new_len, limb value) noexcept { if (new_len > capacity()) { return false; } else { resize_unchecked(new_len, value); return true; } } // check if any limbs are non-zero after the given index. // this needs to be done in reverse order, since the index // is relative to the most significant limbs. FASTFLOAT_CONSTEXPR14 bool nonzero(size_t index) const noexcept { while (index < len()) { if (rindex(index) != 0) { return true; } index++; } return false; } // normalize the big integer, so most-significant zero limbs are removed. FASTFLOAT_CONSTEXPR14 void normalize() noexcept { while (len() > 0 && rindex(0) == 0) { length--; } } }; fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t empty_hi64(bool &truncated) noexcept { truncated = false; return 0; } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t uint64_hi64(uint64_t r0, bool &truncated) noexcept { truncated = false; int shl = leading_zeroes(r0); return r0 << shl; } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t uint64_hi64(uint64_t r0, uint64_t r1, bool &truncated) noexcept { int shl = leading_zeroes(r0); if (shl == 0) { truncated = r1 != 0; return r0; } else { int shr = 64 - shl; truncated = (r1 << shl) != 0; return (r0 << shl) | (r1 >> shr); } } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t uint32_hi64(uint32_t r0, bool &truncated) noexcept { return uint64_hi64(r0, truncated); } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t uint32_hi64(uint32_t r0, uint32_t r1, bool &truncated) noexcept { uint64_t x0 = r0; uint64_t x1 = r1; return uint64_hi64((x0 << 32) | x1, truncated); } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 uint64_t uint32_hi64(uint32_t r0, uint32_t r1, uint32_t r2, bool &truncated) noexcept { uint64_t x0 = r0; uint64_t x1 = r1; uint64_t x2 = r2; return uint64_hi64(x0, (x1 << 32) | x2, truncated); } // add two small integers, checking for overflow. // we want an efficient operation. for msvc, where // we don't have built-in intrinsics, this is still // pretty fast. fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb scalar_add(limb x, limb y, bool &overflow) noexcept { limb z; // gcc and clang #if defined(__has_builtin) #if __has_builtin(__builtin_add_overflow) if (!cpp20_and_in_constexpr()) { overflow = __builtin_add_overflow(x, y, &z); return z; } #endif #endif // generic, this still optimizes correctly on MSVC. z = x + y; overflow = z < x; return z; } // multiply two small integers, getting both the high and low bits. fastfloat_really_inline FASTFLOAT_CONSTEXPR20 limb scalar_mul(limb x, limb y, limb &carry) noexcept { #ifdef FASTFLOAT_64BIT_LIMB #if defined(__SIZEOF_INT128__) // GCC and clang both define it as an extension. __uint128_t z = __uint128_t(x) * __uint128_t(y) + __uint128_t(carry); carry = limb(z >> limb_bits); return limb(z); #else // fallback, no native 128-bit integer multiplication with carry. // on msvc, this optimizes identically, somehow. value128 z = full_multiplication(x, y); bool overflow; z.low = scalar_add(z.low, carry, overflow); z.high += uint64_t(overflow); // cannot overflow carry = z.high; return z.low; #endif #else uint64_t z = uint64_t(x) * uint64_t(y) + uint64_t(carry); carry = limb(z >> limb_bits); return limb(z); #endif } // add scalar value to bigint starting from offset. // used in grade school multiplication template inline FASTFLOAT_CONSTEXPR20 bool small_add_from(stackvec &vec, limb y, size_t start) noexcept { size_t index = start; limb carry = y; bool overflow; while (carry != 0 && index < vec.len()) { vec[index] = scalar_add(vec[index], carry, overflow); carry = limb(overflow); index += 1; } if (carry != 0) { FASTFLOAT_TRY(vec.try_push(carry)); } return true; } // add scalar value to bigint. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool small_add(stackvec &vec, limb y) noexcept { return small_add_from(vec, y, 0); } // multiply bigint by scalar value. template inline FASTFLOAT_CONSTEXPR20 bool small_mul(stackvec &vec, limb y) noexcept { limb carry = 0; for (size_t index = 0; index < vec.len(); index++) { vec[index] = scalar_mul(vec[index], y, carry); } if (carry != 0) { FASTFLOAT_TRY(vec.try_push(carry)); } return true; } // add bigint to bigint starting from index. // used in grade school multiplication template FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec &x, limb_span y, size_t start) noexcept { // the effective x buffer is from `xstart..x.len()`, so exit early // if we can't get that current range. if (x.len() < start || y.len() > x.len() - start) { FASTFLOAT_TRY(x.try_resize(y.len() + start, 0)); } bool carry = false; for (size_t index = 0; index < y.len(); index++) { limb xi = x[index + start]; limb yi = y[index]; bool c1 = false; bool c2 = false; xi = scalar_add(xi, yi, c1); if (carry) { xi = scalar_add(xi, 1, c2); } x[index + start] = xi; carry = c1 | c2; } // handle overflow if (carry) { FASTFLOAT_TRY(small_add_from(x, 1, y.len() + start)); } return true; } // add bigint to bigint. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool large_add_from(stackvec &x, limb_span y) noexcept { return large_add_from(x, y, 0); } // grade-school multiplication algorithm template FASTFLOAT_CONSTEXPR20 bool long_mul(stackvec &x, limb_span y) noexcept { limb_span xs = limb_span(x.data, x.len()); stackvec z(xs); limb_span zs = limb_span(z.data, z.len()); if (y.len() != 0) { limb y0 = y[0]; FASTFLOAT_TRY(small_mul(x, y0)); for (size_t index = 1; index < y.len(); index++) { limb yi = y[index]; stackvec zi; if (yi != 0) { // re-use the same buffer throughout zi.set_len(0); FASTFLOAT_TRY(zi.try_extend(zs)); FASTFLOAT_TRY(small_mul(zi, yi)); limb_span zis = limb_span(zi.data, zi.len()); FASTFLOAT_TRY(large_add_from(x, zis, index)); } } } x.normalize(); return true; } // grade-school multiplication algorithm template FASTFLOAT_CONSTEXPR20 bool large_mul(stackvec &x, limb_span y) noexcept { if (y.len() == 1) { FASTFLOAT_TRY(small_mul(x, y[0])); } else { FASTFLOAT_TRY(long_mul(x, y)); } return true; } template struct pow5_tables { static constexpr uint32_t large_step = 135; static constexpr uint64_t small_power_of_5[] = { 1UL, 5UL, 25UL, 125UL, 625UL, 3125UL, 15625UL, 78125UL, 390625UL, 1953125UL, 9765625UL, 48828125UL, 244140625UL, 1220703125UL, 6103515625UL, 30517578125UL, 152587890625UL, 762939453125UL, 3814697265625UL, 19073486328125UL, 95367431640625UL, 476837158203125UL, 2384185791015625UL, 11920928955078125UL, 59604644775390625UL, 298023223876953125UL, 1490116119384765625UL, 7450580596923828125UL, }; #ifdef FASTFLOAT_64BIT_LIMB constexpr static limb large_power_of_5[] = { 1414648277510068013UL, 9180637584431281687UL, 4539964771860779200UL, 10482974169319127550UL, 198276706040285095UL}; #else constexpr static limb large_power_of_5[] = { 4279965485U, 329373468U, 4020270615U, 2137533757U, 4287402176U, 1057042919U, 1071430142U, 2440757623U, 381945767U, 46164893U}; #endif }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE template constexpr uint32_t pow5_tables::large_step; template constexpr uint64_t pow5_tables::small_power_of_5[]; template constexpr limb pow5_tables::large_power_of_5[]; #endif // big integer type. implements a small subset of big integer // arithmetic, using simple algorithms since asymptotically // faster algorithms are slower for a small number of limbs. // all operations assume the big-integer is normalized. struct bigint : pow5_tables<> { // storage of the limbs, in little-endian order. stackvec vec; FASTFLOAT_CONSTEXPR20 bigint() : vec() {} bigint(bigint const &) = delete; bigint &operator=(bigint const &) = delete; bigint(bigint &&) = delete; bigint &operator=(bigint &&other) = delete; FASTFLOAT_CONSTEXPR20 bigint(uint64_t value) : vec() { #ifdef FASTFLOAT_64BIT_LIMB vec.push_unchecked(value); #else vec.push_unchecked(uint32_t(value)); vec.push_unchecked(uint32_t(value >> 32)); #endif vec.normalize(); } // get the high 64 bits from the vector, and if bits were truncated. // this is to get the significant digits for the float. FASTFLOAT_CONSTEXPR20 uint64_t hi64(bool &truncated) const noexcept { #ifdef FASTFLOAT_64BIT_LIMB if (vec.len() == 0) { return empty_hi64(truncated); } else if (vec.len() == 1) { return uint64_hi64(vec.rindex(0), truncated); } else { uint64_t result = uint64_hi64(vec.rindex(0), vec.rindex(1), truncated); truncated |= vec.nonzero(2); return result; } #else if (vec.len() == 0) { return empty_hi64(truncated); } else if (vec.len() == 1) { return uint32_hi64(vec.rindex(0), truncated); } else if (vec.len() == 2) { return uint32_hi64(vec.rindex(0), vec.rindex(1), truncated); } else { uint64_t result = uint32_hi64(vec.rindex(0), vec.rindex(1), vec.rindex(2), truncated); truncated |= vec.nonzero(3); return result; } #endif } // compare two big integers, returning the large value. // assumes both are normalized. if the return value is // negative, other is larger, if the return value is // positive, this is larger, otherwise they are equal. // the limbs are stored in little-endian order, so we // must compare the limbs in ever order. FASTFLOAT_CONSTEXPR20 int compare(bigint const &other) const noexcept { if (vec.len() > other.vec.len()) { return 1; } else if (vec.len() < other.vec.len()) { return -1; } else { for (size_t index = vec.len(); index > 0; index--) { limb xi = vec[index - 1]; limb yi = other.vec[index - 1]; if (xi > yi) { return 1; } else if (xi < yi) { return -1; } } return 0; } } // shift left each limb n bits, carrying over to the new limb // returns true if we were able to shift all the digits. FASTFLOAT_CONSTEXPR20 bool shl_bits(size_t n) noexcept { // Internally, for each item, we shift left by n, and add the previous // right shifted limb-bits. // For example, we transform (for u8) shifted left 2, to: // b10100100 b01000010 // b10 b10010001 b00001000 FASTFLOAT_DEBUG_ASSERT(n != 0); FASTFLOAT_DEBUG_ASSERT(n < sizeof(limb) * 8); size_t shl = n; size_t shr = limb_bits - shl; limb prev = 0; for (size_t index = 0; index < vec.len(); index++) { limb xi = vec[index]; vec[index] = (xi << shl) | (prev >> shr); prev = xi; } limb carry = prev >> shr; if (carry != 0) { return vec.try_push(carry); } return true; } // move the limbs left by `n` limbs. FASTFLOAT_CONSTEXPR20 bool shl_limbs(size_t n) noexcept { FASTFLOAT_DEBUG_ASSERT(n != 0); if (n + vec.len() > vec.capacity()) { return false; } else if (!vec.is_empty()) { // move limbs limb *dst = vec.data + n; limb const *src = vec.data; std::copy_backward(src, src + vec.len(), dst + vec.len()); // fill in empty limbs limb *first = vec.data; limb *last = first + n; ::std::fill(first, last, 0); vec.set_len(n + vec.len()); return true; } else { return true; } } // move the limbs left by `n` bits. FASTFLOAT_CONSTEXPR20 bool shl(size_t n) noexcept { size_t rem = n % limb_bits; size_t div = n / limb_bits; if (rem != 0) { FASTFLOAT_TRY(shl_bits(rem)); } if (div != 0) { FASTFLOAT_TRY(shl_limbs(div)); } return true; } // get the number of leading zeros in the bigint. FASTFLOAT_CONSTEXPR20 int ctlz() const noexcept { if (vec.is_empty()) { return 0; } else { #ifdef FASTFLOAT_64BIT_LIMB return leading_zeroes(vec.rindex(0)); #else // no use defining a specialized leading_zeroes for a 32-bit type. uint64_t r0 = vec.rindex(0); return leading_zeroes(r0 << 32); #endif } } // get the number of bits in the bigint. FASTFLOAT_CONSTEXPR20 int bit_length() const noexcept { int lz = ctlz(); return int(limb_bits * vec.len()) - lz; } FASTFLOAT_CONSTEXPR20 bool mul(limb y) noexcept { return small_mul(vec, y); } FASTFLOAT_CONSTEXPR20 bool add(limb y) noexcept { return small_add(vec, y); } // multiply as if by 2 raised to a power. FASTFLOAT_CONSTEXPR20 bool pow2(uint32_t exp) noexcept { return shl(exp); } // multiply as if by 5 raised to a power. FASTFLOAT_CONSTEXPR20 bool pow5(uint32_t exp) noexcept { // multiply by a power of 5 size_t large_length = sizeof(large_power_of_5) / sizeof(limb); limb_span large = limb_span(large_power_of_5, large_length); while (exp >= large_step) { FASTFLOAT_TRY(large_mul(vec, large)); exp -= large_step; } #ifdef FASTFLOAT_64BIT_LIMB uint32_t small_step = 27; limb max_native = 7450580596923828125UL; #else uint32_t small_step = 13; limb max_native = 1220703125U; #endif while (exp >= small_step) { FASTFLOAT_TRY(small_mul(vec, max_native)); exp -= small_step; } if (exp != 0) { // Work around clang bug https://godbolt.org/z/zedh7rrhc // This is similar to https://github.com/llvm/llvm-project/issues/47746, // except the workaround described there don't work here FASTFLOAT_TRY(small_mul( vec, limb(((void)small_power_of_5[0], small_power_of_5[exp])))); } return true; } // multiply as if by 10 raised to a power. FASTFLOAT_CONSTEXPR20 bool pow10(uint32_t exp) noexcept { FASTFLOAT_TRY(pow5(exp)); return pow2(exp); } }; } // namespace fast_float #endif fast_float-8.0.0/include/fast_float/constexpr_feature_detect.h000066400000000000000000000025011475170546600246640ustar00rootroot00000000000000#ifndef FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H #define FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H #ifdef __has_include #if __has_include() #include #endif #endif // Testing for https://wg21.link/N3652, adopted in C++14 #if __cpp_constexpr >= 201304 #define FASTFLOAT_CONSTEXPR14 constexpr #else #define FASTFLOAT_CONSTEXPR14 #endif #if defined(__cpp_lib_bit_cast) && __cpp_lib_bit_cast >= 201806L #define FASTFLOAT_HAS_BIT_CAST 1 #else #define FASTFLOAT_HAS_BIT_CAST 0 #endif #if defined(__cpp_lib_is_constant_evaluated) && \ __cpp_lib_is_constant_evaluated >= 201811L #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 1 #else #define FASTFLOAT_HAS_IS_CONSTANT_EVALUATED 0 #endif // Testing for relevant C++20 constexpr library features #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED && FASTFLOAT_HAS_BIT_CAST && \ __cpp_lib_constexpr_algorithms >= 201806L /*For std::copy and std::fill*/ #define FASTFLOAT_CONSTEXPR20 constexpr #define FASTFLOAT_IS_CONSTEXPR 1 #else #define FASTFLOAT_CONSTEXPR20 #define FASTFLOAT_IS_CONSTEXPR 0 #endif #if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) #define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 0 #else #define FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE 1 #endif #endif // FASTFLOAT_CONSTEXPR_FEATURE_DETECT_H fast_float-8.0.0/include/fast_float/decimal_to_binary.h000066400000000000000000000202151475170546600232420ustar00rootroot00000000000000#ifndef FASTFLOAT_DECIMAL_TO_BINARY_H #define FASTFLOAT_DECIMAL_TO_BINARY_H #include "float_common.h" #include "fast_table.h" #include #include #include #include #include #include namespace fast_float { // This will compute or rather approximate w * 5**q and return a pair of 64-bit // words approximating the result, with the "high" part corresponding to the // most significant bits and the low part corresponding to the least significant // bits. // template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 compute_product_approximation(int64_t q, uint64_t w) { int const index = 2 * int(q - powers::smallest_power_of_five); // For small values of q, e.g., q in [0,27], the answer is always exact // because The line value128 firstproduct = full_multiplication(w, // power_of_five_128[index]); gives the exact answer. value128 firstproduct = full_multiplication(w, powers::power_of_five_128[index]); static_assert((bit_precision >= 0) && (bit_precision <= 64), " precision should be in (0,64]"); constexpr uint64_t precision_mask = (bit_precision < 64) ? (uint64_t(0xFFFFFFFFFFFFFFFF) >> bit_precision) : uint64_t(0xFFFFFFFFFFFFFFFF); if ((firstproduct.high & precision_mask) == precision_mask) { // could further guard with (lower + w < lower) // regarding the second product, we only need secondproduct.high, but our // expectation is that the compiler will optimize this extra work away if // needed. value128 secondproduct = full_multiplication(w, powers::power_of_five_128[index + 1]); firstproduct.low += secondproduct.high; if (secondproduct.high > firstproduct.low) { firstproduct.high++; } } return firstproduct; } namespace detail { /** * For q in (0,350), we have that * f = (((152170 + 65536) * q ) >> 16); * is equal to * floor(p) + q * where * p = log(5**q)/log(2) = q * log(5)/log(2) * * For negative values of q in (-400,0), we have that * f = (((152170 + 65536) * q ) >> 16); * is equal to * -ceil(p) + q * where * p = log(5**-q)/log(2) = -q * log(5)/log(2) */ constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept { return (((152170 + 65536) * q) >> 16) + 63; } } // namespace detail // create an adjusted mantissa, biased by the invalid power2 // for significant digits already multiplied by 10 ** q. template fastfloat_really_inline FASTFLOAT_CONSTEXPR14 adjusted_mantissa compute_error_scaled(int64_t q, uint64_t w, int lz) noexcept { int hilz = int(w >> 63) ^ 1; adjusted_mantissa answer; answer.mantissa = w << hilz; int bias = binary::mantissa_explicit_bits() - binary::minimum_exponent(); answer.power2 = int32_t(detail::power(int32_t(q)) + bias - hilz - lz - 62 + invalid_am_bias); return answer; } // w * 10 ** q, without rounding the representation up. // the power2 in the exponent will be adjusted by invalid_am_bias. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa compute_error(int64_t q, uint64_t w) noexcept { int lz = leading_zeroes(w); w <<= lz; value128 product = compute_product_approximation(q, w); return compute_error_scaled(q, product.high, lz); } // Computers w * 10 ** q. // The returned value should be a valid number that simply needs to be // packed. However, in some very rare cases, the computation will fail. In such // cases, we return an adjusted_mantissa with a negative power of 2: the caller // should recompute in such cases. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa compute_float(int64_t q, uint64_t w) noexcept { adjusted_mantissa answer; if ((w == 0) || (q < binary::smallest_power_of_ten())) { answer.power2 = 0; answer.mantissa = 0; // result should be zero return answer; } if (q > binary::largest_power_of_ten()) { // we want to get infinity: answer.power2 = binary::infinite_power(); answer.mantissa = 0; return answer; } // At this point in time q is in [powers::smallest_power_of_five, // powers::largest_power_of_five]. // We want the most significant bit of i to be 1. Shift if needed. int lz = leading_zeroes(w); w <<= lz; // The required precision is binary::mantissa_explicit_bits() + 3 because // 1. We need the implicit bit // 2. We need an extra bit for rounding purposes // 3. We might lose a bit due to the "upperbit" routine (result too small, // requiring a shift) value128 product = compute_product_approximation(q, w); // The computed 'product' is always sufficient. // Mathematical proof: // Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to // appear) See script/mushtak_lemire.py // The "compute_product_approximation" function can be slightly slower than a // branchless approach: value128 product = compute_product(q, w); but in // practice, we can win big with the compute_product_approximation if its // additional branch is easily predicted. Which is best is data specific. int upperbit = int(product.high >> 63); int shift = upperbit + 64 - binary::mantissa_explicit_bits() - 3; answer.mantissa = product.high >> shift; answer.power2 = int32_t(detail::power(int32_t(q)) + upperbit - lz - binary::minimum_exponent()); if (answer.power2 <= 0) { // we have a subnormal? // Here have that answer.power2 <= 0 so -answer.power2 >= 0 if (-answer.power2 + 1 >= 64) { // if we have more than 64 bits below the minimum exponent, you // have a zero for sure. answer.power2 = 0; answer.mantissa = 0; // result should be zero return answer; } // next line is safe because -answer.power2 + 1 < 64 answer.mantissa >>= -answer.power2 + 1; // Thankfully, we can't have both "round-to-even" and subnormals because // "round-to-even" only occurs for powers close to 0 in the 32-bit and // and 64-bit case (with no more than 19 digits). answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa >>= 1; // There is a weird scenario where we don't have a subnormal but just. // Suppose we start with 2.2250738585072013e-308, we end up // with 0x3fffffffffffff x 2^-1023-53 which is technically subnormal // whereas 0x40000000000000 x 2^-1023-53 is normal. Now, we need to round // up 0x3fffffffffffff x 2^-1023-53 and once we do, we are no longer // subnormal, but we can only know this after rounding. // So we only declare a subnormal if we are smaller than the threshold. answer.power2 = (answer.mantissa < (uint64_t(1) << binary::mantissa_explicit_bits())) ? 0 : 1; return answer; } // usually, we round *up*, but if we fall right in between and and we have an // even basis, we need to round down // We are only concerned with the cases where 5**q fits in single 64-bit word. if ((product.low <= 1) && (q >= binary::min_exponent_round_to_even()) && (q <= binary::max_exponent_round_to_even()) && ((answer.mantissa & 3) == 1)) { // we may fall between two floats! // To be in-between two floats we need that in doing // answer.mantissa = product.high >> (upperbit + 64 - // binary::mantissa_explicit_bits() - 3); // ... we dropped out only zeroes. But if this happened, then we can go // back!!! if ((answer.mantissa << shift) == product.high) { answer.mantissa &= ~uint64_t(1); // flip it so that we do not round up } } answer.mantissa += (answer.mantissa & 1); // round up answer.mantissa >>= 1; if (answer.mantissa >= (uint64_t(2) << binary::mantissa_explicit_bits())) { answer.mantissa = (uint64_t(1) << binary::mantissa_explicit_bits()); answer.power2++; // undo previous addition } answer.mantissa &= ~(uint64_t(1) << binary::mantissa_explicit_bits()); if (answer.power2 >= binary::infinite_power()) { // infinity answer.power2 = binary::infinite_power(); answer.mantissa = 0; } return answer; } } // namespace fast_float #endif fast_float-8.0.0/include/fast_float/digit_comparison.h000066400000000000000000000364071475170546600231420ustar00rootroot00000000000000#ifndef FASTFLOAT_DIGIT_COMPARISON_H #define FASTFLOAT_DIGIT_COMPARISON_H #include #include #include #include #include "float_common.h" #include "bigint.h" #include "ascii_number.h" namespace fast_float { // 1e0 to 1e19 constexpr static uint64_t powers_of_ten_uint64[] = {1UL, 10UL, 100UL, 1000UL, 10000UL, 100000UL, 1000000UL, 10000000UL, 100000000UL, 1000000000UL, 10000000000UL, 100000000000UL, 1000000000000UL, 10000000000000UL, 100000000000000UL, 1000000000000000UL, 10000000000000000UL, 100000000000000000UL, 1000000000000000000UL, 10000000000000000000UL}; // calculate the exponent, in scientific notation, of the number. // 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 fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int32_t scientific_exponent(parsed_number_string_t &num) noexcept { uint64_t mantissa = num.mantissa; int32_t exponent = int32_t(num.exponent); while (mantissa >= 10000) { mantissa /= 10000; exponent += 4; } while (mantissa >= 100) { mantissa /= 100; exponent += 2; } while (mantissa >= 10) { mantissa /= 10; exponent += 1; } return exponent; } // this converts a native floating-point number to an extended-precision float. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa to_extended(T value) noexcept { using equiv_uint = equiv_uint_t; constexpr equiv_uint exponent_mask = binary_format::exponent_mask(); constexpr equiv_uint mantissa_mask = binary_format::mantissa_mask(); constexpr equiv_uint hidden_bit_mask = binary_format::hidden_bit_mask(); adjusted_mantissa am; int32_t bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); equiv_uint bits; #if FASTFLOAT_HAS_BIT_CAST bits = std::bit_cast(value); #else ::memcpy(&bits, &value, sizeof(T)); #endif if ((bits & exponent_mask) == 0) { // denormal am.power2 = 1 - bias; am.mantissa = bits & mantissa_mask; } else { // normal am.power2 = int32_t((bits & exponent_mask) >> binary_format::mantissa_explicit_bits()); am.power2 -= bias; am.mantissa = (bits & mantissa_mask) | hidden_bit_mask; } return am; } // get the extended precision value of the halfway point between b and b+u. // we are given a native float that represents b, so we need to adjust it // halfway between b and b+u. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa to_extended_halfway(T value) noexcept { adjusted_mantissa am = to_extended(value); am.mantissa <<= 1; am.mantissa += 1; am.power2 -= 1; return am; } // round an extended-precision float to the nearest machine float. template fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round(adjusted_mantissa &am, callback cb) noexcept { int32_t mantissa_shift = 64 - binary_format::mantissa_explicit_bits() - 1; if (-am.power2 >= mantissa_shift) { // have a denormal float int32_t shift = -am.power2 + 1; cb(am, std::min(shift, 64)); // check for round-up: if rounding-nearest carried us to the hidden bit. am.power2 = (am.mantissa < (uint64_t(1) << binary_format::mantissa_explicit_bits())) ? 0 : 1; return; } // have a normal float, use the default shift. cb(am, mantissa_shift); // check for carry if (am.mantissa >= (uint64_t(2) << binary_format::mantissa_explicit_bits())) { am.mantissa = (uint64_t(1) << binary_format::mantissa_explicit_bits()); am.power2++; } // check for infinite: we could have carried to an infinite power am.mantissa &= ~(uint64_t(1) << binary_format::mantissa_explicit_bits()); if (am.power2 >= binary_format::infinite_power()) { am.power2 = binary_format::infinite_power(); am.mantissa = 0; } } template fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round_nearest_tie_even(adjusted_mantissa &am, int32_t shift, callback cb) noexcept { uint64_t const mask = (shift == 64) ? UINT64_MAX : (uint64_t(1) << shift) - 1; uint64_t const halfway = (shift == 0) ? 0 : uint64_t(1) << (shift - 1); uint64_t truncated_bits = am.mantissa & mask; bool is_above = truncated_bits > halfway; bool is_halfway = truncated_bits == halfway; // shift digits into position if (shift == 64) { am.mantissa = 0; } else { am.mantissa >>= shift; } am.power2 += shift; bool is_odd = (am.mantissa & 1) == 1; am.mantissa += uint64_t(cb(is_odd, is_halfway, is_above)); } fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void round_down(adjusted_mantissa &am, int32_t shift) noexcept { if (shift == 64) { am.mantissa = 0; } else { am.mantissa >>= shift; } am.power2 += shift; } template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void skip_zeros(UC const *&first, UC const *last) noexcept { uint64_t val; while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len()) { ::memcpy(&val, first, sizeof(uint64_t)); if (val != int_cmp_zeros()) { break; } first += int_cmp_len(); } while (first != last) { if (*first != UC('0')) { break; } first++; } } // determine if any non-zero digits were truncated. // all characters must be valid digits. template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool is_truncated(UC const *first, UC const *last) noexcept { // do 8-bit optimizations, can just compare to 8 literal 0s. uint64_t val; while (!cpp20_and_in_constexpr() && std::distance(first, last) >= int_cmp_len()) { ::memcpy(&val, first, sizeof(uint64_t)); if (val != int_cmp_zeros()) { return true; } first += int_cmp_len(); } while (first != last) { if (*first != UC('0')) { return true; } ++first; } return false; } template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 bool is_truncated(span s) noexcept { return is_truncated(s.ptr, s.ptr + s.len()); } template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void parse_eight_digits(UC const *&p, limb &value, size_t &counter, size_t &count) noexcept { value = value * 100000000 + parse_eight_digits_unrolled(p); p += 8; counter += 8; count += 8; } template fastfloat_really_inline FASTFLOAT_CONSTEXPR14 void parse_one_digit(UC const *&p, limb &value, size_t &counter, size_t &count) noexcept { value = value * 10 + limb(*p - UC('0')); p++; counter++; count++; } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void add_native(bigint &big, limb power, limb value) noexcept { big.mul(power); big.add(value); } fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void round_up_bigint(bigint &big, size_t &count) noexcept { // need to round-up the digits, but need to avoid rounding // ....9999 to ...10000, which could cause a false halfway point. add_native(big, 10, 1); count++; } // parse the significant digits into a big integer template inline FASTFLOAT_CONSTEXPR20 void parse_mantissa(bigint &result, parsed_number_string_t &num, size_t max_digits, size_t &digits) noexcept { // try to minimize the number of big integer and scalar multiplication. // therefore, try to parse 8 digits at a time, and multiply by the largest // scalar value (9 or 19 digits) for each step. size_t counter = 0; digits = 0; limb value = 0; #ifdef FASTFLOAT_64BIT_LIMB size_t step = 19; #else size_t step = 9; #endif // process all integer digits. UC const *p = num.integer.ptr; UC const *pend = p + num.integer.len(); skip_zeros(p, pend); // process all digits, in increments of step per loop while (p != pend) { while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { parse_eight_digits(p, value, counter, digits); } while (counter < step && p != pend && digits < max_digits) { parse_one_digit(p, value, counter, digits); } if (digits == max_digits) { // add the temporary value, then check if we've truncated any digits add_native(result, limb(powers_of_ten_uint64[counter]), value); bool truncated = is_truncated(p, pend); if (num.fraction.ptr != nullptr) { truncated |= is_truncated(num.fraction); } if (truncated) { round_up_bigint(result, digits); } return; } else { add_native(result, limb(powers_of_ten_uint64[counter]), value); counter = 0; value = 0; } } // add our fraction digits, if they're available. if (num.fraction.ptr != nullptr) { p = num.fraction.ptr; pend = p + num.fraction.len(); if (digits == 0) { skip_zeros(p, pend); } // process all digits, in increments of step per loop while (p != pend) { while ((std::distance(p, pend) >= 8) && (step - counter >= 8) && (max_digits - digits >= 8)) { parse_eight_digits(p, value, counter, digits); } while (counter < step && p != pend && digits < max_digits) { parse_one_digit(p, value, counter, digits); } if (digits == max_digits) { // add the temporary value, then check if we've truncated any digits add_native(result, limb(powers_of_ten_uint64[counter]), value); bool truncated = is_truncated(p, pend); if (truncated) { round_up_bigint(result, digits); } return; } else { add_native(result, limb(powers_of_ten_uint64[counter]), value); counter = 0; value = 0; } } } if (counter != 0) { add_native(result, limb(powers_of_ten_uint64[counter]), value); } } template inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa positive_digit_comp(bigint &bigmant, int32_t exponent) noexcept { FASTFLOAT_ASSERT(bigmant.pow10(uint32_t(exponent))); adjusted_mantissa answer; bool truncated; answer.mantissa = bigmant.hi64(truncated); int bias = binary_format::mantissa_explicit_bits() - binary_format::minimum_exponent(); answer.power2 = bigmant.bit_length() - 64 + bias; round(answer, [truncated](adjusted_mantissa &a, int32_t shift) { round_nearest_tie_even( a, shift, [truncated](bool is_odd, bool is_halfway, bool is_above) -> bool { return is_above || (is_halfway && truncated) || (is_odd && is_halfway); }); }); return answer; } // the scaling here is quite simple: we have, for the real digits `m * 10^e`, // and for the theoretical digits `n * 2^f`. Since `e` is always negative, // to scale them identically, we do `n * 2^f * 5^-f`, so we now have `m * 2^e`. // we then need to scale by `2^(f- e)`, and then the two significant digits // are of the same magnitude. template inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa negative_digit_comp( bigint &bigmant, adjusted_mantissa am, int32_t exponent) noexcept { bigint &real_digits = bigmant; int32_t real_exp = exponent; // get the value of `b`, rounded down, and get a bigint representation of b+h adjusted_mantissa am_b = am; // gcc7 buf: use a lambda to remove the noexcept qualifier bug with // -Wnoexcept-type. round(am_b, [](adjusted_mantissa &a, int32_t shift) { round_down(a, shift); }); T b; to_float(false, am_b, b); adjusted_mantissa theor = to_extended_halfway(b); bigint theor_digits(theor.mantissa); int32_t theor_exp = theor.power2; // scale real digits and theor digits to be same power. int32_t pow2_exp = theor_exp - real_exp; uint32_t pow5_exp = uint32_t(-real_exp); if (pow5_exp != 0) { FASTFLOAT_ASSERT(theor_digits.pow5(pow5_exp)); } if (pow2_exp > 0) { FASTFLOAT_ASSERT(theor_digits.pow2(uint32_t(pow2_exp))); } else if (pow2_exp < 0) { FASTFLOAT_ASSERT(real_digits.pow2(uint32_t(-pow2_exp))); } // compare digits, and use it to director rounding int ord = real_digits.compare(theor_digits); adjusted_mantissa answer = am; round(answer, [ord](adjusted_mantissa &a, int32_t shift) { round_nearest_tie_even( a, shift, [ord](bool is_odd, bool _, bool __) -> bool { (void)_; // not needed, since we've done our comparison (void)__; // not needed, since we've done our comparison if (ord > 0) { return true; } else if (ord < 0) { return false; } else { return is_odd; } }); }); return answer; } // 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 // algorithm is always correct, and uses one of two approaches. when // the exponent is positive relative to the significant digits (such as // 1234), we create a big-integer representation, get the high 64-bits, // determine if any lower bits are truncated, and use that to direct // rounding. in case of a negative exponent relative to the significant // digits (such as 1.2345), we create a theoretical representation of // `b` as a big-integer type, scaled to the same binary exponent as // the actual digits. we then compare the big integer representations // of both, and use that to direct rounding. template inline FASTFLOAT_CONSTEXPR20 adjusted_mantissa digit_comp(parsed_number_string_t &num, adjusted_mantissa am) noexcept { // remove the invalid exponent bias am.power2 -= invalid_am_bias; int32_t sci_exp = scientific_exponent(num); size_t max_digits = binary_format::max_digits(); size_t digits = 0; bigint bigmant; parse_mantissa(bigmant, num, max_digits, digits); // can't underflow, since digits is at most max_digits. int32_t exponent = sci_exp + 1 - int32_t(digits); if (exponent >= 0) { return positive_digit_comp(bigmant, exponent); } else { return negative_digit_comp(bigmant, am, exponent); } } } // namespace fast_float #endif fast_float-8.0.0/include/fast_float/fast_float.h000066400000000000000000000047311475170546600217250ustar00rootroot00000000000000 #ifndef FASTFLOAT_FAST_FLOAT_H #define FASTFLOAT_FAST_FLOAT_H #include "float_common.h" namespace fast_float { /** * This function parses the character sequence [first,last) for a number. It * parses floating-point numbers expecting a locale-indepent format equivalent * to what is used by std::strtod in the default ("C") locale. The resulting * floating-point value is the closest floating-point values (using either float * or double), using the "round to even" convention for values that would * otherwise fall right in-between two values. That is, we provide exact parsing * according to the IEEE standard. * * Given a successful parse, the pointer (`ptr`) in the returned value is set to * point right after the parsed number, and the `value` referenced is set to the * parsed value. In case of error, the returned `ec` contains a representative * error, otherwise the default (`std::errc()`) value is stored. * * The implementation does not throw and does not allocate memory (e.g., with * `new` or `malloc`). * * 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`. */ template ::value)> FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, chars_format fmt = chars_format::general) noexcept; /** * Like from_chars, but accepts an `options` argument to govern number parsing. * Both for floating-point types and integer types. */ template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept; /** * from_chars for integer types. */ template ::value)> FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, int base = 10) noexcept; } // namespace fast_float #include "parse_number.h" #endif // FASTFLOAT_FAST_FLOAT_H fast_float-8.0.0/include/fast_float/fast_table.h000066400000000000000000000750561475170546600217170ustar00rootroot00000000000000#ifndef FASTFLOAT_FAST_TABLE_H #define FASTFLOAT_FAST_TABLE_H #include namespace fast_float { /** * When mapping numbers from decimal to binary, * we go from w * 10^q to m * 2^p but we have * 10^q = 5^q * 2^q, so effectively * we are trying to match * w * 2^q * 5^q to m * 2^p. Thus the powers of two * are not a concern since they can be represented * exactly using the binary notation, only the powers of five * affect the binary significand. */ /** * The smallest non-zero float (binary64) is 2^-1074. * We take as input numbers of the form w x 10^q where w < 2^64. * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. * However, we have that * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^-1074. * Thus it is possible for a number of the form w * 10^-342 where * w is a 64-bit value to be a non-zero floating-point number. ********* * Any number of form w * 10^309 where w>= 1 is going to be * infinite in binary64 so we never need to worry about powers * of 5 greater than 308. */ template struct powers_template { constexpr static int smallest_power_of_five = binary_format::smallest_power_of_ten(); constexpr static int largest_power_of_five = binary_format::largest_power_of_ten(); constexpr static int number_of_entries = 2 * (largest_power_of_five - smallest_power_of_five + 1); // Powers of five from 5^-342 all the way to 5^308 rounded toward one. constexpr static uint64_t power_of_five_128[number_of_entries] = { 0xeef453d6923bd65a, 0x113faa2906a13b3f, 0x9558b4661b6565f8, 0x4ac7ca59a424c507, 0xbaaee17fa23ebf76, 0x5d79bcf00d2df649, 0xe95a99df8ace6f53, 0xf4d82c2c107973dc, 0x91d8a02bb6c10594, 0x79071b9b8a4be869, 0xb64ec836a47146f9, 0x9748e2826cdee284, 0xe3e27a444d8d98b7, 0xfd1b1b2308169b25, 0x8e6d8c6ab0787f72, 0xfe30f0f5e50e20f7, 0xb208ef855c969f4f, 0xbdbd2d335e51a935, 0xde8b2b66b3bc4723, 0xad2c788035e61382, 0x8b16fb203055ac76, 0x4c3bcb5021afcc31, 0xaddcb9e83c6b1793, 0xdf4abe242a1bbf3d, 0xd953e8624b85dd78, 0xd71d6dad34a2af0d, 0x87d4713d6f33aa6b, 0x8672648c40e5ad68, 0xa9c98d8ccb009506, 0x680efdaf511f18c2, 0xd43bf0effdc0ba48, 0x212bd1b2566def2, 0x84a57695fe98746d, 0x14bb630f7604b57, 0xa5ced43b7e3e9188, 0x419ea3bd35385e2d, 0xcf42894a5dce35ea, 0x52064cac828675b9, 0x818995ce7aa0e1b2, 0x7343efebd1940993, 0xa1ebfb4219491a1f, 0x1014ebe6c5f90bf8, 0xca66fa129f9b60a6, 0xd41a26e077774ef6, 0xfd00b897478238d0, 0x8920b098955522b4, 0x9e20735e8cb16382, 0x55b46e5f5d5535b0, 0xc5a890362fddbc62, 0xeb2189f734aa831d, 0xf712b443bbd52b7b, 0xa5e9ec7501d523e4, 0x9a6bb0aa55653b2d, 0x47b233c92125366e, 0xc1069cd4eabe89f8, 0x999ec0bb696e840a, 0xf148440a256e2c76, 0xc00670ea43ca250d, 0x96cd2a865764dbca, 0x380406926a5e5728, 0xbc807527ed3e12bc, 0xc605083704f5ecf2, 0xeba09271e88d976b, 0xf7864a44c633682e, 0x93445b8731587ea3, 0x7ab3ee6afbe0211d, 0xb8157268fdae9e4c, 0x5960ea05bad82964, 0xe61acf033d1a45df, 0x6fb92487298e33bd, 0x8fd0c16206306bab, 0xa5d3b6d479f8e056, 0xb3c4f1ba87bc8696, 0x8f48a4899877186c, 0xe0b62e2929aba83c, 0x331acdabfe94de87, 0x8c71dcd9ba0b4925, 0x9ff0c08b7f1d0b14, 0xaf8e5410288e1b6f, 0x7ecf0ae5ee44dd9, 0xdb71e91432b1a24a, 0xc9e82cd9f69d6150, 0x892731ac9faf056e, 0xbe311c083a225cd2, 0xab70fe17c79ac6ca, 0x6dbd630a48aaf406, 0xd64d3d9db981787d, 0x92cbbccdad5b108, 0x85f0468293f0eb4e, 0x25bbf56008c58ea5, 0xa76c582338ed2621, 0xaf2af2b80af6f24e, 0xd1476e2c07286faa, 0x1af5af660db4aee1, 0x82cca4db847945ca, 0x50d98d9fc890ed4d, 0xa37fce126597973c, 0xe50ff107bab528a0, 0xcc5fc196fefd7d0c, 0x1e53ed49a96272c8, 0xff77b1fcbebcdc4f, 0x25e8e89c13bb0f7a, 0x9faacf3df73609b1, 0x77b191618c54e9ac, 0xc795830d75038c1d, 0xd59df5b9ef6a2417, 0xf97ae3d0d2446f25, 0x4b0573286b44ad1d, 0x9becce62836ac577, 0x4ee367f9430aec32, 0xc2e801fb244576d5, 0x229c41f793cda73f, 0xf3a20279ed56d48a, 0x6b43527578c1110f, 0x9845418c345644d6, 0x830a13896b78aaa9, 0xbe5691ef416bd60c, 0x23cc986bc656d553, 0xedec366b11c6cb8f, 0x2cbfbe86b7ec8aa8, 0x94b3a202eb1c3f39, 0x7bf7d71432f3d6a9, 0xb9e08a83a5e34f07, 0xdaf5ccd93fb0cc53, 0xe858ad248f5c22c9, 0xd1b3400f8f9cff68, 0x91376c36d99995be, 0x23100809b9c21fa1, 0xb58547448ffffb2d, 0xabd40a0c2832a78a, 0xe2e69915b3fff9f9, 0x16c90c8f323f516c, 0x8dd01fad907ffc3b, 0xae3da7d97f6792e3, 0xb1442798f49ffb4a, 0x99cd11cfdf41779c, 0xdd95317f31c7fa1d, 0x40405643d711d583, 0x8a7d3eef7f1cfc52, 0x482835ea666b2572, 0xad1c8eab5ee43b66, 0xda3243650005eecf, 0xd863b256369d4a40, 0x90bed43e40076a82, 0x873e4f75e2224e68, 0x5a7744a6e804a291, 0xa90de3535aaae202, 0x711515d0a205cb36, 0xd3515c2831559a83, 0xd5a5b44ca873e03, 0x8412d9991ed58091, 0xe858790afe9486c2, 0xa5178fff668ae0b6, 0x626e974dbe39a872, 0xce5d73ff402d98e3, 0xfb0a3d212dc8128f, 0x80fa687f881c7f8e, 0x7ce66634bc9d0b99, 0xa139029f6a239f72, 0x1c1fffc1ebc44e80, 0xc987434744ac874e, 0xa327ffb266b56220, 0xfbe9141915d7a922, 0x4bf1ff9f0062baa8, 0x9d71ac8fada6c9b5, 0x6f773fc3603db4a9, 0xc4ce17b399107c22, 0xcb550fb4384d21d3, 0xf6019da07f549b2b, 0x7e2a53a146606a48, 0x99c102844f94e0fb, 0x2eda7444cbfc426d, 0xc0314325637a1939, 0xfa911155fefb5308, 0xf03d93eebc589f88, 0x793555ab7eba27ca, 0x96267c7535b763b5, 0x4bc1558b2f3458de, 0xbbb01b9283253ca2, 0x9eb1aaedfb016f16, 0xea9c227723ee8bcb, 0x465e15a979c1cadc, 0x92a1958a7675175f, 0xbfacd89ec191ec9, 0xb749faed14125d36, 0xcef980ec671f667b, 0xe51c79a85916f484, 0x82b7e12780e7401a, 0x8f31cc0937ae58d2, 0xd1b2ecb8b0908810, 0xb2fe3f0b8599ef07, 0x861fa7e6dcb4aa15, 0xdfbdcece67006ac9, 0x67a791e093e1d49a, 0x8bd6a141006042bd, 0xe0c8bb2c5c6d24e0, 0xaecc49914078536d, 0x58fae9f773886e18, 0xda7f5bf590966848, 0xaf39a475506a899e, 0x888f99797a5e012d, 0x6d8406c952429603, 0xaab37fd7d8f58178, 0xc8e5087ba6d33b83, 0xd5605fcdcf32e1d6, 0xfb1e4a9a90880a64, 0x855c3be0a17fcd26, 0x5cf2eea09a55067f, 0xa6b34ad8c9dfc06f, 0xf42faa48c0ea481e, 0xd0601d8efc57b08b, 0xf13b94daf124da26, 0x823c12795db6ce57, 0x76c53d08d6b70858, 0xa2cb1717b52481ed, 0x54768c4b0c64ca6e, 0xcb7ddcdda26da268, 0xa9942f5dcf7dfd09, 0xfe5d54150b090b02, 0xd3f93b35435d7c4c, 0x9efa548d26e5a6e1, 0xc47bc5014a1a6daf, 0xc6b8e9b0709f109a, 0x359ab6419ca1091b, 0xf867241c8cc6d4c0, 0xc30163d203c94b62, 0x9b407691d7fc44f8, 0x79e0de63425dcf1d, 0xc21094364dfb5636, 0x985915fc12f542e4, 0xf294b943e17a2bc4, 0x3e6f5b7b17b2939d, 0x979cf3ca6cec5b5a, 0xa705992ceecf9c42, 0xbd8430bd08277231, 0x50c6ff782a838353, 0xece53cec4a314ebd, 0xa4f8bf5635246428, 0x940f4613ae5ed136, 0x871b7795e136be99, 0xb913179899f68584, 0x28e2557b59846e3f, 0xe757dd7ec07426e5, 0x331aeada2fe589cf, 0x9096ea6f3848984f, 0x3ff0d2c85def7621, 0xb4bca50b065abe63, 0xfed077a756b53a9, 0xe1ebce4dc7f16dfb, 0xd3e8495912c62894, 0x8d3360f09cf6e4bd, 0x64712dd7abbbd95c, 0xb080392cc4349dec, 0xbd8d794d96aacfb3, 0xdca04777f541c567, 0xecf0d7a0fc5583a0, 0x89e42caaf9491b60, 0xf41686c49db57244, 0xac5d37d5b79b6239, 0x311c2875c522ced5, 0xd77485cb25823ac7, 0x7d633293366b828b, 0x86a8d39ef77164bc, 0xae5dff9c02033197, 0xa8530886b54dbdeb, 0xd9f57f830283fdfc, 0xd267caa862a12d66, 0xd072df63c324fd7b, 0x8380dea93da4bc60, 0x4247cb9e59f71e6d, 0xa46116538d0deb78, 0x52d9be85f074e608, 0xcd795be870516656, 0x67902e276c921f8b, 0x806bd9714632dff6, 0xba1cd8a3db53b6, 0xa086cfcd97bf97f3, 0x80e8a40eccd228a4, 0xc8a883c0fdaf7df0, 0x6122cd128006b2cd, 0xfad2a4b13d1b5d6c, 0x796b805720085f81, 0x9cc3a6eec6311a63, 0xcbe3303674053bb0, 0xc3f490aa77bd60fc, 0xbedbfc4411068a9c, 0xf4f1b4d515acb93b, 0xee92fb5515482d44, 0x991711052d8bf3c5, 0x751bdd152d4d1c4a, 0xbf5cd54678eef0b6, 0xd262d45a78a0635d, 0xef340a98172aace4, 0x86fb897116c87c34, 0x9580869f0e7aac0e, 0xd45d35e6ae3d4da0, 0xbae0a846d2195712, 0x8974836059cca109, 0xe998d258869facd7, 0x2bd1a438703fc94b, 0x91ff83775423cc06, 0x7b6306a34627ddcf, 0xb67f6455292cbf08, 0x1a3bc84c17b1d542, 0xe41f3d6a7377eeca, 0x20caba5f1d9e4a93, 0x8e938662882af53e, 0x547eb47b7282ee9c, 0xb23867fb2a35b28d, 0xe99e619a4f23aa43, 0xdec681f9f4c31f31, 0x6405fa00e2ec94d4, 0x8b3c113c38f9f37e, 0xde83bc408dd3dd04, 0xae0b158b4738705e, 0x9624ab50b148d445, 0xd98ddaee19068c76, 0x3badd624dd9b0957, 0x87f8a8d4cfa417c9, 0xe54ca5d70a80e5d6, 0xa9f6d30a038d1dbc, 0x5e9fcf4ccd211f4c, 0xd47487cc8470652b, 0x7647c3200069671f, 0x84c8d4dfd2c63f3b, 0x29ecd9f40041e073, 0xa5fb0a17c777cf09, 0xf468107100525890, 0xcf79cc9db955c2cc, 0x7182148d4066eeb4, 0x81ac1fe293d599bf, 0xc6f14cd848405530, 0xa21727db38cb002f, 0xb8ada00e5a506a7c, 0xca9cf1d206fdc03b, 0xa6d90811f0e4851c, 0xfd442e4688bd304a, 0x908f4a166d1da663, 0x9e4a9cec15763e2e, 0x9a598e4e043287fe, 0xc5dd44271ad3cdba, 0x40eff1e1853f29fd, 0xf7549530e188c128, 0xd12bee59e68ef47c, 0x9a94dd3e8cf578b9, 0x82bb74f8301958ce, 0xc13a148e3032d6e7, 0xe36a52363c1faf01, 0xf18899b1bc3f8ca1, 0xdc44e6c3cb279ac1, 0x96f5600f15a7b7e5, 0x29ab103a5ef8c0b9, 0xbcb2b812db11a5de, 0x7415d448f6b6f0e7, 0xebdf661791d60f56, 0x111b495b3464ad21, 0x936b9fcebb25c995, 0xcab10dd900beec34, 0xb84687c269ef3bfb, 0x3d5d514f40eea742, 0xe65829b3046b0afa, 0xcb4a5a3112a5112, 0x8ff71a0fe2c2e6dc, 0x47f0e785eaba72ab, 0xb3f4e093db73a093, 0x59ed216765690f56, 0xe0f218b8d25088b8, 0x306869c13ec3532c, 0x8c974f7383725573, 0x1e414218c73a13fb, 0xafbd2350644eeacf, 0xe5d1929ef90898fa, 0xdbac6c247d62a583, 0xdf45f746b74abf39, 0x894bc396ce5da772, 0x6b8bba8c328eb783, 0xab9eb47c81f5114f, 0x66ea92f3f326564, 0xd686619ba27255a2, 0xc80a537b0efefebd, 0x8613fd0145877585, 0xbd06742ce95f5f36, 0xa798fc4196e952e7, 0x2c48113823b73704, 0xd17f3b51fca3a7a0, 0xf75a15862ca504c5, 0x82ef85133de648c4, 0x9a984d73dbe722fb, 0xa3ab66580d5fdaf5, 0xc13e60d0d2e0ebba, 0xcc963fee10b7d1b3, 0x318df905079926a8, 0xffbbcfe994e5c61f, 0xfdf17746497f7052, 0x9fd561f1fd0f9bd3, 0xfeb6ea8bedefa633, 0xc7caba6e7c5382c8, 0xfe64a52ee96b8fc0, 0xf9bd690a1b68637b, 0x3dfdce7aa3c673b0, 0x9c1661a651213e2d, 0x6bea10ca65c084e, 0xc31bfa0fe5698db8, 0x486e494fcff30a62, 0xf3e2f893dec3f126, 0x5a89dba3c3efccfa, 0x986ddb5c6b3a76b7, 0xf89629465a75e01c, 0xbe89523386091465, 0xf6bbb397f1135823, 0xee2ba6c0678b597f, 0x746aa07ded582e2c, 0x94db483840b717ef, 0xa8c2a44eb4571cdc, 0xba121a4650e4ddeb, 0x92f34d62616ce413, 0xe896a0d7e51e1566, 0x77b020baf9c81d17, 0x915e2486ef32cd60, 0xace1474dc1d122e, 0xb5b5ada8aaff80b8, 0xd819992132456ba, 0xe3231912d5bf60e6, 0x10e1fff697ed6c69, 0x8df5efabc5979c8f, 0xca8d3ffa1ef463c1, 0xb1736b96b6fd83b3, 0xbd308ff8a6b17cb2, 0xddd0467c64bce4a0, 0xac7cb3f6d05ddbde, 0x8aa22c0dbef60ee4, 0x6bcdf07a423aa96b, 0xad4ab7112eb3929d, 0x86c16c98d2c953c6, 0xd89d64d57a607744, 0xe871c7bf077ba8b7, 0x87625f056c7c4a8b, 0x11471cd764ad4972, 0xa93af6c6c79b5d2d, 0xd598e40d3dd89bcf, 0xd389b47879823479, 0x4aff1d108d4ec2c3, 0x843610cb4bf160cb, 0xcedf722a585139ba, 0xa54394fe1eedb8fe, 0xc2974eb4ee658828, 0xce947a3da6a9273e, 0x733d226229feea32, 0x811ccc668829b887, 0x806357d5a3f525f, 0xa163ff802a3426a8, 0xca07c2dcb0cf26f7, 0xc9bcff6034c13052, 0xfc89b393dd02f0b5, 0xfc2c3f3841f17c67, 0xbbac2078d443ace2, 0x9d9ba7832936edc0, 0xd54b944b84aa4c0d, 0xc5029163f384a931, 0xa9e795e65d4df11, 0xf64335bcf065d37d, 0x4d4617b5ff4a16d5, 0x99ea0196163fa42e, 0x504bced1bf8e4e45, 0xc06481fb9bcf8d39, 0xe45ec2862f71e1d6, 0xf07da27a82c37088, 0x5d767327bb4e5a4c, 0x964e858c91ba2655, 0x3a6a07f8d510f86f, 0xbbe226efb628afea, 0x890489f70a55368b, 0xeadab0aba3b2dbe5, 0x2b45ac74ccea842e, 0x92c8ae6b464fc96f, 0x3b0b8bc90012929d, 0xb77ada0617e3bbcb, 0x9ce6ebb40173744, 0xe55990879ddcaabd, 0xcc420a6a101d0515, 0x8f57fa54c2a9eab6, 0x9fa946824a12232d, 0xb32df8e9f3546564, 0x47939822dc96abf9, 0xdff9772470297ebd, 0x59787e2b93bc56f7, 0x8bfbea76c619ef36, 0x57eb4edb3c55b65a, 0xaefae51477a06b03, 0xede622920b6b23f1, 0xdab99e59958885c4, 0xe95fab368e45eced, 0x88b402f7fd75539b, 0x11dbcb0218ebb414, 0xaae103b5fcd2a881, 0xd652bdc29f26a119, 0xd59944a37c0752a2, 0x4be76d3346f0495f, 0x857fcae62d8493a5, 0x6f70a4400c562ddb, 0xa6dfbd9fb8e5b88e, 0xcb4ccd500f6bb952, 0xd097ad07a71f26b2, 0x7e2000a41346a7a7, 0x825ecc24c873782f, 0x8ed400668c0c28c8, 0xa2f67f2dfa90563b, 0x728900802f0f32fa, 0xcbb41ef979346bca, 0x4f2b40a03ad2ffb9, 0xfea126b7d78186bc, 0xe2f610c84987bfa8, 0x9f24b832e6b0f436, 0xdd9ca7d2df4d7c9, 0xc6ede63fa05d3143, 0x91503d1c79720dbb, 0xf8a95fcf88747d94, 0x75a44c6397ce912a, 0x9b69dbe1b548ce7c, 0xc986afbe3ee11aba, 0xc24452da229b021b, 0xfbe85badce996168, 0xf2d56790ab41c2a2, 0xfae27299423fb9c3, 0x97c560ba6b0919a5, 0xdccd879fc967d41a, 0xbdb6b8e905cb600f, 0x5400e987bbc1c920, 0xed246723473e3813, 0x290123e9aab23b68, 0x9436c0760c86e30b, 0xf9a0b6720aaf6521, 0xb94470938fa89bce, 0xf808e40e8d5b3e69, 0xe7958cb87392c2c2, 0xb60b1d1230b20e04, 0x90bd77f3483bb9b9, 0xb1c6f22b5e6f48c2, 0xb4ecd5f01a4aa828, 0x1e38aeb6360b1af3, 0xe2280b6c20dd5232, 0x25c6da63c38de1b0, 0x8d590723948a535f, 0x579c487e5a38ad0e, 0xb0af48ec79ace837, 0x2d835a9df0c6d851, 0xdcdb1b2798182244, 0xf8e431456cf88e65, 0x8a08f0f8bf0f156b, 0x1b8e9ecb641b58ff, 0xac8b2d36eed2dac5, 0xe272467e3d222f3f, 0xd7adf884aa879177, 0x5b0ed81dcc6abb0f, 0x86ccbb52ea94baea, 0x98e947129fc2b4e9, 0xa87fea27a539e9a5, 0x3f2398d747b36224, 0xd29fe4b18e88640e, 0x8eec7f0d19a03aad, 0x83a3eeeef9153e89, 0x1953cf68300424ac, 0xa48ceaaab75a8e2b, 0x5fa8c3423c052dd7, 0xcdb02555653131b6, 0x3792f412cb06794d, 0x808e17555f3ebf11, 0xe2bbd88bbee40bd0, 0xa0b19d2ab70e6ed6, 0x5b6aceaeae9d0ec4, 0xc8de047564d20a8b, 0xf245825a5a445275, 0xfb158592be068d2e, 0xeed6e2f0f0d56712, 0x9ced737bb6c4183d, 0x55464dd69685606b, 0xc428d05aa4751e4c, 0xaa97e14c3c26b886, 0xf53304714d9265df, 0xd53dd99f4b3066a8, 0x993fe2c6d07b7fab, 0xe546a8038efe4029, 0xbf8fdb78849a5f96, 0xde98520472bdd033, 0xef73d256a5c0f77c, 0x963e66858f6d4440, 0x95a8637627989aad, 0xdde7001379a44aa8, 0xbb127c53b17ec159, 0x5560c018580d5d52, 0xe9d71b689dde71af, 0xaab8f01e6e10b4a6, 0x9226712162ab070d, 0xcab3961304ca70e8, 0xb6b00d69bb55c8d1, 0x3d607b97c5fd0d22, 0xe45c10c42a2b3b05, 0x8cb89a7db77c506a, 0x8eb98a7a9a5b04e3, 0x77f3608e92adb242, 0xb267ed1940f1c61c, 0x55f038b237591ed3, 0xdf01e85f912e37a3, 0x6b6c46dec52f6688, 0x8b61313bbabce2c6, 0x2323ac4b3b3da015, 0xae397d8aa96c1b77, 0xabec975e0a0d081a, 0xd9c7dced53c72255, 0x96e7bd358c904a21, 0x881cea14545c7575, 0x7e50d64177da2e54, 0xaa242499697392d2, 0xdde50bd1d5d0b9e9, 0xd4ad2dbfc3d07787, 0x955e4ec64b44e864, 0x84ec3c97da624ab4, 0xbd5af13bef0b113e, 0xa6274bbdd0fadd61, 0xecb1ad8aeacdd58e, 0xcfb11ead453994ba, 0x67de18eda5814af2, 0x81ceb32c4b43fcf4, 0x80eacf948770ced7, 0xa2425ff75e14fc31, 0xa1258379a94d028d, 0xcad2f7f5359a3b3e, 0x96ee45813a04330, 0xfd87b5f28300ca0d, 0x8bca9d6e188853fc, 0x9e74d1b791e07e48, 0x775ea264cf55347e, 0xc612062576589dda, 0x95364afe032a819e, 0xf79687aed3eec551, 0x3a83ddbd83f52205, 0x9abe14cd44753b52, 0xc4926a9672793543, 0xc16d9a0095928a27, 0x75b7053c0f178294, 0xf1c90080baf72cb1, 0x5324c68b12dd6339, 0x971da05074da7bee, 0xd3f6fc16ebca5e04, 0xbce5086492111aea, 0x88f4bb1ca6bcf585, 0xec1e4a7db69561a5, 0x2b31e9e3d06c32e6, 0x9392ee8e921d5d07, 0x3aff322e62439fd0, 0xb877aa3236a4b449, 0x9befeb9fad487c3, 0xe69594bec44de15b, 0x4c2ebe687989a9b4, 0x901d7cf73ab0acd9, 0xf9d37014bf60a11, 0xb424dc35095cd80f, 0x538484c19ef38c95, 0xe12e13424bb40e13, 0x2865a5f206b06fba, 0x8cbccc096f5088cb, 0xf93f87b7442e45d4, 0xafebff0bcb24aafe, 0xf78f69a51539d749, 0xdbe6fecebdedd5be, 0xb573440e5a884d1c, 0x89705f4136b4a597, 0x31680a88f8953031, 0xabcc77118461cefc, 0xfdc20d2b36ba7c3e, 0xd6bf94d5e57a42bc, 0x3d32907604691b4d, 0x8637bd05af6c69b5, 0xa63f9a49c2c1b110, 0xa7c5ac471b478423, 0xfcf80dc33721d54, 0xd1b71758e219652b, 0xd3c36113404ea4a9, 0x83126e978d4fdf3b, 0x645a1cac083126ea, 0xa3d70a3d70a3d70a, 0x3d70a3d70a3d70a4, 0xcccccccccccccccc, 0xcccccccccccccccd, 0x8000000000000000, 0x0, 0xa000000000000000, 0x0, 0xc800000000000000, 0x0, 0xfa00000000000000, 0x0, 0x9c40000000000000, 0x0, 0xc350000000000000, 0x0, 0xf424000000000000, 0x0, 0x9896800000000000, 0x0, 0xbebc200000000000, 0x0, 0xee6b280000000000, 0x0, 0x9502f90000000000, 0x0, 0xba43b74000000000, 0x0, 0xe8d4a51000000000, 0x0, 0x9184e72a00000000, 0x0, 0xb5e620f480000000, 0x0, 0xe35fa931a0000000, 0x0, 0x8e1bc9bf04000000, 0x0, 0xb1a2bc2ec5000000, 0x0, 0xde0b6b3a76400000, 0x0, 0x8ac7230489e80000, 0x0, 0xad78ebc5ac620000, 0x0, 0xd8d726b7177a8000, 0x0, 0x878678326eac9000, 0x0, 0xa968163f0a57b400, 0x0, 0xd3c21bcecceda100, 0x0, 0x84595161401484a0, 0x0, 0xa56fa5b99019a5c8, 0x0, 0xcecb8f27f4200f3a, 0x0, 0x813f3978f8940984, 0x4000000000000000, 0xa18f07d736b90be5, 0x5000000000000000, 0xc9f2c9cd04674ede, 0xa400000000000000, 0xfc6f7c4045812296, 0x4d00000000000000, 0x9dc5ada82b70b59d, 0xf020000000000000, 0xc5371912364ce305, 0x6c28000000000000, 0xf684df56c3e01bc6, 0xc732000000000000, 0x9a130b963a6c115c, 0x3c7f400000000000, 0xc097ce7bc90715b3, 0x4b9f100000000000, 0xf0bdc21abb48db20, 0x1e86d40000000000, 0x96769950b50d88f4, 0x1314448000000000, 0xbc143fa4e250eb31, 0x17d955a000000000, 0xeb194f8e1ae525fd, 0x5dcfab0800000000, 0x92efd1b8d0cf37be, 0x5aa1cae500000000, 0xb7abc627050305ad, 0xf14a3d9e40000000, 0xe596b7b0c643c719, 0x6d9ccd05d0000000, 0x8f7e32ce7bea5c6f, 0xe4820023a2000000, 0xb35dbf821ae4f38b, 0xdda2802c8a800000, 0xe0352f62a19e306e, 0xd50b2037ad200000, 0x8c213d9da502de45, 0x4526f422cc340000, 0xaf298d050e4395d6, 0x9670b12b7f410000, 0xdaf3f04651d47b4c, 0x3c0cdd765f114000, 0x88d8762bf324cd0f, 0xa5880a69fb6ac800, 0xab0e93b6efee0053, 0x8eea0d047a457a00, 0xd5d238a4abe98068, 0x72a4904598d6d880, 0x85a36366eb71f041, 0x47a6da2b7f864750, 0xa70c3c40a64e6c51, 0x999090b65f67d924, 0xd0cf4b50cfe20765, 0xfff4b4e3f741cf6d, 0x82818f1281ed449f, 0xbff8f10e7a8921a4, 0xa321f2d7226895c7, 0xaff72d52192b6a0d, 0xcbea6f8ceb02bb39, 0x9bf4f8a69f764490, 0xfee50b7025c36a08, 0x2f236d04753d5b4, 0x9f4f2726179a2245, 0x1d762422c946590, 0xc722f0ef9d80aad6, 0x424d3ad2b7b97ef5, 0xf8ebad2b84e0d58b, 0xd2e0898765a7deb2, 0x9b934c3b330c8577, 0x63cc55f49f88eb2f, 0xc2781f49ffcfa6d5, 0x3cbf6b71c76b25fb, 0xf316271c7fc3908a, 0x8bef464e3945ef7a, 0x97edd871cfda3a56, 0x97758bf0e3cbb5ac, 0xbde94e8e43d0c8ec, 0x3d52eeed1cbea317, 0xed63a231d4c4fb27, 0x4ca7aaa863ee4bdd, 0x945e455f24fb1cf8, 0x8fe8caa93e74ef6a, 0xb975d6b6ee39e436, 0xb3e2fd538e122b44, 0xe7d34c64a9c85d44, 0x60dbbca87196b616, 0x90e40fbeea1d3a4a, 0xbc8955e946fe31cd, 0xb51d13aea4a488dd, 0x6babab6398bdbe41, 0xe264589a4dcdab14, 0xc696963c7eed2dd1, 0x8d7eb76070a08aec, 0xfc1e1de5cf543ca2, 0xb0de65388cc8ada8, 0x3b25a55f43294bcb, 0xdd15fe86affad912, 0x49ef0eb713f39ebe, 0x8a2dbf142dfcc7ab, 0x6e3569326c784337, 0xacb92ed9397bf996, 0x49c2c37f07965404, 0xd7e77a8f87daf7fb, 0xdc33745ec97be906, 0x86f0ac99b4e8dafd, 0x69a028bb3ded71a3, 0xa8acd7c0222311bc, 0xc40832ea0d68ce0c, 0xd2d80db02aabd62b, 0xf50a3fa490c30190, 0x83c7088e1aab65db, 0x792667c6da79e0fa, 0xa4b8cab1a1563f52, 0x577001b891185938, 0xcde6fd5e09abcf26, 0xed4c0226b55e6f86, 0x80b05e5ac60b6178, 0x544f8158315b05b4, 0xa0dc75f1778e39d6, 0x696361ae3db1c721, 0xc913936dd571c84c, 0x3bc3a19cd1e38e9, 0xfb5878494ace3a5f, 0x4ab48a04065c723, 0x9d174b2dcec0e47b, 0x62eb0d64283f9c76, 0xc45d1df942711d9a, 0x3ba5d0bd324f8394, 0xf5746577930d6500, 0xca8f44ec7ee36479, 0x9968bf6abbe85f20, 0x7e998b13cf4e1ecb, 0xbfc2ef456ae276e8, 0x9e3fedd8c321a67e, 0xefb3ab16c59b14a2, 0xc5cfe94ef3ea101e, 0x95d04aee3b80ece5, 0xbba1f1d158724a12, 0xbb445da9ca61281f, 0x2a8a6e45ae8edc97, 0xea1575143cf97226, 0xf52d09d71a3293bd, 0x924d692ca61be758, 0x593c2626705f9c56, 0xb6e0c377cfa2e12e, 0x6f8b2fb00c77836c, 0xe498f455c38b997a, 0xb6dfb9c0f956447, 0x8edf98b59a373fec, 0x4724bd4189bd5eac, 0xb2977ee300c50fe7, 0x58edec91ec2cb657, 0xdf3d5e9bc0f653e1, 0x2f2967b66737e3ed, 0x8b865b215899f46c, 0xbd79e0d20082ee74, 0xae67f1e9aec07187, 0xecd8590680a3aa11, 0xda01ee641a708de9, 0xe80e6f4820cc9495, 0x884134fe908658b2, 0x3109058d147fdcdd, 0xaa51823e34a7eede, 0xbd4b46f0599fd415, 0xd4e5e2cdc1d1ea96, 0x6c9e18ac7007c91a, 0x850fadc09923329e, 0x3e2cf6bc604ddb0, 0xa6539930bf6bff45, 0x84db8346b786151c, 0xcfe87f7cef46ff16, 0xe612641865679a63, 0x81f14fae158c5f6e, 0x4fcb7e8f3f60c07e, 0xa26da3999aef7749, 0xe3be5e330f38f09d, 0xcb090c8001ab551c, 0x5cadf5bfd3072cc5, 0xfdcb4fa002162a63, 0x73d9732fc7c8f7f6, 0x9e9f11c4014dda7e, 0x2867e7fddcdd9afa, 0xc646d63501a1511d, 0xb281e1fd541501b8, 0xf7d88bc24209a565, 0x1f225a7ca91a4226, 0x9ae757596946075f, 0x3375788de9b06958, 0xc1a12d2fc3978937, 0x52d6b1641c83ae, 0xf209787bb47d6b84, 0xc0678c5dbd23a49a, 0x9745eb4d50ce6332, 0xf840b7ba963646e0, 0xbd176620a501fbff, 0xb650e5a93bc3d898, 0xec5d3fa8ce427aff, 0xa3e51f138ab4cebe, 0x93ba47c980e98cdf, 0xc66f336c36b10137, 0xb8a8d9bbe123f017, 0xb80b0047445d4184, 0xe6d3102ad96cec1d, 0xa60dc059157491e5, 0x9043ea1ac7e41392, 0x87c89837ad68db2f, 0xb454e4a179dd1877, 0x29babe4598c311fb, 0xe16a1dc9d8545e94, 0xf4296dd6fef3d67a, 0x8ce2529e2734bb1d, 0x1899e4a65f58660c, 0xb01ae745b101e9e4, 0x5ec05dcff72e7f8f, 0xdc21a1171d42645d, 0x76707543f4fa1f73, 0x899504ae72497eba, 0x6a06494a791c53a8, 0xabfa45da0edbde69, 0x487db9d17636892, 0xd6f8d7509292d603, 0x45a9d2845d3c42b6, 0x865b86925b9bc5c2, 0xb8a2392ba45a9b2, 0xa7f26836f282b732, 0x8e6cac7768d7141e, 0xd1ef0244af2364ff, 0x3207d795430cd926, 0x8335616aed761f1f, 0x7f44e6bd49e807b8, 0xa402b9c5a8d3a6e7, 0x5f16206c9c6209a6, 0xcd036837130890a1, 0x36dba887c37a8c0f, 0x802221226be55a64, 0xc2494954da2c9789, 0xa02aa96b06deb0fd, 0xf2db9baa10b7bd6c, 0xc83553c5c8965d3d, 0x6f92829494e5acc7, 0xfa42a8b73abbf48c, 0xcb772339ba1f17f9, 0x9c69a97284b578d7, 0xff2a760414536efb, 0xc38413cf25e2d70d, 0xfef5138519684aba, 0xf46518c2ef5b8cd1, 0x7eb258665fc25d69, 0x98bf2f79d5993802, 0xef2f773ffbd97a61, 0xbeeefb584aff8603, 0xaafb550ffacfd8fa, 0xeeaaba2e5dbf6784, 0x95ba2a53f983cf38, 0x952ab45cfa97a0b2, 0xdd945a747bf26183, 0xba756174393d88df, 0x94f971119aeef9e4, 0xe912b9d1478ceb17, 0x7a37cd5601aab85d, 0x91abb422ccb812ee, 0xac62e055c10ab33a, 0xb616a12b7fe617aa, 0x577b986b314d6009, 0xe39c49765fdf9d94, 0xed5a7e85fda0b80b, 0x8e41ade9fbebc27d, 0x14588f13be847307, 0xb1d219647ae6b31c, 0x596eb2d8ae258fc8, 0xde469fbd99a05fe3, 0x6fca5f8ed9aef3bb, 0x8aec23d680043bee, 0x25de7bb9480d5854, 0xada72ccc20054ae9, 0xaf561aa79a10ae6a, 0xd910f7ff28069da4, 0x1b2ba1518094da04, 0x87aa9aff79042286, 0x90fb44d2f05d0842, 0xa99541bf57452b28, 0x353a1607ac744a53, 0xd3fa922f2d1675f2, 0x42889b8997915ce8, 0x847c9b5d7c2e09b7, 0x69956135febada11, 0xa59bc234db398c25, 0x43fab9837e699095, 0xcf02b2c21207ef2e, 0x94f967e45e03f4bb, 0x8161afb94b44f57d, 0x1d1be0eebac278f5, 0xa1ba1ba79e1632dc, 0x6462d92a69731732, 0xca28a291859bbf93, 0x7d7b8f7503cfdcfe, 0xfcb2cb35e702af78, 0x5cda735244c3d43e, 0x9defbf01b061adab, 0x3a0888136afa64a7, 0xc56baec21c7a1916, 0x88aaa1845b8fdd0, 0xf6c69a72a3989f5b, 0x8aad549e57273d45, 0x9a3c2087a63f6399, 0x36ac54e2f678864b, 0xc0cb28a98fcf3c7f, 0x84576a1bb416a7dd, 0xf0fdf2d3f3c30b9f, 0x656d44a2a11c51d5, 0x969eb7c47859e743, 0x9f644ae5a4b1b325, 0xbc4665b596706114, 0x873d5d9f0dde1fee, 0xeb57ff22fc0c7959, 0xa90cb506d155a7ea, 0x9316ff75dd87cbd8, 0x9a7f12442d588f2, 0xb7dcbf5354e9bece, 0xc11ed6d538aeb2f, 0xe5d3ef282a242e81, 0x8f1668c8a86da5fa, 0x8fa475791a569d10, 0xf96e017d694487bc, 0xb38d92d760ec4455, 0x37c981dcc395a9ac, 0xe070f78d3927556a, 0x85bbe253f47b1417, 0x8c469ab843b89562, 0x93956d7478ccec8e, 0xaf58416654a6babb, 0x387ac8d1970027b2, 0xdb2e51bfe9d0696a, 0x6997b05fcc0319e, 0x88fcf317f22241e2, 0x441fece3bdf81f03, 0xab3c2fddeeaad25a, 0xd527e81cad7626c3, 0xd60b3bd56a5586f1, 0x8a71e223d8d3b074, 0x85c7056562757456, 0xf6872d5667844e49, 0xa738c6bebb12d16c, 0xb428f8ac016561db, 0xd106f86e69d785c7, 0xe13336d701beba52, 0x82a45b450226b39c, 0xecc0024661173473, 0xa34d721642b06084, 0x27f002d7f95d0190, 0xcc20ce9bd35c78a5, 0x31ec038df7b441f4, 0xff290242c83396ce, 0x7e67047175a15271, 0x9f79a169bd203e41, 0xf0062c6e984d386, 0xc75809c42c684dd1, 0x52c07b78a3e60868, 0xf92e0c3537826145, 0xa7709a56ccdf8a82, 0x9bbcc7a142b17ccb, 0x88a66076400bb691, 0xc2abf989935ddbfe, 0x6acff893d00ea435, 0xf356f7ebf83552fe, 0x583f6b8c4124d43, 0x98165af37b2153de, 0xc3727a337a8b704a, 0xbe1bf1b059e9a8d6, 0x744f18c0592e4c5c, 0xeda2ee1c7064130c, 0x1162def06f79df73, 0x9485d4d1c63e8be7, 0x8addcb5645ac2ba8, 0xb9a74a0637ce2ee1, 0x6d953e2bd7173692, 0xe8111c87c5c1ba99, 0xc8fa8db6ccdd0437, 0x910ab1d4db9914a0, 0x1d9c9892400a22a2, 0xb54d5e4a127f59c8, 0x2503beb6d00cab4b, 0xe2a0b5dc971f303a, 0x2e44ae64840fd61d, 0x8da471a9de737e24, 0x5ceaecfed289e5d2, 0xb10d8e1456105dad, 0x7425a83e872c5f47, 0xdd50f1996b947518, 0xd12f124e28f77719, 0x8a5296ffe33cc92f, 0x82bd6b70d99aaa6f, 0xace73cbfdc0bfb7b, 0x636cc64d1001550b, 0xd8210befd30efa5a, 0x3c47f7e05401aa4e, 0x8714a775e3e95c78, 0x65acfaec34810a71, 0xa8d9d1535ce3b396, 0x7f1839a741a14d0d, 0xd31045a8341ca07c, 0x1ede48111209a050, 0x83ea2b892091e44d, 0x934aed0aab460432, 0xa4e4b66b68b65d60, 0xf81da84d5617853f, 0xce1de40642e3f4b9, 0x36251260ab9d668e, 0x80d2ae83e9ce78f3, 0xc1d72b7c6b426019, 0xa1075a24e4421730, 0xb24cf65b8612f81f, 0xc94930ae1d529cfc, 0xdee033f26797b627, 0xfb9b7cd9a4a7443c, 0x169840ef017da3b1, 0x9d412e0806e88aa5, 0x8e1f289560ee864e, 0xc491798a08a2ad4e, 0xf1a6f2bab92a27e2, 0xf5b5d7ec8acb58a2, 0xae10af696774b1db, 0x9991a6f3d6bf1765, 0xacca6da1e0a8ef29, 0xbff610b0cc6edd3f, 0x17fd090a58d32af3, 0xeff394dcff8a948e, 0xddfc4b4cef07f5b0, 0x95f83d0a1fb69cd9, 0x4abdaf101564f98e, 0xbb764c4ca7a4440f, 0x9d6d1ad41abe37f1, 0xea53df5fd18d5513, 0x84c86189216dc5ed, 0x92746b9be2f8552c, 0x32fd3cf5b4e49bb4, 0xb7118682dbb66a77, 0x3fbc8c33221dc2a1, 0xe4d5e82392a40515, 0xfabaf3feaa5334a, 0x8f05b1163ba6832d, 0x29cb4d87f2a7400e, 0xb2c71d5bca9023f8, 0x743e20e9ef511012, 0xdf78e4b2bd342cf6, 0x914da9246b255416, 0x8bab8eefb6409c1a, 0x1ad089b6c2f7548e, 0xae9672aba3d0c320, 0xa184ac2473b529b1, 0xda3c0f568cc4f3e8, 0xc9e5d72d90a2741e, 0x8865899617fb1871, 0x7e2fa67c7a658892, 0xaa7eebfb9df9de8d, 0xddbb901b98feeab7, 0xd51ea6fa85785631, 0x552a74227f3ea565, 0x8533285c936b35de, 0xd53a88958f87275f, 0xa67ff273b8460356, 0x8a892abaf368f137, 0xd01fef10a657842c, 0x2d2b7569b0432d85, 0x8213f56a67f6b29b, 0x9c3b29620e29fc73, 0xa298f2c501f45f42, 0x8349f3ba91b47b8f, 0xcb3f2f7642717713, 0x241c70a936219a73, 0xfe0efb53d30dd4d7, 0xed238cd383aa0110, 0x9ec95d1463e8a506, 0xf4363804324a40aa, 0xc67bb4597ce2ce48, 0xb143c6053edcd0d5, 0xf81aa16fdc1b81da, 0xdd94b7868e94050a, 0x9b10a4e5e9913128, 0xca7cf2b4191c8326, 0xc1d4ce1f63f57d72, 0xfd1c2f611f63a3f0, 0xf24a01a73cf2dccf, 0xbc633b39673c8cec, 0x976e41088617ca01, 0xd5be0503e085d813, 0xbd49d14aa79dbc82, 0x4b2d8644d8a74e18, 0xec9c459d51852ba2, 0xddf8e7d60ed1219e, 0x93e1ab8252f33b45, 0xcabb90e5c942b503, 0xb8da1662e7b00a17, 0x3d6a751f3b936243, 0xe7109bfba19c0c9d, 0xcc512670a783ad4, 0x906a617d450187e2, 0x27fb2b80668b24c5, 0xb484f9dc9641e9da, 0xb1f9f660802dedf6, 0xe1a63853bbd26451, 0x5e7873f8a0396973, 0x8d07e33455637eb2, 0xdb0b487b6423e1e8, 0xb049dc016abc5e5f, 0x91ce1a9a3d2cda62, 0xdc5c5301c56b75f7, 0x7641a140cc7810fb, 0x89b9b3e11b6329ba, 0xa9e904c87fcb0a9d, 0xac2820d9623bf429, 0x546345fa9fbdcd44, 0xd732290fbacaf133, 0xa97c177947ad4095, 0x867f59a9d4bed6c0, 0x49ed8eabcccc485d, 0xa81f301449ee8c70, 0x5c68f256bfff5a74, 0xd226fc195c6a2f8c, 0x73832eec6fff3111, 0x83585d8fd9c25db7, 0xc831fd53c5ff7eab, 0xa42e74f3d032f525, 0xba3e7ca8b77f5e55, 0xcd3a1230c43fb26f, 0x28ce1bd2e55f35eb, 0x80444b5e7aa7cf85, 0x7980d163cf5b81b3, 0xa0555e361951c366, 0xd7e105bcc332621f, 0xc86ab5c39fa63440, 0x8dd9472bf3fefaa7, 0xfa856334878fc150, 0xb14f98f6f0feb951, 0x9c935e00d4b9d8d2, 0x6ed1bf9a569f33d3, 0xc3b8358109e84f07, 0xa862f80ec4700c8, 0xf4a642e14c6262c8, 0xcd27bb612758c0fa, 0x98e7e9cccfbd7dbd, 0x8038d51cb897789c, 0xbf21e44003acdd2c, 0xe0470a63e6bd56c3, 0xeeea5d5004981478, 0x1858ccfce06cac74, 0x95527a5202df0ccb, 0xf37801e0c43ebc8, 0xbaa718e68396cffd, 0xd30560258f54e6ba, 0xe950df20247c83fd, 0x47c6b82ef32a2069, 0x91d28b7416cdd27e, 0x4cdc331d57fa5441, 0xb6472e511c81471d, 0xe0133fe4adf8e952, 0xe3d8f9e563a198e5, 0x58180fddd97723a6, 0x8e679c2f5e44ff8f, 0x570f09eaa7ea7648, }; }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE template constexpr uint64_t powers_template::power_of_five_128[number_of_entries]; #endif using powers = powers_template<>; } // namespace fast_float #endif fast_float-8.0.0/include/fast_float/float_common.h000066400000000000000000001151361475170546600222620ustar00rootroot00000000000000#ifndef FASTFLOAT_FLOAT_COMMON_H #define FASTFLOAT_FLOAT_COMMON_H #include #include #include #include #include #include #include #ifdef __has_include #if __has_include() && (__cplusplus > 202002L || _MSVC_LANG > 202002L) #include #endif #endif #include "constexpr_feature_detect.h" #define FASTFLOAT_VERSION_MAJOR 8 #define FASTFLOAT_VERSION_MINOR 0 #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 { enum class chars_format : uint64_t; namespace detail { constexpr chars_format basic_json_fmt = chars_format(1 << 5); constexpr chars_format basic_fortran_fmt = chars_format(1 << 6); } // namespace detail enum class chars_format : uint64_t { scientific = 1 << 0, fixed = 1 << 2, hex = 1 << 3, no_infnan = 1 << 4, // RFC 8259: https://datatracker.ietf.org/doc/html/rfc8259#section-6 json = uint64_t(detail::basic_json_fmt) | fixed | scientific | no_infnan, // Extension of RFC 8259 where, e.g., "inf" and "nan" are allowed. json_or_infnan = uint64_t(detail::basic_json_fmt) | fixed | scientific, fortran = uint64_t(detail::basic_fortran_fmt) | fixed | scientific, general = fixed | scientific, allow_leading_plus = 1 << 7, skip_white_space = 1 << 8, }; template struct from_chars_result_t { UC const *ptr; std::errc ec; }; using from_chars_result = from_chars_result_t; template struct parse_options_t { constexpr explicit parse_options_t(chars_format fmt = chars_format::general, UC dot = UC('.'), int b = 10) : format(fmt), decimal_point(dot), base(b) {} /** Which number formats are accepted */ chars_format format; /** The character used as decimal point */ UC decimal_point; /** The base used for integers */ int base; }; using parse_options = parse_options_t; } // namespace fast_float #if FASTFLOAT_HAS_BIT_CAST #include #endif #if (defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) || \ defined(__amd64) || defined(__aarch64__) || defined(_M_ARM64) || \ defined(__MINGW64__) || defined(__s390x__) || \ (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || \ defined(__PPC64LE__)) || \ defined(__loongarch64)) #define FASTFLOAT_64BIT 1 #elif (defined(__i386) || defined(__i386__) || defined(_M_IX86) || \ defined(__arm__) || defined(_M_ARM) || defined(__ppc__) || \ defined(__MINGW32__) || defined(__EMSCRIPTEN__)) #define FASTFLOAT_32BIT 1 #else // Need to check incrementally, since SIZE_MAX is a size_t, avoid overflow. // We can never tell the register width, but the SIZE_MAX is a good // approximation. UINTPTR_MAX and INTPTR_MAX are optional, so avoid them for max // portability. #if SIZE_MAX == 0xffff #error Unknown platform (16-bit, unsupported) #elif SIZE_MAX == 0xffffffff #define FASTFLOAT_32BIT 1 #elif SIZE_MAX == 0xffffffffffffffff #define FASTFLOAT_64BIT 1 #else #error Unknown platform (not 32-bit, not 64-bit?) #endif #endif #if ((defined(_WIN32) || defined(_WIN64)) && !defined(__clang__)) || \ (defined(_M_ARM64) && !defined(__MINGW32__)) #include #endif #if defined(_MSC_VER) && !defined(__clang__) #define FASTFLOAT_VISUAL_STUDIO 1 #endif #if defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__ #define FASTFLOAT_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #elif defined _WIN32 #define FASTFLOAT_IS_BIG_ENDIAN 0 #else #if defined(__APPLE__) || defined(__FreeBSD__) #include #elif defined(sun) || defined(__sun) #include #elif defined(__MVS__) #include #else #ifdef __has_include #if __has_include() #include #endif //__has_include() #endif //__has_include #endif # #ifndef __BYTE_ORDER__ // safe choice #define FASTFLOAT_IS_BIG_ENDIAN 0 #endif # #ifndef __ORDER_LITTLE_ENDIAN__ // safe choice #define FASTFLOAT_IS_BIG_ENDIAN 0 #endif # #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ #define FASTFLOAT_IS_BIG_ENDIAN 0 #else #define FASTFLOAT_IS_BIG_ENDIAN 1 #endif #endif #if defined(__SSE2__) || (defined(FASTFLOAT_VISUAL_STUDIO) && \ (defined(_M_AMD64) || defined(_M_X64) || \ (defined(_M_IX86_FP) && _M_IX86_FP == 2))) #define FASTFLOAT_SSE2 1 #endif #if defined(__aarch64__) || defined(_M_ARM64) #define FASTFLOAT_NEON 1 #endif #if defined(FASTFLOAT_SSE2) || defined(FASTFLOAT_NEON) #define FASTFLOAT_HAS_SIMD 1 #endif #if defined(__GNUC__) // disable -Wcast-align=strict (GCC only) #define FASTFLOAT_SIMD_DISABLE_WARNINGS \ _Pragma("GCC diagnostic push") \ _Pragma("GCC diagnostic ignored \"-Wcast-align\"") #else #define FASTFLOAT_SIMD_DISABLE_WARNINGS #endif #if defined(__GNUC__) #define FASTFLOAT_SIMD_RESTORE_WARNINGS _Pragma("GCC diagnostic pop") #else #define FASTFLOAT_SIMD_RESTORE_WARNINGS #endif #ifdef FASTFLOAT_VISUAL_STUDIO #define fastfloat_really_inline __forceinline #else #define fastfloat_really_inline inline __attribute__((always_inline)) #endif #ifndef FASTFLOAT_ASSERT #define FASTFLOAT_ASSERT(x) \ { ((void)(x)); } #endif #ifndef FASTFLOAT_DEBUG_ASSERT #define FASTFLOAT_DEBUG_ASSERT(x) \ { ((void)(x)); } #endif // rust style `try!()` macro, or `?` operator #define FASTFLOAT_TRY(x) \ { \ if (!(x)) \ return false; \ } #define FASTFLOAT_ENABLE_IF(...) \ typename std::enable_if<(__VA_ARGS__), int>::type namespace fast_float { fastfloat_really_inline constexpr bool cpp20_and_in_constexpr() { #if FASTFLOAT_HAS_IS_CONSTANT_EVALUATED return std::is_constant_evaluated(); #else return false; #endif } template struct is_supported_float_type : std::integral_constant< bool, std::is_same::value || std::is_same::value #ifdef __STDCPP_FLOAT64_T__ || std::is_same::value #endif #ifdef __STDCPP_FLOAT32_T__ || std::is_same::value #endif #ifdef __STDCPP_FLOAT16_T__ || std::is_same::value #endif #ifdef __STDCPP_BFLOAT16_T__ || std::is_same::value #endif > { }; template using equiv_uint_t = typename std::conditional< sizeof(T) == 1, uint8_t, typename std::conditional< sizeof(T) == 2, uint16_t, typename std::conditional::type>::type>::type; template struct is_supported_integer_type : std::is_integral {}; template struct is_supported_char_type : std::integral_constant::value || std::is_same::value || std::is_same::value || std::is_same::value #ifdef __cpp_char8_t || std::is_same::value #endif > { }; // Compares two ASCII strings in a case insensitive manner. template inline FASTFLOAT_CONSTEXPR14 bool fastfloat_strncasecmp(UC const *actual_mixedcase, UC const *expected_lowercase, size_t length) { for (size_t i = 0; i < length; ++i) { UC const actual = actual_mixedcase[i]; if ((actual < 256 ? actual | 32 : actual) != expected_lowercase[i]) { return false; } } return true; } #ifndef FLT_EVAL_METHOD #error "FLT_EVAL_METHOD should be defined, please include cfloat." #endif // a pointer and a length to a contiguous block of memory template struct span { T const *ptr; size_t length; constexpr span(T const *_ptr, size_t _length) : ptr(_ptr), length(_length) {} constexpr span() : ptr(nullptr), length(0) {} constexpr size_t len() const noexcept { return length; } FASTFLOAT_CONSTEXPR14 const T &operator[](size_t index) const noexcept { FASTFLOAT_DEBUG_ASSERT(index < length); return ptr[index]; } }; struct value128 { uint64_t low; uint64_t high; constexpr value128(uint64_t _low, uint64_t _high) : low(_low), high(_high) {} constexpr value128() : low(0), high(0) {} }; /* Helper C++14 constexpr generic implementation of leading_zeroes */ fastfloat_really_inline FASTFLOAT_CONSTEXPR14 int leading_zeroes_generic(uint64_t input_num, int last_bit = 0) { if (input_num & uint64_t(0xffffffff00000000)) { input_num >>= 32; last_bit |= 32; } if (input_num & uint64_t(0xffff0000)) { input_num >>= 16; last_bit |= 16; } if (input_num & uint64_t(0xff00)) { input_num >>= 8; last_bit |= 8; } if (input_num & uint64_t(0xf0)) { input_num >>= 4; last_bit |= 4; } if (input_num & uint64_t(0xc)) { input_num >>= 2; last_bit |= 2; } if (input_num & uint64_t(0x2)) { /* input_num >>= 1; */ last_bit |= 1; } return 63 - last_bit; } /* result might be undefined when input_num is zero */ fastfloat_really_inline FASTFLOAT_CONSTEXPR20 int leading_zeroes(uint64_t input_num) { assert(input_num > 0); if (cpp20_and_in_constexpr()) { return leading_zeroes_generic(input_num); } #ifdef FASTFLOAT_VISUAL_STUDIO #if defined(_M_X64) || defined(_M_ARM64) unsigned long leading_zero = 0; // Search the mask data from most significant bit (MSB) // to least significant bit (LSB) for a set bit (1). _BitScanReverse64(&leading_zero, input_num); return (int)(63 - leading_zero); #else return leading_zeroes_generic(input_num); #endif #else return __builtin_clzll(input_num); #endif } // slow emulation routine for 32-bit fastfloat_really_inline constexpr uint64_t emulu(uint32_t x, uint32_t y) { return x * (uint64_t)y; } fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t umul128_generic(uint64_t ab, uint64_t cd, uint64_t *hi) { uint64_t ad = emulu((uint32_t)(ab >> 32), (uint32_t)cd); uint64_t bd = emulu((uint32_t)ab, (uint32_t)cd); uint64_t adbc = ad + emulu((uint32_t)ab, (uint32_t)(cd >> 32)); uint64_t adbc_carry = (uint64_t)(adbc < ad); uint64_t lo = bd + (adbc << 32); *hi = emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + (adbc_carry << 32) + (uint64_t)(lo < bd); return lo; } #ifdef FASTFLOAT_32BIT // slow emulation routine for 32-bit #if !defined(__MINGW64__) fastfloat_really_inline FASTFLOAT_CONSTEXPR14 uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { return umul128_generic(ab, cd, hi); } #endif // !__MINGW64__ #endif // FASTFLOAT_32BIT // compute 64-bit a*b fastfloat_really_inline FASTFLOAT_CONSTEXPR20 value128 full_multiplication(uint64_t a, uint64_t b) { if (cpp20_and_in_constexpr()) { value128 answer; answer.low = umul128_generic(a, b, &answer.high); return answer; } value128 answer; #if defined(_M_ARM64) && !defined(__MINGW32__) // ARM64 has native support for 64-bit multiplications, no need to emulate // But MinGW on ARM64 doesn't have native support for 64-bit multiplications answer.high = __umulh(a, b); answer.low = a * b; #elif defined(FASTFLOAT_32BIT) || \ (defined(_WIN64) && !defined(__clang__) && !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; answer.low = uint64_t(r); answer.high = uint64_t(r >> 64); #else answer.low = umul128_generic(a, b, &answer.high); #endif return answer; } struct adjusted_mantissa { uint64_t mantissa{0}; int32_t power2{0}; // a negative value indicates an invalid result adjusted_mantissa() = default; constexpr bool operator==(adjusted_mantissa const &o) const { return mantissa == o.mantissa && power2 == o.power2; } constexpr bool operator!=(adjusted_mantissa const &o) const { return mantissa != o.mantissa || power2 != o.power2; } }; // Bias so we can get the real exponent with an invalid adjusted_mantissa. constexpr static int32_t invalid_am_bias = -0x8000; // used for binary_format_lookup_tables::max_mantissa constexpr uint64_t constant_55555 = 5 * 5 * 5 * 5 * 5; template struct binary_format_lookup_tables; template struct binary_format : binary_format_lookup_tables { using equiv_uint = equiv_uint_t; static constexpr int mantissa_explicit_bits(); static constexpr int minimum_exponent(); static constexpr int infinite_power(); static constexpr int sign_index(); static constexpr int min_exponent_fast_path(); // used when fegetround() == FE_TONEAREST static constexpr int max_exponent_fast_path(); static constexpr int max_exponent_round_to_even(); static constexpr int min_exponent_round_to_even(); static constexpr uint64_t max_mantissa_fast_path(int64_t power); static constexpr uint64_t max_mantissa_fast_path(); // used when fegetround() == FE_TONEAREST static constexpr int largest_power_of_ten(); static constexpr int smallest_power_of_ten(); static constexpr T exact_power_of_ten(int64_t power); static constexpr size_t max_digits(); static constexpr equiv_uint exponent_mask(); static constexpr equiv_uint mantissa_mask(); static constexpr equiv_uint hidden_bit_mask(); }; template struct binary_format_lookup_tables { static constexpr double powers_of_ten[] = { 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; // Largest integer value v so that (5**index * v) <= 1<<53. // 0x20000000000000 == 1 << 53 static constexpr uint64_t max_mantissa[] = { 0x20000000000000, 0x20000000000000 / 5, 0x20000000000000 / (5 * 5), 0x20000000000000 / (5 * 5 * 5), 0x20000000000000 / (5 * 5 * 5 * 5), 0x20000000000000 / (constant_55555), 0x20000000000000 / (constant_55555 * 5), 0x20000000000000 / (constant_55555 * 5 * 5), 0x20000000000000 / (constant_55555 * 5 * 5 * 5), 0x20000000000000 / (constant_55555 * 5 * 5 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555), 0x20000000000000 / (constant_55555 * constant_55555 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * 5 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5), 0x20000000000000 / (constant_55555 * constant_55555 * constant_55555 * constant_55555 * 5 * 5 * 5 * 5)}; }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE template constexpr double binary_format_lookup_tables::powers_of_ten[]; template constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; #endif template struct binary_format_lookup_tables { static constexpr float powers_of_ten[] = {1e0f, 1e1f, 1e2f, 1e3f, 1e4f, 1e5f, 1e6f, 1e7f, 1e8f, 1e9f, 1e10f}; // Largest integer value v so that (5**index * v) <= 1<<24. // 0x1000000 == 1<<24 static constexpr uint64_t max_mantissa[] = { 0x1000000, 0x1000000 / 5, 0x1000000 / (5 * 5), 0x1000000 / (5 * 5 * 5), 0x1000000 / (5 * 5 * 5 * 5), 0x1000000 / (constant_55555), 0x1000000 / (constant_55555 * 5), 0x1000000 / (constant_55555 * 5 * 5), 0x1000000 / (constant_55555 * 5 * 5 * 5), 0x1000000 / (constant_55555 * 5 * 5 * 5 * 5), 0x1000000 / (constant_55555 * constant_55555), 0x1000000 / (constant_55555 * constant_55555 * 5)}; }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE template constexpr float binary_format_lookup_tables::powers_of_ten[]; template constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; #endif template <> inline constexpr int binary_format::min_exponent_fast_path() { #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) return 0; #else return -22; #endif } template <> inline constexpr int binary_format::min_exponent_fast_path() { #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) return 0; #else return -10; #endif } template <> inline constexpr int binary_format::mantissa_explicit_bits() { return 52; } template <> inline constexpr int binary_format::mantissa_explicit_bits() { return 23; } template <> inline constexpr int binary_format::max_exponent_round_to_even() { return 23; } template <> inline constexpr int binary_format::max_exponent_round_to_even() { return 10; } template <> inline constexpr int binary_format::min_exponent_round_to_even() { return -4; } template <> inline constexpr int binary_format::min_exponent_round_to_even() { return -17; } template <> inline constexpr int binary_format::minimum_exponent() { return -1023; } template <> inline constexpr int binary_format::minimum_exponent() { return -127; } template <> inline constexpr int binary_format::infinite_power() { return 0x7FF; } template <> inline constexpr int binary_format::infinite_power() { return 0xFF; } template <> inline constexpr int binary_format::sign_index() { return 63; } template <> inline constexpr int binary_format::sign_index() { return 31; } template <> inline constexpr int binary_format::max_exponent_fast_path() { return 22; } template <> inline constexpr int binary_format::max_exponent_fast_path() { return 10; } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } // credit: Jakub Jelínek #ifdef __STDCPP_FLOAT16_T__ template struct binary_format_lookup_tables { 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 constexpr std::float16_t binary_format_lookup_tables::powers_of_ten[]; template constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; #endif template <> inline constexpr std::float16_t binary_format::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::equiv_uint binary_format::exponent_mask() { return 0x7C00; } template <> inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() { return 0x03FF; } template <> inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() { return 0x0400; } template <> inline constexpr int binary_format::max_exponent_fast_path() { return 4; } template <> inline constexpr int binary_format::mantissa_explicit_bits() { return 10; } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } template <> inline constexpr uint64_t binary_format::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::min_exponent_fast_path() { return 0; } template <> inline constexpr int binary_format::max_exponent_round_to_even() { return 5; } template <> inline constexpr int binary_format::min_exponent_round_to_even() { return -22; } template <> inline constexpr int binary_format::minimum_exponent() { return -15; } template <> inline constexpr int binary_format::infinite_power() { return 0x1F; } template <> inline constexpr int binary_format::sign_index() { return 15; } template <> inline constexpr int binary_format::largest_power_of_ten() { return 4; } template <> inline constexpr int binary_format::smallest_power_of_ten() { return -27; } template <> inline constexpr size_t binary_format::max_digits() { return 22; } #endif // __STDCPP_FLOAT16_T__ // credit: Jakub Jelínek #ifdef __STDCPP_BFLOAT16_T__ template struct binary_format_lookup_tables { 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 constexpr std::bfloat16_t binary_format_lookup_tables::powers_of_ten[]; template constexpr uint64_t binary_format_lookup_tables::max_mantissa[]; #endif template <> inline constexpr std::bfloat16_t binary_format::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::max_exponent_fast_path() { return 3; } template <> inline constexpr binary_format::equiv_uint binary_format::exponent_mask() { return 0x7F80; } template <> inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() { return 0x007F; } template <> inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() { return 0x0080; } template <> inline constexpr int binary_format::mantissa_explicit_bits() { return 7; } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path() { return uint64_t(2) << mantissa_explicit_bits(); } template <> inline constexpr uint64_t binary_format::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::min_exponent_fast_path() { return 0; } template <> inline constexpr int binary_format::max_exponent_round_to_even() { return 3; } template <> inline constexpr int binary_format::min_exponent_round_to_even() { return -24; } template <> inline constexpr int binary_format::minimum_exponent() { return -127; } template <> inline constexpr int binary_format::infinite_power() { return 0xFF; } template <> inline constexpr int binary_format::sign_index() { return 15; } template <> inline constexpr int binary_format::largest_power_of_ten() { return 38; } template <> inline constexpr int binary_format::smallest_power_of_ten() { return -60; } template <> inline constexpr size_t binary_format::max_digits() { return 98; } #endif // __STDCPP_BFLOAT16_T__ template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { // caller is responsible to ensure that // power >= 0 && power <= 22 // // Work around clang bug https://godbolt.org/z/zedh7rrhc return (void)max_mantissa[0], max_mantissa[power]; } template <> inline constexpr uint64_t binary_format::max_mantissa_fast_path(int64_t power) { // caller is responsible to ensure that // power >= 0 && power <= 10 // // Work around clang bug https://godbolt.org/z/zedh7rrhc return (void)max_mantissa[0], max_mantissa[power]; } template <> inline constexpr double binary_format::exact_power_of_ten(int64_t power) { // Work around clang bug https://godbolt.org/z/zedh7rrhc return (void)powers_of_ten[0], powers_of_ten[power]; } template <> inline constexpr float binary_format::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::largest_power_of_ten() { return 308; } template <> inline constexpr int binary_format::largest_power_of_ten() { return 38; } template <> inline constexpr int binary_format::smallest_power_of_ten() { return -342; } template <> inline constexpr int binary_format::smallest_power_of_ten() { return -64; } template <> inline constexpr size_t binary_format::max_digits() { return 769; } template <> inline constexpr size_t binary_format::max_digits() { return 114; } template <> inline constexpr binary_format::equiv_uint binary_format::exponent_mask() { return 0x7F800000; } template <> inline constexpr binary_format::equiv_uint binary_format::exponent_mask() { return 0x7FF0000000000000; } template <> inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() { return 0x007FFFFF; } template <> inline constexpr binary_format::equiv_uint binary_format::mantissa_mask() { return 0x000FFFFFFFFFFFFF; } template <> inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() { return 0x00800000; } template <> inline constexpr binary_format::equiv_uint binary_format::hidden_bit_mask() { return 0x0010000000000000; } template fastfloat_really_inline FASTFLOAT_CONSTEXPR20 void to_float(bool negative, adjusted_mantissa am, T &value) { using equiv_uint = equiv_uint_t; equiv_uint word = equiv_uint(am.mantissa); word = equiv_uint(word | equiv_uint(am.power2) << binary_format::mantissa_explicit_bits()); word = equiv_uint(word | equiv_uint(negative) << binary_format::sign_index()); #if FASTFLOAT_HAS_BIT_CAST value = std::bit_cast(word); #else ::memcpy(&value, &word, sizeof(T)); #endif } template struct space_lut { static constexpr bool value[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE template constexpr bool space_lut::value[]; #endif template constexpr bool is_space(UC c) { return c < 256 && space_lut<>::value[uint8_t(c)]; } template static constexpr uint64_t int_cmp_zeros() { static_assert((sizeof(UC) == 1) || (sizeof(UC) == 2) || (sizeof(UC) == 4), "Unsupported character size"); return (sizeof(UC) == 1) ? 0x3030303030303030 : (sizeof(UC) == 2) ? (uint64_t(UC('0')) << 48 | uint64_t(UC('0')) << 32 | uint64_t(UC('0')) << 16 | UC('0')) : (uint64_t(UC('0')) << 32 | UC('0')); } template static constexpr int int_cmp_len() { return sizeof(uint64_t) / sizeof(UC); } template constexpr UC const *str_const_nan(); template <> constexpr char const *str_const_nan() { return "nan"; } template <> constexpr wchar_t const *str_const_nan() { return L"nan"; } template <> constexpr char16_t const *str_const_nan() { return u"nan"; } template <> constexpr char32_t const *str_const_nan() { return U"nan"; } #ifdef __cpp_char8_t template <> constexpr char8_t const *str_const_nan() { return u8"nan"; } #endif template constexpr UC const *str_const_inf(); template <> constexpr char const *str_const_inf() { return "infinity"; } template <> constexpr wchar_t const *str_const_inf() { return L"infinity"; } template <> constexpr char16_t const *str_const_inf() { return u"infinity"; } template <> constexpr char32_t const *str_const_inf() { return U"infinity"; } #ifdef __cpp_char8_t template <> constexpr char8_t const *str_const_inf() { return u8"infinity"; } #endif template struct int_luts { 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, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 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, 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, 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, 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, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}; static constexpr size_t maxdigits_u64[] = { 64, 41, 32, 28, 25, 23, 22, 21, 20, 19, 18, 18, 17, 17, 16, 16, 16, 16, 15, 15, 15, 15, 14, 14, 14, 14, 14, 14, 14, 13, 13, 13, 13, 13, 13}; static constexpr uint64_t min_safe_u64[] = { 9223372036854775808ull, 12157665459056928801ull, 4611686018427387904, 7450580596923828125, 4738381338321616896, 3909821048582988049, 9223372036854775808ull, 12157665459056928801ull, 10000000000000000000ull, 5559917313492231481, 2218611106740436992, 8650415919381337933, 2177953337809371136, 6568408355712890625, 1152921504606846976, 2862423051509815793, 6746640616477458432, 15181127029874798299ull, 1638400000000000000, 3243919932521508681, 6221821273427820544, 11592836324538749809ull, 876488338465357824, 1490116119384765625, 2481152873203736576, 4052555153018976267, 6502111422497947648, 10260628712958602189ull, 15943230000000000000ull, 787662783788549761, 1152921504606846976, 1667889514952984961, 2386420683693101056, 3379220508056640625, 4738381338321616896}; }; #if FASTFLOAT_DETAIL_MUST_DEFINE_CONSTEXPR_VARIABLE template constexpr uint8_t int_luts::chdigit[]; template constexpr size_t int_luts::maxdigits_u64[]; template constexpr uint64_t int_luts::min_safe_u64[]; #endif template fastfloat_really_inline constexpr uint8_t ch_to_digit(UC c) { return int_luts<>::chdigit[static_cast(c)]; } fastfloat_really_inline constexpr size_t max_digits_u64(int base) { return int_luts<>::maxdigits_u64[base - 2]; } // If a u64 is exactly max_digits_u64() in length, this is // the value below which it has definitely overflowed. fastfloat_really_inline constexpr uint64_t min_safe_u64(int base) { return int_luts<>::min_safe_u64[base - 2]; } static_assert(std::is_same, uint64_t>::value, "equiv_uint should be uint64_t for double"); static_assert(std::numeric_limits::is_iec559, "double must fulfill the requirements of IEC 559 (IEEE 754)"); static_assert(std::is_same, uint32_t>::value, "equiv_uint should be uint32_t for float"); static_assert(std::numeric_limits::is_iec559, "float must fulfill the requirements of IEC 559 (IEEE 754)"); #ifdef __STDCPP_FLOAT64_T__ static_assert(std::is_same, uint64_t>::value, "equiv_uint should be uint64_t for std::float64_t"); static_assert( std::numeric_limits::is_iec559, "std::float64_t must fulfill the requirements of IEC 559 (IEEE 754)"); #endif // __STDCPP_FLOAT64_T__ #ifdef __STDCPP_FLOAT32_T__ static_assert(std::is_same, uint32_t>::value, "equiv_uint should be uint32_t for std::float32_t"); static_assert( std::numeric_limits::is_iec559, "std::float32_t must fulfill the requirements of IEC 559 (IEEE 754)"); #endif // __STDCPP_FLOAT32_T__ #ifdef __STDCPP_FLOAT16_T__ static_assert( std::is_same::equiv_uint, uint16_t>::value, "equiv_uint should be uint16_t for std::float16_t"); static_assert( std::numeric_limits::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::equiv_uint, uint16_t>::value, "equiv_uint should be uint16_t for std::bfloat16_t"); static_assert( std::numeric_limits::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::type; return static_cast(~static_cast(rhs)); } constexpr chars_format operator&(chars_format lhs, chars_format rhs) noexcept { using int_type = std::underlying_type::type; return static_cast(static_cast(lhs) & static_cast(rhs)); } constexpr chars_format operator|(chars_format lhs, chars_format rhs) noexcept { using int_type = std::underlying_type::type; return static_cast(static_cast(lhs) | static_cast(rhs)); } constexpr chars_format operator^(chars_format lhs, chars_format rhs) noexcept { using int_type = std::underlying_type::type; return static_cast(static_cast(lhs) ^ static_cast(rhs)); } fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & operator&=(chars_format &lhs, chars_format rhs) noexcept { return lhs = (lhs & rhs); } fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & operator|=(chars_format &lhs, chars_format rhs) noexcept { return lhs = (lhs | rhs); } fastfloat_really_inline FASTFLOAT_CONSTEXPR14 chars_format & operator^=(chars_format &lhs, chars_format rhs) noexcept { return lhs = (lhs ^ rhs); } namespace detail { // adjust for deprecated feature macros constexpr chars_format adjust_for_feature_macros(chars_format fmt) { return fmt #ifdef FASTFLOAT_ALLOWS_LEADING_PLUS | chars_format::allow_leading_plus #endif #ifdef FASTFLOAT_SKIP_WHITE_SPACE | chars_format::skip_white_space #endif ; } } // namespace detail } // namespace fast_float #endif fast_float-8.0.0/include/fast_float/parse_number.h000066400000000000000000000335621475170546600222710ustar00rootroot00000000000000#ifndef FASTFLOAT_PARSE_NUMBER_H #define FASTFLOAT_PARSE_NUMBER_H #include "ascii_number.h" #include "decimal_to_binary.h" #include "digit_comparison.h" #include "float_common.h" #include #include #include #include namespace fast_float { namespace detail { /** * Special case +inf, -inf, nan, infinity, -infinity. * The case comparisons could be made much faster given that we know that the * strings a null-free and fixed. **/ template from_chars_result_t FASTFLOAT_CONSTEXPR14 parse_infnan(UC const *first, UC const *last, T &value, chars_format fmt) noexcept { from_chars_result_t answer{}; answer.ptr = first; answer.ec = std::errc(); // be optimistic // assume first < last, so dereference without checks; bool const minusSign = (*first == UC('-')); // C++17 20.19.3.(7.1) explicitly forbids '+' sign here if ((*first == UC('-')) || (uint64_t(fmt & chars_format::allow_leading_plus) && (*first == UC('+')))) { ++first; } if (last - first >= 3) { if (fastfloat_strncasecmp(first, str_const_nan(), 3)) { answer.ptr = (first += 3); value = minusSign ? -std::numeric_limits::quiet_NaN() : std::numeric_limits::quiet_NaN(); // Check for possible nan(n-char-seq-opt), C++17 20.19.3.7, // C11 7.20.1.3.3. At least MSVC produces nan(ind) and nan(snan). if (first != last && *first == UC('(')) { for (UC const *ptr = first + 1; ptr != last; ++ptr) { if (*ptr == UC(')')) { answer.ptr = ptr + 1; // valid nan(n-char-seq-opt) break; } else if (!((UC('a') <= *ptr && *ptr <= UC('z')) || (UC('A') <= *ptr && *ptr <= UC('Z')) || (UC('0') <= *ptr && *ptr <= UC('9')) || *ptr == UC('_'))) break; // forbidden char, not nan(n-char-seq-opt) } } return answer; } if (fastfloat_strncasecmp(first, str_const_inf(), 3)) { if ((last - first >= 8) && fastfloat_strncasecmp(first + 3, str_const_inf() + 3, 5)) { answer.ptr = first + 8; } else { answer.ptr = first + 3; } value = minusSign ? -std::numeric_limits::infinity() : std::numeric_limits::infinity(); return answer; } } answer.ec = std::errc::invalid_argument; return answer; } /** * Returns true if the floating-pointing rounding mode is to 'nearest'. * It is the default on most system. This function is meant to be inexpensive. * Credit : @mwalcott3 */ fastfloat_really_inline bool rounds_to_nearest() noexcept { // https://lemire.me/blog/2020/06/26/gcc-not-nearest/ #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) return false; #endif // See // A fast function to check your floating-point rounding mode // https://lemire.me/blog/2022/11/16/a-fast-function-to-check-your-floating-point-rounding-mode/ // // This function is meant to be equivalent to : // prior: #include // return fegetround() == FE_TONEAREST; // However, it is expected to be much faster than the fegetround() // function call. // // The volatile keyword prevents the compiler from computing the function // at compile-time. // There might be other ways to prevent compile-time optimizations (e.g., // asm). The value does not need to be std::numeric_limits::min(), any // small value so that 1 + x should round to 1 would do (after accounting for // excess precision, as in 387 instructions). static float volatile fmin = std::numeric_limits::min(); float fmini = fmin; // we copy it so that it gets loaded at most once. // // Explanation: // Only when fegetround() == FE_TONEAREST do we have that // fmin + 1.0f == 1.0f - fmin. // // FE_UPWARD: // fmin + 1.0f > 1 // 1.0f - fmin == 1 // // FE_DOWNWARD or FE_TOWARDZERO: // fmin + 1.0f == 1 // 1.0f - fmin < 1 // // Note: This may fail to be accurate if fast-math has been // enabled, as rounding conventions may not apply. #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(push) // todo: is there a VS warning? // see // https://stackoverflow.com/questions/46079446/is-there-a-warning-for-floating-point-equality-checking-in-visual-studio-2013 #elif defined(__clang__) #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wfloat-equal" #elif defined(__GNUC__) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wfloat-equal" #endif return (fmini + 1.0f == 1.0f - fmini); #ifdef FASTFLOAT_VISUAL_STUDIO #pragma warning(pop) #elif defined(__clang__) #pragma clang diagnostic pop #elif defined(__GNUC__) #pragma GCC diagnostic pop #endif } } // namespace detail template struct from_chars_caller { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_advanced(first, last, value, options); } }; #ifdef __STDCPP_FLOAT32_T__ template <> struct from_chars_caller { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, std::float32_t &value, parse_options_t options) noexcept { // if std::float32_t is defined, and we are in C++23 mode; macro set for // float32; set value to float due to equivalence between float and // float32_t float val; auto ret = from_chars_advanced(first, last, val, options); value = val; return ret; } }; #endif #ifdef __STDCPP_FLOAT64_T__ template <> struct from_chars_caller { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, std::float64_t &value, parse_options_t options) noexcept { // if std::float64_t is defined, and we are in C++23 mode; macro set for // float64; set value as double due to equivalence between double and // float64_t double val; auto ret = from_chars_advanced(first, last, val, options); value = val; return ret; } }; #endif template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, chars_format fmt /*= chars_format::general*/) noexcept { return from_chars_caller::call(first, last, value, parse_options_t(fmt)); } /** * This function overload takes parsed_number_string_t structure that is created * and populated either by from_chars_advanced function taking chars range and * parsing options or other parsing custom function implemented by user. */ template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(parsed_number_string_t &pns, T &value) noexcept { static_assert(is_supported_float_type::value, "only some floating-point types are supported"); static_assert(is_supported_char_type::value, "only char, wchar_t, char16_t and char32_t are supported"); from_chars_result_t 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::min_exponent_fast_path() <= pns.exponent && pns.exponent <= binary_format::max_exponent_fast_path() && !pns.too_many_digits) { // Unfortunately, the conventional Clinger's fast path is only possible // when the system rounds to the nearest float. // // We expect the next branch to almost always be selected. // We could check it first (before the previous branch), but // there might be performance advantages at having the check // be last. if (!cpp20_and_in_constexpr() && detail::rounds_to_nearest()) { // We have that fegetround() == FE_TONEAREST. // Next is Clinger's fast path. if (pns.mantissa <= binary_format::max_mantissa_fast_path()) { value = T(pns.mantissa); if (pns.exponent < 0) { value = value / binary_format::exact_power_of_ten(-pns.exponent); } else { value = value * binary_format::exact_power_of_ten(pns.exponent); } if (pns.negative) { value = -value; } return answer; } } else { // We do not have that fegetround() == FE_TONEAREST. // Next is a modified Clinger's fast path, inspired by Jakub Jelínek's // proposal if (pns.exponent >= 0 && pns.mantissa <= binary_format::max_mantissa_fast_path(pns.exponent)) { #if defined(__clang__) || defined(FASTFLOAT_32BIT) // Clang may map 0 to -0.0 when fegetround() == FE_DOWNWARD if (pns.mantissa == 0) { value = pns.negative ? T(-0.) : T(0.); return answer; } #endif value = T(pns.mantissa) * binary_format::exact_power_of_ten(pns.exponent); if (pns.negative) { value = -value; } return answer; } } } adjusted_mantissa am = compute_float>(pns.exponent, pns.mantissa); if (pns.too_many_digits && am.power2 >= 0) { if (am != compute_float>(pns.exponent, pns.mantissa + 1)) { am = compute_error>(pns.exponent, pns.mantissa); } } // If we called compute_float>(pns.exponent, pns.mantissa) // and we have an invalid power (am.power2 < 0), then we need to go the long // way around again. This is very uncommon. if (am.power2 < 0) { am = digit_comp(pns, am); } to_float(pns.negative, am, value); // Test for over/underflow. if ((pns.mantissa != 0 && am.mantissa == 0 && am.power2 == 0) || am.power2 == binary_format::infinite_power()) { answer.ec = std::errc::result_out_of_range; } return answer; } template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_float_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { static_assert(is_supported_float_type::value, "only some floating-point types are supported"); static_assert(is_supported_char_type::value, "only char, wchar_t, char16_t and char32_t are supported"); chars_format const fmt = detail::adjust_for_feature_macros(options.format); from_chars_result_t answer; if (uint64_t(fmt & chars_format::skip_white_space)) { while ((first != last) && fast_float::is_space(*first)) { first++; } } if (first == last) { answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } parsed_number_string_t pns = parse_number_string(first, last, options); if (!pns.valid) { if (uint64_t(fmt & chars_format::no_infnan)) { answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } else { return detail::parse_infnan(first, last, value, fmt); } } // call overload that takes parsed_number_string_t directly. return from_chars_advanced(pns, value); } template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars(UC const *first, UC const *last, T &value, int base) noexcept { static_assert(is_supported_integer_type::value, "only integer types are supported"); static_assert(is_supported_char_type::value, "only char, wchar_t, char16_t and char32_t are supported"); parse_options_t options; options.base = base; return from_chars_advanced(first, last, value, options); } template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_int_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { static_assert(is_supported_integer_type::value, "only integer types are supported"); static_assert(is_supported_char_type::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 answer; if (uint64_t(fmt & chars_format::skip_white_space)) { while ((first != last) && fast_float::is_space(*first)) { first++; } } if (first == last || base < 2 || base > 36) { answer.ec = std::errc::invalid_argument; answer.ptr = first; return answer; } return parse_int_string(first, last, value, options); } template struct from_chars_advanced_caller { static_assert(TypeIx > 0, "unsupported type"); }; template <> struct from_chars_advanced_caller<1> { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_float_advanced(first, last, value, options); } }; template <> struct from_chars_advanced_caller<2> { template FASTFLOAT_CONSTEXPR20 static from_chars_result_t call(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_int_advanced(first, last, value, options); } }; template FASTFLOAT_CONSTEXPR20 from_chars_result_t from_chars_advanced(UC const *first, UC const *last, T &value, parse_options_t options) noexcept { return from_chars_advanced_caller< size_t(is_supported_float_type::value) + 2 * size_t(is_supported_integer_type::value)>::call(first, last, value, options); } } // namespace fast_float #endif fast_float-8.0.0/script/000077500000000000000000000000001475170546600151645ustar00rootroot00000000000000fast_float-8.0.0/script/amalgamate.py000066400000000000000000000072561475170546600176410ustar00rootroot00000000000000# text parts processed_files = {} # authors for filename in ["AUTHORS", "CONTRIBUTORS"]: with open(filename, encoding="utf8") as f: text = "" for line in f: if filename == "AUTHORS": text += "// fast_float by " + line if filename == "CONTRIBUTORS": text += "// with contributions from " + line processed_files[filename] = text + "//\n//\n" # licenses for filename in ["LICENSE-MIT", "LICENSE-APACHE", "LICENSE-BOOST"]: lines = [] with open(filename, encoding="utf8") as f: lines = f.readlines() # Retrieve subset required for inclusion in source if filename == "LICENSE-APACHE": lines = [" Copyright 2021 The fast_float authors\n", *lines[179:-1]] text = "" for line in lines: line = line.strip() if len(line): line = " " + line text += "//" + line + "\n" processed_files[filename] = text # code for filename in [ "constexpr_feature_detect.h", "float_common.h", "fast_float.h", "ascii_number.h", "fast_table.h", "decimal_to_binary.h", "bigint.h", "digit_comparison.h", "parse_number.h", ]: with open("include/fast_float/" + filename, encoding="utf8") as f: text = "" for line in f: if line.startswith('#include "'): continue text += line processed_files[filename] = "\n" + text # command line import argparse parser = argparse.ArgumentParser(description="Amalgamate fast_float.") parser.add_argument( "--license", default="TRIPLE", choices=["DUAL", "TRIPLE", "MIT", "APACHE", "BOOST"], help="choose license", ) parser.add_argument("--output", default="", help="output file (stdout if none") args = parser.parse_args() def license_content(license_arg): result = [] if license_arg == "TRIPLE": result += [ "// Licensed under the Apache License, Version 2.0, or the\n", "// MIT License or the Boost License. This file may not be copied,\n", "// modified, or distributed except according to those terms.\n", "//\n", ] if license_arg == "DUAL": result += [ "// Licensed under the Apache License, Version 2.0, or the\n", "// MIT License at your option. This file may not be copied,\n", "// modified, or distributed except according to those terms.\n", "//\n", ] if license_arg in ("DUAL", "TRIPLE", "MIT"): result.append("// MIT License Notice\n//\n") result.append(processed_files["LICENSE-MIT"]) result.append("//\n") if license_arg in ("DUAL", "TRIPLE", "APACHE"): result.append("// Apache License (Version 2.0) Notice\n//\n") result.append(processed_files["LICENSE-APACHE"]) result.append("//\n") if license_arg in ("TRIPLE", "BOOST"): result.append("// BOOST License Notice\n//\n") result.append(processed_files["LICENSE-BOOST"]) result.append("//\n") return result text = "".join( [ processed_files["AUTHORS"], processed_files["CONTRIBUTORS"], *license_content(args.license), processed_files["constexpr_feature_detect.h"], processed_files["float_common.h"], processed_files["fast_float.h"], processed_files["ascii_number.h"], processed_files["fast_table.h"], processed_files["decimal_to_binary.h"], processed_files["bigint.h"], processed_files["digit_comparison.h"], processed_files["parse_number.h"], ] ) if args.output: with open(args.output, "wt", encoding="utf8") as f: f.write(text) else: print(text) fast_float-8.0.0/script/analysis.py000066400000000000000000000013131475170546600173570ustar00rootroot00000000000000import sys from math import floor def log2(x): """returns ceil(log2(x)))""" y = 0 while (1 << y) < x: y = y + 1 return y for q in range(1, 17 + 1): d = 5 ** q b = 127 + log2(d) t = 2 ** b c = t // d + 1 assert c < 2 ** 128 assert c >= 2 ** 127 K = 2 ** 127 if not (c * K * d <= (K + 1) * t): print(q) top = floor(t / (c * d - t)) sys.exit(-1) for q in range(18, 344 + 1): d = 5 ** q b = 64 + 2 * log2(d) t = 2 ** b c = t // d + 1 assert c > 2 ** (64 + log2(d)) K = 2 ** 64 if not (c * K * d <= (K + 1) * t): print(q) top = floor(t / (c * d - t)) sys.exit(-1) print("all good") fast_float-8.0.0/script/mushtak_lemire.py000066400000000000000000000043741475170546600205570ustar00rootroot00000000000000# # Reference : # Noble Mushtak and Daniel Lemire, Fast Number Parsing Without Fallback (to appear) # all_tqs = [] # Generates all possible values of T[q] # Appendix B of Number parsing at a gigabyte per second. # Software: Practice and Experience 2021;51(8):1700–1727. for q in range(-342, -27): power5 = 5 ** -q z = 0 while (1 << z) < power5: z += 1 b = 2 * z + 2 * 64 c = 2 ** b // power5 + 1 while c >= (1 << 128): c //= 2 all_tqs.append(c) for q in range(-27, 0): power5 = 5 ** -q z = 0 while (1 << z) < power5: z += 1 b = z + 127 c = 2 ** b // power5 + 1 all_tqs.append(c) for q in range(0, 308 + 1): power5 = 5 ** q while power5 < (1 << 127): power5 *= 2 while power5 >= (1 << 128): power5 //= 2 all_tqs.append(power5) # Returns the continued fraction of numer/denom as a list [a0; a1, a2, ..., an] def continued_fraction(numer, denom): # (look at page numbers in top-left, not PDF page numbers) cf = [] while denom != 0: quot, rem = divmod(numer, denom) cf.append(quot) numer, denom = denom, rem return cf # Given a continued fraction [a0; a1, a2, ..., an], returns # all the convergents of that continued fraction # as pairs of the form (numer, denom), where numer/denom is # a convergent of the continued fraction in simple form. def convergents(cf): p_n_minus_2 = 0 q_n_minus_2 = 1 p_n_minus_1 = 1 q_n_minus_1 = 0 convergents = [] for a_n in cf: p_n = a_n * p_n_minus_1 + p_n_minus_2 q_n = a_n * q_n_minus_1 + q_n_minus_2 convergents.append((p_n, q_n)) p_n_minus_2, q_n_minus_2, p_n_minus_1, q_n_minus_1 = ( p_n_minus_1, q_n_minus_1, p_n, q_n, ) return convergents # Enumerate through all the convergents of T[q] / 2^137 with denominators < 2^64 found_solution = False for j, tq in enumerate(all_tqs): for _, w in convergents(continued_fraction(tq, 2 ** 137)): if w >= 2 ** 64: break if (tq * w) % 2 ** 137 > 2 ** 137 - 2 ** 64: print(f"SOLUTION: q={j-342} T[q]={tq} w={w}") found_solution = True if not found_solution: print("No solutions!") fast_float-8.0.0/script/release.py000077500000000000000000000124471475170546600171710ustar00rootroot00000000000000#!/usr/bin/env python3 ######################################################################## # Generates a new release. ######################################################################## import sys import re import subprocess import io import os import fileinput if sys.version_info < (3, 0): sys.stdout.write("Sorry, requires Python 3.x or better\n") sys.exit(1) def colored(r, g, b, text): return f"\033[38;2;{r};{g};{b}m{text} \033[38;2;255;255;255m" def extractnumbers(s): return tuple(map(int, re.findall(r"(\d+)\.(\d+)\.(\d+)", str(s))[0])) def toversionstring(major, minor, rev): return f"{major}.{minor}.{rev}" print("Calling git rev-parse --abbrev-ref HEAD") pipe = subprocess.Popen( ["git", "rev-parse", "--abbrev-ref", "HEAD"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) branchresult = pipe.communicate()[0].decode().strip() if branchresult != "main": print( colored( 255, 0, 0, f"We recommend that you release on main, you are on '{branchresult}'", ) ) ret = subprocess.call(["git", "remote", "update"]) if ret != 0: sys.exit(ret) print("Calling git log HEAD.. --oneline") pipe = subprocess.Popen( ["git", "log", "HEAD..", "--oneline"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) uptodateresult = pipe.communicate()[0].decode().strip() if len(uptodateresult) != 0: print(uptodateresult) sys.exit(-1) pipe = subprocess.Popen( ["git", "rev-parse", "--show-toplevel"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) maindir = pipe.communicate()[0].decode().strip() scriptlocation = os.path.dirname(os.path.abspath(__file__)) print(f"repository: {maindir}") pipe = subprocess.Popen( ["git", "describe", "--abbrev=0", "--tags"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ) versionresult = pipe.communicate()[0].decode().strip() print(f"last version: {versionresult}") try: currentv = extractnumbers(versionresult) except: currentv = [0, 0, 0] if len(sys.argv) != 2: nextv = (currentv[0], currentv[1], currentv[2] + 1) print(f"please specify version number, e.g. {toversionstring(*nextv)}") sys.exit(-1) try: newversion = extractnumbers(sys.argv[1]) print(newversion) except: print(f"can't parse version number {sys.argv[1]}") sys.exit(-1) print("checking that new version is valid") if newversion[0] != currentv[0]: assert newversion[0] == currentv[0] + 1 assert newversion[1] == 0 assert newversion[2] == 0 elif newversion[1] != currentv[1]: assert newversion[1] == currentv[1] + 1 assert newversion[2] == 0 else: assert newversion[2] == currentv[2] + 1 atleastminor = (currentv[0] != newversion[0]) or (currentv[1] != newversion[1]) newmajorversionstring = str(newversion[0]) newminorversionstring = str(newversion[1]) newpatchversionstring = str(newversion[2]) newversionstring = f"{newversion[0]}.{newversion[1]}.{newversion[2]}" cmakefile = f"{maindir}{os.sep}CMakeLists.txt" for line in fileinput.input(cmakefile, inplace=1, backup=".bak"): line = re.sub( r"project\(fast_float VERSION \d+\.\d+\.\d+ LANGUAGES CXX\)", f"project(fast_float VERSION {newversionstring} LANGUAGES CXX)", line.rstrip(), ) print(line) print(f"modified {cmakefile}, a backup was made") versionfilerel = f"{os.sep}include{os.sep}fast_float{os.sep}float_common.h" versionfile = f"{maindir}{versionfilerel}" for line in fileinput.input(versionfile, inplace=1, backup=".bak"): line = re.sub( r"#define FASTFLOAT_VERSION_MAJOR \d+", f"#define FASTFLOAT_VERSION_MAJOR {newmajorversionstring}", line.rstrip(), ) line = re.sub( r"#define FASTFLOAT_VERSION_MINOR \d+", f"#define FASTFLOAT_VERSION_MINOR {newminorversionstring}", line.rstrip(), ) line = re.sub( r"#define FASTFLOAT_VERSION_PATCH \d+", f"#define FASTFLOAT_VERSION_PATCH {newpatchversionstring}", line.rstrip(), ) print(line) print(f"{versionfile} modified") readmefile = f"{maindir}{os.sep}README.md" for line in fileinput.input(readmefile, inplace=1, backup=".bak"): line = re.sub( r"https://github.com/fastfloat/fast_float/releases/download/v(\d+\.\d+\.\d+)/fast_float.h", f"https://github.com/fastfloat/fast_float/releases/download/v{newversionstring}/fast_float.h", line.rstrip(), ) line = re.sub( r"GIT_TAG tags/v(\d+\.\d+\.\d+)", f"GIT_TAG tags/v{newversionstring}", line.rstrip(), ) line = re.sub( r"GIT_TAG v(\d+\.\d+\.\d+)\)", f"GIT_TAG v{newversionstring})", line.rstrip() ) print(line) print(f"modified {readmefile}, a backup was made") print("running amalgamate.py") with open(f"{maindir}{os.sep}fast_float.h", "w") as outfile: cp = subprocess.run( [f"python3", f"{maindir}{os.sep}script{os.sep}amalgamate.py"], stdout=outfile ) if cp.returncode != 0: print("Failed to run amalgamate") else: print("amalgamate.py ran successfully") print(f"You should upload {maindir}{os.sep}fast_float.h") print("Please run the tests before issuing a release.\n") print( f'to issue release, enter\n git commit -a && git push && git tag -a v{toversionstring(*newversion)} -m "version {toversionstring(*newversion)}" && git push --tags\n' ) fast_float-8.0.0/script/run-clangcldocker.sh000077500000000000000000000021771475170546600211270ustar00rootroot00000000000000#!/usr/bin/env bash set -e COMMAND=$* SCRIPTPATH="$( cd "$(dirname "$0")" ; pwd -P )" MAINSOURCE=$SCRIPTPATH/.. ALL_FILES=$(cd $MAINSOURCE && git ls-tree --full-tree --name-only -r HEAD | grep -e ".*\.\(c\|h\|cc\|cpp\|hh\)\$" | grep -vFf clang-format-ignore.txt) if clang-format-17 --version 2>/dev/null | grep -qF 'version 17.'; then cd $MAINSOURCE; clang-format-17 --style=file --verbose -i "$@" $ALL_FILES exit 0 elif clang-format --version 2>/dev/null | grep -qF 'version 17.'; then cd $MAINSOURCE; clang-format --style=file --verbose -i "$@" $ALL_FILES exit 0 fi echo "Trying to use docker" command -v docker >/dev/null 2>&1 || { echo >&2 "Please install docker. E.g., go to https://www.docker.com/products/docker-desktop Type 'docker' to diagnose the problem."; exit 1; } docker info >/dev/null 2>&1 || { echo >&2 "Docker server is not running? type 'docker info'."; exit 1; } if [ -t 0 ]; then DOCKER_ARGS=-it; fi docker pull kszonek/clang-format-17 docker run --rm $DOCKER_ARGS -v "$MAINSOURCE":"$MAINSOURCE":Z -w "$MAINSOURCE" -u "$(id -u $USER):$(id -g $USER)" kszonek/clang-format-17 --style=file --verbose -i "$@" $ALL_FILES fast_float-8.0.0/script/table_generation.py000066400000000000000000000013201475170546600210340ustar00rootroot00000000000000def format(number): upper = number // (1 << 64) lower = number % (1 << 64) print("" + hex(upper) + "," + hex(lower) + ",") for q in range(-342, 0): power5 = 5 ** -q z = 0 while (1 << z) < power5: z += 1 if q >= -27: b = z + 127 c = 2 ** b // power5 + 1 format(c) else: b = 2 * z + 2 * 64 c = 2 ** b // power5 + 1 # truncate while c >= (1 << 128): c //= 2 format(c) for q in range(0, 308 + 1): power5 = 5 ** q # move the most significant bit in position while power5 < (1 << 127): power5 *= 2 # *truncate* while power5 >= (1 << 128): power5 //= 2 format(power5) fast_float-8.0.0/tests/000077500000000000000000000000001475170546600150225ustar00rootroot00000000000000fast_float-8.0.0/tests/BUILD.bazel000066400000000000000000000036051475170546600167040ustar00rootroot00000000000000cc_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", ], ) fast_float-8.0.0/tests/CMakeLists.txt000066400000000000000000000107571475170546600175740ustar00rootroot00000000000000# FetchContent requires cmake >=3.11 # see https://cmake.org/cmake/help/v3.11/module/FetchContent.html cmake_minimum_required(VERSION 3.11 FATAL_ERROR) include(FetchContent) option(SYSTEM_DOCTEST "Use system copy of doctest" OFF) option(FASTFLOAT_SUPPLEMENTAL_TESTS "Run supplemental tests" ON) if (NOT SYSTEM_DOCTEST) FetchContent_Declare(doctest GIT_REPOSITORY https://github.com/onqtam/doctest.git GIT_TAG v2.4.11) else () find_package(doctest REQUIRED) endif() if (FASTFLOAT_SUPPLEMENTAL_TESTS) FetchContent_Declare(supplemental_test_files GIT_REPOSITORY https://github.com/fastfloat/supplemental_test_files.git GIT_TAG origin/main) endif() # FetchContent_MakeAvailable() was only introduced in 3.14 # https://cmake.org/cmake/help/v3.14/release/3.14.html#modules # FetchContent_MakeAvailable(doctest) if (NOT SYSTEM_DOCTEST) FetchContent_GetProperties(doctest) if(NOT doctest_POPULATED) FetchContent_Populate(doctest) add_subdirectory(${doctest_SOURCE_DIR} ${doctest_BINARY_DIR}) endif() endif() add_library(supplemental-data INTERFACE) if (FASTFLOAT_SUPPLEMENTAL_TESTS) FetchContent_GetProperties(supplemental_test_files) if(NOT supplemental_test_files_POPULATED) message(STATUS "Supplemental tests enabled. Retrieving test files.") FetchContent_Populate(supplemental_test_files) message(STATUS "Supplemental test files retrieved.") add_subdirectory(${supplemental_test_files_SOURCE_DIR} ${supplemental_test_files_BINARY_DIR}) endif() target_compile_definitions(supplemental-data INTERFACE SUPPLEMENTAL_TEST_DATA_DIR="${supplemental_test_files_BINARY_DIR}/data") endif() function(fast_float_add_cpp_test TEST_NAME) add_executable(${TEST_NAME} ${TEST_NAME}.cpp) if(CMAKE_CROSSCOMPILING) set(ccemulator ${CMAKE_CROSSCOMPILING_EMULATOR}) endif() add_test(NAME ${TEST_NAME} COMMAND ${ccemulator} $) if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") target_compile_options(${TEST_NAME} PUBLIC /EHsc) endif() if(NOT WIN32) target_compile_options(${TEST_NAME} PUBLIC -Werror -Wall -Wextra -Weffc++) target_compile_options(${TEST_NAME} PUBLIC -Wsign-compare -Wshadow -Wwrite-strings -Wpointer-arith -Winit-self -Wconversion -Wsign-conversion) endif() target_link_libraries(${TEST_NAME} PUBLIC fast_float supplemental-data) if (NOT SYSTEM_DOCTEST) target_link_libraries(${TEST_NAME} PUBLIC doctest) else () target_link_libraries(${TEST_NAME} PUBLIC doctest::doctest) endif() endfunction(fast_float_add_cpp_test) fast_float_add_cpp_test(rcppfastfloat_test) fast_float_add_cpp_test(wide_char_test) fast_float_add_cpp_test(supported_chars_test) fast_float_add_cpp_test(example_test) fast_float_add_cpp_test(example_comma_test) fast_float_add_cpp_test(basictest) option(FASTFLOAT_CONSTEXPR_TESTS "Require constexpr tests (build will fail if the compiler won't support it)" OFF) if (FASTFLOAT_CONSTEXPR_TESTS) target_compile_features(basictest PRIVATE cxx_std_20) target_compile_definitions(basictest PRIVATE FASTFLOAT_CONSTEXPR_TESTS) else() target_compile_features(basictest PRIVATE cxx_std_17) endif() if (FASTFLOAT_SUPPLEMENTAL_TESTS) target_compile_definitions(basictest PRIVATE FASTFLOAT_SUPPLEMENTAL_TESTS) endif() fast_float_add_cpp_test(long_test) fast_float_add_cpp_test(powersoffive_hardround) fast_float_add_cpp_test(string_test) fast_float_add_cpp_test(fast_int) target_compile_features(fast_int PRIVATE cxx_std_17) fast_float_add_cpp_test(json_fmt) fast_float_add_cpp_test(fortran) if(CMAKE_CXX_STANDARD GREATER_EQUAL 23) option(FASTFLOAT_FIXEDWIDTH_TESTS "Require fixed width test for C++23 (build will fail if the compiler won't support it)" ON) else() option(FASTFLOAT_FIXEDWIDTH_TESTS "Require fixed width test for C++23 (build will fail if the compiler won't support it)" OFF) endif() if (FASTFLOAT_FIXEDWIDTH_TESTS) fast_float_add_cpp_test(fixedwidthtest) target_compile_features(fixedwidthtest PUBLIC cxx_std_23) endif() option(FASTFLOAT_EXHAUSTIVE "Exhaustive tests" OFF) if (FASTFLOAT_EXHAUSTIVE) fast_float_add_cpp_test(short_random_string) fast_float_add_cpp_test(exhaustive32_midpoint) fast_float_add_cpp_test(random_string) fast_float_add_cpp_test(exhaustive32) fast_float_add_cpp_test(exhaustive32_64) fast_float_add_cpp_test(long_exhaustive32) fast_float_add_cpp_test(long_exhaustive32_64) fast_float_add_cpp_test(long_random64) fast_float_add_cpp_test(random64) endif(FASTFLOAT_EXHAUSTIVE) add_subdirectory(build_tests) add_subdirectory(bloat_analysis) fast_float-8.0.0/tests/basictest.cpp000066400000000000000000003022101475170546600175050ustar00rootroot00000000000000#define DOCTEST_CONFIG_SUPER_FAST_ASSERTS #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN #include "doctest/doctest.h" #include "fast_float/fast_float.h" #include #include #include #include #include #include #include #include #include #include #include #if FASTFLOAT_IS_CONSTEXPR #ifndef FASTFLOAT_CONSTEXPR_TESTS #define FASTFLOAT_CONSTEXPR_TESTS 1 #endif // #ifndef FASTFLOAT_CONSTEXPR_TESTS #endif // FASTFLOAT_IS_CONSTEXPR #if FASTFLOAT_HAS_BIT_CAST #include #endif #ifndef SUPPLEMENTAL_TEST_DATA_DIR #define SUPPLEMENTAL_TEST_DATA_DIR "data/" #endif #ifndef __cplusplus #error fastfloat requires a C++ compiler #endif #ifndef FASTFLOAT_CPLUSPLUS #if defined(_MSVC_LANG) && !defined(__clang__) #define FASTFLOAT_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) #else #define FASTFLOAT_CPLUSPLUS __cplusplus #endif #endif #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) #define FASTFLOAT_ODDPLATFORM 1 #endif #if defined __has_include #if __has_include() #else // filesystem is not available #define FASTFLOAT_ODDPLATFORM 1 #endif #else // __has_include is not available #define FASTFLOAT_ODDPLATFORM 1 #endif template std::string iHexAndDec(T v) { std::ostringstream ss; ss << std::hex << "0x" << (v) << " (" << std::dec << (v) << ")"; return ss.str(); } template std::string fHexAndDec(T v) { std::ostringstream ss; ss << std::hexfloat << (v) << " (" << std::defaultfloat << std::setprecision(DBL_MAX_10_EXP + 1) << (v) << ")"; return ss.str(); } char const *round_name(int d) { switch (d) { case FE_UPWARD: return "FE_UPWARD"; case FE_DOWNWARD: return "FE_DOWNWARD"; case FE_TOWARDZERO: return "FE_TOWARDZERO"; case FE_TONEAREST: return "FE_TONEAREST"; default: return "UNKNOWN"; } } #define FASTFLOAT_STR(x) #x #define SHOW_DEFINE(x) printf("%s='%s'\n", #x, FASTFLOAT_STR(x)) TEST_CASE("system_info") { std::cout << "system info:" << std::endl; #ifdef FASTFLOAT_CONSTEXPR_TESTS SHOW_DEFINE(FASTFLOAT_CONSTEXPR_TESTS); #endif #ifdef _MSC_VER SHOW_DEFINE(_MSC_VER); #endif #ifdef FASTFLOAT_64BIT_LIMB SHOW_DEFINE(FASTFLOAT_64BIT_LIMB); #endif #ifdef __clang__ SHOW_DEFINE(__clang__); #endif #ifdef FASTFLOAT_VISUAL_STUDIO SHOW_DEFINE(FASTFLOAT_VISUAL_STUDIO); #endif #ifdef FASTFLOAT_IS_BIG_ENDIAN #if FASTFLOAT_IS_BIG_ENDIAN printf("big endian\n"); #else printf("little endian\n"); #endif #endif #ifdef FASTFLOAT_32BIT SHOW_DEFINE(FASTFLOAT_32BIT); #endif #ifdef FASTFLOAT_64BIT SHOW_DEFINE(FASTFLOAT_64BIT); #endif #ifdef FLT_EVAL_METHOD SHOW_DEFINE(FLT_EVAL_METHOD); #endif #ifdef _WIN32 SHOW_DEFINE(_WIN32); #endif #ifdef _WIN64 SHOW_DEFINE(_WIN64); #endif std::cout << "fegetround() = " << round_name(fegetround()) << std::endl; std::cout << std::endl; } TEST_CASE("double.rounds_to_nearest") { // // If this function fails, we may be left in a non-standard rounding state. // static double volatile fmin = std::numeric_limits::min(); fesetround(FE_UPWARD); std::cout << "FE_UPWARD: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_UPWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_DOWNWARD); std::cout << "FE_DOWNWARD: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_DOWNWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TOWARDZERO); std::cout << "FE_TOWARDZERO: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_TOWARDZERO); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TONEAREST); std::cout << "FE_TONEAREST: fmin + 1.0 = " << fHexAndDec(fmin + 1.0) << " 1.0 - fmin = " << fHexAndDec(1.0 - fmin) << std::endl; CHECK(fegetround() == FE_TONEAREST); #if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0) CHECK(fast_float::detail::rounds_to_nearest() == true); #endif } TEST_CASE("double.parse_zero") { // // If this function fails, we may be left in a non-standard rounding state. // char const *zero = "0"; uint64_t float64_parsed; double f = 0; ::memcpy(&float64_parsed, &f, sizeof(f)); CHECK(float64_parsed == 0); fesetround(FE_UPWARD); auto r1 = fast_float::from_chars(zero, zero + 1, f); CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0); fesetround(FE_TOWARDZERO); auto r2 = fast_float::from_chars(zero, zero + 1, f); CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0); fesetround(FE_DOWNWARD); auto r3 = fast_float::from_chars(zero, zero + 1, f); CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0); fesetround(FE_TONEAREST); auto r4 = fast_float::from_chars(zero, zero + 1, f); CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0); } TEST_CASE("double.parse_negative_zero") { // // If this function fails, we may be left in a non-standard rounding state. // char const *negative_zero = "-0"; uint64_t float64_parsed; double f = -0.; ::memcpy(&float64_parsed, &f, sizeof(f)); CHECK(float64_parsed == 0x8000'0000'0000'0000); fesetround(FE_UPWARD); auto r1 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); fesetround(FE_TOWARDZERO); auto r2 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); fesetround(FE_DOWNWARD); auto r3 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); fesetround(FE_TONEAREST); auto r4 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.); ::memcpy(&float64_parsed, &f, sizeof(f)); std::cout << "double as uint64_t is " << iHexAndDec(float64_parsed) << std::endl; CHECK(float64_parsed == 0x8000'0000'0000'0000); } TEST_CASE("float.rounds_to_nearest") { // // If this function fails, we may be left in a non-standard rounding state. // static float volatile fmin = std::numeric_limits::min(); fesetround(FE_UPWARD); std::cout << "FE_UPWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_UPWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_DOWNWARD); std::cout << "FE_DOWNWARD: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_DOWNWARD); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TOWARDZERO); std::cout << "FE_TOWARDZERO: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_TOWARDZERO); CHECK(fast_float::detail::rounds_to_nearest() == false); fesetround(FE_TONEAREST); std::cout << "FE_TONEAREST: fmin + 1.0f = " << fHexAndDec(fmin + 1.0f) << " 1.0f - fmin = " << fHexAndDec(1.0f - fmin) << std::endl; CHECK(fegetround() == FE_TONEAREST); #if (FLT_EVAL_METHOD == 1) || (FLT_EVAL_METHOD == 0) CHECK(fast_float::detail::rounds_to_nearest() == true); #endif } TEST_CASE("float.parse_zero") { // // If this function fails, we may be left in a non-standard rounding state. // char const *zero = "0"; uint32_t float32_parsed; float f = 0; ::memcpy(&float32_parsed, &f, sizeof(f)); CHECK(float32_parsed == 0); fesetround(FE_UPWARD); auto r1 = fast_float::from_chars(zero, zero + 1, f); CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0); fesetround(FE_TOWARDZERO); auto r2 = fast_float::from_chars(zero, zero + 1, f); CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0); fesetround(FE_DOWNWARD); auto r3 = fast_float::from_chars(zero, zero + 1, f); CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0); fesetround(FE_TONEAREST); auto r4 = fast_float::from_chars(zero, zero + 1, f); CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0); } TEST_CASE("float.parse_negative_zero") { // // If this function fails, we may be left in a non-standard rounding state. // char const *negative_zero = "-0"; uint32_t float32_parsed; float f = -0.; ::memcpy(&float32_parsed, &f, sizeof(f)); CHECK(float32_parsed == 0x8000'0000); fesetround(FE_UPWARD); auto r1 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r1.ec == std::errc()); std::cout << "FE_UPWARD parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0x8000'0000); fesetround(FE_TOWARDZERO); auto r2 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r2.ec == std::errc()); std::cout << "FE_TOWARDZERO parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0x8000'0000); fesetround(FE_DOWNWARD); auto r3 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r3.ec == std::errc()); std::cout << "FE_DOWNWARD parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0x8000'0000); fesetround(FE_TONEAREST); auto r4 = fast_float::from_chars(negative_zero, negative_zero + 2, f); CHECK(r4.ec == std::errc()); std::cout << "FE_TONEAREST parsed negative zero as " << fHexAndDec(f) << std::endl; CHECK(f == 0.f); ::memcpy(&float32_parsed, &f, sizeof(f)); std::cout << "float as uint32_t is " << iHexAndDec(float32_parsed) << std::endl; CHECK(float32_parsed == 0x8000'0000); } #if FASTFLOAT_SUPPLEMENTAL_TESTS // C++ 17 because it is otherwise annoying to browse all files in a directory. // We also only run these tests on little endian systems. #if (FASTFLOAT_CPLUSPLUS >= 201703L) && (FASTFLOAT_IS_BIG_ENDIAN == 0) && \ !defined(FASTFLOAT_ODDPLATFORM) #include #include // return true on success bool check_file(std::string file_name) { std::cout << "Checking " << file_name << std::endl; // We check all rounding directions, for each file. std::vector directions = {FE_UPWARD, FE_DOWNWARD, FE_TOWARDZERO, FE_TONEAREST}; for (int d : directions) { std::cout << "fesetround to " << round_name(d) << std::endl; fesetround(d); size_t number{0}; std::fstream newfile(file_name, std::ios::in); if (newfile.is_open()) { std::string str; while (std::getline(newfile, str)) { if (str.size() > 0) { #ifdef __STDCPP_FLOAT16_T__ // Read 16-bit hex uint16_t float16{}; auto r16 = std::from_chars(str.data(), str.data() + str.size(), float16, 16); if (r16.ec != std::errc()) { std::cerr << "16-bit parsing failure: " << str << "\n"; return false; } #endif // Read 32-bit hex uint32_t float32{}; auto r32 = std::from_chars(str.data() + 5, str.data() + str.size(), float32, 16); if (r32.ec != std::errc()) { std::cerr << "32-bit parsing failure: " << str << "\n"; return false; } // Read 64-bit hex uint64_t float64{}; auto r64 = std::from_chars(str.data() + 14, str.data() + str.size(), float64, 16); if (r64.ec != std::errc()) { std::cerr << "64-bit parsing failure: " << str << "\n"; return false; } // The string to parse: char const *number_string = str.data() + 31; char const *end_of_string = str.data() + str.size(); #ifdef __STDCPP_FLOAT16_T__ // Parse as 16-bit float std::float16_t parsed_16{}; auto fast_float_r16 = fast_float::from_chars(number_string, end_of_string, 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: " << str << "\n"; return false; } #endif // Parse as 32-bit float float parsed_32{}; auto fast_float_r32 = fast_float::from_chars(number_string, end_of_string, parsed_32); if (fast_float_r32.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) { std::cerr << "32-bit fast_float parsing failure: " << str << "\n"; return false; } // Parse as 64-bit float double parsed_64{}; auto fast_float_r64 = fast_float::from_chars(number_string, end_of_string, parsed_64); if (fast_float_r64.ec != std::errc() && fast_float_r32.ec != std::errc::result_out_of_range) { std::cerr << "64-bit fast_float parsing failure: " << str << "\n"; return false; } // Convert the floats to unsigned ints. #ifdef __STDCPP_FLOAT16_T__ uint16_t float16_parsed{}; #endif uint32_t float32_parsed{}; uint64_t float64_parsed{}; #ifdef __STDCPP_FLOAT16_T__ ::memcpy(&float16_parsed, &parsed_16, sizeof(parsed_16)); #endif ::memcpy(&float32_parsed, &parsed_32, sizeof(parsed_32)); ::memcpy(&float64_parsed, &parsed_64, sizeof(parsed_64)); // Compare with expected results #ifdef __STDCPP_FLOAT16_T__ if (float16_parsed != float16) { std::cout << "bad 16: " << str << std::endl; std::cout << "parsed as " << fHexAndDec(parsed_16) << std::endl; std::cout << "as raw uint16_t, parsed = " << float16_parsed << ", expected = " << float16 << std::endl; std::cout << "fesetround: " << round_name(d) << std::endl; const bool is_ulfjack = file_name.find("ulfjack") != std::string::npos; if (is_ulfjack) { std::cout << "This is a known issue with ulfjack's test suite." << std::endl; } else { fesetround(FE_TONEAREST); return false; } } #endif if (float32_parsed != float32) { std::cout << "bad 32: " << str << std::endl; std::cout << "parsed as " << fHexAndDec(parsed_32) << std::endl; std::cout << "as raw uint32_t, parsed = " << float32_parsed << ", expected = " << float32 << std::endl; std::cout << "fesetround: " << round_name(d) << std::endl; fesetround(FE_TONEAREST); return false; } if (float64_parsed != float64) { std::cout << "bad 64: " << str << std::endl; std::cout << "parsed as " << fHexAndDec(parsed_64) << std::endl; std::cout << "as raw uint64_t, parsed = " << float64_parsed << ", expected = " << float64 << std::endl; std::cout << "fesetround: " << round_name(d) << std::endl; fesetround(FE_TONEAREST); return false; } number++; } } std::cout << "checked " << std::defaultfloat << number << " values" << std::endl; newfile.close(); // close the file object } else { std::cout << "Could not read " << file_name << std::endl; fesetround(FE_TONEAREST); return false; } } fesetround(FE_TONEAREST); return true; } TEST_CASE("supplemental") { std::string path = SUPPLEMENTAL_TEST_DATA_DIR; for (auto const &entry : std::filesystem::directory_iterator(path)) { const auto file = entry.path().string(); CAPTURE(file); CHECK(check_file(file)); } } #endif #endif TEST_CASE("leading_zeroes") { constexpr uint64_t const bit = 1; CHECK(fast_float::leading_zeroes(bit << 0) == 63); CHECK(fast_float::leading_zeroes(bit << 1) == 62); CHECK(fast_float::leading_zeroes(bit << 2) == 61); CHECK(fast_float::leading_zeroes(bit << 61) == 2); CHECK(fast_float::leading_zeroes(bit << 62) == 1); CHECK(fast_float::leading_zeroes(bit << 63) == 0); } void test_full_multiplication(uint64_t lhs, uint64_t rhs, uint64_t expected_lo, uint64_t expected_hi) { fast_float::value128 v; v = fast_float::full_multiplication(lhs, rhs); INFO("lhs=" << iHexAndDec(lhs) << " " << "rhs=" << iHexAndDec(rhs) << "\n actualLo=" << iHexAndDec(v.low) << " " << "actualHi=" << iHexAndDec(v.high) << "\n expectedLo=" << iHexAndDec(expected_lo) << " " << "expectedHi=" << iHexAndDec(expected_hi)); CHECK_EQ(v.low, expected_lo); CHECK_EQ(v.high, expected_hi); v = fast_float::full_multiplication(rhs, lhs); CHECK_EQ(v.low, expected_lo); CHECK_EQ(v.high, expected_hi); } TEST_CASE("full_multiplication") { constexpr uint64_t const bit = 1; // lhs rhs lo hi test_full_multiplication(bit << 0, bit << 0, 1u, 0u); test_full_multiplication(bit << 0, bit << 63, bit << 63, 0u); test_full_multiplication(bit << 1, bit << 63, 0u, 1u); test_full_multiplication(bit << 63, bit << 0, bit << 63, 0u); test_full_multiplication(bit << 63, bit << 1, 0u, 1u); test_full_multiplication(bit << 63, bit << 2, 0u, 2u); test_full_multiplication(bit << 63, bit << 63, 0u, bit << 62); } TEST_CASE("issue8") { char const *s = "3." "141592653589793238462643383279502884197169399375105820974944592307816406" "286208998628034825342117067982148086513282306647093844609550582231725359" "408128481117450284102701938521105559644622948954930381964428810975665933" "446128475648233786783165271201909145648566923460348610454326648213393607" "260249141273724587006606315588174881520920962829254091715364367892590360" "011330530548820466521384146951941511609433057270365759591953092186117381" "932611793105118548074462379962749567351885752724891227938183011949129833" "673362440656643086021394946395224737190702179860943702770539217176293176" "752384674818467669405132000568127145263560827785771342757789609173637178" "721468440901224953430146549585371050792279689258923542019956112129021960" "864034418159813629774771309960518707211349999998372978"; for (int i = 0; i < 16; i++) { // Parse all but the last i chars. We should still get 3.141ish. double d = 0.0; auto answer = fast_float::from_chars(s, s + strlen(s) - i, d); CHECK_MESSAGE(answer.ec == std::errc(), "i=" << i); CHECK_MESSAGE(d == 0x1.921fb54442d18p+1, "i=" << i << "\n" << std::string(s, strlen(s) - size_t(i)) << "\n" << std::hexfloat << d << "\n" << std::defaultfloat << "\n"); } } TEST_CASE("check_behavior") { std::string const input = "abc"; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); CHECK_MESSAGE(answer.ec != std::errc(), "expected parse failure"); CHECK_MESSAGE( answer.ptr == input.data(), "If there is no pattern match, we should have ptr equals first"); } TEST_CASE("decimal_point_parsing") { double result; fast_float::parse_options options{}; { std::string const input = "1,25"; auto answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); CHECK_MESSAGE(answer.ptr == input.data() + 1, "Parsing should have stopped at comma"); CHECK_EQ(result, 1.0); options.decimal_point = ','; answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); CHECK_MESSAGE(answer.ptr == input.data() + input.size(), "Parsing should have stopped at end"); CHECK_EQ(result, 1.25); } { std::string const input = "1.25"; auto answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); CHECK_MESSAGE(answer.ptr == input.data() + 1, "Parsing should have stopped at dot"); CHECK_EQ(result, 1.0); options.decimal_point = '.'; answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); CHECK_MESSAGE(answer.ec == std::errc(), "expected parse success"); CHECK_MESSAGE(answer.ptr == input.data() + input.size(), "Parsing should have stopped at end"); CHECK_EQ(result, 1.25); } } TEST_CASE("issue19") { std::string const input = "234532.3426362,7869234.9823,324562.645"; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 234532.3426362\n"); CHECK_MESSAGE(answer.ptr == input.data() + 14, "Parsed the number " << result << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); CHECK_MESSAGE(result == 234532.3426362, "We want to parse234532.3426362\n"); CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n"); answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 7869234.9823\n"); CHECK_MESSAGE(answer.ptr == input.data() + 27, "Parsed the number " << result << " and stopped at the wrong character " << (answer.ptr - input.data())); CHECK_MESSAGE(answer.ptr[0] == ',', "We want to parse up to the comma\n"); CHECK_MESSAGE(result == 7869234.9823, "We want to parse up 7869234.9823\n"); answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse 324562.645\n"); CHECK_MESSAGE(answer.ptr == input.data() + 38, "Parsed the number " << result << " and stopped at the wrong character " << (answer.ptr - input.data())); CHECK_MESSAGE(result == 324562.645, "We want to parse up 7869234.9823\n"); } TEST_CASE("issue19") { std::string const input = "3.14e"; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 3.14\n"); CHECK_MESSAGE(answer.ptr == input.data() + 4, "Parsed the number " << result << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); } TEST_CASE("scientific_only") { // first, we try with something that should fail... std::string input = "3.14"; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result, fast_float::chars_format::scientific); CHECK_MESSAGE(answer.ec != std::errc(), "It is not scientific! Parsed: " << result); input = "3.14e10"; answer = fast_float::from_chars(input.data(), input.data() + input.size(), result, fast_float::chars_format::scientific); CHECK_MESSAGE(answer.ec == std::errc(), "It is scientific! Parsed: " << result); CHECK_MESSAGE(answer.ptr == input.data() + input.size(), "Parsed the number " << result << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); } TEST_CASE("test_fixed_only") { std::string const input = "3.14e10"; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result, fast_float::chars_format::fixed); CHECK_MESSAGE(answer.ec == std::errc(), "We want to parse up to 3.14; parsed: " << result); CHECK_MESSAGE(answer.ptr == input.data() + 4, "Parsed the number " << result << " and stopped at the wrong character: after " << (answer.ptr - input.data()) << " characters"); } static double const testing_power_of_ten[] = { 1e-323, 1e-322, 1e-321, 1e-320, 1e-319, 1e-318, 1e-317, 1e-316, 1e-315, 1e-314, 1e-313, 1e-312, 1e-311, 1e-310, 1e-309, 1e-308, 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, 1e-280, 1e-279, 1e-278, 1e-277, 1e-276, 1e-275, 1e-274, 1e-273, 1e-272, 1e-271, 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, 1e-263, 1e-262, 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, 1e-253, 1e-252, 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, 1e-244, 1e-243, 1e-242, 1e-241, 1e-240, 1e-239, 1e-238, 1e-237, 1e-236, 1e-235, 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, 1e-227, 1e-226, 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, 1e-217, 1e-216, 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, 1e-208, 1e-207, 1e-206, 1e-205, 1e-204, 1e-203, 1e-202, 1e-201, 1e-200, 1e-199, 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, 1e-191, 1e-190, 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, 1e-181, 1e-180, 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, 1e-172, 1e-171, 1e-170, 1e-169, 1e-168, 1e-167, 1e-166, 1e-165, 1e-164, 1e-163, 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, 1e-155, 1e-154, 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, 1e-145, 1e-144, 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, 1e-136, 1e-135, 1e-134, 1e-133, 1e-132, 1e-131, 1e-130, 1e-129, 1e-128, 1e-127, 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, 1e-119, 1e-118, 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, 1e-109, 1e-108, 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, 1e-100, 1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92, 1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83, 1e-82, 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 1e-73, 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 1e-64, 1e-63, 1e-62, 1e-61, 1e-60, 1e-59, 1e-58, 1e-57, 1e-56, 1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, 1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38, 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20, 1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43, 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52, 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60, 1e61, 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 1e98, 1e99, 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 1e107, 1e108, 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 1e116, 1e117, 1e118, 1e119, 1e120, 1e121, 1e122, 1e123, 1e124, 1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132, 1e133, 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140, 1e141, 1e142, 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151, 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 1e160, 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169, 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 1e179, 1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 1e188, 1e189, 1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, 1e197, 1e198, 1e199, 1e200, 1e201, 1e202, 1e203, 1e204, 1e205, 1e206, 1e207, 1e208, 1e209, 1e210, 1e211, 1e212, 1e213, 1e214, 1e215, 1e216, 1e217, 1e218, 1e219, 1e220, 1e221, 1e222, 1e223, 1e224, 1e225, 1e226, 1e227, 1e228, 1e229, 1e230, 1e231, 1e232, 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, 1e240, 1e241, 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, 1e250, 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259, 1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, 1e269, 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, 1e278, 1e279, 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 1e287, 1e288, 1e289, 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 1e296, 1e297, 1e298, 1e299, 1e300, 1e301, 1e302, 1e303, 1e304, 1e305, 1e306, 1e307, 1e308}; TEST_CASE("powers_of_ten") { char buf[1024]; WARN_MESSAGE(1e-308 == std::pow(10, -308), "On your system, the pow function is busted. Sorry about that."); bool is_pow_correct{1e-308 == std::pow(10, -308)}; // large negative values should be zero. int start_point = is_pow_correct ? -1000 : -307; for (int i = start_point; i <= 308; ++i) { INFO("i=" << i); size_t n = size_t(snprintf(buf, sizeof(buf), "1e%d", i)); REQUIRE(n < sizeof(buf)); // if false, fails the test and exits double actual; auto result = fast_float::from_chars(buf, buf + 1000, actual); double expected = ((i >= -323) ? testing_power_of_ten[i + 323] : std::pow(10, i)); auto expected_ec = (i < -323 || i > 308) ? std::errc::result_out_of_range : std::errc(); CHECK_MESSAGE(result.ec == expected_ec, " I could not parse " << buf); CHECK_MESSAGE(actual == expected, "String '" << buf << "'parsed to " << actual); } } template std::string to_string(T d) { std::string s(64, '\0'); auto written = std::snprintf(&s[0], s.size(), "%.*e", std::numeric_limits::max_digits10 - 1, d); s.resize(size_t(written)); return s; } template std::string to_long_string(T d) { std::string s(4096, '\0'); auto written = std::snprintf(&s[0], s.size(), "%.*e", std::numeric_limits::max_digits10 * 10, d); s.resize(size_t(written)); return s; } uint32_t get_mantissa(float f) { uint32_t m; memcpy(&m, &f, sizeof(f)); return (m & ((uint32_t(1) << 23) - 1)); } uint64_t get_mantissa(double f) { uint64_t m; memcpy(&m, &f, sizeof(f)); return (m & ((uint64_t(1) << 57) - 1)); } #ifdef __STDCPP_FLOAT64_T__ uint64_t get_mantissa(std::float64_t f) { uint64_t m; memcpy(&m, &f, sizeof(f)); return (m & ((uint64_t(1) << 10) - 1)); } #endif #ifdef __STDCPP_FLOAT32_T__ uint32_t get_mantissa(std::float32_t f) { uint32_t m; memcpy(&m, &f, sizeof(f)); return (m & ((uint32_t(1) << 10) - 1)); } #endif #ifdef __STDCPP_FLOAT16_T__ uint16_t get_mantissa(std::float16_t f) { uint16_t m; memcpy(&m, &f, sizeof(f)); return (m & ((uint16_t(1) << 10) - 1)); } #endif #ifdef __STDCPP_BFLOAT16_T__ uint16_t get_mantissa(std::bfloat16_t f) { uint16_t m; memcpy(&m, &f, sizeof(f)); return (m & ((uint16_t(1) << 7) - 1)); } #endif std::string append_zeros(std::string str, size_t number_of_zeros) { std::string answer(str); for (size_t i = 0; i < number_of_zeros; i++) { answer += "0"; } return answer; } namespace { enum class Diag { runtime, comptime }; } // anonymous namespace constexpr size_t global_string_capacity = 2048; template constexpr void check_basic_test_result(stringtype str, result_type result, T actual, T expected, std::errc expected_ec) { struct ComptimeDiag { // Purposely not constexpr static void error_not_equal() {} }; #define FASTFLOAT_CHECK_EQ(...) \ if constexpr (diag == Diag::runtime) { \ char narrow[global_string_capacity]{}; \ for (size_t i = 0; i < str.size(); i++) { \ narrow[i] = char(str[i]); \ } \ INFO("str(char" << 8 * sizeof(typename stringtype::value_type) \ << ")=" << narrow << "\n" \ << " expected=" << fHexAndDec(expected) << "\n" \ << " ..actual=" << fHexAndDec(actual) << "\n" \ << " expected mantissa=" \ << iHexAndDec(get_mantissa(expected)) << "\n" \ << " ..actual mantissa=" \ << iHexAndDec(get_mantissa(actual))); \ CHECK_EQ(__VA_ARGS__); \ } else { \ if ([](auto const &lhs, auto const &rhs) { \ return lhs != rhs; \ }(__VA_ARGS__)) { \ ComptimeDiag::error_not_equal(); \ } \ } auto copysign = [](double x, double y) -> double { #if FASTFLOAT_HAS_BIT_CAST if (fast_float::cpp20_and_in_constexpr()) { using equiv_int = std::make_signed_t>; auto const i = std::bit_cast(y); if (i < 0) { return -x; } return x; } else #endif return std::copysign(x, y); }; auto isnan = [](double x) -> bool { return x != x; }; FASTFLOAT_CHECK_EQ(result.ec, expected_ec); FASTFLOAT_CHECK_EQ(result.ptr, str.data() + str.size()); FASTFLOAT_CHECK_EQ(copysign(1, actual), copysign(1, expected)); FASTFLOAT_CHECK_EQ(isnan(actual), isnan(expected)); FASTFLOAT_CHECK_EQ(actual, expected); #undef FASTFLOAT_CHECK_EQ } template constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec = std::errc()) { T actual{}; auto result = fast_float::from_chars(str.data(), str.data() + str.size(), actual); check_basic_test_result(str, result, actual, expected, expected_ec); if (str.size() > global_string_capacity) { return; } // We give plenty of memory: 2048 characters. char16_t u16[global_string_capacity]{}; for (size_t i = 0; i < str.size(); i++) { u16[i] = char16_t(str[i]); } auto result16 = fast_float::from_chars(u16, u16 + str.size(), actual); check_basic_test_result(std::u16string_view(u16, str.size()), result16, actual, expected, expected_ec); char32_t u32[global_string_capacity]{}; for (size_t i = 0; i < str.size(); i++) { u32[i] = char32_t(str[i]); } auto result32 = fast_float::from_chars(u32, u32 + str.size(), actual); check_basic_test_result(std::u32string_view(u32, str.size()), result32, actual, expected, expected_ec); } template constexpr void basic_test(std::string_view str, T expected, fast_float::parse_options options) { T actual{}; auto result = fast_float::from_chars_advanced( str.data(), str.data() + str.size(), actual, options); check_basic_test_result(str, result, actual, expected, std::errc()); } template constexpr void basic_test(std::string_view str, T expected, std::errc expected_ec, fast_float::parse_options options) { T actual{}; auto result = fast_float::from_chars_advanced( str.data(), str.data() + str.size(), actual, options); check_basic_test_result(str, result, actual, expected, expected_ec); } void basic_test(float val) { { std::string long_vals = to_long_string(val); INFO("long vals: " << long_vals); basic_test(long_vals, val); } { std::string vals = to_string(val); INFO("vals: " << vals); basic_test(vals, val); } } #define verify_runtime(...) \ do { \ basic_test(__VA_ARGS__); \ } while (false) #define verify_comptime(...) \ do { \ constexpr int verify_comptime_var = \ (basic_test(__VA_ARGS__), 0); \ (void)verify_comptime_var; \ } while (false) #define verify_options_runtime(...) \ do { \ basic_test(__VA_ARGS__, options); \ } while (false) #define verify_options_comptime(...) \ do { \ constexpr int verify_options_comptime_var = \ (basic_test(__VA_ARGS__, options), 0); \ (void)verify_options_comptime_var; \ } while (false) #if defined(FASTFLOAT_CONSTEXPR_TESTS) #if !FASTFLOAT_IS_CONSTEXPR #error "from_chars must be constexpr for constexpr tests" #endif #define verify(...) \ do { \ verify_runtime(__VA_ARGS__); \ verify_comptime(__VA_ARGS__); \ } while (false) #define verify_options(...) \ do { \ verify_options_runtime(__VA_ARGS__); \ verify_options_comptime(__VA_ARGS__); \ } while (false) #else #define verify verify_runtime #define verify_options verify_options_runtime #endif #define verify32(val) \ { \ INFO(#val); \ basic_test(val); \ } TEST_CASE("double.inf") { verify("INF", std::numeric_limits::infinity()); verify("-INF", -std::numeric_limits::infinity()); verify("INFINITY", std::numeric_limits::infinity()); verify("-INFINITY", -std::numeric_limits::infinity()); verify("infinity", std::numeric_limits::infinity()); verify("-infinity", -std::numeric_limits::infinity()); verify("inf", std::numeric_limits::infinity()); verify("-inf", -std::numeric_limits::infinity()); verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("-2139879401095466344511101915470454744.9813888656856943E+272", -std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("1.8e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("1.832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("2e30000000000000000", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("1.9e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); } TEST_CASE("double.general") { verify("0.95000000000000000000", 0.95); verify("22250738585072012e-324", 0x1p-1022); /* limit between normal and subnormal*/ verify("-22250738585072012e-324", -0x1p-1022); /* limit between normal and subnormal*/ verify("-1e-999", -0.0, std::errc::result_out_of_range); verify("-2.2222222222223e-322", -0x1.68p-1069); verify("9007199254740993.0", 0x1p+53); verify("860228122.6654514319E+90", 0x1.92bb20990715fp+328); verify_runtime(append_zeros("9007199254740993.0", 1000), 0x1p+53); verify("10000000000000000000", 0x1.158e460913dp+63); verify("10000000000000000000000000000001000000000000", 0x1.cb2d6f618c879p+142); verify("10000000000000000000000000000000000000000001", 0x1.cb2d6f618c879p+142); verify("1.1920928955078125e-07", 1.1920928955078125e-07); verify("9355950000000000000." "000000000000000000000000000000000018446744073709551616000001844674407" "370955161618446744073709551614073709551616184467440737095516160001844" "674407370955161660000018446744073709551616184467440737095516140737095" "516161844674407370955161600018446744073709551616018446744073709556744" "516161844674407370955161407370955161618446744073709551616000184467440" "737095516160184467440737095516116160001844674407370950018446744073709" "551616001844674407370955161600184467440737095511681644674407370955161" "600018440737095516160184467440737095516161844674407370955161600018446" "744075369107516016116160001844674407370950018446744073709551616001844" "674407370955161600184467440737095516161844674407370955161600018449551" "61618446744073709551616000184467440753691075160018446744073709", 0x1.03ae05e8fca1cp+63); verify("-0", -0.0); verify( "2." "225073858507202124188701479202220329072405282794390378143031338374351073" "192441946867544064325638818513821882185024380699999477330130056498841077" "919287413419292972009704819519930679932909690427840647316820415659267286" "329336304746701233168529834221527445172608358596545663192828352447877877" "998943107797838336991592885945552137141811284582511455843192230798975043" "950868594124572308917389461693683723211913736589779777232866988403563902" "510444430354573967337065839810554204566938246584137476071559811765738776" "267476659123871999319040063173347090030127901881752034471902500280612777" "779167983910905785840064647159438105114891542827750411746821941339524666" "825034313061815878293790042053923750720833666932415800027583911188541886" "41513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022); verify("1.0000000000000006661338147750939242541790008544921875", 1.0000000000000007); verify("1090544144181609348835077142190", 0x1.b8779f2474dfbp+99); verify("2.2250738585072013e-308", 2.2250738585072013e-308); verify("-92666518056446206563E3", -92666518056446206563E3); verify("-92666518056446206563E3", -92666518056446206563E3); verify("-42823146028335318693e-128", -42823146028335318693e-128); verify("90054602635948575728E72", 90054602635948575728E72); verify( "1." "000000000000001885589208702234638701745660206917535153946435506630705583" "68373221972569761144603605635692374830246134201063722058e-309", 1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309); verify("0e9999999999999999999999999999", 0.0); verify("-2402844368454405395.2", -2402844368454405395.2); verify("2402844368454405395.2", 2402844368454405395.2); verify( "7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); verify( "7.0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); verify( "-1.7339253062092163730578609458683877051596800000000000000000000000e+42", -1.7339253062092163730578609458683877051596800000000000000000000000e+42); verify( "-2.0972622234386619214559824785284023792871122537545728000000000000e+52", -2.0972622234386619214559824785284023792871122537545728000000000000e+52); verify( "-1.0001803374372191849407179462120053338028379051879898808320000000e+57", -1.0001803374372191849407179462120053338028379051879898808320000000e+57); verify( "-1.8607245283054342363818436991534856973992070520151142825984000000e+58", -1.8607245283054342363818436991534856973992070520151142825984000000e+58); verify( "-1.9189205311132686907264385602245237137907390376574976000000000000e+52", -1.9189205311132686907264385602245237137907390376574976000000000000e+52); verify( "-2.8184483231688951563253238886553506793085187889855201280000000000e+54", -2.8184483231688951563253238886553506793085187889855201280000000000e+54); verify( "-1.7664960224650106892054063261344555646357024359107788800000000000e+53", -1.7664960224650106892054063261344555646357024359107788800000000000e+53); verify( "-2.1470977154320536489471030463761883783915110400000000000000000000e+45", -2.1470977154320536489471030463761883783915110400000000000000000000e+45); verify( "-4.4900312744003159009338275160799498340862630046359789166919680000e+61", -4.4900312744003159009338275160799498340862630046359789166919680000e+61); verify("1", 1.0); verify("1.797693134862315700000000000000001e308", 1.7976931348623157e308); verify("3e-324", 0x0.0000000000001p-1022); verify("1.00000006e+09", 0x1.dcd651ep+29); verify("4.9406564584124653e-324", 0x0.0000000000001p-1022); verify("4.9406564584124654e-324", 0x0.0000000000001p-1022); verify("2.2250738585072009e-308", 0x0.fffffffffffffp-1022); verify("2.2250738585072014e-308", 0x1p-1022); verify("1.7976931348623157e308", 0x1.fffffffffffffp+1023); verify("1.7976931348623158e308", 0x1.fffffffffffffp+1023); verify("4503599627370496.5", 4503599627370496.5); verify("4503599627475352.5", 4503599627475352.5); verify("4503599627475353.5", 4503599627475353.5); verify("2251799813685248.25", 2251799813685248.25); verify("1125899906842624.125", 1125899906842624.125); verify("1125899906842901.875", 1125899906842901.875); verify("2251799813685803.75", 2251799813685803.75); verify("4503599627370497.5", 4503599627370497.5); verify("45035996.273704995", 45035996.273704995); verify("45035996.273704985", 45035996.273704985); verify( "0." "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000044501477170144022721148195934182639518696390927032912" "960468522194496444440421538910330590478162701758282983178260792422137401" "728773891892910553144148156412434867599762821265346585071045737627442980" "259622449029037796981144446145705102663115100318287949527959668236039986" "479250965780342141637013812613333119898765515451440315261253813266652951" "306000184917766328660755595837392240989947807556594098101021612198814605" "258742579179000071675999344145086087205681577915435923018910334964869420" "614052182892431445797605163650903606514140377217442262561590244668525767" "372446430075513332450079650686719491377688478005309963967709758965844137" "894433796621993967316936280457084866613206797017728916080020698679408551" "343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375); verify( "0." "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000022250738585072008890245868760858598876504231122409594" "654935248025624400092282356951787758888037591552642309780950434312085877" "387158357291821993020294379224223559819827501242041788969571311791082261" "043971979604000454897391938079198936081525613113376149842043271751033627" "391549782731594143828136275113838604094249464942286316695429105080201815" "926642134996606517803095075913058719846423906068637102005108723282784678" "843631944515866135041223479014792369585208321597621066375401613736583044" "193603714778355306682834535634005074073040135602968046375918583163124224" "521599262546494300836851861719422417646455137135420132217031370496583210" "154654068035397417906022589503023501937519773030945763173210852507299305" "089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375); verify( "143845666314139027352611820764223558118322784524633123116263665379036815" "209139419693036582863468763794815794077659918279138752713535303473835713" "411031060945569390082419354977279201654318268051974058035436546798544018" "359870131225762454556233139701832992861319612559027418772007391481806253" "083031653315809862498411888929828137181228878953731059903752911341543873" "895489475212472498306724110876448834645437669901867307840475112141480493" "722424080599312381693232622368309077056159757045779393298582616260425588" "452913412639628220212652625338938342180672795458852559611437980126909409" "632980505480308929973699687095125857301087740440745195384669860919821392" "688269207855703322826525930548119852605981316446918758669325733577952202" "040764549868426333992190522755661669812996741289128223168550466067127792" "719829000982468018631975097866573457668378425580226970891736171946604317" "520115884909788137047711185017157986905601606166617302905958843377601564" "443970505037755427769614392827809345379280384625271596601673322264644238" "289212394005244134682242972159388437821255870100435692424303005951748934" "664657772462249891975259738209522250031112418182351225107135618176937657" "765139002829779615620881537508915912839494571051586133448626710179749711" "112590927250519479287088961717975870344260801614334326215999814970060659" "779253557445756042922697427344363032381874773077131676339857211087495998" "192373246307688452867739265415001026982223940199342748237651323138921235" "358357356637691557265091686655361236618737895955498356671276709337290603" "018897622016905802535497362221166650454931695827188097569714354656446980" "679135870731887307570838334500409015197406832583817753126695417740666139" "2229801349994695941509935655355652985723782153570084089560139142231." "738475042362596875449154552392299548947138162081694168675340677843807613" "129780449323363759027012972466987370921816813162658754726545121090545507" "240267000456594786540949605260722461937870630634874991729398208026467698" "131898691830012167897399682179601734569071423681e-733", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("-2240084132271013504.131248280843119943687942846658579428", -0x1.f1660a65b00bfp+60); } TEST_CASE("double.decimal_point") { constexpr auto options = [] { fast_float::parse_options ret{}; ret.decimal_point = ','; return ret; }(); // infinities verify_options("1,8e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify_options("1,832312213213213232132132143451234453123412321321312e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify_options("2e30000000000000000", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify_options("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify_options("1,9e308", std::numeric_limits::infinity(), std::errc::result_out_of_range); // finites verify_options("-2,2222222222223e-322", -0x1.68p-1069); verify_options("9007199254740993,0", 0x1p+53); verify_options("860228122,6654514319E+90", 0x1.92bb20990715fp+328); verify_options_runtime(append_zeros("9007199254740993,0", 1000), 0x1p+53); verify_options("1,1920928955078125e-07", 1.1920928955078125e-07); verify_options( "9355950000000000000," "000000000000000000000000000000000018446744073709551616000001844674407370" "955161618446744073709551614073709551616184467440737095516160001844674407" "370955161660000018446744073709551616184467440737095516140737095516161844" "674407370955161600018446744073709551616018446744073709556744516161844674" "407370955161407370955161618446744073709551616000184467440737095516160184" "467440737095516116160001844674407370950018446744073709551616001844674407" "370955161600184467440737095511681644674407370955161600018440737095516160" "184467440737095516161844674407370955161600018446744075369107516016116160" "001844674407370950018446744073709551616001844674407370955161600184467440" "737095516161844674407370955161600018449551616184467440737095516160001844" "67440753691075160018446744073709", 0x1.03ae05e8fca1cp+63); verify_options( "2," "225073858507202124188701479202220329072405282794390378143031338374351073" "192441946867544064325638818513821882185024380699999477330130056498841077" "919287413419292972009704819519930679932909690427840647316820415659267286" "329336304746701233168529834221527445172608358596545663192828352447877877" "998943107797838336991592885945552137141811284582511455843192230798975043" "950868594124572308917389461693683723211913736589779777232866988403563902" "510444430354573967337065839810554204566938246584137476071559811765738776" "267476659123871999319040063173347090030127901881752034471902500280612777" "779167983910905785840064647159438105114891542827750411746821941339524666" "825034313061815878293790042053923750720833666932415800027583911188541886" "41513168478436313080237596295773983001708984375e-308", 0x1.0000000000002p-1022); verify_options("1,0000000000000006661338147750939242541790008544921875", 1.0000000000000007); verify_options("2,2250738585072013e-308", 2.2250738585072013e-308); verify_options( "1," "000000000000001885589208702234638701745660206917535153946435506630705583" "68373221972569761144603605635692374830246134201063722058e-309", 1.00000000000000188558920870223463870174566020691753515394643550663070558368373221972569761144603605635692374830246134201063722058e-309); verify_options("-2402844368454405395,2", -2402844368454405395.2); verify_options("2402844368454405395,2", 2402844368454405395.2); verify_options( "7,0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); verify_options( "7,0420557077594588669468784357561207962098443483187940792729600000e+59", 7.0420557077594588669468784357561207962098443483187940792729600000e+59); verify_options( "-1,7339253062092163730578609458683877051596800000000000000000000000e+42", -1.7339253062092163730578609458683877051596800000000000000000000000e+42); verify_options( "-2,0972622234386619214559824785284023792871122537545728000000000000e+52", -2.0972622234386619214559824785284023792871122537545728000000000000e+52); verify_options( "-1,0001803374372191849407179462120053338028379051879898808320000000e+57", -1.0001803374372191849407179462120053338028379051879898808320000000e+57); verify_options( "-1,8607245283054342363818436991534856973992070520151142825984000000e+58", -1.8607245283054342363818436991534856973992070520151142825984000000e+58); verify_options( "-1,9189205311132686907264385602245237137907390376574976000000000000e+52", -1.9189205311132686907264385602245237137907390376574976000000000000e+52); verify_options( "-2,8184483231688951563253238886553506793085187889855201280000000000e+54", -2.8184483231688951563253238886553506793085187889855201280000000000e+54); verify_options( "-1,7664960224650106892054063261344555646357024359107788800000000000e+53", -1.7664960224650106892054063261344555646357024359107788800000000000e+53); verify_options( "-2,1470977154320536489471030463761883783915110400000000000000000000e+45", -2.1470977154320536489471030463761883783915110400000000000000000000e+45); verify_options( "-4,4900312744003159009338275160799498340862630046359789166919680000e+61", -4.4900312744003159009338275160799498340862630046359789166919680000e+61); verify_options("1", 1.0); verify_options("1,797693134862315700000000000000001e308", 1.7976931348623157e308); verify_options("3e-324", 0x0.0000000000001p-1022); verify_options("1,00000006e+09", 0x1.dcd651ep+29); verify_options("4,9406564584124653e-324", 0x0.0000000000001p-1022); verify_options("4,9406564584124654e-324", 0x0.0000000000001p-1022); verify_options("2,2250738585072009e-308", 0x0.fffffffffffffp-1022); verify_options("2,2250738585072014e-308", 0x1p-1022); verify_options("1,7976931348623157e308", 0x1.fffffffffffffp+1023); verify_options("1,7976931348623158e308", 0x1.fffffffffffffp+1023); verify_options("4503599627370496,5", 4503599627370496.5); verify_options("4503599627475352,5", 4503599627475352.5); verify_options("4503599627475353,5", 4503599627475353.5); verify_options("2251799813685248,25", 2251799813685248.25); verify_options("1125899906842624,125", 1125899906842624.125); verify_options("1125899906842901,875", 1125899906842901.875); verify_options("2251799813685803,75", 2251799813685803.75); verify_options("4503599627370497,5", 4503599627370497.5); verify_options("45035996,273704995", 45035996.273704995); verify_options("45035996,273704985", 45035996.273704985); verify_options( "0," "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000044501477170144022721148195934182639518696390927032912" "960468522194496444440421538910330590478162701758282983178260792422137401" "728773891892910553144148156412434867599762821265346585071045737627442980" "259622449029037796981144446145705102663115100318287949527959668236039986" "479250965780342141637013812613333119898765515451440315261253813266652951" "306000184917766328660755595837392240989947807556594098101021612198814605" "258742579179000071675999344145086087205681577915435923018910334964869420" "614052182892431445797605163650903606514140377217442262561590244668525767" "372446430075513332450079650686719491377688478005309963967709758965844137" "894433796621993967316936280457084866613206797017728916080020698679408551" "343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375); verify_options( "0," "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000000000000000000000000000000000000000000000000000000000" "000000000000000000022250738585072008890245868760858598876504231122409594" "654935248025624400092282356951787758888037591552642309780950434312085877" "387158357291821993020294379224223559819827501242041788969571311791082261" "043971979604000454897391938079198936081525613113376149842043271751033627" "391549782731594143828136275113838604094249464942286316695429105080201815" "926642134996606517803095075913058719846423906068637102005108723282784678" "843631944515866135041223479014792369585208321597621066375401613736583044" "193603714778355306682834535634005074073040135602968046375918583163124224" "521599262546494300836851861719422417646455137135420132217031370496583210" "154654068035397417906022589503023501937519773030945763173210852507299305" "089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375); } TEST_CASE("float.inf") { verify("INF", std::numeric_limits::infinity()); verify("-INF", -std::numeric_limits::infinity()); verify("INFINITY", std::numeric_limits::infinity()); verify("-INFINITY", -std::numeric_limits::infinity()); verify("infinity", std::numeric_limits::infinity()); verify("-infinity", -std::numeric_limits::infinity()); verify("inf", std::numeric_limits::infinity()); verify("-inf", -std::numeric_limits::infinity()); verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("3.5028234666e38", std::numeric_limits::infinity(), std::errc::result_out_of_range); } TEST_CASE("float.general") { // max verify("340282346638528859811704183484516925440", 0x1.fffffep+127f); // -max verify("-340282346638528859811704183484516925440", -0x1.fffffep+127f); verify("-1e-999", -0.0f, std::errc::result_out_of_range); verify("1." "175494140627517859246175898662808184331245864732796240031385942718174" "6759860647699724722770042717456817626953125", 0x1.2ced3p+0f); verify("1." "175494140627517859246175898662808184331245864732796240031385942718174" "6759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f); verify_runtime( append_zeros("1." "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 655), 0x1.2ced3p+0f); verify_runtime( append_zeros("1." "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 656), 0x1.2ced3p+0f); verify_runtime( append_zeros("1." "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 1000), 0x1.2ced3p+0f); std::string test_string; test_string = append_zeros("1." "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 655) + std::string("e-38"); verify_runtime(test_string, 0x1.fffff8p-127f); test_string = append_zeros("1." "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 656) + std::string("e-38"); verify_runtime(test_string, 0x1.fffff8p-127f); test_string = append_zeros("1." "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 1000) + std::string("e-38"); verify_runtime(test_string, 0x1.fffff8p-127f); verify32(1.00000006e+09f); verify32(1.4012984643e-45f); verify32(1.1754942107e-38f); verify32(1.1754943508e-45f); verify("-0", -0.0f); verify("1090544144181609348835077142190", 0x1.b877ap+99f); verify("1.1754943508e-38", 1.1754943508e-38f); verify("30219.0830078125", 30219.0830078125f); verify("16252921.5", 16252921.5f); verify("5322519.25", 5322519.25f); verify("3900245.875", 3900245.875f); verify("1510988.3125", 1510988.3125f); verify("782262.28125", 782262.28125f); verify("328381.484375", 328381.484375f); verify("156782.0703125", 156782.0703125f); verify("85003.24609375", 85003.24609375f); verify("17419.6494140625", 17419.6494140625f); verify("15498.36376953125", 15498.36376953125f); verify("6318.580322265625", 6318.580322265625f); verify("2525.2840576171875", 2525.2840576171875f); verify("1370.9265747070312", 1370.9265747070312f); verify("936.3702087402344", 936.3702087402344f); verify("411.88682556152344", 411.88682556152344f); verify("206.50310516357422", 206.50310516357422f); verify("124.16878890991211", 124.16878890991211f); verify("50.811574935913086", 50.811574935913086f); verify("17.486443519592285", 17.486443519592285f); verify("13.91745138168335", 13.91745138168335f); verify("7.5464513301849365", 0x1.e2f90ep+2f); verify("2.687217116355896", 2.687217116355896f); verify("1.1877630352973938", 0x1.30113ep+0f); verify("0.7622503340244293", 0.7622503340244293f); verify("0.30531780421733856", 0x1.38a53ap-2f); verify("0.21791061013936996", 0x1.be47eap-3f); verify("0.09289376810193062", 0x1.7c7e2ep-4f); verify("0.03706067614257336", 0.03706067614257336f); verify("0.028068351559340954", 0.028068351559340954f); verify("0.012114629615098238", 0x1.8cf8e2p-7f); verify("0.004221370676532388", 0x1.14a6dap-8f); verify("0.002153817447833717", 0.002153817447833717f); verify("0.0015924838953651488", 0x1.a175cap-10f); verify("0.0008602388261351734", 0.0008602388261351734f); verify("0.00036393293703440577", 0x1.7d9c82p-12f); verify("0.00013746770127909258", 0.00013746770127909258f); verify("16407.9462890625", 16407.9462890625f); verify("1.1754947011469036e-38", 0x1.000006p-126f); verify("7.0064923216240854e-46", 0x1p-149f); verify("8388614.5", 8388614.5f); verify("0e9999999999999999999999999999", 0.f); verify( "4.7019774032891500318749461488889827112746622270883500860350068251e-38", 4.7019774032891500318749461488889827112746622270883500860350068251e-38f); verify( "3." "141592653589793238462643383279502884197169399375105820974944592307816406" "2862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f); verify( "2.3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f); verify("1", 1.f); verify("7.0060e-46", 0.f, std::errc::result_out_of_range); verify("3.4028234664e38", 0x1.fffffep+127f); verify("3.4028234665e38", 0x1.fffffep+127f); verify("3.4028234666e38", 0x1.fffffep+127f); verify( "0." "000000000000000000000000000000000000011754943508222875079687365372222456" "778186655567720875215087517062784172594547271728515625", 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625); verify( "0." "000000000000000000000000000000000000000000001401298464324817070923729583" "289916131280261941876515771757068283889791082685860601486638188362121582" "03125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f); verify( "0." "000000000000000000000000000000000000023509885615147285834557659820715330" "266457179855179808553659262368500061299303460771170648513361811637878417" "96875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f); verify( "0." "000000000000000000000000000000000000011754942106924410754870294448492873" "488270524287458933338571745305715888704756189042655023513361811637878417" "96875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f); } TEST_CASE("float.decimal_point") { constexpr auto options = [] { fast_float::parse_options ret{}; ret.decimal_point = ','; return ret; }(); // infinity verify_options("3,5028234666e38", std::numeric_limits::infinity(), std::errc::result_out_of_range); // finites verify_options("1," "1754941406275178592461758986628081843312458647327962400313859" "427181746759860647699724722770042717456817626953125", 0x1.2ced3p+0f); verify_options("1," "1754941406275178592461758986628081843312458647327962400313859" "427181746759860647699724722770042717456817626953125e-38", 0x1.fffff8p-127f); verify_options_runtime( append_zeros("1," "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 655), 0x1.2ced3p+0f); verify_options_runtime( append_zeros("1," "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 656), 0x1.2ced3p+0f); verify_options_runtime( append_zeros("1," "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 1000), 0x1.2ced3p+0f); std::string test_string; test_string = append_zeros("1," "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 655) + std::string("e-38"); verify_options_runtime(test_string, 0x1.fffff8p-127f); test_string = append_zeros("1," "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 656) + std::string("e-38"); verify_options_runtime(test_string, 0x1.fffff8p-127f); test_string = append_zeros("1," "17549414062751785924617589866280818433124586473279624003138" "59427181746759860647699724722770042717456817626953125", 1000) + std::string("e-38"); verify_options_runtime(test_string, 0x1.fffff8p-127f); verify_options("1,1754943508e-38", 1.1754943508e-38f); verify_options("30219,0830078125", 30219.0830078125f); verify_options("1,1754947011469036e-38", 0x1.000006p-126f); verify_options("7,0064923216240854e-46", 0x1p-149f); verify_options("8388614,5", 8388614.5f); verify_options("0e9999999999999999999999999999", 0.f); verify_options( "4,7019774032891500318749461488889827112746622270883500860350068251e-38", 4.7019774032891500318749461488889827112746622270883500860350068251e-38f); verify_options( "3," "141592653589793238462643383279502884197169399375105820974944592307816406" "2862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f); verify_options( "2,3509887016445750159374730744444913556373311135441750430175034126e-38", 2.3509887016445750159374730744444913556373311135441750430175034126e-38f); verify_options("1", 1.f); verify_options("7,0060e-46", 0.f, std::errc::result_out_of_range); verify_options("3,4028234664e38", 0x1.fffffep+127f); verify_options("3,4028234665e38", 0x1.fffffep+127f); verify_options("3,4028234666e38", 0x1.fffffep+127f); verify_options( "0," "000000000000000000000000000000000000011754943508222875079687365372222456" "778186655567720875215087517062784172594547271728515625", 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625f); verify_options( "0," "000000000000000000000000000000000000000000001401298464324817070923729583" "289916131280261941876515771757068283889791082685860601486638188362121582" "03125", 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f); verify_options( "0," "000000000000000000000000000000000000023509885615147285834557659820715330" "266457179855179808553659262368500061299303460771170648513361811637878417" "96875", 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f); verify_options( "0," "000000000000000000000000000000000000011754942106924410754870294448492873" "488270524287458933338571745305715888704756189042655023513361811637878417" "96875", 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f); } #ifdef __STDCPP_FLOAT16_T__ TEST_CASE("float16.inf") { verify("INF", std::numeric_limits::infinity()); verify("-INF", -std::numeric_limits::infinity()); verify("INFINITY", std::numeric_limits::infinity()); verify("-INFINITY", -std::numeric_limits::infinity()); verify("infinity", std::numeric_limits::infinity()); verify("-infinity", -std::numeric_limits::infinity()); verify("inf", std::numeric_limits::infinity()); verify("-inf", -std::numeric_limits::infinity()); verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("3.5028234666e38", std::numeric_limits::infinity(), std::errc::result_out_of_range); } TEST_CASE("float16.general") { // max verify("65504", 0x1.ffcp+15f16); // -max verify("-65504", -0x1.ffcp+15f16); // min verify("0.000060975551605224609375", 0x1.ff8p-15f16); verify("6.0975551605224609375e-5", 0x1.ff8p-15f16); // denorm_min verify("0.000000059604644775390625", 0x1p-24f16); verify("5.9604644775390625e-8", 0x1p-24f16); // -min verify("-0.000060975551605224609375", -0x1.ff8p-15f16); verify("-6.0975551605224609375e-5", -0x1.ff8p-15f16); // -denorm_min verify("-0.000000059604644775390625", -0x1p-24f16); verify("-5.9604644775390625e-8", -0x1p-24f16); verify("-1e-999", -0.0f16, std::errc::result_out_of_range); verify("6.0975551605224609375", 0x1.864p+2f16); verify_runtime(append_zeros("6.0975551605224609375", 655), 0x1.864p+2f16); verify_runtime(append_zeros("6.0975551605224609375", 656), 0x1.864p+2f16); verify_runtime(append_zeros("6.0975551605224609375", 1000), 0x1.864p+2f16); verify_runtime(append_zeros("6.0975551605224609375", 655) + std::string("e-5"), 0x1.ff8p-15f16); verify_runtime(append_zeros("6.0975551605224609375", 656) + std::string("e-5"), 0x1.ff8p-15f16); verify_runtime(append_zeros("6.0975551605224609375", 1000) + std::string("e-5"), 0x1.ff8p-15f16); verify("-0", -0.0f16); // verify("1090544144181609348835077142190", 0x1.b877ap+99f16); // verify("1.1754943508e-38", 1.1754943508e-38f16); verify("30219.0830078125", 30219.0830078125f16); verify("17419.6494140625", 17419.6494140625f16); verify("15498.36376953125", 15498.36376953125f16); verify("6318.580322265625", 6318.580322265625f16); verify("2525.2840576171875", 2525.2840576171875f16); verify("1370.9265747070312", 1370.9265747070312f16); verify("936.3702087402344", 936.3702087402344f16); verify("411.88682556152344", 411.88682556152344f16); verify("206.50310516357422", 206.50310516357422f16); verify("124.16878890991211", 124.16878890991211f16); verify("50.811574935913086", 50.811574935913086f16); verify("17.486443519592285", 17.486443519592285f16); verify("13.91745138168335", 13.91745138168335f16); verify("7.5464513301849365", 0x1.e2f90ep+2f16); verify("2.687217116355896", 2.687217116355896f16); verify("1.1877630352973938", 0x1.30113ep+0f16); verify("0.7622503340244293", 0.7622503340244293f16); verify("0.30531780421733856", 0x1.38a53ap-2f16); verify("0.21791061013936996", 0x1.be47eap-3f16); verify("0.09289376810193062", 0x1.7c7e2ep-4f16); verify("0.03706067614257336", 0.03706067614257336f16); verify("0.028068351559340954", 0.028068351559340954f16); verify("0.012114629615098238", 0x1.8cf8e2p-7f16); verify("0.004221370676532388", 0x1.14a6dap-8f16); verify("0.002153817447833717", 0.002153817447833717f16); verify("0.0015924838953651488", 0x1.a175cap-10f16); verify("0.0008602388261351734", 0.0008602388261351734f16); verify("0.00036393293703440577", 0x1.7d9c82p-12f16); verify("0.00013746770127909258", 0.00013746770127909258f16); verify("16407.9462890625", 16407.9462890625f16); // verify("1.1754947011469036e-38", 0x1.000006p-126f16); // verify("7.0064923216240854e-46", 0x1p-149f16); // verify("8388614.5", 8388614.5f16); verify("0e9999999999999999999999999999", 0.f16); // verify( // "4.7019774032891500318749461488889827112746622270883500860350068251e-38", // 4.7019774032891500318749461488889827112746622270883500860350068251e-38f16); verify( "3." "141592653589793238462643383279502884197169399375105820974944592307816406" "2862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679f16); // verify( // "2.3509887016445750159374730744444913556373311135441750430175034126e-38", // 2.3509887016445750159374730744444913556373311135441750430175034126e-38f16); verify("1", 1.f16); // verify("7.0060e-46", 0.f16, std::errc::result_out_of_range); // verify("3.4028234664e38", 0x1.fffffep+127f16); // verify("3.4028234665e38", 0x1.fffffep+127f16); // verify("3.4028234666e38", 0x1.fffffep+127f16); // verify( // "0." // "000000000000000000000000000000000000011754943508222875079687365372222456" // "778186655567720875215087517062784172594547271728515625", // 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625f16); // verify( // "0." // "000000000000000000000000000000000000000000001401298464324817070923729583" // "289916131280261941876515771757068283889791082685860601486638188362121582" // "03125", // 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125f16); // verify( // "0." // "000000000000000000000000000000000000023509885615147285834557659820715330" // "266457179855179808553659262368500061299303460771170648513361811637878417" // "96875", // 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875f16); // verify( // "0." // "000000000000000000000000000000000000011754942106924410754870294448492873" // "488270524287458933338571745305715888704756189042655023513361811637878417" // "96875", // 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875f16); } #endif #ifdef __STDCPP_BFLOAT16_T__ TEST_CASE("bfloat16.inf") { verify("INF", std::numeric_limits::infinity()); verify("-INF", -std::numeric_limits::infinity()); verify("INFINITY", std::numeric_limits::infinity()); verify("-INFINITY", -std::numeric_limits::infinity()); verify("infinity", std::numeric_limits::infinity()); verify("-infinity", -std::numeric_limits::infinity()); verify("inf", std::numeric_limits::infinity()); verify("-inf", -std::numeric_limits::infinity()); verify("1234456789012345678901234567890e9999999999999999999999999999", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("2e3000", std::numeric_limits::infinity(), std::errc::result_out_of_range); verify("3.5028234666e38", std::numeric_limits::infinity(), std::errc::result_out_of_range); } TEST_CASE("bfloat16.general") { // max verify("338953138925153547590470800371487866880", 0x1.fep+127bf16); // -max verify("-338953138925153547590470800371487866880", -0x1.fep+127bf16); // min verify( "0." "000000000000000000000000000000000000011754943508222875079687365372222456" "778186655567720875215087517062784172594547271728515625", 0x1p-126bf16); verify("1." "175494350822287507968736537222245677818665556772087521508751706278417" "2594" "547271728515625e-38", 0x1p-126bf16); // denorm_min verify("0." "000000000000000000000000000000000000000091835496157991211560057541970" "4879" "435795832466228193376178712270530013483949005603790283203125", 0x1p-133bf16); verify("9." "183549615799121156005754197048794357958324662281933761787122705300134" "8394" "9005603790283203125e-41", 0x1p-133bf16); // -min verify( "-0." "000000000000000000000000000000000000011754943508222875079687365372222456" "778186655567720875215087517062784172594547271728515625", -0x1p-126bf16); verify( "-1." "175494350822287507968736537222245677818665556772087521508751706278417259" "4547271728515625e-38", -0x1p-126bf16); // -denorm_min verify("-0" ".00000000000000000000000000000000000000009183549615799121156005754197" "0487" "9435795832466228193376178712270530013483949005603790283203125", -0x1p-133bf16); verify("-9" ".18354961579912115600575419704879435795832466228193376178712270530013" "4839" "49005603790283203125e-41", -0x1p-133bf16); verify("-1e-999", -0.0bf16, std::errc::result_out_of_range); verify_runtime( "1." "175494350822287507968736537222245677818665556772087521508751706278417" "2594547271728515625", 0x1.2cp+0bf16); verify_runtime(append_zeros("1." "175494350822287507968736537222245677818665556772" "0875215087517062784172594547271728515625", 655), 0x1.2cp+0bf16); verify_runtime(append_zeros("1." "175494350822287507968736537222245677818665556772" "0875215087517062784172594547271728515625", 656), 0x1.2cp+0bf16); verify_runtime(append_zeros("1." "175494350822287507968736537222245677818665556772" "0875215087517062784172594547271728515625", 1000), 0x1.2cp+0bf16); verify_runtime(append_zeros("1." "175494350822287507968736537222245677818665556772" "0875215087517062784172594547271728515625", 655) + std::string("e-38"), 0x1p-126bf16); verify_runtime(append_zeros("1." "175494350822287507968736537222245677818665556772" "0875215087517062784172594547271728515625", 656) + std::string("e-38"), 0x1p-126bf16); verify_runtime(append_zeros("1." "175494350822287507968736537222245677818665556772" "0875215087517062784172594547271728515625", 1000) + std::string("e-38"), 0x1p-126bf16); verify("-0", -0.0bf16); // verify("1090544144181609348835077142190", 0x1.b877ap+99bf16); // verify("1.1754943508e-38", 1.1754943508e-38bf16); verify("30219.0830078125", 30219.0830078125bf16); verify("16252921.5", 16252921.5bf16); verify("5322519.25", 5322519.25bf16); verify("3900245.875", 3900245.875bf16); verify("1510988.3125", 1510988.3125bf16); verify("782262.28125", 782262.28125bf16); verify("328381.484375", 328381.484375bf16); verify("156782.0703125", 156782.0703125bf16); verify("85003.24609375", 85003.24609375bf16); verify("17419.6494140625", 17419.6494140625bf16); verify("15498.36376953125", 15498.36376953125bf16); verify("6318.580322265625", 6318.580322265625bf16); verify("2525.2840576171875", 2525.2840576171875bf16); verify("1370.9265747070312", 1370.9265747070312bf16); verify("936.3702087402344", 936.3702087402344bf16); verify("411.88682556152344", 411.88682556152344bf16); verify("206.50310516357422", 206.50310516357422bf16); verify("124.16878890991211", 124.16878890991211bf16); verify("50.811574935913086", 50.811574935913086bf16); verify("17.486443519592285", 17.486443519592285bf16); verify("13.91745138168335", 13.91745138168335bf16); verify("7.5464513301849365", 0x1.e2f90ep+2bf16); verify("2.687217116355896", 2.687217116355896bf16); verify("1.1877630352973938", 0x1.30113ep+0bf16); verify("0.7622503340244293", 0.7622503340244293bf16); verify("0.30531780421733856", 0x1.38a53ap-2bf16); verify("0.21791061013936996", 0x1.be47eap-3bf16); verify("0.09289376810193062", 0x1.7c7e2ep-4bf16); verify("0.03706067614257336", 0.03706067614257336bf16); verify("0.028068351559340954", 0.028068351559340954bf16); verify("0.012114629615098238", 0x1.8cf8e2p-7bf16); verify("0.004221370676532388", 0x1.14a6dap-8bf16); verify("0.002153817447833717", 0.002153817447833717bf16); verify("0.0015924838953651488", 0x1.a175cap-10bf16); verify("0.0008602388261351734", 0.0008602388261351734bf16); verify("0.00036393293703440577", 0x1.7d9c82p-12bf16); verify("0.00013746770127909258", 0.00013746770127909258bf16); verify("16407.9462890625", 16407.9462890625bf16); // verify("1.1754947011469036e-38", 0x1.000006p-126bf16); // verify("7.0064923216240854e-46", 0x1p-149bf16); // verify("8388614.5", 8388614.5bf16); verify("0e9999999999999999999999999999", 0.bf16); // verify( // "4.7019774032891500318749461488889827112746622270883500860350068251e-38", // 4.7019774032891500318749461488889827112746622270883500860350068251e-38bf16); verify( "3." "141592653589793238462643383279502884197169399375105820974944592307816406" "2862089986280348253421170679", 3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679bf16); // verify( // "2.3509887016445750159374730744444913556373311135441750430175034126e-38", // 2.3509887016445750159374730744444913556373311135441750430175034126e-38bf16); verify("1", 1.bf16); verify("7.0060e-46", 0.bf16, std::errc::result_out_of_range); verify("3.388e+38", 0x1.fep+127bf16); verify("3.389e+38", 0x1.fep+127bf16); verify("3.390e+38", 0x1.fep+127bf16); // verify( // "0." // "000000000000000000000000000000000000011754943508222875079687365372222456" // "778186655567720875215087517062784172594547271728515625", // 0.000000000000000000000000000000000000011754943508222875079687365372222456778186655567720875215087517062784172594547271728515625bf16); // verify( // "0." // "000000000000000000000000000000000000000000001401298464324817070923729583" // "289916131280261941876515771757068283889791082685860601486638188362121582" // "03125", // 0.00000000000000000000000000000000000000000000140129846432481707092372958328991613128026194187651577175706828388979108268586060148663818836212158203125bf16); // verify( // "0." // "000000000000000000000000000000000000023509885615147285834557659820715330" // "266457179855179808553659262368500061299303460771170648513361811637878417" // "96875", // 0.00000000000000000000000000000000000002350988561514728583455765982071533026645717985517980855365926236850006129930346077117064851336181163787841796875bf16); // verify( // "0." // "000000000000000000000000000000000000011754942106924410754870294448492873" // "488270524287458933338571745305715888704756189042655023513361811637878417" // "96875", // 0.00000000000000000000000000000000000001175494210692441075487029444849287348827052428745893333857174530571588870475618904265502351336181163787841796875bf16); } #endif fast_float-8.0.0/tests/bloat_analysis/000077500000000000000000000000001475170546600200265ustar00rootroot00000000000000fast_float-8.0.0/tests/bloat_analysis/CMakeLists.txt000066400000000000000000000003701475170546600225660ustar00rootroot00000000000000add_executable(bloaty main.cpp a1.cpp a2.cpp a3.cpp a4.cpp a4.cpp a5.cpp a6.cpp a7.cpp a8.cpp a9.cpp a10.cpp) target_link_libraries(bloaty PUBLIC fast_float) add_executable(bloatyref main_ref.cpp) target_link_libraries(bloatyref PUBLIC fast_float)fast_float-8.0.0/tests/bloat_analysis/a1.cpp000066400000000000000000000004021475170546600210270ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get1(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 1; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a10.cpp000066400000000000000000000004041475170546600211110ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get10(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 10; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a2.cpp000066400000000000000000000004021475170546600210300ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get2(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 2; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a3.cpp000066400000000000000000000004021475170546600210310ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get3(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 3; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a4.cpp000066400000000000000000000004021475170546600210320ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get4(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 4; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a5.cpp000066400000000000000000000004021475170546600210330ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get5(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 5; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a6.cpp000066400000000000000000000004021475170546600210340ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get6(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 6; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a7.cpp000066400000000000000000000004021475170546600210350ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get7(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 7; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a8.cpp000066400000000000000000000004021475170546600210360ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get8(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 8; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/a9.cpp000066400000000000000000000004021475170546600210370ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get9(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 9; } return result_value; }fast_float-8.0.0/tests/bloat_analysis/main.cpp000066400000000000000000000010771475170546600214630ustar00rootroot00000000000000 double get1(char const *input); double get2(char const *input); double get3(char const *input); double get4(char const *input); double get5(char const *input); double get6(char const *input); double get7(char const *input); double get8(char const *input); double get9(char const *input); double get10(char const *input); int main(int arg, char **argv) { double x = get1(argv[0]) + get2(argv[0]) + get3(argv[0]) + get4(argv[0]) + get5(argv[0]) + get6(argv[0]) + get7(argv[0]) + get8(argv[0]) + get9(argv[0]) + get10(argv[0]); return int(x); }fast_float-8.0.0/tests/bloat_analysis/main_ref.cpp000066400000000000000000000005221475170546600223110ustar00rootroot00000000000000#include "fast_float/fast_float.h" double get(char const *input) { double result_value; auto result = fast_float::from_chars(input, input + strlen(input), result_value); if (result.ec != std::errc()) { return 10; } return result_value; } int main(int arg, char **argv) { double x = get(argv[0]); return int(x); }fast_float-8.0.0/tests/build_tests/000077500000000000000000000000001475170546600173435ustar00rootroot00000000000000fast_float-8.0.0/tests/build_tests/CMakeLists.txt000066400000000000000000000000311475170546600220750ustar00rootroot00000000000000add_subdirectory(issue72)fast_float-8.0.0/tests/build_tests/issue72/000077500000000000000000000000001475170546600206445ustar00rootroot00000000000000fast_float-8.0.0/tests/build_tests/issue72/CMakeLists.txt000066400000000000000000000001311475170546600233770ustar00rootroot00000000000000add_executable(issue72 main.cpp foo.cpp) target_link_libraries(issue72 PUBLIC fast_float)fast_float-8.0.0/tests/build_tests/issue72/foo.cpp000066400000000000000000000000401475170546600221250ustar00rootroot00000000000000#include "test.h" void foo() {}fast_float-8.0.0/tests/build_tests/issue72/main.cpp000066400000000000000000000000531475170546600222720ustar00rootroot00000000000000#include "test.h" int main() { return 0; }fast_float-8.0.0/tests/build_tests/issue72/test.h000066400000000000000000000000571475170546600217760ustar00rootroot00000000000000#pragma once #include "fast_float/fast_float.h"fast_float-8.0.0/tests/example_comma_test.cpp000066400000000000000000000011011475170546600213650ustar00rootroot00000000000000 #include "fast_float/fast_float.h" #include #include #include int main() { std::string const input = "3,1416 xyz "; double result; fast_float::parse_options options{fast_float::chars_format::general, ','}; auto answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); if ((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/example_test.cpp000066400000000000000000000077641475170546600202360ustar00rootroot00000000000000 #include "fast_float/fast_float.h" #include #include #include bool many() { std::string const input = "234532.3426362,7869234.9823,324562.645"; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); if (answer.ec != std::errc()) { return false; } if (result != 234532.3426362) { return false; } if (answer.ptr[0] != ',') { return false; } answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); if (answer.ec != std::errc()) { return false; } if (result != 7869234.9823) { return false; } if (answer.ptr[0] != ',') { return false; } answer = fast_float::from_chars(answer.ptr + 1, input.data() + input.size(), result); if (answer.ec != std::errc()) { return false; } if (result != 324562.645) { return false; } return true; } void many_loop() { std::string const input = "234532.3426362,7869234.9823,324562.645"; double result; char const *pointer = input.data(); char const *end_pointer = input.data() + input.size(); while (pointer < end_pointer) { auto answer = fast_float::from_chars(pointer, end_pointer, result); if (answer.ec != std::errc()) { std::cerr << "error while parsing" << std::endl; break; } std::cout << "parsed: " << result << std::endl; pointer = answer.ptr; if ((answer.ptr < end_pointer) && (*pointer == ',')) { pointer++; } } } #if FASTFLOAT_IS_CONSTEXPR // consteval forces compile-time evaluation of the function in C++20. consteval double parse(std::string_view input) { double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); if (answer.ec != std::errc()) { return -1.0; } return result; } // This function should compile to a function which // merely returns 3.1415. constexpr double constexptest() { return parse("3.1415 input"); } #endif bool small() { double result = -1; std::string str = "3e-1000"; auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); if (r.ec != std::errc::result_out_of_range) { return false; } if (r.ptr != str.data() + 7) { return false; } if (result != 0) { return false; } printf("small values go to zero\n"); return true; } bool large() { double result = -1; std::string str = "3e1000"; auto r = fast_float::from_chars(str.data(), str.data() + str.size(), result); if (r.ec != std::errc::result_out_of_range) { return false; } if (r.ptr != str.data() + 6) { return false; } if (result != std::numeric_limits::infinity()) { return false; } printf("large values go to infinity\n"); return true; } int main() { std::string input = "3.1416 xyz "; double result; auto answer = fast_float::from_chars(input.data(), input.data() + input.size(), result); if ((answer.ec != std::errc()) || ((result != 3.1416))) { std::cerr << "parsing failure\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; #ifdef __STDCPP_FLOAT16_T__ printf("16-bit float\n"); // Parse as 16-bit float std::float16_t parsed_16{}; input = "10000e-1452"; auto fast_float_r16 = fast_float::from_chars( input.data(), input.data() + input.size(), parsed_16); if (fast_float_r16.ec != std::errc() && fast_float_r16.ec != std::errc::result_out_of_range) { std::cerr << "16-bit fast_float parsing failure for: " + input + "\n"; return false; } std::cout << "parsed the 16-bit value " << float(parsed_16) << std::endl; #endif if (!small()) { printf("Bug\n"); return EXIT_FAILURE; } if (!large()) { printf("Bug\n"); return EXIT_FAILURE; } if (!many()) { printf("Bug\n"); return EXIT_FAILURE; } many_loop(); #if FASTFLOAT_IS_CONSTEXPR if constexpr (constexptest() != 3.1415) { return EXIT_FAILURE; } #endif return EXIT_SUCCESS; } fast_float-8.0.0/tests/exhaustive32.cpp000066400000000000000000000040411475170546600200570ustar00rootroot00000000000000 #include "fast_float/fast_float.h" #include #include #include #include #include #include #include template char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 64, "%.*e", std::numeric_limits::max_digits10 - 1, d); return buffer + written; } void allvalues() { char buffer[64]; for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { float v; if ((w % 1048576) == 0) { std::cout << "."; std::cout.flush(); } uint32_t word = uint32_t(w); memcpy(&v, &word, sizeof(v)); { char const *string_end = to_string(v, buffer); float result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); // Starting with version 4.0 for fast_float, we return result_out_of_range // if the value is either too small (too close to zero) or too large // (effectively infinity). So std::errc::result_out_of_range is normal for // well-formed input strings. if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; abort(); } if (std::isnan(v)) { if (!std::isnan(result_value)) { std::cerr << "not nan" << buffer << std::endl; abort(); } } else if (copysign(1, result_value) != copysign(1, v)) { std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v << std::endl; abort(); } else if (result_value != v) { std::cerr << "no match ? " << buffer << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; abort(); } } } std::cout << std::endl; } int main() { allvalues(); std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/exhaustive32_64.cpp000066400000000000000000000040631475170546600203740ustar00rootroot00000000000000 #include "fast_float/fast_float.h" #include #include #include #include #include #include #include #include template char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 64, "%.*e", std::numeric_limits::max_digits10 - 1, d); return buffer + written; } bool basic_test_64bit(std::string vals, double val) { double result_value; auto result = fast_float::from_chars(vals.data(), vals.data() + vals.size(), result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << " I could not parse " << vals << std::endl; return false; } if (std::isnan(val)) { if (!std::isnan(result_value)) { std::cerr << vals << std::endl; std::cerr << "not nan" << result_value << std::endl; return false; } } else if (copysign(1, result_value) != copysign(1, val)) { std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val << std::endl; return false; } else if (result_value != val) { std::cerr << vals << std::endl; std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val << std::endl; std::cerr << std::dec; std::cerr << "string: " << vals << std::endl; return false; } return true; } void all_32bit_values() { char buffer[64]; for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { float v32; if ((w % 1048576) == 0) { std::cout << "."; std::cout.flush(); } uint32_t word = uint32_t(w); memcpy(&v32, &word, sizeof(v32)); double v = v32; { char const *string_end = to_string(v, buffer); std::string s(buffer, size_t(string_end - buffer)); if (!basic_test_64bit(s, v)) { return; } } } std::cout << std::endl; } int main() { all_32bit_values(); std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/exhaustive32_midpoint.cpp000066400000000000000000000132771475170546600217750ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include #include #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. #include #include // workaround for CYGWIN double cygwin_strtod_l(char const *start, char **end) { double d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } float cygwin_strtof_l(char const *start, char **end) { float d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } #endif template char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 64, "%.*e", std::numeric_limits::max_digits10 - 1, d); return buffer + written; } void strtof_from_string(char const *st, float &d) { char *pr = (char *)st; #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtof_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtof_l(st, &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtof_l(st, &pr, c_locale); #endif if (pr == st) { throw std::runtime_error("bug in strtod_from_string"); } } bool allvalues() { char buffer[64]; for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { float v; if ((w % 1048576) == 0) { std::cout << "."; std::cout.flush(); } uint32_t word = uint32_t(w); memcpy(&v, &word, sizeof(v)); if (std::isfinite(v)) { float nextf = std::nextafterf(v, INFINITY); if (copysign(1, v) != copysign(1, nextf)) { continue; } if (!std::isfinite(nextf)) { continue; } double v1{v}; assert(float(v1) == v); double v2{nextf}; assert(float(v2) == nextf); double midv{v1 + (v2 - v1) / 2}; float expected_midv = float(midv); char const *string_end = to_string(midv, buffer); float str_answer; strtof_from_string(buffer, str_answer); float result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); // Starting with version 4.0 for fast_float, we return result_out_of_range // if the value is either too small (too close to zero) or too large // (effectively infinity). So std::errc::result_out_of_range is normal for // well-formed input strings. if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; return false; } if (std::isnan(v)) { if (!std::isnan(result_value)) { std::cerr << "not nan" << buffer << std::endl; std::cerr << "v " << std::hexfloat << v << std::endl; std::cerr << "v2 " << std::hexfloat << v2 << std::endl; std::cerr << "midv " << std::hexfloat << midv << std::endl; std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl; return false; } } else if (copysign(1, result_value) != copysign(1, v)) { std::cerr << buffer << std::endl; std::cerr << "v " << std::hexfloat << v << std::endl; std::cerr << "v2 " << std::hexfloat << v2 << std::endl; std::cerr << "midv " << std::hexfloat << midv << std::endl; std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl; std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v << std::endl; return false; } else if (result_value != str_answer) { std::cerr << "no match ? " << buffer << std::endl; std::cerr << "v " << std::hexfloat << v << std::endl; std::cerr << "v2 " << std::hexfloat << v2 << std::endl; std::cerr << "midv " << std::hexfloat << midv << std::endl; std::cerr << "expected_midv " << std::hexfloat << expected_midv << std::endl; std::cout << "started with " << std::hexfloat << midv << std::endl; std::cout << "round down to " << std::hexfloat << str_answer << std::endl; std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; return false; } } } std::cout << std::endl; return true; } inline void Assert(bool Assertion) { #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; } #else if (!Assertion) { throw std::runtime_error("bug"); } #endif } int main() { #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) std::cout << "Warning: msys/cygwin or solaris detected. This particular test " "is likely to generate false failures due to our reliance on " "the underlying runtime library as a gold standard." << std::endl; #endif Assert(allvalues()); std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/fast_int.cpp000066400000000000000000000737421475170546600173520ustar00rootroot00000000000000#ifndef __cplusplus #error fastfloat requires a C++ compiler #endif // We want to enable the tests only for C++17 and above. #ifndef FASTFLOAT_CPLUSPLUS #if defined(_MSVC_LANG) && !defined(__clang__) #define FASTFLOAT_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG) #else #define FASTFLOAT_CPLUSPLUS __cplusplus #endif #endif #if FASTFLOAT_CPLUSPLUS >= 201703L #include #include #include #include #include #include "fast_float/fast_float.h" #include /* all tests conducted are to check fast_float::from_chars functionality with int and unsigned test cases include: int basic tests - numbers only, numbers with strings behind, decimals, negative numbers unsigned basic tests - numbers only, numbers with strings behind, decimals int invalid tests - strings only, strings with numbers behind, space in front of number, plus sign in front of number unsigned invalid tests - strings only, strings with numbers behind, space in front of number, plus/minus sign in front of number int out of range tests - numbers exceeding int bit size for 8, 16, 32, and 64 bits unsigned out of range tests - numbers exceeding unsigned bit size 8, 16, 32, and 64 bits int pointer tests - points to first character that is not recognized as int unsigned pointer tests - points to first character that is not recognized as unsigned int/unsigned base 2 tests - numbers are converted from binary to decimal octal tests - numbers are converted from octal to decimal hex tests - numbers are converted from hex to decimal (Note: 0x and 0X are considered invalid) invalid base tests - any base not within 2-36 is invalid out of range base tests - numbers exceeding int/unsigned bit size after converted from base (Note: only 64 bit int and unsigned are tested) within range base tests - max/min numbers are still within int/unsigned bit size after converted from base (Note: only 64 bit int and unsigned are tested) leading zeros tests - ignores all zeroes in front of valid number after converted from base */ int main() { // int basic test std::vector const int_basic_test_expected{0, 10, -40, 1001, 9}; std::vector const int_basic_test{"0", "10 ", "-40", "1001 with text", "9.999"}; for (std::size_t i = 0; i < int_basic_test.size(); ++i) { auto const f = int_basic_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { if (answer.ec == std::errc::invalid_argument) { std::cerr << "could not convert to int for input: \"" << f << "\" because of invalid argument" << std::endl; } else if (answer.ec == std::errc::result_out_of_range) { std::cerr << "could not convert to int for input: \"" << f << "\" because it's out of range" << std::endl; } else { std::cerr << "could not convert to int for input: \"" << f << "\" because of an unknown error" << std::endl; } return EXIT_FAILURE; } else if (result != int_basic_test_expected[i]) { std::cerr << "result \"" << f << "\" did not match with expected int: " << int_basic_test_expected[i] << std::endl; return EXIT_FAILURE; } } // unsigned basic test std::vector const unsigned_basic_test_expected{0, 10, 1001, 9}; std::vector const unsigned_basic_test{ "0", "10 ", "1001 with text", "9.999"}; for (std::size_t i = 0; i < unsigned_basic_test.size(); ++i) { auto const &f = unsigned_basic_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { std::cerr << "could not convert to unsigned for input: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } else if (result != unsigned_basic_test_expected[i]) { std::cerr << "result \"" << f << "\" did not match with expected unsigned: " << unsigned_basic_test_expected[i] << std::endl; return EXIT_FAILURE; } } // int invalid error test std::vector const int_invalid_argument_test{ "text", "text with 1002", "+50", " 50"}; for (std::size_t i = 0; i < int_invalid_argument_test.size(); ++i) { auto const &f = int_invalid_argument_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::invalid_argument) { std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // unsigned invalid error test std::vector const unsigned_invalid_argument_test{ "text", "text with 1002", "+50", " 50", "-50"}; for (std::size_t i = 0; i < unsigned_invalid_argument_test.size(); ++i) { auto const &f = unsigned_invalid_argument_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::invalid_argument) { std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // int out of range error test #1 (8 bit) std::vector const int_out_of_range_test_1{ "2000000000000000000000", "128", "-129"}; for (std::size_t i = 0; i < int_out_of_range_test_1.size(); ++i) { auto const &f = int_out_of_range_test_1[i]; int8_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // int out of range error test #2 (16 bit) std::vector const int_out_of_range_test_2{ "2000000000000000000000", "32768", "-32769"}; for (std::size_t i = 0; i < int_out_of_range_test_2.size(); ++i) { auto const &f = int_out_of_range_test_2[i]; int16_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // int out of range error test #3 (32 bit) std::vector const int_out_of_range_test_3{ "2000000000000000000000", "2147483648", "-2147483649"}; for (std::size_t i = 0; i < int_out_of_range_test_3.size(); ++i) { auto const &f = int_out_of_range_test_3[i]; int32_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // int out of range error test #4 (64 bit) std::vector const int_out_of_range_test_4{ "2000000000000000000000", "9223372036854775808", "-9223372036854775809"}; for (std::size_t i = 0; i < int_out_of_range_test_4.size(); ++i) { auto const &f = int_out_of_range_test_4[i]; int64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #1 (8 bit) std::vector const unsigned_out_of_range_test_1{ "2000000000000000000000", "256"}; 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]; uint8_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #2 (16 bit) std::vector const unsigned_out_of_range_test_2{ "2000000000000000000000", "65536"}; 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]; uint16_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #3 (32 bit) std::vector const unsigned_out_of_range_test_3{ "2000000000000000000000", "4294967296"}; 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]; uint32_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // unsigned out of range error test #4 (64 bit) std::vector const unsigned_out_of_range_test_4{ "2000000000000000000000", "18446744073709551616"}; 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]; uint64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // int pointer test #1 (only numbers) std::vector const int_pointer_test_1{"0", "010", "-40"}; for (std::size_t i = 0; i < int_pointer_test_1.size(); ++i) { auto const &f = int_pointer_test_1[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } else if (strcmp(answer.ptr, "") != 0) { std::cerr << "ptr of result " << f << " did not match with expected ptr: \"\"" << std::endl; return EXIT_FAILURE; } } // int pointer test #2 (string behind numbers) std::string_view const int_pointer_test_2 = "1001 with text"; auto const &f2 = int_pointer_test_2; int result2; auto answer2 = fast_float::from_chars(f2.data(), f2.data() + f2.size(), result2); if (strcmp(answer2.ptr, " with text") != 0) { std::cerr << "ptr of result " << f2 << " did not match with expected ptr: \"with text\"" << std::endl; return EXIT_FAILURE; } // int pointer test #3 (string with newline behind numbers) std::string_view const int_pointer_test_3 = "1001 with text\n"; auto const &f3 = int_pointer_test_3; int result3; auto answer3 = fast_float::from_chars(f3.data(), f3.data() + f3.size(), result3); if (strcmp(answer3.ptr, " with text\n") != 0) { std::cerr << "ptr of result " << f3 << " did not match with expected ptr: with text" << std::endl; return EXIT_FAILURE; } // int pointer test #4 (float) std::string_view const int_pointer_test_4 = "9.999"; auto const &f4 = int_pointer_test_4; int result4; auto answer4 = fast_float::from_chars(f4.data(), f4.data() + f4.size(), result4); if (strcmp(answer4.ptr, ".999") != 0) { std::cerr << "ptr of result " << f4 << " did not match with expected ptr: .999" << std::endl; return EXIT_FAILURE; } // int pointer test #5 (invalid int) std::string_view const int_pointer_test_5 = "+50"; auto const &f5 = int_pointer_test_5; int result5; auto answer5 = fast_float::from_chars(f5.data(), f5.data() + f5.size(), result5); if (strcmp(answer5.ptr, "+50") != 0) { std::cerr << "ptr of result " << f5 << " did not match with expected ptr: +50" << std::endl; return EXIT_FAILURE; } // unsigned pointer test #2 (string behind numbers) std::string_view const unsigned_pointer_test_1 = "1001 with text"; auto const &f6 = unsigned_pointer_test_1; unsigned result6; auto answer6 = fast_float::from_chars(f6.data(), f6.data() + f6.size(), result6); if (strcmp(answer6.ptr, " with text") != 0) { std::cerr << "ptr of result " << f6 << " did not match with expected ptr: with text" << std::endl; return EXIT_FAILURE; } // unsigned pointer test #2 (invalid unsigned) std::string_view const unsigned_pointer_test_2 = "-50"; auto const &f7 = unsigned_pointer_test_2; unsigned result7; auto answer7 = fast_float::from_chars(f7.data(), f7.data() + f7.size(), result7); if (strcmp(answer7.ptr, "-50") != 0) { std::cerr << "ptr of result " << f7 << " did not match with expected ptr: -50" << std::endl; return EXIT_FAILURE; } // int base 2 test std::vector const int_base_2_test_expected{0, 1, 4, 2, -1}; std::vector const int_base_2_test{"0", "1", "100", "010", "-1"}; for (std::size_t i = 0; i < int_base_2_test.size(); ++i) { auto const f = int_base_2_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc()) { std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } else if (result != int_base_2_test_expected[i]) { std::cerr << "result " << f << " did not match with expected int: " << int_base_2_test_expected[i] << std::endl; return EXIT_FAILURE; } } // unsigned base 2 test std::vector const unsigned_base_2_test_expected{0, 1, 4, 2}; std::vector const unsigned_base_2_test{"0", "1", "100", "010"}; for (std::size_t i = 0; i < unsigned_base_2_test.size(); ++i) { auto const &f = unsigned_base_2_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc()) { std::cerr << "could not convert to unsigned for input: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } else if (result != unsigned_base_2_test_expected[i]) { std::cerr << "result " << f << " did not match with expected unsigned: " << unsigned_base_2_test_expected[i] << std::endl; return EXIT_FAILURE; } } // int invalid error base 2 test std::vector const int_invalid_argument_base_2_test{"2", "A", "-2"}; for (std::size_t i = 0; i < int_invalid_argument_base_2_test.size(); ++i) { auto const &f = int_invalid_argument_base_2_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc::invalid_argument) { std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // unsigned invalid error base 2 test std::vector const unsigned_invalid_argument_base_2_test{ "2", "A", "-1", "-2"}; for (std::size_t i = 0; i < unsigned_invalid_argument_base_2_test.size(); ++i) { auto const &f = unsigned_invalid_argument_base_2_test[i]; unsigned result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 2); if (answer.ec != std::errc::invalid_argument) { std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // octal test std::vector const base_octal_test_expected{0, 1, 7, 8, 9}; std::vector const base_octal_test{"0", "1", "07", "010", "0011"}; for (std::size_t i = 0; i < base_octal_test.size(); ++i) { auto const &f = base_octal_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 8); if (answer.ec != std::errc()) { std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } else if (result != base_octal_test_expected[i]) { std::cerr << "result " << f << " did not match with expected int: " << base_octal_test_expected[i] << std::endl; return EXIT_FAILURE; } } // hex test std::vector const base_hex_test_expected{0, 1, 15, 31, 0, 16}; std::vector const base_hex_test{"0", "1", "F", "01f", "0x11", "10X11"}; for (std::size_t i = 0; i < base_hex_test.size(); ++i) { auto const &f = base_hex_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 16); if (answer.ec != std::errc()) { std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } else if (result != base_hex_test_expected[i]) { std::cerr << "result " << f << " did not match with expected int: " << base_hex_test_expected[i] << std::endl; return EXIT_FAILURE; } } // invalid base test #1 (-1) std::vector const invalid_base_test_1{"0", "1", "-1", "F", "10Z"}; for (std::size_t i = 0; i < invalid_base_test_1.size(); ++i) { auto const &f = invalid_base_test_1[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, -1); if (answer.ec != std::errc::invalid_argument) { std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // invalid base test #2 (37) std::vector const invalid_base_test_2{"0", "1", "F", "Z", "10Z"}; for (std::size_t i = 0; i < invalid_base_test_2.size(); ++i) { auto const &f = invalid_base_test_2[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, 37); if (answer.ec != std::errc::invalid_argument) { std::cerr << "expected error should be 'invalid_argument' for: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // int out of range error base test (64 bit) std::vector const int_out_of_range_base_test{ "1000000000000000000000000000000000000000000000000000000000000000", "-1000000000000000000000000000000000000000000000000000000000000001", "2021110011022210012102010021220101220222", "-2021110011022210012102010021220101221000", "20000000000000000000000000000000", "-20000000000000000000000000000001", "1104332401304422434310311213", "-1104332401304422434310311214", "1540241003031030222122212", "-1540241003031030222122213", "22341010611245052052301", "-22341010611245052052302", "1000000000000000000000", "-1000000000000000000001", "67404283172107811828", "-67404283172107811830", "9223372036854775808", "-9223372036854775809", "1728002635214590698", "-1728002635214590699", "41A792678515120368", "-41A792678515120369", "10B269549075433C38", "-10B269549075433C39", "4340724C6C71DC7A8", "-4340724C6C71DC7A9", "160E2AD3246366808", "-160E2AD3246366809", "8000000000000000", "-8000000000000001", "33D3D8307B214009", "-33D3D8307B21400A", "16AGH595DF825FA8", "-16AGH595DF825FA9", "BA643DCI0FFEEHI", "-BA643DCI0FFEEI0", "5CBFJIA3FH26JA8", "-5CBFJIA3FH26JA9", "2HEICIIIE82DH98", "-2HEICIIIE82DH99", "1ADAIBB21DCKFA8", "-1ADAIBB21DCKFA9", "I6K448CF4192C3", "-I6K448CF4192C4", "ACD772JNC9L0L8", "-ACD772JNC9L0L9", "64IE1FOCNN5G78", "-64IE1FOCNN5G79", "3IGOECJBMCA688", "-3IGOECJBMCA689", "27C48L5B37OAOQ", "-27C48L5B37OAP0", "1BK39F3AH3DMQ8", "-1BK39F3AH3DMQ9", "Q1SE8F0M04ISC", "-Q1SE8F0M04ISD", "HAJPPBC1FC208", "-HAJPPBC1FC209", "BM03I95HIA438", "-BM03I95HIA439", "8000000000000", "-8000000000001", "5HG4CK9JD4U38", "-5HG4CK9JD4U39", "3TDTK1V8J6TPQ", "-3TDTK1V8J6TPR", "2PIJMIKEXRXP8", "-2PIJMIKEXRXP9", "1Y2P0IJ32E8E8", "-1Y2P0IJ32E8E9"}; 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]; int64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } } // unsigned out of range error base test (64 bit) std::vector const unsigned_out_of_range_base_test{ "10000000000000000000000000000000000000000000000000000000000000000", "11112220022122120101211020120210210211221", "100000000000000000000000000000000", "2214220303114400424121122431", "3520522010102100444244424", "45012021522523134134602", "2000000000000000000000", "145808576354216723757", "18446744073709551616", "335500516A429071285", "839365134A2A240714", "219505A9511A867B73", "8681049ADB03DB172", "2C1D56B648C6CD111", "10000000000000000", "67979G60F5428011", "2D3FGB0B9CG4BD2G", "141C8786H1CCAAGH", "B53BJH07BE4DJ0G", "5E8G4GGG7G56DIG", "2L4LF104353J8KG", "1DDH88H2782I516", "L12EE5FN0JI1IG", "C9C336O0MLB7EG", "7B7N2PCNIOKCGG", "4EO8HFAM6FLLMP", "2NC6J26L66RHOG", "1N3RSH11F098RO", "14L9LKMO30O40G", "ND075IB45K86G", "G000000000000", "B1W8P7J5Q9R6G", "7ORP63SH4DPHI", "5G24A25TWKWFG", "3W5E11264SGSG"}; int base_unsigned = 2; 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]; uint64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned); if (answer.ec != std::errc::result_out_of_range) { std::cerr << "expected error for should be 'result_out_of_range': \"" << f << "\"" << std::endl; return EXIT_FAILURE; } ++base_unsigned; } // just within range base test (64 bit) std::vector const int_within_range_base_test{ "111111111111111111111111111111111111111111111111111111111111111", "-1000000000000000000000000000000000000000000000000000000000000000", "2021110011022210012102010021220101220221", "-2021110011022210012102010021220101220222", "13333333333333333333333333333333", "-20000000000000000000000000000000", "1104332401304422434310311212", "-1104332401304422434310311213", "1540241003031030222122211", "-1540241003031030222122212", "22341010611245052052300", "-22341010611245052052301", "777777777777777777777", "-1000000000000000000000", "67404283172107811827", "-67404283172107811828", "9223372036854775807", "-9223372036854775808", "1728002635214590697", "-1728002635214590698", "41A792678515120367", "-41A792678515120368", "10B269549075433C37", "-10B269549075433C38", "4340724C6C71DC7A7", "-4340724C6C71DC7A8", "160E2AD3246366807", "-160E2AD3246366808", "7FFFFFFFFFFFFFFF", "-8000000000000000", "33D3D8307B214008", "-33D3D8307B214009", "16AGH595DF825FA7", "-16AGH595DF825FA8", "BA643DCI0FFEEHH", "-BA643DCI0FFEEHI", "5CBFJIA3FH26JA7", "-5CBFJIA3FH26JA8", "2HEICIIIE82DH97", "-2HEICIIIE82DH98", "1ADAIBB21DCKFA7", "-1ADAIBB21DCKFA8", "I6K448CF4192C2", "-I6K448CF4192C3", "ACD772JNC9L0L7", "-ACD772JNC9L0L8", "64IE1FOCNN5G77", "-64IE1FOCNN5G78", "3IGOECJBMCA687", "-3IGOECJBMCA688", "27C48L5B37OAOP", "-27C48L5B37OAOQ", "1BK39F3AH3DMQ7", "-1BK39F3AH3DMQ8", "Q1SE8F0M04ISB", "-Q1SE8F0M04ISC", "HAJPPBC1FC207", "-HAJPPBC1FC208", "BM03I95HIA437", "-BM03I95HIA438", "7VVVVVVVVVVVV", "-8000000000000", "5HG4CK9JD4U37", "-5HG4CK9JD4U38", "3TDTK1V8J6TPP", "-3TDTK1V8J6TPQ", "2PIJMIKEXRXP7", "-2PIJMIKEXRXP8", "1Y2P0IJ32E8E7", "-1Y2P0IJ32E8E8"}; for (std::size_t i = 0; i < int_within_range_base_test.size(); ++i) { auto const &f = int_within_range_base_test[i]; int64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(2 + (i / 2))); if (answer.ec != std::errc()) { std::cerr << "converting " << f << " to int failed (most likely out of range)" << std::endl; return EXIT_FAILURE; } } // unsigned within range base test (64 bit) std::vector const unsigned_within_range_base_test{ "1111111111111111111111111111111111111111111111111111111111111111", "11112220022122120101211020120210210211220", "33333333333333333333333333333333", "2214220303114400424121122430", "3520522010102100444244423", "45012021522523134134601", "1777777777777777777777", "145808576354216723756", "18446744073709551615", "335500516A429071284", "839365134A2A240713", "219505A9511A867B72", "8681049ADB03DB171", "2C1D56B648C6CD110", "FFFFFFFFFFFFFFFF", "67979G60F5428010", "2D3FGB0B9CG4BD2F", "141C8786H1CCAAGG", "B53BJH07BE4DJ0F", "5E8G4GGG7G56DIF", "2L4LF104353J8KF", "1DDH88H2782I515", "L12EE5FN0JI1IF", "C9C336O0MLB7EF", "7B7N2PCNIOKCGF", "4EO8HFAM6FLLMO", "2NC6J26L66RHOF", "1N3RSH11F098RN", "14L9LKMO30O40F", "ND075IB45K86F", "FVVVVVVVVVVVV", "B1W8P7J5Q9R6F", "7ORP63SH4DPHH", "5G24A25TWKWFF", "3W5E11264SGSF"}; int base_unsigned2 = 2; for (std::size_t i = 0; i < unsigned_within_range_base_test.size(); ++i) { auto const &f = unsigned_within_range_base_test[i]; uint64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, base_unsigned2); if (answer.ec != std::errc()) { std::cerr << "converting " << f << " to unsigned failed (most likely out of range)" << std::endl; return EXIT_FAILURE; } ++base_unsigned2; } // int leading zeros test std::vector const int_leading_zeros_test{ "000000000000000000000000000000000000000000000000000000000000000000000011" "11110111", "000000000000000000000000000000000000000000000000001101121", "000000000000000000000000000000000000000033313", "00000000000000000000000000000013030", "0000000000000000000000000000004411", "0000000000000000000000000000002650", "0000000000000000000000000000001767", "0000000000000000000000000000001347", "0000000000000000000000000000001015", "00000000000000000000843", "00000000000000000000707", "00000000000000000000601", "00000000000000000000527", "0000000000000000000047A", "000000000000000000003F7", "0000000000000000000038C", "00000000000000000000327", "000000000000000000002F8", "000000000000000000002AF", "00000000000000000000267", "00000000000000000000223", "000000000000000000001L3", "000000000000000000001I7", "000000000000000000001FF", "000000000000000000001D1", "000000000000000000001AG", "00000000000000000000187", "00000000000000000000160", "0000000000000000000013P", "0000000000000000000011N", "00000000000000000000VN", "00000000000000000000UP", "00000000000000000000TT", "00000000000000000000T0", "00000000000000000000S7"}; for (std::size_t i = 0; i < int_leading_zeros_test.size(); ++i) { auto const &f = int_leading_zeros_test[i]; int result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result, int(i + 2)); if (answer.ec != std::errc()) { std::cerr << "could not convert to int for input: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } else if (result != 1015) { std::cerr << "result " << f << " did not match with expected int: " << 1015 << std::endl; return EXIT_FAILURE; } } // issue 235 { std::vector s = {'0'}; s.shrink_to_fit(); int foo; auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), foo); if (answer.ec != std::errc()) { std::cerr << "could not convert to int for input: '0'" << std::endl; return EXIT_FAILURE; } else if (foo != 0) { std::cerr << "expected zero: " << foo << std::endl; return EXIT_FAILURE; } } return EXIT_SUCCESS; } #else #include #include int main() { std::cerr << "The test requires C++17." << std::endl; return EXIT_SUCCESS; } #endiffast_float-8.0.0/tests/fixedwidthtest.cpp000066400000000000000000000050111475170546600205620ustar00rootroot00000000000000#include #include #include #include #include "fast_float/fast_float.h" #include #if __cplusplus >= 202300L #include #endif int main() { // Write some testcases for the parsing of floating point numbers in the // float32_t type. We use the from_chars function defined in this library. #ifdef __STDCPP_FLOAT32_T__ std::vector const float32_test_expected{ 123.456f, -78.9f, 0.0001f, 3.40282e+038f}; std::vector const float32_test{"123.456", "-78.9", "0.0001", "3.40282e+038"}; std::cout << "runing float32 test" << std::endl; for (std::size_t i = 0; i < float32_test.size(); ++i) { auto const &f = float32_test[i]; std::float32_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { std::cerr << "Failed to parse: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } if (result != float32_test_expected[i]) { std::cerr << "Test failed for input: \"" << f << "\" expected " << float32_test_expected[i] << " got " << result << std::endl; return EXIT_FAILURE; } } #else std::cout << "No std::float32_t type available." << std::endl; #endif #ifdef __STDCPP_FLOAT64_T__ // Test cases for std::float64_t std::vector const float64_test_expected{ 1.23e4, -5.67e-8, 1.7976931348623157e+308, -1.7976931348623157e+308}; std::vector const float64_test{"1.23e4", "-5.67e-8", "1.7976931348623157e+308", "-1.7976931348623157e+308"}; std::cout << "runing float64 test" << std::endl; for (std::size_t i = 0; i < float64_test.size(); ++i) { auto const &f = float64_test[i]; std::float64_t result; auto answer = fast_float::from_chars(f.data(), f.data() + f.size(), result); if (answer.ec != std::errc()) { std::cerr << "Failed to parse: \"" << f << "\"" << std::endl; return EXIT_FAILURE; } if (result != float64_test_expected[i]) { std::cerr << "Test failed for input: \"" << f << "\" expected " << float64_test_expected[i] << " got " << result << std::endl; return EXIT_FAILURE; } } #else std::cout << "No std::float64_t type available." << std::endl; #endif std::cout << "All tests passed successfully." << std::endl; return EXIT_SUCCESS; return 0; }fast_float-8.0.0/tests/fortran.cpp000066400000000000000000000052551475170546600172100ustar00rootroot00000000000000/* * Exercise the Fortran conversion option. */ #include #include #include #include "fast_float/fast_float.h" int main_readme() { std::string const input = "1d+4"; double result; fast_float::parse_options options{ fast_float::chars_format::fortran | fast_float::chars_format::allow_leading_plus}; auto answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); if ((answer.ec != std::errc()) || ((result != 10000))) { std::cerr << "parsing failure\n" << result << "\n"; return EXIT_FAILURE; } std::cout << "parsed the number " << result << std::endl; return EXIT_SUCCESS; } int main() { std::vector const expected{10000, 1000, 100, 10, 1, .1, .01, .001, .0001}; std::vector const fmt1{"1+4", "1+3", "1+2", "1+1", "1+0", "1-1", "1-2", "1-3", "1-4"}; std::vector const fmt2{"1d+4", "1d+3", "1d+2", "1d+1", "1d+0", "1d-1", "1d-2", "1d-3", "1d-4"}; std::vector const fmt3{"+1+4", "+1+3", "+1+2", "+1+1", "+1+0", "+1-1", "+1-2", "+1-3", "+1-4"}; fast_float::parse_options const options{ fast_float::chars_format::fortran | fast_float::chars_format::allow_leading_plus}; for (auto const &f : fmt1) { auto d{std::distance(&fmt1[0], &f)}; double result; auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(), result, options)}; if (answer.ec != std::errc() || result != expected[std::size_t(d)]) { std::cerr << "parsing failure on " << f << std::endl; return EXIT_FAILURE; } } for (auto const &f : fmt2) { auto d{std::distance(&fmt2[0], &f)}; double result; auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(), result, options)}; if (answer.ec != std::errc() || result != expected[std::size_t(d)]) { std::cerr << "parsing failure on " << f << std::endl; return EXIT_FAILURE; } } for (auto const &f : fmt3) { auto d{std::distance(&fmt3[0], &f)}; double result; auto answer{fast_float::from_chars_advanced(f.data(), f.data() + f.size(), result, options)}; if (answer.ec != std::errc() || result != expected[std::size_t(d)]) { std::cerr << "parsing failure on " << f << std::endl; return EXIT_FAILURE; } } if (main_readme() != EXIT_SUCCESS) { return EXIT_FAILURE; } return EXIT_SUCCESS; } fast_float-8.0.0/tests/installation_tests/000077500000000000000000000000001475170546600207455ustar00rootroot00000000000000fast_float-8.0.0/tests/installation_tests/find/000077500000000000000000000000001475170546600216655ustar00rootroot00000000000000fast_float-8.0.0/tests/installation_tests/find/CMakeLists.txt000066400000000000000000000016231475170546600244270ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) project(test_install VERSION 0.1.0 LANGUAGES CXX) set(FASTFLOAT_CXX_STANDARD 17 CACHE STRING "the C++ standard to use for fastfloat") set(CMAKE_CXX_STANDARD ${FASTFLOAT_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(MSVC_VERSION GREATER 1910) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -permissive-") endif() find_package(FastFloat REQUIRED) file(WRITE main.cpp " #include \"fast_float/fast_float.h\" #include int main() { std::string input = \"3.1416 xyz \"; double result; auto answer = fast_float::from_chars(input.data(), input.data()+input.size(), result); if (answer.ec != std::errc()) { std::cerr << \"parsing failure\\n\"; return EXIT_FAILURE; } std::cout << \"parsed the number \" << result << std::endl; return EXIT_SUCCESS; }") add_executable(repro main.cpp) target_link_libraries(repro PUBLIC FastFloat::fast_float) fast_float-8.0.0/tests/installation_tests/issue72_installation/000077500000000000000000000000001475170546600250275ustar00rootroot00000000000000fast_float-8.0.0/tests/installation_tests/issue72_installation/CMakeLists.txt000066400000000000000000000011231475170546600275640ustar00rootroot00000000000000cmake_minimum_required(VERSION 3.15) project(test_simdjson_install VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if(MSVC_VERSION GREATER 1910) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -permissive-") endif() find_package(FastFloat REQUIRED) file(WRITE test.h " #pragma once #include \"fast_float/fast_float.h\"") file(WRITE main.cpp " #include \"test.h\" int main() { return 0; }") file(WRITE foo.cpp " #include \"test.h\" void foo() { }") add_executable(issue72 main.cpp main.cpp) target_link_libraries(issue72 PUBLIC FastFloat::fast_float)fast_float-8.0.0/tests/json_fmt.cpp000066400000000000000000000132431475170546600173500ustar00rootroot00000000000000 #include #include #include #include "fast_float/fast_float.h" int main_readme() { std::string const input = "+.1"; // not valid double result; fast_float::parse_options options{ fast_float::chars_format::json | fast_float::chars_format::allow_leading_plus}; // should be ignored auto answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } int main_readme2() { std::string const input = "inf"; // not valid in JSON double result; fast_float::parse_options options{ fast_float::chars_format::json | fast_float::chars_format::allow_leading_plus}; // should be ignored auto answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); if (answer.ec == std::errc()) { std::cerr << "should have failed\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } int main_readme3() { std::string const input = "inf"; // not valid in JSON but we allow it with json_or_infnan double result; fast_float::parse_options options{ fast_float::chars_format::json_or_infnan | fast_float::chars_format::allow_leading_plus}; // should be ignored auto answer = fast_float::from_chars_advanced( input.data(), input.data() + input.size(), result, options); if (answer.ec != std::errc() || (!std::isinf(result))) { std::cerr << "should have parsed infinity\n"; return EXIT_FAILURE; } return EXIT_SUCCESS; } struct ExpectedResult { double value; std::string junk_chars; }; struct AcceptedValue { std::string input; ExpectedResult expected; }; struct RejectReason { fast_float::parse_error error; intptr_t location_offset; }; struct RejectedValue { std::string input; RejectReason reason; }; int main() { std::vector const accept{ {"-0.2", {-0.2, ""}}, {"0.02", {0.02, ""}}, {"0.002", {0.002, ""}}, {"1e+0000", {1., ""}}, {"0e-2", {0., ""}}, {"1e", {1., "e"}}, {"1e+", {1., "e+"}}, {"inf", {std::numeric_limits::infinity(), ""}}}; std::vector const reject{ {"-.2", {fast_float::parse_error::missing_integer_after_sign, 1}}, {"00.02", {fast_float::parse_error::leading_zeros_in_integer_part, 0}}, {"0.e+1", {fast_float::parse_error::no_digits_in_fractional_part, 2}}, {"00.e+1", {fast_float::parse_error::leading_zeros_in_integer_part, 0}}, {".25", {fast_float::parse_error::no_digits_in_integer_part, 0}}, // The following cases already start as invalid JSON, so they are // handled as trailing junk and the error is for not having digits in the // empty string before the invalid token. {"+0.25", {fast_float::parse_error::no_digits_in_integer_part, 0}}, {"inf", {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) { auto const &s = accept[i].input; auto const &expected = accept[i].expected; double result; auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, fast_float::chars_format::json_or_infnan); if (answer.ec != std::errc()) { std::cerr << "json fmt rejected valid json " << s << std::endl; return EXIT_FAILURE; } if (result != expected.value) { std::cerr << "json fmt gave wrong result " << s << " (expected " << expected.value << " got " << result << ")" << std::endl; return EXIT_FAILURE; } if (std::string(answer.ptr) != expected.junk_chars) { std::cerr << "json fmt has wrong trailing characters " << s << " (expected " << expected.junk_chars << " got " << answer.ptr << ")" << std::endl; return EXIT_FAILURE; } } for (std::size_t i = 0; i < reject.size(); ++i) { auto const &s = reject[i].input; double result; auto answer = fast_float::from_chars(s.data(), s.data() + s.size(), result, fast_float::chars_format::json); if (answer.ec == std::errc()) { std::cerr << "json fmt accepted invalid json " << s << std::endl; return EXIT_FAILURE; } } 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( f.data(), f.data() + f.size(), fast_float::parse_options( fast_float::chars_format::json | fast_float::chars_format::allow_leading_plus)); // should be ignored if (answer.valid) { std::cerr << "json parse accepted invalid json " << f << std::endl; return EXIT_FAILURE; } if (answer.error != expected_reason.error) { std::cerr << "json parse failure had invalid error reason " << f << std::endl; return EXIT_FAILURE; } intptr_t error_location = answer.lastmatch - f.data(); if (error_location != expected_reason.location_offset) { std::cerr << "json parse failure had invalid error location " << f << " (expected " << expected_reason.location_offset << " got " << error_location << ")" << std::endl; return EXIT_FAILURE; } } if (main_readme() != EXIT_SUCCESS) { return EXIT_FAILURE; } if (main_readme2() != EXIT_SUCCESS) { return EXIT_FAILURE; } #ifndef __FAST_MATH__ if (main_readme3() != EXIT_SUCCESS) { return EXIT_FAILURE; } #endif return EXIT_SUCCESS; }fast_float-8.0.0/tests/long_exhaustive32.cpp000066400000000000000000000040701475170546600211000ustar00rootroot00000000000000 #include "fast_float/fast_float.h" #include #include #include #include #include #include template char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 128, "%.*e", 64, d); return buffer + written; } void allvalues() { char buffer[128]; for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { float v; if ((w % 1048576) == 0) { std::cout << "."; std::cout.flush(); } uint32_t word = uint32_t(w); memcpy(&v, &word, sizeof(v)); { char const *string_end = to_string(v, buffer); float result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); // Starting with version 4.0 for fast_float, we return result_out_of_range // if the value is either too small (too close to zero) or too large // (effectively infinity). So std::errc::result_out_of_range is normal for // well-formed input strings. if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; abort(); } if (std::isnan(v)) { if (!std::isnan(result_value)) { std::cerr << "not nan" << buffer << std::endl; abort(); } } else if (copysign(1, result_value) != copysign(1, v)) { std::cerr << buffer << std::endl; std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v << std::endl; abort(); } else if (result_value != v) { std::cerr << "no match ? " << buffer << " got " << result_value << " expected " << v << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; abort(); } } } std::cout << std::endl; } int main() { allvalues(); std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/long_exhaustive32_64.cpp000066400000000000000000000041751475170546600214170ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include template char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 128, "%.*e", 64, d); return buffer + written; } void all_32bit_values() { char buffer[128]; for (uint64_t w = 0; w <= 0xFFFFFFFF; w++) { float v32; if ((w % 1048576) == 0) { std::cout << "."; std::cout.flush(); } uint32_t word = uint32_t(w); memcpy(&v32, &word, sizeof(v32)); double v = v32; { char const *string_end = to_string(v, buffer); double result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); // Starting with version 4.0 for fast_float, we return result_out_of_range // if the value is either too small (too close to zero) or too large // (effectively infinity). So std::errc::result_out_of_range is normal for // well-formed input strings. if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; abort(); } if (std::isnan(v)) { if (!std::isnan(result_value)) { std::cerr << "not nan" << buffer << std::endl; abort(); } } else if (copysign(1, result_value) != copysign(1, v)) { std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v << std::endl; abort(); } else if (std::isnan(v)) { if (!std::isnan(result_value)) { std::cerr << "not nan" << buffer << std::endl; abort(); } } else if (result_value != v) { std::cerr << "no match ? " << buffer << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; abort(); } } } std::cout << std::endl; } int main() { all_32bit_values(); std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/long_random64.cpp000066400000000000000000000065021475170546600202020ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include template char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 128, "%.*e", 64, d); return buffer + written; } static fast_float::value128 g_lehmer64_state; /** * D. H. Lehmer, Mathematical methods in large-scale computing units. * Proceedings of a Second Symposium on Large Scale Digital Calculating * Machinery; * Annals of the Computation Laboratory, Harvard Univ. 26 (1951), pp. 141-146. * * P L'Ecuyer, Tables of linear congruential generators of different sizes and * good lattice structure. Mathematics of Computation of the American * Mathematical * Society 68.225 (1999): 249-260. */ static inline void lehmer64_seed(uint64_t seed) { g_lehmer64_state.high = 0; g_lehmer64_state.low = seed; } static inline uint64_t lehmer64() { fast_float::value128 v = fast_float::full_multiplication( g_lehmer64_state.low, UINT64_C(0xda942042e4dd58b5)); v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5); g_lehmer64_state = v; return v.high; } size_t errors; void random_values(size_t N) { char buffer[128]; lehmer64_seed(N); for (size_t t = 0; t < N; t++) { if ((t % 1048576) == 0) { std::cout << "."; std::cout.flush(); } uint64_t word = lehmer64(); double v; memcpy(&v, &word, sizeof(v)); { char const *string_end = to_string(v, buffer); double result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); // Starting with version 4.0 for fast_float, we return result_out_of_range // if the value is either too small (too close to zero) or too large // (effectively infinity). So std::errc::result_out_of_range is normal for // well-formed input strings. if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; errors++; if (errors > 10) { abort(); } continue; } if (std::isnan(v)) { if (!std::isnan(result_value)) { std::cerr << "not nan" << buffer << std::endl; errors++; if (errors > 10) { abort(); } } } else if (copysign(1, result_value) != copysign(1, v)) { std::cerr << buffer << std::endl; std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v << std::endl; abort(); } else if (result_value != v) { std::cerr << "no match ? '" << buffer << "'" << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; errors++; if (errors > 10) { abort(); } } } } std::cout << std::endl; } int main() { errors = 0; size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit random_values(N); if (errors == 0) { std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } std::cerr << std::endl; std::cerr << "errors were encountered" << std::endl; return EXIT_FAILURE; } fast_float-8.0.0/tests/long_test.cpp000066400000000000000000000034161475170546600175300ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include inline void Assert(bool Assertion) { if (!Assertion) { throw std::runtime_error("bug"); } } template bool test() { std::string input = "0.156250000000000000000000000000000000000000 " "3.14159265358979323846264338327950288419716939937510 " "2.71828182845904523536028747135266249775724709369995"; std::vector answers = {T(0.15625), T(3.141592653589793), T(2.718281828459045)}; char const *begin = input.data(); char const *end = input.data() + input.size(); for (size_t i = 0; i < answers.size(); i++) { T result_value; while ((begin < end) && (std::isspace(*begin))) { begin++; } auto result = fast_float::from_chars(begin, end, result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(end - begin), begin); std::cerr << " I could not parse " << std::endl; return false; } if (result_value != answers[i]) { printf("parsing %.*s\n", int(end - begin), begin); std::cerr << " Mismatch " << std::endl; std::cerr << " Expected " << answers[i] << std::endl; std::cerr << " Got " << result_value << std::endl; return false; } begin = result.ptr; } if (begin != end) { std::cerr << " bad ending " << std::endl; return false; } return true; } int main() { std::cout << "32 bits checks" << std::endl; Assert(test()); std::cout << "64 bits checks" << std::endl; Assert(test()); std::cout << "All ok" << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/powersoffive_hardround.cpp000066400000000000000000000100041475170546600223050ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include #include #include #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. #include // workaround for CYGWIN double cygwin_strtod_l(char const *start, char **end) { double d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } float cygwin_strtof_l(char const *start, char **end) { float d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } #endif std::pair strtod_from_string(char const *st) { double d; char *pr; #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtod_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtod_l(st, &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtod_l(st, &pr, c_locale); #endif if (st == pr) { std::cerr << "strtod_l could not parse '" << st << std::endl; return std::make_pair(0, false); } return std::make_pair(d, true); } std::pair strtof_from_string(char *st) { float d; char *pr; #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtof_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtof_l(st, &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtof_l(st, &pr, c_locale); #endif if (st == pr) { std::cerr << "strtof_l could not parse '" << st << std::endl; return std::make_pair(0.0f, false); } return std::make_pair(d, true); } bool tester() { std::random_device rd; std::mt19937 gen(rd()); for (int q = 18; q <= 27; q++) { std::cout << "q = " << -q << std::endl; uint64_t power5 = 1; for (int k = 0; k < q; k++) { power5 *= 5; } uint64_t low_threshold = 0x20000000000000 / power5 + 1; uint64_t threshold = 0xFFFFFFFFFFFFFFFF / power5; std::uniform_int_distribution dis(low_threshold, threshold); for (size_t i = 0; i < 10000; i++) { uint64_t mantissa = dis(gen) * power5; std::stringstream ss; ss << mantissa; ss << "e"; ss << -q; std::string to_be_parsed = ss.str(); std::pair expected_double = strtod_from_string(to_be_parsed.c_str()); double result_value; auto result = fast_float::from_chars( to_be_parsed.data(), to_be_parsed.data() + to_be_parsed.size(), result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cout << to_be_parsed << std::endl; std::cerr << " I could not parse " << std::endl; return false; } if (result_value != expected_double.first) { std::cout << to_be_parsed << std::endl; std::cerr << std::hexfloat << result_value << std::endl; std::cerr << std::hexfloat << expected_double.first << std::endl; std::cerr << " Mismatch " << std::endl; return false; } } } return true; } int main() { if (tester()) { std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } std::cerr << std::endl; std::cerr << "errors were encountered" << std::endl; return EXIT_FAILURE; } fast_float-8.0.0/tests/random64.cpp000066400000000000000000000066551475170546600171740ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include #include template char *to_string(T d, char *buffer) { auto written = std::snprintf(buffer, 64, "%.*e", std::numeric_limits::max_digits10 - 1, d); return buffer + written; } static fast_float::value128 g_lehmer64_state; /** * D. H. Lehmer, Mathematical methods in large-scale computing units. * Proceedings of a Second Symposium on Large Scale Digital Calculating * Machinery; * Annals of the Computation Laboratory, Harvard Univ. 26 (1951), pp. 141-146. * * P L'Ecuyer, Tables of linear congruential generators of different sizes and * good lattice structure. Mathematics of Computation of the American * Mathematical * Society 68.225 (1999): 249-260. */ static inline void lehmer64_seed(uint64_t seed) { g_lehmer64_state.high = 0; g_lehmer64_state.low = seed; } static inline uint64_t lehmer64() { fast_float::value128 v = fast_float::full_multiplication( g_lehmer64_state.low, UINT64_C(0xda942042e4dd58b5)); v.high += g_lehmer64_state.high * UINT64_C(0xda942042e4dd58b5); g_lehmer64_state = v; return v.high; } size_t errors; void random_values(size_t N) { char buffer[64]; lehmer64_seed(N); for (size_t t = 0; t < N; t++) { if ((t % 1048576) == 0) { std::cout << "."; std::cout.flush(); } uint64_t word = lehmer64(); double v; memcpy(&v, &word, sizeof(v)); // if (!std::isnormal(v)) { char const *string_end = to_string(v, buffer); double result_value; auto result = fast_float::from_chars(buffer, string_end, result_value); // Starting with version 4.0 for fast_float, we return result_out_of_range // if the value is either too small (too close to zero) or too large // (effectively infinity). So std::errc::result_out_of_range is normal for // well-formed input strings. if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { std::cerr << "parsing error ? " << buffer << std::endl; errors++; if (errors > 10) { abort(); } continue; } if (std::isnan(v)) { if (!std::isnan(result_value)) { std::cerr << "not nan" << buffer << std::endl; errors++; if (errors > 10) { abort(); } } } else if (copysign(1, result_value) != copysign(1, v)) { std::cerr << buffer << std::endl; std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << v << std::endl; abort(); } else if (result_value != v) { std::cerr << "no match ? " << buffer << std::endl; std::cout << "started with " << std::hexfloat << v << std::endl; std::cout << "got back " << std::hexfloat << result_value << std::endl; std::cout << std::dec; errors++; if (errors > 10) { abort(); } } } } std::cout << std::endl; } int main() { errors = 0; size_t N = size_t(1) << (sizeof(size_t) * 4); // shift: 32 for 64bit, 16 for 32bit random_values(N); if (errors == 0) { std::cout << std::endl; std::cout << "all ok" << std::endl; return EXIT_SUCCESS; } std::cerr << std::endl; std::cerr << "errors were encountered" << std::endl; return EXIT_FAILURE; } fast_float-8.0.0/tests/random_string.cpp000066400000000000000000000200471475170546600203770ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. #include #include // workaround for CYGWIN double cygwin_strtod_l(char const *start, char **end) { double d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } float cygwin_strtof_l(char const *start, char **end) { float d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } #endif class RandomEngine { public: RandomEngine() = delete; RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){}; uint64_t next() { // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Inspired from // https://github.com/lemire/testingRNG/blob/master/source/wyhash.h wyhash64_x_ += UINT64_C(0x60bee2bee120fc15); fast_float::value128 tmp = fast_float::full_multiplication( wyhash64_x_, UINT64_C(0xa3b195354a39b70d)); uint64_t m1 = (tmp.high) ^ tmp.low; tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9)); uint64_t m2 = (tmp.high) ^ tmp.low; return m2; } bool next_bool() { return (next() & 1) == 1; } int next_int() { return static_cast(next()); } char next_char() { return static_cast(next()); } double next_double() { return static_cast(next()); } int next_ranged_int(int min, int max) { // min and max are included // Adapted from // https://lemire.me/blog/2019/06/06/nearly-divisionless-random-integer-generation-on-various-systems/ /* if (min == max) { return min; }*/ uint64_t s = uint64_t(max - min + 1); uint64_t x = next(); fast_float::value128 m = fast_float::full_multiplication(x, s); uint64_t l = m.low; if (l < s) { uint64_t t = -s % s; while (l < t) { x = next(); m = fast_float::full_multiplication(x, s); l = m.low; } } return int(m.high) + min; } int next_digit() { return next_ranged_int(0, 9); } private: uint64_t wyhash64_x_; }; size_t build_random_string(RandomEngine &rand, char *buffer) { size_t pos{0}; if (rand.next_bool()) { buffer[pos++] = '-'; } int number_of_digits = rand.next_ranged_int(1, 100); if (number_of_digits == 100) { // With low probability, we want to allow very long strings just to stress // the system. number_of_digits = rand.next_ranged_int(1, 2000); } int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits); for (size_t i = 0; i < size_t(number_of_digits); i++) { if (i == size_t(location_of_decimal_separator)) { buffer[pos++] = '.'; } buffer[pos] = char(rand.next_digit() + '0'); // We can have a leading zero only if location_of_decimal_separator = 1. while (i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') { buffer[pos] = char(rand.next_digit() + '0'); } pos++; } if (rand.next_bool()) { if (rand.next_bool()) { buffer[pos++] = 'e'; } else { buffer[pos++] = 'E'; } if (rand.next_bool()) { buffer[pos++] = '-'; } else { if (rand.next_bool()) { buffer[pos++] = '+'; } } number_of_digits = rand.next_ranged_int(1, 3); for (size_t i = 0; i < size_t(number_of_digits); i++) { buffer[pos++] = char(rand.next_digit() + '0'); } } buffer[pos] = '\0'; // null termination return pos; } std::pair strtod_from_string(char *st) { double d; char *pr; #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtod_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtod_l(st, &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtod_l(st, &pr, c_locale); #endif if (st == pr) { std::cerr << "strtod_l could not parse '" << st << std::endl; return std::make_pair(0, false); } return std::make_pair(d, true); } std::pair strtof_from_string(char *st) { float d; char *pr; #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtof_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtof_l(st, &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtof_l(st, &pr, c_locale); #endif if (st == pr) { std::cerr << "strtof_l could not parse '" << st << std::endl; return std::make_pair(0.0f, false); } return std::make_pair(d, true); } /** * We generate random strings and we try to parse them with both strtod/strtof, * and we verify that we get the same answer with with fast_float::from_chars. */ bool tester(uint64_t seed, size_t volume) { char buffer[4096]; // large buffer (can't overflow) RandomEngine rand(seed); for (size_t i = 0; i < volume; i++) { if ((i % 100000) == 0) { std::cout << "."; std::cout.flush(); } size_t length = build_random_string(rand, buffer); std::pair expected_double = strtod_from_string(buffer); if (expected_double.second) { double result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; } if (result.ptr != buffer + length) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " Did not get to the end " << std::endl; return false; } if (result_value != expected_double.first) { printf("parsing %.*s\n", int(length), buffer); std::cerr << std::hexfloat << result_value << std::endl; std::cerr << std::hexfloat << expected_double.first << std::endl; std::cerr << " Mismatch " << std::endl; return false; } } std::pair expected_float = strtof_from_string(buffer); if (expected_float.second) { float result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; } if (result.ptr != buffer + length) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " Did not get to the end " << std::endl; return false; } if (result_value != expected_float.first) { printf("parsing %.*s\n", int(length), buffer); std::cerr << std::hexfloat << result_value << std::endl; std::cerr << std::hexfloat << expected_float.first << std::endl; std::cerr << " Mismatch " << std::endl; return false; } } } return true; } int main() { #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) std::cout << "Warning: msys/cygwin or solaris detected." << std::endl; return EXIT_SUCCESS; #else if (tester(1234344, 100000000)) { std::cout << "All tests ok." << std::endl; return EXIT_SUCCESS; } std::cout << "Failure." << std::endl; return EXIT_FAILURE; #endif } fast_float-8.0.0/tests/rcppfastfloat_test.cpp000066400000000000000000000075371475170546600214510ustar00rootroot00000000000000/** * See https://github.com/eddelbuettel/rcppfastfloat/issues/4 */ #include "fast_float/fast_float.h" #include #include #include struct test_data { std::string input; bool expected_success; double expected_result; }; bool eddelbuettel() { std::vector const test_datas = { {"infinity", true, std::numeric_limits::infinity()}, {" \r\n\t\f\v3.16227766016838 \r\n\t\f\v", true, 3.16227766016838}, {" \r\n\t\f\v3 \r\n\t\f\v", true, 3.0}, {" 1970-01-01", false, 0.0}, {"-NaN", true, std::numeric_limits::quiet_NaN()}, {"-inf", true, -std::numeric_limits::infinity()}, {" \r\n\t\f\v2.82842712474619 \r\n\t\f\v", true, 2.82842712474619}, {"nan", true, std::numeric_limits::quiet_NaN()}, {" \r\n\t\f\v2.44948974278318 \r\n\t\f\v", true, 2.44948974278318}, {"Inf", true, std::numeric_limits::infinity()}, {" \r\n\t\f\v2 \r\n\t\f\v", true, 2.0}, {"-infinity", true, -std::numeric_limits::infinity()}, {" \r\n\t\f\v0 \r\n\t\f\v", true, 0.0}, {" \r\n\t\f\v1.73205080756888 \r\n\t\f\v", true, 1.73205080756888}, {" \r\n\t\f\v1 \r\n\t\f\v", true, 1.0}, {" \r\n\t\f\v1.4142135623731 \r\n\t\f\v", true, 1.4142135623731}, {" \r\n\t\f\v2.23606797749979 \r\n\t\f\v", true, 2.23606797749979}, {"1970-01-02 ", false, 0.0}, {" \r\n\t\f\v2.64575131106459 \r\n\t\f\v", true, 2.64575131106459}, {"inf", true, std::numeric_limits::infinity()}, {"-nan", true, std::numeric_limits::quiet_NaN()}, {"NaN", true, std::numeric_limits::quiet_NaN()}, {"", false, 0.0}, {"-Inf", true, -std::numeric_limits::infinity()}, {"+2.2", true, 2.2}, {"1d+4", false, 0.0}, {"1d-1", false, 0.0}, {"0.", true, 0.0}, {"-.1", true, -0.1}, {"+.1", true, 0.1}, {"1e+1", true, 10.0}, {"+1e1", true, 10.0}, {"-+0", false, 0.0}, {"-+inf", false, 0.0}, {"-+nan", false, 0.0}, }; for (size_t i = 0; i < test_datas.size(); i++) { auto const &input = test_datas[i].input; auto const expected_success = test_datas[i].expected_success; auto const expected_result = test_datas[i].expected_result; double result; // answer contains a error code and a pointer to the end of the // parsed region (on success). auto const answer = fast_float::from_chars( input.data(), input.data() + input.size(), result, fast_float::chars_format::general | fast_float::chars_format::allow_leading_plus | fast_float::chars_format::skip_white_space); if (answer.ec != std::errc()) { std::cout << "could not parse" << std::endl; if (expected_success) { return false; } continue; } bool non_space_trailing_content = false; if (answer.ptr != input.data() + input.size()) { // check that there is no content left for (char const *leftover = answer.ptr; leftover != input.data() + input.size(); leftover++) { if (!fast_float::is_space(*leftover)) { non_space_trailing_content = true; break; } } } if (non_space_trailing_content) { std::cout << "found trailing content " << std::endl; if (!expected_success) { continue; } else { return false; } } std::cout << "parsed " << result << std::endl; if (!expected_success) { return false; } if (result != expected_result && !(std::isnan(result) && std::isnan(expected_result))) { std::cout << "results do not match. Expected " << expected_result << std::endl; return false; } } return true; } int main() { if (!eddelbuettel()) { printf("Bug.\n"); return EXIT_FAILURE; } printf("All ok.\n"); return EXIT_SUCCESS; } fast_float-8.0.0/tests/short_random_string.cpp000066400000000000000000000177271475170546600216310ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. #include #include // workaround for CYGWIN double cygwin_strtod_l(char const *start, char **end) { double d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } float cygwin_strtof_l(char const *start, char **end) { float d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } #endif class RandomEngine { public: RandomEngine() = delete; RandomEngine(uint64_t new_seed) : wyhash64_x_(new_seed){}; uint64_t next() { // Adapted from https://github.com/wangyi-fudan/wyhash/blob/master/wyhash.h // Inspired from // https://github.com/lemire/testingRNG/blob/master/source/wyhash.h wyhash64_x_ += UINT64_C(0x60bee2bee120fc15); fast_float::value128 tmp = fast_float::full_multiplication( wyhash64_x_, UINT64_C(0xa3b195354a39b70d)); uint64_t m1 = (tmp.high) ^ tmp.low; tmp = fast_float::full_multiplication(m1, UINT64_C(0x1b03738712fad5c9)); uint64_t m2 = (tmp.high) ^ tmp.low; return m2; } bool next_bool() { return (next() & 1) == 1; } int next_int() { return static_cast(next()); } char next_char() { return static_cast(next()); } double next_double() { return static_cast(next()); } int next_ranged_int(int min, int max) { // min and max are included // Adapted from // https://lemire.me/blog/2019/06/06/nearly-divisionless-random-integer-generation-on-various-systems/ /* if (min == max) { return min; }*/ uint64_t s = uint64_t(max - min + 1); uint64_t x = next(); fast_float::value128 m = fast_float::full_multiplication(x, s); uint64_t l = m.low; if (l < s) { uint64_t t = -s % s; while (l < t) { x = next(); m = fast_float::full_multiplication(x, s); l = m.low; } } return int(m.high) + min; } int next_digit() { return next_ranged_int(0, 9); } private: uint64_t wyhash64_x_; }; size_t build_random_string(RandomEngine &rand, char *buffer) { size_t pos{0}; if (rand.next_bool()) { buffer[pos++] = '-'; } int number_of_digits = rand.next_ranged_int(1, 19); int location_of_decimal_separator = rand.next_ranged_int(1, number_of_digits); for (size_t i = 0; i < size_t(number_of_digits); i++) { if (i == size_t(location_of_decimal_separator)) { buffer[pos++] = '.'; } buffer[pos] = char(rand.next_digit() + '0'); // We can have a leading zero only if location_of_decimal_separator = 1. while (i == 0 && 1 != size_t(location_of_decimal_separator) && buffer[pos] == '0') { buffer[pos] = char(rand.next_digit() + '0'); } pos++; } if (rand.next_bool()) { if (rand.next_bool()) { buffer[pos++] = 'e'; } else { buffer[pos++] = 'E'; } if (rand.next_bool()) { buffer[pos++] = '-'; } else { if (rand.next_bool()) { buffer[pos++] = '+'; } } number_of_digits = rand.next_ranged_int(1, 3); for (size_t i = 0; i < size_t(number_of_digits); i++) { buffer[pos++] = char(rand.next_digit() + '0'); } } buffer[pos] = '\0'; // null termination return pos; } std::pair strtod_from_string(char *st) { double d; char *pr; #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtod_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtod_l(st, &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtod_l(st, &pr, c_locale); #endif if (st == pr) { std::cerr << "strtod_l could not parse '" << st << std::endl; return std::make_pair(0, false); } return std::make_pair(d, true); } std::pair strtof_from_string(char *st) { float d; char *pr; #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtof_l(st, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtof_l(st, &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtof_l(st, &pr, c_locale); #endif if (st == pr) { std::cerr << "strtof_l could not parse '" << st << std::endl; return std::make_pair(0.0f, false); } return std::make_pair(d, true); } /** * We generate random strings and we try to parse them with both strtod/strtof, * and we verify that we get the same answer with with fast_float::from_chars. */ bool tester(uint64_t seed, size_t volume) { char buffer[4096]; // large buffer (can't overflow) RandomEngine rand(seed); for (size_t i = 0; i < volume; i++) { if ((i % 1000000) == 0) { std::cout << "."; std::cout.flush(); } size_t length = build_random_string(rand, buffer); std::pair expected_double = strtod_from_string(buffer); if (expected_double.second) { double result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; } if (result.ptr != buffer + length) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " Did not get to the end " << std::endl; return false; } if (result_value != expected_double.first) { printf("parsing %.*s\n", int(length), buffer); std::cerr << std::hexfloat << result_value << std::endl; std::cerr << std::hexfloat << expected_double.first << std::endl; std::cerr << " Mismatch " << std::endl; return false; } } std::pair expected_float = strtof_from_string(buffer); if (expected_float.second) { float result_value; auto result = fast_float::from_chars(buffer, buffer + length, result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " I could not parse " << std::endl; return false; } if (result.ptr != buffer + length) { printf("parsing %.*s\n", int(length), buffer); std::cerr << " Did not get to the end " << std::endl; return false; } if (result_value != expected_float.first) { printf("parsing %.*s\n", int(length), buffer); std::cerr << std::hexfloat << result_value << std::endl; std::cerr << std::hexfloat << expected_float.first << std::endl; std::cerr << " Mismatch " << std::endl; return false; } } } return true; } int main() { #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) std::cout << "Warning: msys/cygwin detected. This particular test is likely " "to generate false failures due to our reliance on the " "underlying runtime library." << std::endl; return EXIT_SUCCESS; #else if (tester(1234344, 100000000)) { std::cout << "All tests ok." << std::endl; return EXIT_SUCCESS; } return EXIT_FAILURE; #endif } fast_float-8.0.0/tests/string_test.cpp000066400000000000000000000534221475170546600201010ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include #include #include #include #include #include #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) // Anything at all that is related to cygwin, msys and so forth will // always use this fallback because we cannot rely on it behaving as normal // gcc. #include #include // workaround for CYGWIN double cygwin_strtod_l(char const *start, char **end) { double d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } float cygwin_strtof_l(char const *start, char **end) { float d; std::stringstream ss; ss.imbue(std::locale::classic()); ss << start; ss >> d; if (ss.fail()) { *end = nullptr; } if (ss.eof()) { ss.clear(); } auto nread = ss.tellg(); *end = const_cast(start) + nread; return d; } #endif inline void Assert(bool Assertion) { #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) if (!Assertion) { std::cerr << "Omitting hard failure on msys/cygwin/sun systems."; } #else if (!Assertion) { throw std::runtime_error("bug"); } #endif } template std::string to_string(T d) { std::string s(64, '\0'); auto written = std::snprintf(&s[0], s.size(), "%.*e", std::numeric_limits::max_digits10 - 1, d); s.resize(size_t(written)); return s; } template bool test() { std::string input = "0.1 1e1000 100000 3.14159265359 -1e-500 001 1e01 1e0000001 -inf"; std::vector answers = {T(0.1), std::numeric_limits::infinity(), 100000, T(3.14159265359), -0.0, 1, 10, 10, -std::numeric_limits::infinity()}; std::vector expected_ec = {std::errc(), std::errc::result_out_of_range, std::errc(), std::errc(), std::errc::result_out_of_range, std::errc(), std::errc(), std::errc(), std::errc()}; char const *begin = input.data(); char const *end = input.data() + input.size(); for (size_t i = 0; i < answers.size(); i++) { T result_value; while ((begin < end) && (std::isspace(*begin))) { begin++; } auto result = fast_float::from_chars(begin, end, result_value); if (result.ec != expected_ec[i]) { printf("parsing %.*s\n", int(end - begin), begin); std::cerr << " I could not parse " << std::endl; return false; } if (result_value != answers[i]) { printf("parsing %.*s\n", int(end - begin), begin); std::cerr << " Mismatch " << std::endl; return false; } begin = result.ptr; } if (begin != end) { std::cerr << " bad ending " << std::endl; return false; } return true; } template void strtod_from_string(std::string const &st, T &d); template <> void strtod_from_string(std::string const &st, double &d) { char *pr = (char *)st.c_str(); #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtod_l(pr, &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtod_l(st.c_str(), &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtod_l(st.c_str(), &pr, c_locale); #endif if (pr == st.c_str()) { throw std::runtime_error("bug in strtod_from_string"); } } template <> void strtod_from_string(std::string const &st, float &d) { char *pr = (char *)st.c_str(); #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) d = cygwin_strtof_l(st.c_str(), &pr); #elif defined(_WIN32) static _locale_t c_locale = _create_locale(LC_ALL, "C"); d = _strtof_l(st.c_str(), &pr, c_locale); #else static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); d = strtof_l(st.c_str(), &pr, c_locale); #endif if (pr == st.c_str()) { throw std::runtime_error("bug in strtod_from_string"); } } template bool partow_test() { // credit: // https://github.com/ArashPartow/strtk/blob/master/strtk_tokenizer_cmp.cpp#L568 // MIT license std::string const strint_list[] = { "9007199254740993", "9007199254740994", "9007199254740995", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "917049", "4931205", "6768064", "6884243", "5647132", "7371203", "-8629878", "4941840", "4543268", "1075600", "290", "823", "111", "715", "-866", "367", "666", "-706", "850", "-161", "9922547", "6960207", "1883152", "2300759", "-279294", "4187292", "3699841", "8386395", "-1441129", "-887892", "-635422", "9742573", "2326186", "-5903851", "5648486", "3057647", "2980079", "2957468", "7929158", "1925615", "879", "130", "292", "705", "817", "446", "576", "750", "523", "-527", "4365041", "5624958", "8990205", "2652177", "3993588", "-298316", "2901599", "3887387", "-5202979", "1196268", "5968501", "7619928", "3565643", "1885272", "-749485", "2961381", "2982579", "2387454", "4250081", "5958205", "00000", "00001", "00002", "00003", "00004", "00005", "00006", "00007", "00008", "00009", "4907034", "2592882", "3269234", "549815", "6256292", "9721039", "-595225", "5587491", "4596297", "-3885009", "673", "-899", "174", "354", "870", "147", "898", "-510", "369", "859", "6518423", "5149762", "8834164", "-8085586", "3233120", "8166948", "4172345", "6735549", "-934295", "9481935", "-430406", "6932717", "4087292", "4047263", "3236400", "-3863050", "4312079", "6956261", "5689446", "3871332", "535", "691", "326", "-409", "704", "-568", "301", "951", "121", "384", "4969414", "9378599", "7971781", "5380630", "5001363", "1715827", "6044615", "9118925", "9956168", "-8865496", "5962464", "7408980", "6646513", "-634564", "4188330", "9805948", "5625691", "7641113", "-4212929", "7802447", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "2174248", "7449361", "9896659", "-25961", "1706598", "2412368", "-4617035", "6314554", "2225957", "7521434", "-9530566", "3914164", "2394759", "7157744", "9919392", "6406949", "-744004", "9899789", "8380325", "-1416284", "3402833", "2150043", "5191009", "8979538", "9565778", "3750211", "7304823", "2829359", "6544236", "-615740", "363", "-627", "129", "656", "135", "113", "381", "646", "198", "38", "8060564", "-176752", "1184717", "-666343", "-1273292", "-485827", "6241066", "6579411", "8093119", "7481306", "-4924485", "7467889", "9813178", "7927100", "3614859", "7293354", "9232973", "4323115", "1133911", "9511638", "4443188", "2289448", "5639726", "9073898", "8540394", "5389992", "1397726", "-589230", "1017086", "1852330", "-840", "267", "201", "533", "-675", "494", "315", "706", "-920", "784", "9097353", "6002251", "-308780", "-3830169", "4340467", "2235284", "3314444", "1085967", "4152107", "5431117", "-0000", "-0001", "-0002", "-0003", "-0004", "-0005", "-0006", "-0007", "-0008", "-0009", "-444999", "2136400", "6925907", "6990614", "3588271", "8422028", "-4034772", "5804039", "-6740545", "9381873", "-924923", "1652367", "2302616", "6776663", "2567821", "-248935", "2587688", "7076742", "-6461467", "1562896", "-768116", "2338768", "9887307", "9992184", "2045182", "2797589", "9784597", "9696554", "5113329", "1067216", "-76247763", "58169007", "29408062", "85342511", "42092201", "-95817703", "-1912517", "-26275135", "54656606", "-58188878", "473", "74", "374", "-64", "266", "715", "937", "-249", "249", "780", "3907360", "-23063423", "59062754", "83711047", "-95221044", "34894840", "-38562139", "-82018330", "14226223", "-10799717", "8529722", "88961903", "25608618", "-39988247", "33228241", "38598533", "21161480", "-33723784", "8873948", "96505557", "-47385048", "-79413272", "-85904404", "87791158", "49194195", "13051222", "57773302", "31904423", "3142966", "27846156", "7420011", "-72376922", "-68873971", "23765361", "4040725", "-22359806", "85777219", "10099223", "-90364256", "-40158172", "-7948696", "-64344821", "34404238", "84037448", "-85084788", "-42078409", "-56550310", "96898389", "-595829", "-73166703", "-0", "-1", "-2", "-3", "-4", "-5", "-6", "-7", "-8", "-9", "2147483647", "31", "2147483610", "33", "2147483573", "37", "2147483536", "-82838342", "64441808", "43641062", "-64419642", "-44421934", "75232413", "-75773725", "-89139509", "12812089", "-97633526", "36090916", "-57706234", "17804655", "4189936", "-4100124", "38803710", "-39735126", "-62397437", "75801648", "51302332", "73433906", "13015224", "-12624818", "91360377", "11576319", "-54467535", "8892431", "36319780", "38832042", "50172572", "-317", "109", "-888", "302", "-463", "716", "916", "665", "826", "513", "42423473", "41078812", "40445652", "-76722281", "95092224", "12075234", "-4045888", "-74396490", "-57304222", "-21726885", "92038121", "-31899682", "21589254", "-30260046", "56000244", "69686659", "93327838", "96882881", "-91419389", "77529147", "43288506", "1192435", "-74095920", "76756590", "-31184683", "-35716724", "9451980", "-63168350", "62864002", "26283194", "37188395", "29151634", "99343471", "-69450330", "-55680090", "-64957599", "47577948", "47107924", "2490477", "48633003", "-82740809", "-24122215", "67301713", "-63649610", "75499016", "82746620", "17052193", "4602244", "-32721165", "20837836", "674", "467", "706", "889", "172", "282", "-795", "188", "87", "153", "64501793", "53146328", "5152287", "-9674493", "68105580", "57245637", "39740229", "-74071854", "86777268", "86484437", "-86962508", "12644427", "-62944073", "59539680", "43340539", "30661534", "20143968", "-68183731", "-48250926", "42669063", "000", "001", "002", "003", "004", "005", "006", "007", "008", "009", "2147483499", "71", "2147483462", "73", "2147483425", "77", "2147483388", "87736852", "-4444906", "-48094147", "54774735", "54571890", "-22473078", "95053418", "393654", "-33229960", "32276798", "-48361110", "44295939", "-79813406", "11630865", "38544571", "70972830", "-9821748", "-60965384", "-13096675", "-24569041", "708", "-467", "-794", "610", "929", "766", "152", "482", "397", "-191", "97233152", "51028396", "-13796948", "95437272", "71352512", "-83233730", "-68517318", "61832742", "-42667174", "-18002395", "-92239407", "12701336", "-63830875", "41514172", "-5726049", "18668677", "69555144", "-13737009", "-22626233", "-55078143", "00", "11", "22", "33", "44", "-00", "-11", "-22", "-33", "-44", "000", "111", "222", "333", "444", "-000", "-111", "-222", "-333", "-444", "0000", "1111", "2222", "3333", "4444", "-0000", "-1111", "-2222", "-3333", "-4444", "00000", "11111", "22222", "33333", "44444", "-00000", "-11111", "-22222", "-33333", "-44444", "000000", "111111", "222222", "333333", "444444", "-000000", "-111111", "-222222", "-333333", "-444444", "0000000", "1111111", "2222222", "3333333", "4444444", "-0000000", "-1111111", "-2222222", "-3333333", "-4444444", "00000000", "11111111", "22222222", "33333333", "44444444", "-00000000", "-11111111", "-22222222", "-33333333", "-44444444", "000000000", "111111111", "222222222", "333333333", "444444444", "-000000000", "-111111111", "-222222222", "-333333333", "-444444444", "2147483351", "51", "2147483314", "53", "-2147483648", "57", "-2147483611", "55", "66", "77", "88", "99", "-55", "-66", "-77", "-88", "-99", "555", "666", "777", "888", "999", "-555", "-666", "-777", "-888", "-999", "5555", "6666", "7777", "8888", "9999", "-5555", "-6666", "-7777", "-8888", "-9999", "55555", "66666", "77777", "88888", "99999", "-55555", "-66666", "-77777", "-88888", "-99999", "555555", "666666", "777777", "888888", "999999", "-555555", "-666666", "-777777", "-888888", "-999999", "5555555", "6666666", "7777777", "8888888", "9999999", "-5555555", "-6666666", "-7777777", "-8888888", "-9999999", "55555555", "66666666", "77777777", "88888888", "99999999", "-55555555", "-66666666", "-77777777", "-88888888", "-99999999", "555555555", "666666666", "777777777", "888888888", "999999999", "-555555555", "-666666666", "-777777777", "-888888888", "-999999999", "-2147483574", "91", "-2147483537", "93", "-2147483500", "97", "-2147483463", "0000000011", "0000000022", "0000000033", "0000000044", "-000000011", "-000000022", "-000000033", "-000000044", "-000000088", "0000000111", "0000000222", "0000000333", "0000000444", "-000000111", "-000000222", "-000000333", "-000000444", "-000000888", "0000001111", "0000002222", "0000003333", "0000004444", "-000001111", "-000002222", "-000003333", "-000004444", "-000008888", "0000011111", "0000022222", "0000033333", "0000044444", "-000011111", "-000022222", "-000033333", "-000044444", "-000088888", "0000111111", "0000222222", "0000333333", "0000444444", "-000111111", "-000222222", "-000333333", "-000444444", "-000888888", "0001111111", "0002222222", "0003333333", "0004444444", "-001111111", "-002222222", "-003333333", "-004444444", "-008888888", "0011111111", "0022222222", "0033333333", "0044444444", "-011111111", "-022222222", "-033333333", "-044444444", "-088888888", "0111111111", "0222222222", "0333333333", "0444444444", "-111111111", "-222222222", "-333333333", "-444444444", "-888888888", "0000000055", "0000000066", "0000000077", "0000000088", "0000000099", "-000000055", "-000000066", "-000000077", "-000000099", "0000000555", "0000000666", "0000000777", "0000000888", "0000000999", "-000000555", "-000000666", "-000000777", "-000000999", "0000005555", "0000006666", "0000007777", "0000008888", "0000009999", "-000005555", "-000006666", "-000007777", "-000009999", "0000055555", "0000066666", "0000077777", "0000088888", "0000099999", "-000055555", "-000066666", "-000077777", "-000099999", "0000555555", "0000666666", "0000777777", "0000888888", "0000999999", "-000555555", "-000666666", "-000777777", "-000999999", "0005555555", "0006666666", "0007777777", "0008888888", "0009999999", "-005555555", "-006666666", "-007777777", "-009999999", "0055555555", "0066666666", "0077777777", "0088888888", "0099999999", "-055555555", "-066666666", "-077777777", "-099999999", "0555555555", "0666666666", "0777777777", "0888888888", "0999999999", "-555555555", "-666666666", "-777777777", "-999999999", "-2147483426", "101", "-2147483389", "103", "-2147483352", "105", "-2147483315", "0000001234567890", "0000001234567890", "-0000001234567890", "000001234567890", "000001234567890", "-000001234567890", "00001234567890", "00001234567890", "-00001234567890", "0001234567890", "0001234567890", "-0001234567890", "001234567890", "001234567890", "-001234567890", "01234567890", "01234567890", "-01234567890", "1234567890", "1234567890", "-1234567890", }; for (std::string const &st : strint_list) { T expected_value; strtod_from_string(st, expected_value); T result_value; auto result = fast_float::from_chars(st.data(), st.data() + st.size(), result_value); if (result.ec != std::errc() && result.ec != std::errc::result_out_of_range) { printf("parsing %.*s\n", int(st.size()), st.data()); std::cerr << " I could not parse " << std::endl; return false; } if (result.ptr != st.data() + st.size()) { printf("parsing %.*s\n", int(st.size()), st.data()); std::cerr << " Did not get to the end " << std::endl; return false; } if (result_value != expected_value) { printf("parsing %.*s\n", int(st.size()), st.data()); std::cerr << "expected value : " << to_string(expected_value) << std::endl; std::cerr << "result value : " << to_string(result_value) << std::endl; std::cerr << " Mismatch " << std::endl; return false; } } return true; } int main() { #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) || \ defined(sun) || defined(__sun) std::cout << "Warning: msys/cygwin or solaris detected." << std::endl; #endif std::cout << "32 bits checks" << std::endl; Assert(partow_test()); Assert(test()); std::cout << "64 bits checks" << std::endl; Assert(partow_test()); Assert(test()); std::cout << "All ok" << std::endl; return EXIT_SUCCESS; } fast_float-8.0.0/tests/supported_chars_test.cpp000066400000000000000000000026041475170546600217740ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include template bool test(std::string s, double expected) { std::basic_string 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("4.2", 4.2)) { std::cout << "test failure for char" << std::endl; return EXIT_FAILURE; } if (!test("4.2", 4.2)) { std::cout << "test failure for wchar_t" << std::endl; return EXIT_FAILURE; } #ifdef __cpp_char8_t if (!test("4.2", 4.2)) { std::cout << "test failure for char8_t" << std::endl; return EXIT_FAILURE; } #endif if (!test("4.2", 4.2)) { std::cout << "test failure for char16_t" << std::endl; return EXIT_FAILURE; } if (!test("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; } fast_float-8.0.0/tests/wide_char_test.cpp000066400000000000000000000032501475170546600205120ustar00rootroot00000000000000#include "fast_float/fast_float.h" #include #include #include 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::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; }