├── .appveyor.yml ├── .cirrus.yml ├── .clusterfuzzlite ├── Dockerfile ├── README.md ├── build.sh ├── parse_number_fuzzer.cpp └── project.yaml ├── .drone.yml ├── .github └── workflows │ ├── cflite_pr.yml │ ├── codeql.yml │ ├── msys2-clang.yml │ ├── msys2.yml │ ├── rhub.yml │ ├── ubuntu.yml │ ├── vs17-ci.yml │ └── vs17-clang-ci.yml ├── .gitignore ├── .gitmodules ├── BUILD.bazel ├── CMakeLists.txt ├── LICENSE ├── LICENSE.BSL ├── MODULE.bazel ├── Makefile ├── README.md ├── benchmarks ├── benchmark.cpp └── data │ └── canada.txt ├── fast_double_parser-config.cmake.in ├── include └── fast_double_parser.h ├── script └── table_generation.py └── tests ├── bogus.cpp ├── rebogus.cpp └── unit.cpp /.appveyor.yml: -------------------------------------------------------------------------------- 1 | version: '{build}' 2 | branches: { only: [ master ] } 3 | configuration: Release 4 | image: Visual Studio 2019 5 | platform: x64 6 | 7 | environment: 8 | matrix: 9 | - job_name: VS2019 10 | CMAKE_ARGS: -A %Platform% 11 | - job_name: VS2019ARM 12 | CMAKE_ARGS: -A ARM64 -DCMAKE_CROSSCOMPILING=1 13 | - job_name: VS2017 (Static, No Threads) 14 | image: Visual Studio 2017 15 | CMAKE_ARGS: -A %Platform% 16 | - job_name: VS2019 (Win32) 17 | platform: Win32 18 | CMAKE_ARGS: -A %Platform% 19 | 20 | 21 | build_script: 22 | - mkdir build 23 | - cd build 24 | - cmake --version 25 | - cmake %CMAKE_ARGS% .. 26 | - cmake .. 27 | - cmake --build . --config %Configuration% --verbose 28 | 29 | for: 30 | - 31 | matrix: 32 | except: 33 | - job_name: VS2019ARM 34 | 35 | test_script: 36 | - ctest --output-on-failure -C %Configuration% --verbose %CTEST_ARGS% 37 | 38 | clone_folder: c:\projects\fast_double_parser 39 | 40 | matrix: 41 | fast_finish: true 42 | -------------------------------------------------------------------------------- /.cirrus.yml: -------------------------------------------------------------------------------- 1 | task: 2 | timeout_in: 120m 3 | freebsd_instance: 4 | matrix: 5 | - image_family: freebsd-13-0-snap 6 | 7 | env: 8 | ASSUME_ALWAYS_YES: YES 9 | setup_script: 10 | - pkg update -f 11 | - pkg install bash 12 | - pkg install cmake 13 | - pkg install git 14 | build_script: 15 | - mkdir build 16 | - cd build 17 | - cmake .. 18 | - cmake --build . --verbose 19 | test_script: 20 | - cd build 21 | - ctest --output-on-failure -------------------------------------------------------------------------------- /.clusterfuzzlite/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gcr.io/oss-fuzz-base/base-builder 2 | RUN apt-get update && apt-get install -y make autoconf automake libtool 3 | 4 | COPY . $SRC/fast_double_parser 5 | COPY .clusterfuzzlite/build.sh $SRC/build.sh 6 | WORKDIR $SRC/fast_double_parser -------------------------------------------------------------------------------- /.clusterfuzzlite/README.md: -------------------------------------------------------------------------------- 1 | # ClusterFuzzLite set up 2 | 3 | This folder contains a fuzzing set for [ClusterFuzzLite](https://google.github.io/clusterfuzzlite). -------------------------------------------------------------------------------- /.clusterfuzzlite/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | # Copy all fuzzer executable to $OUT/ 4 | $CXX $CFLAGS $LIB_FUZZING_ENGINE \ 5 | $SRC/fast_double_parser/.clusterfuzzlite/parse_number_fuzzer.cpp \ 6 | -o $OUT/parse_number_fuzzer \ 7 | -I$SRC/fast_double_parser/include 8 | -------------------------------------------------------------------------------- /.clusterfuzzlite/parse_number_fuzzer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { 5 | std::string fuzz_input(reinterpret_cast(data), size); 6 | 7 | double x; 8 | fast_double_parser::parse_number(fuzz_input.c_str(), &x); 9 | 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /.clusterfuzzlite/project.yaml: -------------------------------------------------------------------------------- 1 | language: c++ -------------------------------------------------------------------------------- /.drone.yml: -------------------------------------------------------------------------------- 1 | kind: pipeline 2 | name: gcc9 3 | platform: { os: linux, arch: amd64 } 4 | steps: 5 | - name: Build and Test 6 | image: gcc:9 7 | environment: 8 | CC: gcc 9 | CXX: g++ 10 | commands: 11 | - apt-get update -qq 12 | - apt-get install -y cmake 13 | - mkdir build 14 | - cd build 15 | - cmake .. 16 | - cmake --build . 17 | - ctest . --output-on-failure 18 | --- 19 | kind: pipeline 20 | name: clang6 21 | platform: { os: linux, arch: amd64 } 22 | steps: 23 | - name: Build and Test 24 | image: conanio/clang60 25 | user: root 26 | environment: 27 | CC: clang-6.0 28 | CXX: clang++-6.0 29 | commands: 30 | - apt-get update -qq 31 | - apt-get install -y cmake 32 | - mkdir build 33 | - cd build 34 | - cmake .. 35 | - cmake --build . 36 | - ctest . --output-on-failure 37 | --- 38 | kind: pipeline 39 | name: sanitize-gcc9 40 | platform: { os: linux, arch: amd64 } 41 | steps: 42 | - name: Build and Test 43 | image: gcc:9 44 | environment: 45 | CC: gcc 46 | CXX: g++ 47 | commands: 48 | - apt-get update -qq 49 | - apt-get install -y cmake 50 | - mkdir build 51 | - cd build 52 | - cmake .. -DFAST_DOUBLE_PARSER_SANITIZE=ON 53 | - cmake --build . 54 | - ASAN_OPTIONS="detect_leaks=0" ctest . --output-on-failure 55 | --- 56 | kind: pipeline 57 | name: sanitize-clang9 58 | platform: { os: linux, arch: amd64 } 59 | steps: 60 | - name: Build and Test 61 | image: conanio/clang9 62 | user: root 63 | environment: 64 | CC: clang-9 65 | CXX: clang++-9 66 | commands: 67 | - apt-get update -qq 68 | - apt-get install -y cmake 69 | - mkdir build 70 | - cd build 71 | - cmake .. -DFAST_DOUBLE_PARSER_SANITIZE=ON 72 | - cmake --build . 73 | - ASAN_OPTIONS="detect_leaks=0" ctest . --output-on-failure 74 | --- 75 | kind: pipeline 76 | name: arm64-gcc8 77 | platform: { os: linux, arch: arm64 } 78 | steps: 79 | - name: Build and Test 80 | image: gcc:8 81 | environment: 82 | CC: gcc 83 | CXX: g++ 84 | commands: 85 | - apt-get update -qq 86 | - apt-get install -y cmake 87 | - mkdir build 88 | - cd build 89 | - cmake .. 90 | - cmake --build . 91 | - ctest . --output-on-failure 92 | --- 93 | kind: pipeline 94 | name: arm64-sanitize-gcc8 95 | platform: { os: linux, arch: arm64 } 96 | steps: 97 | - name: Build and Test 98 | image: gcc:8 99 | environment: 100 | CC: gcc 101 | CXX: g++ 102 | commands: 103 | - apt-get update -qq 104 | - apt-get install -y cmake libstdc++6 105 | - mkdir build 106 | - cd build 107 | - cmake .. -DFAST_DOUBLE_PARSER_SANITIZE=ON 108 | - cmake --build . 109 | - ASAN_OPTIONS="detect_leaks=0" ctest . --output-on-failure 110 | --- 111 | kind: pipeline 112 | name: libcpp-clang9 113 | platform: { os: linux, arch: amd64 } 114 | steps: 115 | - name: Build and Test 116 | image: conanio/clang9 117 | user: root 118 | environment: 119 | CC: clang-9 120 | CXX: clang++-9 121 | CXXFLAGS: -stdlib=libc++ 122 | commands: 123 | - apt-get update -qq 124 | - apt-get install -y cmake 125 | - mkdir build 126 | - cd build 127 | - cmake .. 128 | - cmake --build . 129 | - ctest . --output-on-failure -------------------------------------------------------------------------------- /.github/workflows/cflite_pr.yml: -------------------------------------------------------------------------------- 1 | name: ClusterFuzzLite PR fuzzing 2 | on: 3 | workflow_dispatch: 4 | pull_request: 5 | branches: [ main ] 6 | permissions: read-all 7 | jobs: 8 | PR: 9 | runs-on: ubuntu-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | sanitizer: [address] 14 | steps: 15 | - name: Build Fuzzers (${{ matrix.sanitizer }}) 16 | id: build 17 | uses: google/clusterfuzzlite/actions/build_fuzzers@v1 18 | with: 19 | sanitizer: ${{ matrix.sanitizer }} 20 | language: c++ 21 | bad-build-check: false 22 | - name: Run Fuzzers (${{ matrix.sanitizer }}) 23 | id: run 24 | uses: google/clusterfuzzlite/actions/run_fuzzers@v1 25 | with: 26 | github-token: ${{ secrets.GITHUB_TOKEN }} 27 | fuzz-seconds: 100 28 | mode: 'code-change' 29 | report-unreproducible-crashes: false 30 | sanitizer: ${{ matrix.sanitizer }} 31 | -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [ "master" ] 6 | pull_request: 7 | branches: [ "master" ] 8 | schedule: 9 | - cron: "1 8 * * 3" 10 | 11 | jobs: 12 | analyze: 13 | name: Analyze 14 | runs-on: ubuntu-latest 15 | permissions: 16 | actions: read 17 | contents: read 18 | security-events: write 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | language: [ cpp ] 24 | 25 | steps: 26 | - name: Checkout 27 | uses: actions/checkout@v3 28 | 29 | - name: Initialize CodeQL 30 | uses: github/codeql-action/init@v2 31 | with: 32 | languages: ${{ matrix.language }} 33 | queries: +security-and-quality 34 | 35 | - name: Autobuild 36 | uses: github/codeql-action/autobuild@v2 37 | 38 | - name: Perform CodeQL Analysis 39 | uses: github/codeql-action/analyze@v2 40 | with: 41 | category: "/language:${{ matrix.language }}" 42 | -------------------------------------------------------------------------------- /.github/workflows/msys2-clang.yml: -------------------------------------------------------------------------------- 1 | name: MSYS2-CLANG-CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | windows-mingw: 7 | name: ${{ matrix.msystem }} 8 | runs-on: windows-latest 9 | defaults: 10 | run: 11 | shell: msys2 {0} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - msystem: "MINGW64" 17 | install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang 18 | type: Release 19 | - msystem: "MINGW32" 20 | install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang 21 | type: Release 22 | - msystem: "MINGW64" 23 | install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-clang 24 | type: Debug 25 | - msystem: "MINGW32" 26 | install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-clang 27 | type: Debug 28 | env: 29 | CMAKE_GENERATOR: Ninja 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | - uses: msys2/setup-msys2@v2 34 | with: 35 | update: true 36 | msystem: ${{ matrix.msystem }} 37 | install: ${{ matrix.install }} 38 | - name: Build and Test 39 | run: | 40 | mkdir build 41 | cd build 42 | cmake -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=${{ matrix.type }} .. 43 | cmake --build . --verbose 44 | ctest -j4 --output-on-failure 45 | -------------------------------------------------------------------------------- /.github/workflows/msys2.yml: -------------------------------------------------------------------------------- 1 | name: MSYS2-CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | windows-mingw: 7 | name: ${{ matrix.msystem }} 8 | runs-on: windows-latest 9 | defaults: 10 | run: 11 | shell: msys2 {0} 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - msystem: "MINGW64" 17 | install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc 18 | type: Release 19 | - msystem: "MINGW32" 20 | install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc 21 | type: Release 22 | - msystem: "MINGW64" 23 | install: mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc 24 | type: Debug 25 | - msystem: "MINGW32" 26 | install: mingw-w64-i686-cmake mingw-w64-i686-ninja mingw-w64-i686-gcc 27 | type: Debug 28 | env: 29 | CMAKE_GENERATOR: Ninja 30 | 31 | steps: 32 | - uses: actions/checkout@v3 33 | - uses: msys2/setup-msys2@v2 34 | with: 35 | update: true 36 | msystem: ${{ matrix.msystem }} 37 | install: ${{ matrix.install }} 38 | - name: Build and Test 39 | run: | 40 | mkdir build 41 | cd build 42 | cmake -DCMAKE_BUILD_TYPE=${{ matrix.type }} .. 43 | cmake --build . --verbose 44 | ctest -j4 --output-on-failure 45 | -------------------------------------------------------------------------------- /.github/workflows/rhub.yml: -------------------------------------------------------------------------------- 1 | name: rhub 2 | 3 | 'on': 4 | - push 5 | - pull_request 6 | jobs: 7 | ubuntu-build: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v3 11 | - name: start docker 12 | run: | 13 | docker run -w /src -dit --name rhub -v $PWD:/src rhub/rocker-gcc-san 14 | echo 'docker exec rhub "$@";' > ./rhub.sh 15 | chmod +x ./rhub.sh 16 | - name: build 17 | run: | 18 | ./rhub.sh c++ tests/unit.cpp -I include 19 | - name: test 20 | run: | 21 | ./rhub.sh ./a.out 22 | -------------------------------------------------------------------------------- /.github/workflows/ubuntu.yml: -------------------------------------------------------------------------------- 1 | name: Ubuntu 22.04 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ubuntu-build: 7 | runs-on: ubuntu-22.04 8 | strategy: 9 | matrix: 10 | cxx: [g++-12, clang++-14] 11 | steps: 12 | - uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 13 | - name: Use cmake 14 | run: | 15 | mkdir build && 16 | cd build && 17 | cmake -DFAST_DOUBLE_BENCHMARKS=ON .. && 18 | cmake --build . && 19 | ctest -j --output-on-failure 20 | -------------------------------------------------------------------------------- /.github/workflows/vs17-ci.yml: -------------------------------------------------------------------------------- 1 | name: VS17-CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ci: 7 | if: >- 8 | ! contains(toJSON(github.event.commits.*.message), '[skip ci]') && 9 | ! contains(toJSON(github.event.commits.*.message), '[skip github]') 10 | name: windows-vs17 11 | runs-on: windows-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - {gen: Visual Studio 17 2022, arch: Win32} 17 | - {gen: Visual Studio 17 2022, arch: Win32} 18 | - {gen: Visual Studio 17 2022, arch: x64} 19 | - {gen: Visual Studio 17 2022, arch: x64} 20 | steps: 21 | - name: checkout 22 | uses: actions/checkout@v3 23 | - name: Configure 24 | run: | 25 | cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -B build 26 | - name: Build Debug 27 | run: cmake --build build --config Debug --verbose 28 | - name: Build Release 29 | run: cmake --build build --config Release --verbose 30 | - name: Run Release tests 31 | run: | 32 | cd build 33 | ctest -C Release --output-on-failure 34 | - name: Run Debug tests 35 | run: | 36 | cd build 37 | ctest -C Debug --output-on-failure 38 | -------------------------------------------------------------------------------- /.github/workflows/vs17-clang-ci.yml: -------------------------------------------------------------------------------- 1 | name: VS17-CLANG-CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | ci: 7 | if: >- 8 | ! contains(toJSON(github.event.commits.*.message), '[skip ci]') && 9 | ! contains(toJSON(github.event.commits.*.message), '[skip github]') 10 | name: windows-vs17 11 | runs-on: windows-latest 12 | strategy: 13 | fail-fast: false 14 | matrix: 15 | include: 16 | - {gen: Visual Studio 17 2022, arch: Win32} 17 | - {gen: Visual Studio 17 2022, arch: Win32} 18 | - {gen: Visual Studio 17 2022, arch: x64} 19 | - {gen: Visual Studio 17 2022, arch: x64} 20 | steps: 21 | - name: checkout 22 | uses: actions/checkout@v3 23 | - name: Configure 24 | run: | 25 | cmake -G "${{matrix.gen}}" -A ${{matrix.arch}} -T ClangCL -B build 26 | - name: Build Debug 27 | run: cmake --build build --config Debug --verbose 28 | - name: Build Release 29 | run: cmake --build build --config Release --verbose 30 | - name: Run Release tests 31 | run: | 32 | cd build 33 | ctest -C Release --output-on-failure 34 | - name: Run Debug tests 35 | run: | 36 | cd build 37 | ctest -C Debug --output-on-failure 38 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # Make output 35 | /benchmark 36 | /unit 37 | /build 38 | 39 | # QtCreator generic project 40 | /fast_double_parser.cflags 41 | /fast_double_parser.config 42 | /fast_double_parser.creator 43 | /fast_double_parser.creator.user 44 | /fast_double_parser.cxxflags 45 | /fast_double_parser.files 46 | /fast_double_parser.includes 47 | 48 | # Visual Studio 49 | /.vs 50 | /out 51 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "benchmark/dependencies/abseil-cpp"] 2 | path = benchmarks/dependencies/abseil-cpp 3 | url = https://github.com/abseil/abseil-cpp.git 4 | [submodule "benchmark/dependencies/double-conversion"] 5 | path = benchmarks/dependencies/double-conversion 6 | url = https://github.com/google/double-conversion.git 7 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | cc_library( 2 | name = "fast_double_parser", 3 | hdrs = ["include/fast_double_parser.h"], 4 | strip_include_prefix = "include", 5 | visibility = ["//visibility:public"], 6 | ) 7 | 8 | cc_test( 9 | name = "unit", 10 | srcs = ["tests/unit.cpp"], 11 | deps = [":fast_double_parser"], 12 | ) 13 | 14 | cc_binary( 15 | name = "benchmark", 16 | srcs = ["benchmarks/benchmark.cpp"], 17 | deps = [ 18 | ":fast_double_parser", 19 | "@abseil-cpp//absl/strings", 20 | "@double-conversion", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | cmake_policy(SET CMP0048 NEW) 3 | 4 | project(fast_double_parser LANGUAGES CXX VERSION 0.0.0.0) 5 | set(CMAKE_CXX_STANDARD 11) 6 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 7 | if (NOT CMAKE_BUILD_TYPE) 8 | message(STATUS "No build type selected, default to Release") 9 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) 10 | endif() 11 | 12 | option(FAST_DOUBLE_PARSER_SANITIZE "Sanitize addresses" OFF) 13 | 14 | set(headers include/fast_double_parser.h) 15 | set(unit_src tests/unit.cpp) 16 | set(bogus_src tests/bogus.cpp) 17 | set(rebogus_src tests/bogus.cpp) 18 | 19 | set(benchmark_src benchmarks/benchmark.cpp) 20 | 21 | 22 | add_library(fast_double_parser INTERFACE) 23 | target_include_directories(fast_double_parser 24 | INTERFACE 25 | $ 26 | $ 27 | ) 28 | 29 | include(GNUInstallDirs) 30 | install(FILES ${headers} DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") 31 | install(TARGETS fast_double_parser EXPORT fast_double_parser-targets) 32 | install( 33 | EXPORT fast_double_parser-targets 34 | DESTINATION "share/fast_double_parser" 35 | NAMESPACE fast_double_parser:: 36 | ) 37 | 38 | include(CMakePackageConfigHelpers) 39 | configure_package_config_file( 40 | "${CMAKE_CURRENT_SOURCE_DIR}/fast_double_parser-config.cmake.in" 41 | "${CMAKE_CURRENT_BINARY_DIR}/fast_double_parser-config.cmake" 42 | INSTALL_DESTINATION "share/fast_double_parser" 43 | ) 44 | install( 45 | FILES "${CMAKE_CURRENT_BINARY_DIR}/fast_double_parser-config.cmake" 46 | DESTINATION "share/fast_double_parser" 47 | ) 48 | 49 | option(BUILD_TESTING "Build unit tests" ON) 50 | if(BUILD_TESTING) 51 | add_executable(unit ${unit_src} ${bogus_src} ${rebogus_src}) 52 | 53 | if(FAST_DOUBLE_PARSER_SANITIZE) 54 | target_compile_options(unit PUBLIC -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all) 55 | target_link_options(unit PUBLIC -fsanitize=address -fno-omit-frame-pointer -fsanitize=undefined -fno-sanitize-recover=all) 56 | # Ubuntu bug for GCC 5.0+ (safe for all versions) 57 | if (CMAKE_COMPILER_IS_GNUCC AND NOT APPLE) 58 | target_link_libraries(unit PUBLIC -fuse-ld=gold) 59 | endif() 60 | endif() 61 | target_link_libraries(unit PRIVATE fast_double_parser) 62 | 63 | enable_testing() 64 | add_test(unit unit) 65 | endif() 66 | 67 | option(FAST_DOUBLE_BENCHMARKS "include benchmarks" OFF) 68 | 69 | 70 | 71 | if(FAST_DOUBLE_BENCHMARKS) 72 | 73 | include(FetchContent) 74 | include(ExternalProject) 75 | 76 | set(ABSL_ENABLE_INSTALL ON) 77 | set(ABSL_RUN_TEST OFF CACHE INTERNAL "") 78 | set(ABSL_USE_GOOGLETEST_HEAD OFF CACHE INTERNAL "") 79 | 80 | FetchContent_Declare(abseil 81 | GIT_REPOSITORY https://github.com/abseil/abseil-cpp.git 82 | GIT_TAG "20210324.2") 83 | FetchContent_GetProperties(abseil) 84 | if(NOT abseil_POPULATED) 85 | set(BUILD_TESTING OFF) 86 | FetchContent_Populate(abseil) 87 | add_subdirectory(${abseil_SOURCE_DIR} ${abseil_BINARY_DIR}) 88 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${abseil_SOURCE_DIR}/absl/copts) 89 | include(${abseil_SOURCE_DIR}/absl/copts/AbseilConfigureCopts.cmake) 90 | endif() 91 | 92 | FetchContent_Declare(doubleconversion 93 | GIT_REPOSITORY https://github.com/google/double-conversion.git 94 | GIT_TAG "v3.1.5") 95 | FetchContent_GetProperties(doubleconversion) 96 | FetchContent_MakeAvailable(doubleconversion) 97 | 98 | add_executable(benchmark ${benchmark_src}) 99 | target_link_libraries(benchmark PUBLIC double-conversion absl::strings) 100 | target_include_directories(benchmark PUBLIC include) 101 | endif(FAST_DOUBLE_BENCHMARKS) 102 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2020 Daniel Lemire 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE.BSL: -------------------------------------------------------------------------------- 1 | Copyright (c) Daniel Lemire 2 | 3 | Boost Software License - Version 1.0 - August 17th, 2003 4 | 5 | Permission is hereby granted, free of charge, to any person or organization 6 | obtaining a copy of the software and accompanying documentation covered by 7 | this license (the "Software") to use, reproduce, display, distribute, 8 | execute, and transmit the Software, and to prepare derivative works of the 9 | Software, and to permit third-parties to whom the Software is furnished to 10 | do so, all subject to the following: 11 | 12 | The copyright notices in the Software and this entire statement, including 13 | the above license grant, this restriction and the following disclaimer, 14 | must be included in all copies of the Software, in whole or in part, and 15 | all derivative works of the Software, unless such copies or derivative 16 | works are solely in the form of machine-executable object code generated by 17 | a source language processor. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /MODULE.bazel: -------------------------------------------------------------------------------- 1 | """fast_double_parser: 4x faster than strtod.""" 2 | 3 | module( 4 | name = "fast_double_parser", 5 | version = "0.8.0", 6 | compatibility_level = 0, 7 | ) 8 | 9 | bazel_dep(name = "abseil-cpp", version = "20240722.0", dev_dependency = True) 10 | bazel_dep(name = "double-conversion", version = "3.3.0", dev_dependency = True) 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: benchmark unit 2 | 3 | LIBABSEIL:=benchmarks/dependencies/abseil-cpp/build/absl/strings/libabsl_strings.a 4 | LIBABSEIL_INCLUDE:=-Ibenchmarks/dependencies/abseil-cpp 5 | LIBABSEIL_LIBS:=-Lbenchmarks/dependencies/abseil-cpp/build/absl/base -Lbenchmarks/dependencies/abseil-cpp/build/absl/strings -Lbenchmarks/dependencies/abseil-cpp/build/absl/numeric/ -labsl_strings -labsl_raw_logging_internal -labsl_throw_delegate -labsl_int128 6 | 7 | 8 | LIBDOUBLE:=benchmarks/dependencies/double-conversion/libdouble-conversion.a 9 | LIBDOUBLE_INCLUDE:=-Ibenchmarks/dependencies/double-conversion 10 | LIBDOUBLE_LIBS:=-Lbenchmarks/dependencies/double-conversion -ldouble-conversion 11 | 12 | 13 | headers:= include/fast_double_parser.h 14 | 15 | benchmark: ./benchmarks/benchmark.cpp $(headers) $(LIBABSEIL) $(LIBDOUBLE) $(headers) 16 | $(CXX) -O2 -std=c++14 -march=haswell -o benchmark ./benchmarks/benchmark.cpp -Wall -Iinclude $(LIBABSEIL_INCLUDE) $(LIBDOUBLE_INCLUDE) $(LIBDOUBLE_LIBS) $(LIBABSEIL_LIBS) -lm 17 | 18 | 19 | unit: ./tests/unit.cpp $(headers) 20 | $(CXX) -O2 -std=c++14 -march=native -o unit ./tests/unit.cpp -Wall -Iinclude 21 | 22 | 23 | bench: benchmark 24 | ./benchmark 25 | ./benchmark benchmarks/data/canada.txt 26 | 27 | submodules: 28 | -git submodule update --init --recursive 29 | -touch submodules 30 | 31 | 32 | $(LIBABSEIL):submodules 33 | rm -r -f benchmarks/dependencies/abseil-cpp/build && cd benchmarks/dependencies/abseil-cpp && mkdir build && cd build && cmake .. -DABSL_RUN_TESTS=OFF -DABSL_USE_GOOGLETEST_HEAD=OFF -DCMAKE_CXX_STANDARD=14 -DCMAKE_BUILD_TYPE=Release && cmake --build . --target base && cmake --build . --target strings 34 | 35 | $(LIBDOUBLE):submodules 36 | cd benchmarks/dependencies/double-conversion && cmake . -DCMAKE_BUILD_TYPE=Release && make 37 | 38 | clean: 39 | rm -r -f benchmark unit benchmarks/dependencies/abseil-cpp/build 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fast_double_parser: 4x faster than strtod 2 | ![MSYS2-CI](https://github.com/lemire/fast_double_parser/workflows/MSYS2-CI/badge.svg)[![Ubuntu 22.04](https://github.com/lemire/fast_double_parser/actions/workflows/ubuntu.yml/badge.svg)](https://github.com/lemire/fast_double_parser/actions/workflows/ubuntu.yml) 3 | 4 | ### :exclamation: :exclamation: :exclamation: :exclamation: We encourage users to adopt [fast_float](https://github.com/fastfloat/fast_float?tab=readme-ov-file#you-may-also-enforce-the-json-format-rfc-8259) library instead of fast_double_parser. It has more functionality. It provides full support for the JSON syntax and the same great speed. The fast_float library is part of most major Web browsers, GCC and so forth: it is well tested and supported. :exclamation: :exclamation: :exclamation: :exclamation: 5 | 6 | 7 | ## Introduction 8 | 9 | 10 | Fast function to parse ASCII strings containing decimal numbers into double-precision (binary64) floating-point values. That is, given the string "1.0e10", it should return a 64-bit floating-point value equal to 10000000000. We do not sacrifice accuracy. The function will match exactly (down the smallest bit) the result of a standard function like `strtod`. 11 | 12 | We support all major compilers: Visual Studio, GNU GCC, LLVM Clang. We require C++11. 13 | 14 | The core of this library was ported to Go by Nigel Tao and is now a standard float-parsing routine in Go (`strconv.ParseFloat`). 15 | 16 | 17 | ## Reference 18 | 19 | - Daniel Lemire, [Number Parsing at a Gigabyte per Second](https://arxiv.org/abs/2101.11408), Software: Practice and Experience 51 (8), 2021. 20 | 21 | 22 | 23 | ## Usage 24 | 25 | You should be able to just drop the header file into your project, it is a header-only library. 26 | 27 | 28 | The current API is simple enough: 29 | 30 | ```C++ 31 | #include "fast_double_parser.h" // the file is in the include directory 32 | 33 | 34 | double x; 35 | char * string = ... 36 | const char * endptr = fast_double_parser::parse_number(string, &x); 37 | ``` 38 | 39 | You must check the value returned (`endptr`): if it is `nullptr`, then the function refused to parse the input. 40 | Otherwise, we return a pointer (`const char *`) to the end of the parsed string. The provided 41 | pointer (`string`) should point at the beginning of the number: if you must skip whitespace characters, 42 | it is your responsibility to do so. 43 | 44 | 45 | We expect string numbers to follow [RFC 7159](https://tools.ietf.org/html/rfc7159) (JSON standard). In particular, 46 | the parser will reject overly large values that would not fit in binary64. It will not accept 47 | NaN or infinite values. 48 | 49 | It works much like the C standard function `strtod` expect that the parsing is locale-independent. E.g., it will parse 0.5 as 1/2, but it will not parse 0,5 as 50 | 1/2 even if you are under a French system. Locale independence is by design (it is not a limitation). Like the standard C functions, it expects that the string 51 | representation of your number ends with a non-number character (e.g., a null character, a space, a colon, etc.). If you wish the specify the end point of the string, as is common in C++, please consider the [fast_float](https://github.com/lemire/fast_float) C++ library instead. 52 | 53 | 54 | We assume that the rounding mode is set to nearest, the default setting (`std::fegetround() == FE_TONEAREST`). It is uncommon to have a different setting. 55 | 56 | ## What if I prefer another API? 57 | 58 | The [fast_float](https://github.com/lemire/fast_float) offers an API resembling that of the C++17 `std::from_chars` functions. In particular, you can specify the beginning and the end of the string. 59 | Furthermore [fast_float](https://github.com/lemire/fast_float) supports both 32-bit and 64-bit floating-point numbers. The [fast_float](https://github.com/lemire/fast_float) library is part of Apache Arrow, GCC 12, Safari/WebKit and other important systems. 60 | 61 | ## Why should I expect this function to be faster? 62 | 63 | Parsing strings into binary numbers (IEEE 754) is surprisingly difficult. Parsing a single number can take hundreds of instructions and CPU cycles, if not thousands. It is relatively easy to parse numbers faster if you sacrifice accuracy (e.g., tolerate 1 ULP errors), but we are interested in "perfect" parsing. 64 | 65 | Instead of trying to solve the general problem, we cover what we believe are the most common scenarios, providing really fast parsing. We fall back on the standard library for the difficult cases. We believe that, in this manner, we achieve the best performance on some of the most important cases. 66 | 67 | We have benchmarked our parser on a collection of strings from a sample geojson file (canada.json). Here are some of our results: 68 | 69 | 70 | | parser | MB/s | 71 | | ------------------------------------- | ---- | 72 | | fast_double_parser | 660 MB/s | 73 | | abseil, from_chars | 330 MB/s | 74 | | double_conversion | 250 MB/s | 75 | | strtod | 70 MB/s | 76 | 77 | (configuration: Apple clang version 11.0.0, I7-7700K) 78 | 79 | We expect string numbers to follow [RFC 7159](https://tools.ietf.org/html/rfc7159). In particular, 80 | the parser will reject overly large values that would not fit in binary64. It will not produce 81 | NaN or infinite values. It will refuse to parse `001` or `0.` as these are invalid number strings as 82 | per the [JSON specification](https://tools.ietf.org/html/rfc7159). Users who prefer a more 83 | lenient C++ parser may consider the [fast_float](https://github.com/lemire/fast_float) C++ library. 84 | 85 | The parsing is locale-independent. E.g., it will parse 0.5 as 1/2, but it will not parse 0,5 as 86 | 1/2 even if you are under a French system. 87 | 88 | 89 | ## Requirements 90 | 91 | 92 | If you want to run our benchmarks, you should have 93 | 94 | - Windows, Linux or macOS; presumably other systems can be supported as well 95 | - A recent C++ compiler 96 | - A recent cmake (cmake 3.11 or better) is necessary for the benchmarks 97 | 98 | This code falls back on your platform's `strdtod_l` /`_strtod_l` implementation for numbers with a long decimal mantissa (more than 19 digits). 99 | 100 | ## Usage (benchmarks) 101 | 102 | ``` 103 | git clone https://github.com/lemire/fast_double_parser.git 104 | cd fast_double_parser 105 | mkdir build 106 | cd build 107 | cmake .. -DFAST_DOUBLE_BENCHMARKS=ON 108 | cmake --build . --config Release 109 | ctest . 110 | ./benchmark 111 | ``` 112 | Under Windows, the last line should be `./Release/benchmark.exe`. 113 | 114 | Be mindful that the benchmarks include the abseil library which is not supported everywhere. 115 | 116 | ## Sample results 117 | 118 | 119 | ``` 120 | $ ./benchmark 121 | parsing random integers in the range [0,1) 122 | 123 | 124 | === trial 1 === 125 | fast_double_parser 460.64 MB/s 126 | strtod 186.90 MB/s 127 | abslfromch 168.61 MB/s 128 | absl 140.62 MB/s 129 | double-conv 206.15 MB/s 130 | 131 | 132 | === trial 2 === 133 | fast_double_parser 449.76 MB/s 134 | strtod 174.59 MB/s 135 | abslfromch 152.68 MB/s 136 | absl 157.52 MB/s 137 | double-conv 193.97 MB/s 138 | 139 | 140 | ``` 141 | 142 | ``` 143 | $ ./benchmark benchmarks/data/canada.txt 144 | read 111126 lines 145 | 146 | 147 | === trial 1 === 148 | fast_double_parser 662.01 MB/s 149 | strtod 69.73 MB/s 150 | abslfromch 341.74 MB/s 151 | absl 325.23 MB/s 152 | double-conv 249.68 MB/s 153 | 154 | 155 | === trial 2 === 156 | fast_double_parser 611.56 MB/s 157 | strtod 69.53 MB/s 158 | abslfromch 330.00 MB/s 159 | absl 328.45 MB/s 160 | double-conv 243.90 MB/s 161 | ``` 162 | 163 | 164 | ## Ports and users 165 | 166 | - The algorithm is part of the [LLVM standard libraries](https://github.com/llvm/llvm-project/commit/87c016078ad72c46505461e4ff8bfa04819fe7ba). 167 | - This library has been ported to Go and integrated in the Go standard library. 168 | - It is part of the [Microsoft LightGBM machine-learning framework](https://github.com/microsoft/LightGBM). 169 | - It is part of the database engine [noisepage](https://github.com/cmu-db/noisepage). 170 | - The library has been reimplemented in [Google wuffs](https://github.com/google/wuffs/). 171 | - [There is a Julia port](https://github.com/JuliaData/Parsers.jl). 172 | - [There is a Rust port](https://github.com/ezrosent/frawk/tree/master/src/runtime/float_parse). 173 | - [There is a Java port](https://github.com/wrandelshofer/FastDoubleParser). 174 | - [There is a C# port](https://github.com/CarlVerret/csFastFloat). 175 | - [Bazel Central Registry](https://registry.bazel.build/modules/fast_double_parser). 176 | 177 | ## Credit 178 | 179 | Contributions are invited. 180 | 181 | This is based on an original idea by Michael Eisel (joint work). 182 | 183 | ## License 184 | 185 | You may use this code under either the Apache License 2.0 or 186 | the Boost Software License 1.0. 187 | -------------------------------------------------------------------------------- /benchmarks/benchmark.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "absl/strings/charconv.h" 3 | #include "absl/strings/numbers.h" 4 | #include "fast_double_parser.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include "double-conversion/ieee.h" 23 | #include "double-conversion/double-conversion.h" 24 | 25 | double findmax_fast_double_parser(const std::vector& s) { 26 | double answer = 0; 27 | double x; 28 | for (const std::string & st : s) { 29 | bool isok = fast_double_parser::parse_number(st.c_str(), &x); 30 | if (!isok) 31 | throw std::runtime_error("bug in findmax_fast_double_parser"); 32 | answer = answer > x ? answer : x; 33 | } 34 | return answer; 35 | } 36 | 37 | 38 | double findmax_strtod(const std::vector& s) { 39 | double answer = 0; 40 | double x = 0; 41 | for (const std::string& st : s) { 42 | char *pr = (char *)st.data(); 43 | #ifdef _WIN32 44 | static _locale_t c_locale = _create_locale(LC_ALL, "C"); 45 | x = _strtod_l(st.data(), &pr, c_locale); 46 | #else 47 | static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); 48 | x = strtod_l(st.data(), &pr, c_locale); 49 | #endif 50 | if ((pr == nullptr) || (pr == st.data())) { 51 | throw std::runtime_error("bug in findmax_strtod"); 52 | } 53 | answer = answer > x ? answer : x; 54 | } 55 | return answer; 56 | } 57 | 58 | double findmax_absl(const std::vector& s) { 59 | double answer = 0; 60 | double x = 0; 61 | for (const std::string& st : s) { 62 | bool isok = absl::SimpleAtod(st, &x); 63 | if (!isok) { 64 | throw std::runtime_error("bug in findmax_absl"); 65 | } 66 | answer = answer > x ? answer : x; 67 | } 68 | return answer; 69 | } 70 | 71 | double findmax_absl_from_chars(const std::vector& s) { 72 | double answer = 0; 73 | double x = 0; 74 | for (const std::string& st : s) { 75 | auto res = absl::from_chars(st.data(), st.data() + st.size(), x); 76 | if (res.ptr == st.data()) { 77 | throw std::runtime_error("bug in findmax_absl_from_chars"); 78 | } 79 | answer = answer > x ? answer : x; 80 | } 81 | return answer; 82 | } 83 | 84 | double findmax_doubleconversion(const std::vector& s) { 85 | double answer = 0; 86 | double x; 87 | int flags = double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | 88 | double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK | 89 | double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES; 90 | double empty_string_value = 0.0; 91 | uc16 separator = double_conversion::StringToDoubleConverter::kNoSeparator; 92 | double_conversion::StringToDoubleConverter converter( 93 | flags, empty_string_value, double_conversion::Double::NaN(), NULL, NULL, 94 | separator); 95 | int processed_characters_count; 96 | for (const std::string& st : s) { 97 | x = converter.StringToDouble(st.data(), int(st.size()), 98 | &processed_characters_count); 99 | if (processed_characters_count == 0) { 100 | throw std::runtime_error("bug in findmax_doubleconversion"); 101 | } 102 | answer = answer > x ? answer : x; 103 | } 104 | return answer; 105 | } 106 | 107 | // ulp distance 108 | // Marc B. Reynolds, 2016-2019 109 | // Public Domain under http://unlicense.org, see link for details. 110 | // adapted by D. Lemire 111 | inline uint64_t f64_ulp_dist(double a, double b) { 112 | uint64_t ua, ub; 113 | memcpy(&ua, &a, sizeof(ua)); 114 | memcpy(&ub, &b, sizeof(ub)); 115 | if ((int64_t)(ub ^ ua) >= 0) 116 | return (int64_t)(ua - ub) >= 0 ? (ua - ub) : (ub - ua); 117 | return ua + ub + 0x80000000; 118 | } 119 | 120 | void validate(const std::vector& s) { 121 | 122 | double x, xref; 123 | int flags = double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | 124 | double_conversion::StringToDoubleConverter::ALLOW_TRAILING_JUNK | 125 | double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES; 126 | double empty_string_value = 0.0; 127 | uc16 separator = double_conversion::StringToDoubleConverter::kNoSeparator; 128 | double_conversion::StringToDoubleConverter converter( 129 | flags, empty_string_value, double_conversion::Double::NaN(), NULL, NULL, 130 | separator); 131 | int processed_characters_count; 132 | for (const std::string& st : s) { 133 | #ifdef _WIN32 134 | static _locale_t c_locale = _create_locale(LC_ALL, "C"); 135 | xref = _strtod_l(st.data(), nullptr, c_locale); 136 | #else 137 | static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); 138 | xref = strtod_l(st.data(), nullptr, c_locale); 139 | #endif 140 | x = converter.StringToDouble(st.data(), int(st.size()), 141 | &processed_characters_count); 142 | if (xref != x) { 143 | std::cerr << "double conversion disagrees" << std::endl; 144 | printf("double conversion: %.*e\n", DBL_DIG + 1, x); 145 | printf("reference: %.*e\n", DBL_DIG + 1, xref); 146 | printf("string: %s\n", st.c_str()); 147 | printf("f64_ulp_dist = %d\n", (int)f64_ulp_dist(x, xref)); 148 | throw std::runtime_error("double conversion disagrees"); 149 | } 150 | absl::from_chars(st.data(), st.data() + st.size(), x); 151 | if (xref != x) { 152 | std::cerr << "abseil from_chars disagrees" << std::endl; 153 | printf("abseil from_chars: %.*e\n", DBL_DIG + 1, x); 154 | printf("reference: %.*e\n", DBL_DIG + 1, xref); 155 | printf("string: %s\n", st.c_str()); 156 | printf("f64_ulp_dist = %d\n", (int)f64_ulp_dist(x, xref)); 157 | throw std::runtime_error("abseil from_chars disagrees"); 158 | } 159 | bool isok = absl::SimpleAtod(st, &x); 160 | if (!isok) { 161 | throw std::runtime_error("bug in absl::SimpleAtod"); 162 | } 163 | if (xref != x) { 164 | std::cerr << "abseil disagrees" << std::endl; 165 | printf("abseil: %.*e\n", DBL_DIG + 1, x); 166 | printf("reference: %.*e\n", DBL_DIG + 1, xref); 167 | printf("string: %s\n", st.c_str()); 168 | printf("f64_ulp_dist = %d\n", (int)f64_ulp_dist(x, xref)); 169 | throw std::runtime_error("abseil disagrees"); 170 | } 171 | isok = fast_double_parser::parse_number(st.c_str(), &x); 172 | if (!isok) { 173 | printf("fast_double_parser refused to parse %s\n", st.c_str()); 174 | throw std::runtime_error("fast_double_parser refused to parse"); 175 | } 176 | if (xref != x) { 177 | std::cerr << "fast_double_parser disagrees" << std::endl; 178 | printf("fast_double_parser: %.*e\n", DBL_DIG + 1, x); 179 | printf("reference: %.*e\n", DBL_DIG + 1, xref); 180 | printf("string: %s\n", st.c_str()); 181 | printf("f64_ulp_dist = %d\n", (int)f64_ulp_dist(x, xref)); 182 | throw std::runtime_error("fast_double_parser disagrees"); 183 | } 184 | } 185 | } 186 | 187 | void printvec(const std::vector& evts, size_t volume) { 188 | printf("%.2f cycles %.2f instr %.4f branch miss %.2f cache ref %.2f cache " 189 | "miss \n", 190 | evts[0] * 1.0 / volume, evts[1] * 1.0 / volume, evts[2] * 1.0 / volume, 191 | evts[3] * 1.0 / volume, evts[4] * 1.0 / volume); 192 | } 193 | 194 | void process(const std::vector& lines, size_t volume) { 195 | double volumeMB = volume / (1024. * 1024.); 196 | // size_t howmany = lines.size(); 197 | std::chrono::high_resolution_clock::time_point t1, t2; 198 | double dif, ts; 199 | for (size_t i = 0; i < 3; i++) { 200 | if (i > 0) 201 | printf("=== trial %zu ===\n", i); 202 | 203 | t1 = std::chrono::high_resolution_clock::now(); 204 | ts = findmax_fast_double_parser(lines); 205 | t2 = std::chrono::high_resolution_clock::now(); 206 | if (ts == 0) 207 | printf("bug\n"); 208 | dif = double(std::chrono::duration_cast(t2 - t1).count()); 209 | if (i > 0) 210 | printf("fast_double_parser %.2f MB/s\n", volumeMB * 1000000000 / dif); 211 | t1 = std::chrono::high_resolution_clock::now(); 212 | ts = findmax_strtod(lines); 213 | t2 = std::chrono::high_resolution_clock::now(); 214 | if (ts == 0) 215 | printf("bug\n"); 216 | dif = double(std::chrono::duration_cast(t2 - t1).count()); 217 | if (i > 0) 218 | printf("strtod %.2f MB/s\n", volumeMB * 1000000000 / dif); 219 | t1 = std::chrono::high_resolution_clock::now(); 220 | ts = findmax_absl_from_chars(lines); 221 | t2 = std::chrono::high_resolution_clock::now(); 222 | if (ts == 0) 223 | printf("bug\n"); 224 | dif = double(std::chrono::duration_cast(t2 - t1).count()); 225 | if (i > 0) 226 | printf("abslfromch %.2f MB/s\n", volumeMB * 1000000000 / dif); 227 | t1 = std::chrono::high_resolution_clock::now(); 228 | ts = findmax_absl(lines); 229 | t2 = std::chrono::high_resolution_clock::now(); 230 | if (ts == 0) 231 | printf("bug\n"); 232 | dif = double(std::chrono::duration_cast(t2 - t1).count()); 233 | if (i > 0) 234 | printf("absl %.2f MB/s\n", volumeMB * 1000000000 / dif); 235 | t1 = std::chrono::high_resolution_clock::now(); 236 | ts = findmax_doubleconversion(lines); 237 | t2 = std::chrono::high_resolution_clock::now(); 238 | if (ts == 0) 239 | printf("bug\n"); 240 | dif = double(std::chrono::duration_cast(t2 - t1).count()); 241 | if (i > 0) 242 | printf("double-conv %.2f MB/s\n", volumeMB * 1000000000 / dif); 243 | printf("\n\n"); 244 | } 245 | } 246 | 247 | void fileload(char *filename) { 248 | 249 | std::ifstream inputfile(filename); 250 | if (!inputfile) { 251 | std::cerr << "can't open " << filename << std::endl; 252 | return; 253 | } 254 | std::string line; 255 | std::vector lines; 256 | lines.reserve(10000); // let us reserve plenty of memory. 257 | size_t volume = 0; 258 | while (getline(inputfile, line)) { 259 | volume += line.size(); 260 | lines.push_back(line); 261 | } 262 | std::cout << "read " << lines.size() << " lines " << std::endl; 263 | validate(lines); 264 | process(lines, volume); 265 | } 266 | 267 | void demo(size_t howmany) { 268 | std::cout << "parsing random integers in the range [0,1)" << std::endl; 269 | std::vector lines; 270 | lines.reserve(howmany); // let us reserve plenty of memory. 271 | size_t volume = 0; 272 | for (size_t i = 0; i < howmany; i++) { 273 | double x = (double)rand() / RAND_MAX; 274 | std::string line = std::to_string(x); 275 | volume += line.size(); 276 | lines.push_back(line); 277 | } 278 | validate(lines); 279 | process(lines, volume); 280 | } 281 | 282 | int main(int argc, char **argv) { 283 | if (argc == 1) { 284 | demo(100 * 1000); 285 | std::cout << "You can also provide a filename: it should contain one " 286 | "string per line corresponding to a number" 287 | << std::endl; 288 | } else { 289 | fileload(argv[1]); 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /fast_double_parser-config.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/fast_double_parser-targets.cmake") 4 | -------------------------------------------------------------------------------- /include/fast_double_parser.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FAST_DOUBLE_PARSER_H 3 | #define FAST_DOUBLE_PARSER_H 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #if (defined(sun) || defined(__sun)) 14 | #define FAST_DOUBLE_PARSER_SOLARIS 15 | #endif 16 | 17 | #if defined(__CYGWIN__) || defined(__MINGW32__) || defined(__MINGW64__) 18 | #define FAST_DOUBLE_PARSER_CYGWIN 19 | #endif 20 | 21 | /** 22 | * Determining whether we should import xlocale.h or not is 23 | * a bit of a nightmare. 24 | */ 25 | #if defined(FAST_DOUBLE_PARSER_SOLARIS) || defined(FAST_DOUBLE_PARSER_CYGWIN) 26 | // Anything at all that is related to cygwin, msys, solaris and so forth will 27 | // always use this fallback because we cannot rely on it behaving as normal 28 | // gcc. 29 | #include 30 | #include 31 | // workaround for CYGWIN 32 | static inline double cygwin_strtod_l(const char* start, char** end) { 33 | double d; 34 | std::stringstream ss; 35 | ss.imbue(std::locale::classic()); 36 | ss << start; 37 | ss >> d; 38 | if(ss.fail()) { *end = nullptr; } 39 | if(ss.eof()) { ss.clear(); } 40 | auto nread = ss.tellg(); 41 | *end = const_cast(start) + nread; 42 | return d; 43 | } 44 | #else 45 | 46 | #ifdef __has_include 47 | // This is the easy case: we have __has_include and can check whether 48 | // xlocale is available. If so, we load it up. 49 | #if __has_include() 50 | #include 51 | #endif // __has_include 52 | #else // We do not have __has_include 53 | 54 | #ifdef __GLIBC__ 55 | #include 56 | #if !((__GLIBC__ > 2) || ((__GLIBC__ == 2) && (__GLIBC_MINOR__ > 25))) 57 | #include // old glibc 58 | #endif 59 | #else // not glibc 60 | #if !(defined(_WIN32) || (__FreeBSD_version < 1000010)) 61 | #include 62 | #endif 63 | #endif 64 | #endif // __has_include 65 | 66 | 67 | #endif // defined(FAST_DOUBLE_PARSER_SOLARIS) || defined(FAST_DOUBLE_PARSER_CYGWIN) 68 | 69 | 70 | 71 | #ifdef _MSC_VER 72 | #include 73 | #define WARN_UNUSED 74 | #else 75 | #define WARN_UNUSED __attribute__((warn_unused_result)) 76 | #endif 77 | 78 | namespace fast_double_parser { 79 | 80 | /** 81 | * The smallest non-zero float (binary64) is 2^−1074. 82 | * We take as input numbers of the form w x 10^q where w < 2^64. 83 | * We have that w * 10^-343 < 2^(64-344) 5^-343 < 2^-1076. 84 | * However, we have that 85 | * (2^64-1) * 10^-342 = (2^64-1) * 2^-342 * 5^-342 > 2^−1074. 86 | * Thus it is possible for a number of the form w * 10^-342 where 87 | * w is a 64-bit value to be a non-zero floating-point number. 88 | ********* 89 | * If we are solely interested in the *normal* numbers then the 90 | * smallest value is 2^-1022. We can generate a value larger 91 | * than 2^-1022 with expressions of the form w * 10^-326. 92 | * Thus we need to pick FASTFLOAT_SMALLEST_POWER >= -326. 93 | ********* 94 | * Any number of form w * 10^309 where w>= 1 is going to be 95 | * infinite in binary64 so we never need to worry about powers 96 | * of 5 greater than 308. 97 | */ 98 | #define FASTFLOAT_SMALLEST_POWER -325 99 | #define FASTFLOAT_LARGEST_POWER 308 100 | 101 | #ifdef _MSC_VER 102 | #ifndef really_inline 103 | #define really_inline __forceinline 104 | #endif // really_inline 105 | #ifndef unlikely 106 | #define unlikely(x) x 107 | #endif // unlikely 108 | #else // _MSC_VER 109 | #ifndef unlikely 110 | #define unlikely(x) __builtin_expect(!!(x), 0) 111 | #endif // unlikely 112 | #ifndef really_inline 113 | #define really_inline __attribute__((always_inline)) inline 114 | #endif // really_inline 115 | #endif // _MSC_VER 116 | 117 | struct value128 { 118 | uint64_t low; 119 | uint64_t high; 120 | }; 121 | 122 | #ifdef _MSC_VER 123 | #define FAST_DOUBLE_PARSER_VISUAL_STUDIO 1 124 | #ifdef __clang__ 125 | // clang under visual studio 126 | #define FAST_DOUBLE_PARSER_CLANG_VISUAL_STUDIO 1 127 | #else 128 | // just regular visual studio (best guess) 129 | #define FAST_DOUBLE_PARSER_REGULAR_VISUAL_STUDIO 1 130 | #endif // __clang__ 131 | #endif // _MSC_VER 132 | 133 | #if defined(FAST_DOUBLE_PARSER_REGULAR_VISUAL_STUDIO) && \ 134 | !defined(_M_X64) && !defined(_M_ARM64)// _umul128 for x86, arm 135 | // this is a slow emulation routine for 32-bit Windows 136 | // 137 | static inline uint64_t __emulu(uint32_t x, uint32_t y) { 138 | return x * (uint64_t)y; 139 | } 140 | static inline uint64_t _umul128(uint64_t ab, uint64_t cd, uint64_t *hi) { 141 | uint64_t ad = __emulu((uint32_t)(ab >> 32), (uint32_t)cd); 142 | uint64_t bd = __emulu((uint32_t)ab, (uint32_t)cd); 143 | uint64_t adbc = ad + __emulu((uint32_t)ab, (uint32_t)(cd >> 32)); 144 | uint64_t adbc_carry = !!(adbc < ad); 145 | uint64_t lo = bd + (adbc << 32); 146 | *hi = __emulu((uint32_t)(ab >> 32), (uint32_t)(cd >> 32)) + (adbc >> 32) + 147 | (adbc_carry << 32) + !!(lo < bd); 148 | return lo; 149 | } 150 | #endif 151 | 152 | // We need a backup on old systems. 153 | // credit: https://stackoverflow.com/questions/28868367/getting-the-high-part-of-64-bit-integer-multiplication 154 | really_inline uint64_t Emulate64x64to128(uint64_t& r_hi, const uint64_t x, const uint64_t y) { 155 | const uint64_t x0 = (uint32_t)x, x1 = x >> 32; 156 | const uint64_t y0 = (uint32_t)y, y1 = y >> 32; 157 | const uint64_t p11 = x1 * y1, p01 = x0 * y1; 158 | const uint64_t p10 = x1 * y0, p00 = x0 * y0; 159 | 160 | // 64-bit product + two 32-bit values 161 | const uint64_t middle = p10 + (p00 >> 32) + (uint32_t)p01; 162 | 163 | // 64-bit product + two 32-bit values 164 | r_hi = p11 + (middle >> 32) + (p01 >> 32); 165 | 166 | // Add LOW PART and lower half of MIDDLE PART 167 | return (middle << 32) | (uint32_t)p00; 168 | } 169 | 170 | really_inline value128 full_multiplication(uint64_t value1, uint64_t value2) { 171 | value128 answer; 172 | #ifdef FAST_DOUBLE_PARSER_REGULAR_VISUAL_STUDIO 173 | #ifdef _M_ARM64 174 | // ARM64 has native support for 64-bit multiplications, no need to emultate 175 | answer.high = __umulh(value1, value2); 176 | answer.low = value1 * value2; 177 | #else 178 | answer.low = _umul128(value1, value2, &answer.high); // _umul128 not available on ARM64 179 | #endif // _M_ARM64 180 | #else // SIMDJSON_REGULAR_VISUAL_STUDIO 181 | #ifdef __SIZEOF_INT128__ // this is what we have on most 32-bit systems 182 | __uint128_t r = ((__uint128_t)value1) * value2; 183 | answer.low = uint64_t(r); 184 | answer.high = uint64_t(r >> 64); 185 | #else 186 | // fallback 187 | answer.low = Emulate64x64to128(answer.high, value1, value2); 188 | #endif 189 | #endif 190 | return answer; 191 | } 192 | 193 | /* result might be undefined when input_num is zero */ 194 | inline int leading_zeroes(uint64_t input_num) { 195 | #ifdef _MSC_VER 196 | unsigned long leading_zero = 0; 197 | // Search the mask data from most significant bit (MSB) 198 | // to least significant bit (LSB) for a set bit (1). 199 | #ifdef _WIN64 200 | // To avoid warnings, let us force a return. Note that 201 | // input_num is non-zero by assumption. 202 | (void)_BitScanReverse64(&leading_zero, input_num); 203 | return (int)(63 - leading_zero); 204 | #else 205 | // To avoid warnings, let us force a return. Note that 206 | // input_num is non-zero by assumption. 207 | if (input_num >> 32) { 208 | (void)_BitScanReverse(&leading_zero, (uint32_t)(input_num >> 32)); 209 | return (int)(63 - (leading_zero + 32)); 210 | } 211 | (void)_BitScanReverse(&leading_zero, (uint32_t)input_num); 212 | return (int)(63 - leading_zero); 213 | #endif // _WIN64 214 | #else 215 | return __builtin_clzll(input_num); 216 | #endif // _MSC_VER 217 | } 218 | 219 | static inline bool is_integer(char c) { 220 | return (c >= '0' && c <= '9'); 221 | // this gets compiled to (uint8_t)(c - '0') <= 9 on all decent compilers 222 | } 223 | 224 | 225 | /** 226 | * When mapping numbers from decimal to binary, 227 | * we go from w * 10^q to m * 2^p but we have 228 | * 10^q = 5^q * 2^q, so effectively 229 | * we are trying to match 230 | * w * 2^q * 5^q to m * 2^p. Thus the powers of two 231 | * are not a concern since they can be represented 232 | * exactly using the binary notation, only the powers of five 233 | * affect the binary significand. 234 | */ 235 | 236 | // Attempts to compute i * 10^(power) exactly; and if "negative" is 237 | // true, negate the result. 238 | // This function will only work in some cases, when it does not work, success is 239 | // set to false. This should work *most of the time* (like 99% of the time). 240 | // We assume that power is in the [FASTFLOAT_SMALLEST_POWER, 241 | // FASTFLOAT_LARGEST_POWER] interval: the caller is responsible for this check. 242 | really_inline double compute_float_64(int64_t power, uint64_t i, bool negative, 243 | bool *success) { 244 | 245 | // Precomputed powers of ten from 10^0 to 10^22. These 246 | // can be represented exactly using the double type. 247 | static const double power_of_ten[] = { 248 | 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 249 | 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22}; 250 | 251 | // The mantissas of powers of ten from -308 to 308, extended out to sixty four 252 | // bits. The array contains the powers of ten approximated 253 | // as a 64-bit mantissa. It goes from 10^FASTFLOAT_SMALLEST_POWER to 254 | // 10^FASTFLOAT_LARGEST_POWER (inclusively). 255 | // The mantissa is truncated, and 256 | // never rounded up. Uses about 5KB. 257 | static const uint64_t mantissa_64[] = { 258 | 0xa5ced43b7e3e9188, 0xcf42894a5dce35ea, 259 | 0x818995ce7aa0e1b2, 0xa1ebfb4219491a1f, 260 | 0xca66fa129f9b60a6, 0xfd00b897478238d0, 261 | 0x9e20735e8cb16382, 0xc5a890362fddbc62, 262 | 0xf712b443bbd52b7b, 0x9a6bb0aa55653b2d, 263 | 0xc1069cd4eabe89f8, 0xf148440a256e2c76, 264 | 0x96cd2a865764dbca, 0xbc807527ed3e12bc, 265 | 0xeba09271e88d976b, 0x93445b8731587ea3, 266 | 0xb8157268fdae9e4c, 0xe61acf033d1a45df, 267 | 0x8fd0c16206306bab, 0xb3c4f1ba87bc8696, 268 | 0xe0b62e2929aba83c, 0x8c71dcd9ba0b4925, 269 | 0xaf8e5410288e1b6f, 0xdb71e91432b1a24a, 270 | 0x892731ac9faf056e, 0xab70fe17c79ac6ca, 271 | 0xd64d3d9db981787d, 0x85f0468293f0eb4e, 272 | 0xa76c582338ed2621, 0xd1476e2c07286faa, 273 | 0x82cca4db847945ca, 0xa37fce126597973c, 274 | 0xcc5fc196fefd7d0c, 0xff77b1fcbebcdc4f, 275 | 0x9faacf3df73609b1, 0xc795830d75038c1d, 276 | 0xf97ae3d0d2446f25, 0x9becce62836ac577, 277 | 0xc2e801fb244576d5, 0xf3a20279ed56d48a, 278 | 0x9845418c345644d6, 0xbe5691ef416bd60c, 279 | 0xedec366b11c6cb8f, 0x94b3a202eb1c3f39, 280 | 0xb9e08a83a5e34f07, 0xe858ad248f5c22c9, 281 | 0x91376c36d99995be, 0xb58547448ffffb2d, 282 | 0xe2e69915b3fff9f9, 0x8dd01fad907ffc3b, 283 | 0xb1442798f49ffb4a, 0xdd95317f31c7fa1d, 284 | 0x8a7d3eef7f1cfc52, 0xad1c8eab5ee43b66, 285 | 0xd863b256369d4a40, 0x873e4f75e2224e68, 286 | 0xa90de3535aaae202, 0xd3515c2831559a83, 287 | 0x8412d9991ed58091, 0xa5178fff668ae0b6, 288 | 0xce5d73ff402d98e3, 0x80fa687f881c7f8e, 289 | 0xa139029f6a239f72, 0xc987434744ac874e, 290 | 0xfbe9141915d7a922, 0x9d71ac8fada6c9b5, 291 | 0xc4ce17b399107c22, 0xf6019da07f549b2b, 292 | 0x99c102844f94e0fb, 0xc0314325637a1939, 293 | 0xf03d93eebc589f88, 0x96267c7535b763b5, 294 | 0xbbb01b9283253ca2, 0xea9c227723ee8bcb, 295 | 0x92a1958a7675175f, 0xb749faed14125d36, 296 | 0xe51c79a85916f484, 0x8f31cc0937ae58d2, 297 | 0xb2fe3f0b8599ef07, 0xdfbdcece67006ac9, 298 | 0x8bd6a141006042bd, 0xaecc49914078536d, 299 | 0xda7f5bf590966848, 0x888f99797a5e012d, 300 | 0xaab37fd7d8f58178, 0xd5605fcdcf32e1d6, 301 | 0x855c3be0a17fcd26, 0xa6b34ad8c9dfc06f, 302 | 0xd0601d8efc57b08b, 0x823c12795db6ce57, 303 | 0xa2cb1717b52481ed, 0xcb7ddcdda26da268, 304 | 0xfe5d54150b090b02, 0x9efa548d26e5a6e1, 305 | 0xc6b8e9b0709f109a, 0xf867241c8cc6d4c0, 306 | 0x9b407691d7fc44f8, 0xc21094364dfb5636, 307 | 0xf294b943e17a2bc4, 0x979cf3ca6cec5b5a, 308 | 0xbd8430bd08277231, 0xece53cec4a314ebd, 309 | 0x940f4613ae5ed136, 0xb913179899f68584, 310 | 0xe757dd7ec07426e5, 0x9096ea6f3848984f, 311 | 0xb4bca50b065abe63, 0xe1ebce4dc7f16dfb, 312 | 0x8d3360f09cf6e4bd, 0xb080392cc4349dec, 313 | 0xdca04777f541c567, 0x89e42caaf9491b60, 314 | 0xac5d37d5b79b6239, 0xd77485cb25823ac7, 315 | 0x86a8d39ef77164bc, 0xa8530886b54dbdeb, 316 | 0xd267caa862a12d66, 0x8380dea93da4bc60, 317 | 0xa46116538d0deb78, 0xcd795be870516656, 318 | 0x806bd9714632dff6, 0xa086cfcd97bf97f3, 319 | 0xc8a883c0fdaf7df0, 0xfad2a4b13d1b5d6c, 320 | 0x9cc3a6eec6311a63, 0xc3f490aa77bd60fc, 321 | 0xf4f1b4d515acb93b, 0x991711052d8bf3c5, 322 | 0xbf5cd54678eef0b6, 0xef340a98172aace4, 323 | 0x9580869f0e7aac0e, 0xbae0a846d2195712, 324 | 0xe998d258869facd7, 0x91ff83775423cc06, 325 | 0xb67f6455292cbf08, 0xe41f3d6a7377eeca, 326 | 0x8e938662882af53e, 0xb23867fb2a35b28d, 327 | 0xdec681f9f4c31f31, 0x8b3c113c38f9f37e, 328 | 0xae0b158b4738705e, 0xd98ddaee19068c76, 329 | 0x87f8a8d4cfa417c9, 0xa9f6d30a038d1dbc, 330 | 0xd47487cc8470652b, 0x84c8d4dfd2c63f3b, 331 | 0xa5fb0a17c777cf09, 0xcf79cc9db955c2cc, 332 | 0x81ac1fe293d599bf, 0xa21727db38cb002f, 333 | 0xca9cf1d206fdc03b, 0xfd442e4688bd304a, 334 | 0x9e4a9cec15763e2e, 0xc5dd44271ad3cdba, 335 | 0xf7549530e188c128, 0x9a94dd3e8cf578b9, 336 | 0xc13a148e3032d6e7, 0xf18899b1bc3f8ca1, 337 | 0x96f5600f15a7b7e5, 0xbcb2b812db11a5de, 338 | 0xebdf661791d60f56, 0x936b9fcebb25c995, 339 | 0xb84687c269ef3bfb, 0xe65829b3046b0afa, 340 | 0x8ff71a0fe2c2e6dc, 0xb3f4e093db73a093, 341 | 0xe0f218b8d25088b8, 0x8c974f7383725573, 342 | 0xafbd2350644eeacf, 0xdbac6c247d62a583, 343 | 0x894bc396ce5da772, 0xab9eb47c81f5114f, 344 | 0xd686619ba27255a2, 0x8613fd0145877585, 345 | 0xa798fc4196e952e7, 0xd17f3b51fca3a7a0, 346 | 0x82ef85133de648c4, 0xa3ab66580d5fdaf5, 347 | 0xcc963fee10b7d1b3, 0xffbbcfe994e5c61f, 348 | 0x9fd561f1fd0f9bd3, 0xc7caba6e7c5382c8, 349 | 0xf9bd690a1b68637b, 0x9c1661a651213e2d, 350 | 0xc31bfa0fe5698db8, 0xf3e2f893dec3f126, 351 | 0x986ddb5c6b3a76b7, 0xbe89523386091465, 352 | 0xee2ba6c0678b597f, 0x94db483840b717ef, 353 | 0xba121a4650e4ddeb, 0xe896a0d7e51e1566, 354 | 0x915e2486ef32cd60, 0xb5b5ada8aaff80b8, 355 | 0xe3231912d5bf60e6, 0x8df5efabc5979c8f, 356 | 0xb1736b96b6fd83b3, 0xddd0467c64bce4a0, 357 | 0x8aa22c0dbef60ee4, 0xad4ab7112eb3929d, 358 | 0xd89d64d57a607744, 0x87625f056c7c4a8b, 359 | 0xa93af6c6c79b5d2d, 0xd389b47879823479, 360 | 0x843610cb4bf160cb, 0xa54394fe1eedb8fe, 361 | 0xce947a3da6a9273e, 0x811ccc668829b887, 362 | 0xa163ff802a3426a8, 0xc9bcff6034c13052, 363 | 0xfc2c3f3841f17c67, 0x9d9ba7832936edc0, 364 | 0xc5029163f384a931, 0xf64335bcf065d37d, 365 | 0x99ea0196163fa42e, 0xc06481fb9bcf8d39, 366 | 0xf07da27a82c37088, 0x964e858c91ba2655, 367 | 0xbbe226efb628afea, 0xeadab0aba3b2dbe5, 368 | 0x92c8ae6b464fc96f, 0xb77ada0617e3bbcb, 369 | 0xe55990879ddcaabd, 0x8f57fa54c2a9eab6, 370 | 0xb32df8e9f3546564, 0xdff9772470297ebd, 371 | 0x8bfbea76c619ef36, 0xaefae51477a06b03, 372 | 0xdab99e59958885c4, 0x88b402f7fd75539b, 373 | 0xaae103b5fcd2a881, 0xd59944a37c0752a2, 374 | 0x857fcae62d8493a5, 0xa6dfbd9fb8e5b88e, 375 | 0xd097ad07a71f26b2, 0x825ecc24c873782f, 376 | 0xa2f67f2dfa90563b, 0xcbb41ef979346bca, 377 | 0xfea126b7d78186bc, 0x9f24b832e6b0f436, 378 | 0xc6ede63fa05d3143, 0xf8a95fcf88747d94, 379 | 0x9b69dbe1b548ce7c, 0xc24452da229b021b, 380 | 0xf2d56790ab41c2a2, 0x97c560ba6b0919a5, 381 | 0xbdb6b8e905cb600f, 0xed246723473e3813, 382 | 0x9436c0760c86e30b, 0xb94470938fa89bce, 383 | 0xe7958cb87392c2c2, 0x90bd77f3483bb9b9, 384 | 0xb4ecd5f01a4aa828, 0xe2280b6c20dd5232, 385 | 0x8d590723948a535f, 0xb0af48ec79ace837, 386 | 0xdcdb1b2798182244, 0x8a08f0f8bf0f156b, 387 | 0xac8b2d36eed2dac5, 0xd7adf884aa879177, 388 | 0x86ccbb52ea94baea, 0xa87fea27a539e9a5, 389 | 0xd29fe4b18e88640e, 0x83a3eeeef9153e89, 390 | 0xa48ceaaab75a8e2b, 0xcdb02555653131b6, 391 | 0x808e17555f3ebf11, 0xa0b19d2ab70e6ed6, 392 | 0xc8de047564d20a8b, 0xfb158592be068d2e, 393 | 0x9ced737bb6c4183d, 0xc428d05aa4751e4c, 394 | 0xf53304714d9265df, 0x993fe2c6d07b7fab, 395 | 0xbf8fdb78849a5f96, 0xef73d256a5c0f77c, 396 | 0x95a8637627989aad, 0xbb127c53b17ec159, 397 | 0xe9d71b689dde71af, 0x9226712162ab070d, 398 | 0xb6b00d69bb55c8d1, 0xe45c10c42a2b3b05, 399 | 0x8eb98a7a9a5b04e3, 0xb267ed1940f1c61c, 400 | 0xdf01e85f912e37a3, 0x8b61313bbabce2c6, 401 | 0xae397d8aa96c1b77, 0xd9c7dced53c72255, 402 | 0x881cea14545c7575, 0xaa242499697392d2, 403 | 0xd4ad2dbfc3d07787, 0x84ec3c97da624ab4, 404 | 0xa6274bbdd0fadd61, 0xcfb11ead453994ba, 405 | 0x81ceb32c4b43fcf4, 0xa2425ff75e14fc31, 406 | 0xcad2f7f5359a3b3e, 0xfd87b5f28300ca0d, 407 | 0x9e74d1b791e07e48, 0xc612062576589dda, 408 | 0xf79687aed3eec551, 0x9abe14cd44753b52, 409 | 0xc16d9a0095928a27, 0xf1c90080baf72cb1, 410 | 0x971da05074da7bee, 0xbce5086492111aea, 411 | 0xec1e4a7db69561a5, 0x9392ee8e921d5d07, 412 | 0xb877aa3236a4b449, 0xe69594bec44de15b, 413 | 0x901d7cf73ab0acd9, 0xb424dc35095cd80f, 414 | 0xe12e13424bb40e13, 0x8cbccc096f5088cb, 415 | 0xafebff0bcb24aafe, 0xdbe6fecebdedd5be, 416 | 0x89705f4136b4a597, 0xabcc77118461cefc, 417 | 0xd6bf94d5e57a42bc, 0x8637bd05af6c69b5, 418 | 0xa7c5ac471b478423, 0xd1b71758e219652b, 419 | 0x83126e978d4fdf3b, 0xa3d70a3d70a3d70a, 420 | 0xcccccccccccccccc, 0x8000000000000000, 421 | 0xa000000000000000, 0xc800000000000000, 422 | 0xfa00000000000000, 0x9c40000000000000, 423 | 0xc350000000000000, 0xf424000000000000, 424 | 0x9896800000000000, 0xbebc200000000000, 425 | 0xee6b280000000000, 0x9502f90000000000, 426 | 0xba43b74000000000, 0xe8d4a51000000000, 427 | 0x9184e72a00000000, 0xb5e620f480000000, 428 | 0xe35fa931a0000000, 0x8e1bc9bf04000000, 429 | 0xb1a2bc2ec5000000, 0xde0b6b3a76400000, 430 | 0x8ac7230489e80000, 0xad78ebc5ac620000, 431 | 0xd8d726b7177a8000, 0x878678326eac9000, 432 | 0xa968163f0a57b400, 0xd3c21bcecceda100, 433 | 0x84595161401484a0, 0xa56fa5b99019a5c8, 434 | 0xcecb8f27f4200f3a, 0x813f3978f8940984, 435 | 0xa18f07d736b90be5, 0xc9f2c9cd04674ede, 436 | 0xfc6f7c4045812296, 0x9dc5ada82b70b59d, 437 | 0xc5371912364ce305, 0xf684df56c3e01bc6, 438 | 0x9a130b963a6c115c, 0xc097ce7bc90715b3, 439 | 0xf0bdc21abb48db20, 0x96769950b50d88f4, 440 | 0xbc143fa4e250eb31, 0xeb194f8e1ae525fd, 441 | 0x92efd1b8d0cf37be, 0xb7abc627050305ad, 442 | 0xe596b7b0c643c719, 0x8f7e32ce7bea5c6f, 443 | 0xb35dbf821ae4f38b, 0xe0352f62a19e306e, 444 | 0x8c213d9da502de45, 0xaf298d050e4395d6, 445 | 0xdaf3f04651d47b4c, 0x88d8762bf324cd0f, 446 | 0xab0e93b6efee0053, 0xd5d238a4abe98068, 447 | 0x85a36366eb71f041, 0xa70c3c40a64e6c51, 448 | 0xd0cf4b50cfe20765, 0x82818f1281ed449f, 449 | 0xa321f2d7226895c7, 0xcbea6f8ceb02bb39, 450 | 0xfee50b7025c36a08, 0x9f4f2726179a2245, 451 | 0xc722f0ef9d80aad6, 0xf8ebad2b84e0d58b, 452 | 0x9b934c3b330c8577, 0xc2781f49ffcfa6d5, 453 | 0xf316271c7fc3908a, 0x97edd871cfda3a56, 454 | 0xbde94e8e43d0c8ec, 0xed63a231d4c4fb27, 455 | 0x945e455f24fb1cf8, 0xb975d6b6ee39e436, 456 | 0xe7d34c64a9c85d44, 0x90e40fbeea1d3a4a, 457 | 0xb51d13aea4a488dd, 0xe264589a4dcdab14, 458 | 0x8d7eb76070a08aec, 0xb0de65388cc8ada8, 459 | 0xdd15fe86affad912, 0x8a2dbf142dfcc7ab, 460 | 0xacb92ed9397bf996, 0xd7e77a8f87daf7fb, 461 | 0x86f0ac99b4e8dafd, 0xa8acd7c0222311bc, 462 | 0xd2d80db02aabd62b, 0x83c7088e1aab65db, 463 | 0xa4b8cab1a1563f52, 0xcde6fd5e09abcf26, 464 | 0x80b05e5ac60b6178, 0xa0dc75f1778e39d6, 465 | 0xc913936dd571c84c, 0xfb5878494ace3a5f, 466 | 0x9d174b2dcec0e47b, 0xc45d1df942711d9a, 467 | 0xf5746577930d6500, 0x9968bf6abbe85f20, 468 | 0xbfc2ef456ae276e8, 0xefb3ab16c59b14a2, 469 | 0x95d04aee3b80ece5, 0xbb445da9ca61281f, 470 | 0xea1575143cf97226, 0x924d692ca61be758, 471 | 0xb6e0c377cfa2e12e, 0xe498f455c38b997a, 472 | 0x8edf98b59a373fec, 0xb2977ee300c50fe7, 473 | 0xdf3d5e9bc0f653e1, 0x8b865b215899f46c, 474 | 0xae67f1e9aec07187, 0xda01ee641a708de9, 475 | 0x884134fe908658b2, 0xaa51823e34a7eede, 476 | 0xd4e5e2cdc1d1ea96, 0x850fadc09923329e, 477 | 0xa6539930bf6bff45, 0xcfe87f7cef46ff16, 478 | 0x81f14fae158c5f6e, 0xa26da3999aef7749, 479 | 0xcb090c8001ab551c, 0xfdcb4fa002162a63, 480 | 0x9e9f11c4014dda7e, 0xc646d63501a1511d, 481 | 0xf7d88bc24209a565, 0x9ae757596946075f, 482 | 0xc1a12d2fc3978937, 0xf209787bb47d6b84, 483 | 0x9745eb4d50ce6332, 0xbd176620a501fbff, 484 | 0xec5d3fa8ce427aff, 0x93ba47c980e98cdf, 485 | 0xb8a8d9bbe123f017, 0xe6d3102ad96cec1d, 486 | 0x9043ea1ac7e41392, 0xb454e4a179dd1877, 487 | 0xe16a1dc9d8545e94, 0x8ce2529e2734bb1d, 488 | 0xb01ae745b101e9e4, 0xdc21a1171d42645d, 489 | 0x899504ae72497eba, 0xabfa45da0edbde69, 490 | 0xd6f8d7509292d603, 0x865b86925b9bc5c2, 491 | 0xa7f26836f282b732, 0xd1ef0244af2364ff, 492 | 0x8335616aed761f1f, 0xa402b9c5a8d3a6e7, 493 | 0xcd036837130890a1, 0x802221226be55a64, 494 | 0xa02aa96b06deb0fd, 0xc83553c5c8965d3d, 495 | 0xfa42a8b73abbf48c, 0x9c69a97284b578d7, 496 | 0xc38413cf25e2d70d, 0xf46518c2ef5b8cd1, 497 | 0x98bf2f79d5993802, 0xbeeefb584aff8603, 498 | 0xeeaaba2e5dbf6784, 0x952ab45cfa97a0b2, 499 | 0xba756174393d88df, 0xe912b9d1478ceb17, 500 | 0x91abb422ccb812ee, 0xb616a12b7fe617aa, 501 | 0xe39c49765fdf9d94, 0x8e41ade9fbebc27d, 502 | 0xb1d219647ae6b31c, 0xde469fbd99a05fe3, 503 | 0x8aec23d680043bee, 0xada72ccc20054ae9, 504 | 0xd910f7ff28069da4, 0x87aa9aff79042286, 505 | 0xa99541bf57452b28, 0xd3fa922f2d1675f2, 506 | 0x847c9b5d7c2e09b7, 0xa59bc234db398c25, 507 | 0xcf02b2c21207ef2e, 0x8161afb94b44f57d, 508 | 0xa1ba1ba79e1632dc, 0xca28a291859bbf93, 509 | 0xfcb2cb35e702af78, 0x9defbf01b061adab, 510 | 0xc56baec21c7a1916, 0xf6c69a72a3989f5b, 511 | 0x9a3c2087a63f6399, 0xc0cb28a98fcf3c7f, 512 | 0xf0fdf2d3f3c30b9f, 0x969eb7c47859e743, 513 | 0xbc4665b596706114, 0xeb57ff22fc0c7959, 514 | 0x9316ff75dd87cbd8, 0xb7dcbf5354e9bece, 515 | 0xe5d3ef282a242e81, 0x8fa475791a569d10, 516 | 0xb38d92d760ec4455, 0xe070f78d3927556a, 517 | 0x8c469ab843b89562, 0xaf58416654a6babb, 518 | 0xdb2e51bfe9d0696a, 0x88fcf317f22241e2, 519 | 0xab3c2fddeeaad25a, 0xd60b3bd56a5586f1, 520 | 0x85c7056562757456, 0xa738c6bebb12d16c, 521 | 0xd106f86e69d785c7, 0x82a45b450226b39c, 522 | 0xa34d721642b06084, 0xcc20ce9bd35c78a5, 523 | 0xff290242c83396ce, 0x9f79a169bd203e41, 524 | 0xc75809c42c684dd1, 0xf92e0c3537826145, 525 | 0x9bbcc7a142b17ccb, 0xc2abf989935ddbfe, 526 | 0xf356f7ebf83552fe, 0x98165af37b2153de, 527 | 0xbe1bf1b059e9a8d6, 0xeda2ee1c7064130c, 528 | 0x9485d4d1c63e8be7, 0xb9a74a0637ce2ee1, 529 | 0xe8111c87c5c1ba99, 0x910ab1d4db9914a0, 530 | 0xb54d5e4a127f59c8, 0xe2a0b5dc971f303a, 531 | 0x8da471a9de737e24, 0xb10d8e1456105dad, 532 | 0xdd50f1996b947518, 0x8a5296ffe33cc92f, 533 | 0xace73cbfdc0bfb7b, 0xd8210befd30efa5a, 534 | 0x8714a775e3e95c78, 0xa8d9d1535ce3b396, 535 | 0xd31045a8341ca07c, 0x83ea2b892091e44d, 536 | 0xa4e4b66b68b65d60, 0xce1de40642e3f4b9, 537 | 0x80d2ae83e9ce78f3, 0xa1075a24e4421730, 538 | 0xc94930ae1d529cfc, 0xfb9b7cd9a4a7443c, 539 | 0x9d412e0806e88aa5, 0xc491798a08a2ad4e, 540 | 0xf5b5d7ec8acb58a2, 0x9991a6f3d6bf1765, 541 | 0xbff610b0cc6edd3f, 0xeff394dcff8a948e, 542 | 0x95f83d0a1fb69cd9, 0xbb764c4ca7a4440f, 543 | 0xea53df5fd18d5513, 0x92746b9be2f8552c, 544 | 0xb7118682dbb66a77, 0xe4d5e82392a40515, 545 | 0x8f05b1163ba6832d, 0xb2c71d5bca9023f8, 546 | 0xdf78e4b2bd342cf6, 0x8bab8eefb6409c1a, 547 | 0xae9672aba3d0c320, 0xda3c0f568cc4f3e8, 548 | 0x8865899617fb1871, 0xaa7eebfb9df9de8d, 549 | 0xd51ea6fa85785631, 0x8533285c936b35de, 550 | 0xa67ff273b8460356, 0xd01fef10a657842c, 551 | 0x8213f56a67f6b29b, 0xa298f2c501f45f42, 552 | 0xcb3f2f7642717713, 0xfe0efb53d30dd4d7, 553 | 0x9ec95d1463e8a506, 0xc67bb4597ce2ce48, 554 | 0xf81aa16fdc1b81da, 0x9b10a4e5e9913128, 555 | 0xc1d4ce1f63f57d72, 0xf24a01a73cf2dccf, 556 | 0x976e41088617ca01, 0xbd49d14aa79dbc82, 557 | 0xec9c459d51852ba2, 0x93e1ab8252f33b45, 558 | 0xb8da1662e7b00a17, 0xe7109bfba19c0c9d, 559 | 0x906a617d450187e2, 0xb484f9dc9641e9da, 560 | 0xe1a63853bbd26451, 0x8d07e33455637eb2, 561 | 0xb049dc016abc5e5f, 0xdc5c5301c56b75f7, 562 | 0x89b9b3e11b6329ba, 0xac2820d9623bf429, 563 | 0xd732290fbacaf133, 0x867f59a9d4bed6c0, 564 | 0xa81f301449ee8c70, 0xd226fc195c6a2f8c, 565 | 0x83585d8fd9c25db7, 0xa42e74f3d032f525, 566 | 0xcd3a1230c43fb26f, 0x80444b5e7aa7cf85, 567 | 0xa0555e361951c366, 0xc86ab5c39fa63440, 568 | 0xfa856334878fc150, 0x9c935e00d4b9d8d2, 569 | 0xc3b8358109e84f07, 0xf4a642e14c6262c8, 570 | 0x98e7e9cccfbd7dbd, 0xbf21e44003acdd2c, 571 | 0xeeea5d5004981478, 0x95527a5202df0ccb, 572 | 0xbaa718e68396cffd, 0xe950df20247c83fd, 573 | 0x91d28b7416cdd27e, 0xb6472e511c81471d, 574 | 0xe3d8f9e563a198e5, 0x8e679c2f5e44ff8f}; 575 | // A complement to mantissa_64 576 | // complete to a 128-bit mantissa. 577 | // Uses about 5KB but is rarely accessed. 578 | static const uint64_t mantissa_128[] = { 579 | 0x419ea3bd35385e2d, 0x52064cac828675b9, 580 | 0x7343efebd1940993, 0x1014ebe6c5f90bf8, 581 | 0xd41a26e077774ef6, 0x8920b098955522b4, 582 | 0x55b46e5f5d5535b0, 0xeb2189f734aa831d, 583 | 0xa5e9ec7501d523e4, 0x47b233c92125366e, 584 | 0x999ec0bb696e840a, 0xc00670ea43ca250d, 585 | 0x380406926a5e5728, 0xc605083704f5ecf2, 586 | 0xf7864a44c633682e, 0x7ab3ee6afbe0211d, 587 | 0x5960ea05bad82964, 0x6fb92487298e33bd, 588 | 0xa5d3b6d479f8e056, 0x8f48a4899877186c, 589 | 0x331acdabfe94de87, 0x9ff0c08b7f1d0b14, 590 | 0x7ecf0ae5ee44dd9, 0xc9e82cd9f69d6150, 591 | 0xbe311c083a225cd2, 0x6dbd630a48aaf406, 592 | 0x92cbbccdad5b108, 0x25bbf56008c58ea5, 593 | 0xaf2af2b80af6f24e, 0x1af5af660db4aee1, 594 | 0x50d98d9fc890ed4d, 0xe50ff107bab528a0, 595 | 0x1e53ed49a96272c8, 0x25e8e89c13bb0f7a, 596 | 0x77b191618c54e9ac, 0xd59df5b9ef6a2417, 597 | 0x4b0573286b44ad1d, 0x4ee367f9430aec32, 598 | 0x229c41f793cda73f, 0x6b43527578c1110f, 599 | 0x830a13896b78aaa9, 0x23cc986bc656d553, 600 | 0x2cbfbe86b7ec8aa8, 0x7bf7d71432f3d6a9, 601 | 0xdaf5ccd93fb0cc53, 0xd1b3400f8f9cff68, 602 | 0x23100809b9c21fa1, 0xabd40a0c2832a78a, 603 | 0x16c90c8f323f516c, 0xae3da7d97f6792e3, 604 | 0x99cd11cfdf41779c, 0x40405643d711d583, 605 | 0x482835ea666b2572, 0xda3243650005eecf, 606 | 0x90bed43e40076a82, 0x5a7744a6e804a291, 607 | 0x711515d0a205cb36, 0xd5a5b44ca873e03, 608 | 0xe858790afe9486c2, 0x626e974dbe39a872, 609 | 0xfb0a3d212dc8128f, 0x7ce66634bc9d0b99, 610 | 0x1c1fffc1ebc44e80, 0xa327ffb266b56220, 611 | 0x4bf1ff9f0062baa8, 0x6f773fc3603db4a9, 612 | 0xcb550fb4384d21d3, 0x7e2a53a146606a48, 613 | 0x2eda7444cbfc426d, 0xfa911155fefb5308, 614 | 0x793555ab7eba27ca, 0x4bc1558b2f3458de, 615 | 0x9eb1aaedfb016f16, 0x465e15a979c1cadc, 616 | 0xbfacd89ec191ec9, 0xcef980ec671f667b, 617 | 0x82b7e12780e7401a, 0xd1b2ecb8b0908810, 618 | 0x861fa7e6dcb4aa15, 0x67a791e093e1d49a, 619 | 0xe0c8bb2c5c6d24e0, 0x58fae9f773886e18, 620 | 0xaf39a475506a899e, 0x6d8406c952429603, 621 | 0xc8e5087ba6d33b83, 0xfb1e4a9a90880a64, 622 | 0x5cf2eea09a55067f, 0xf42faa48c0ea481e, 623 | 0xf13b94daf124da26, 0x76c53d08d6b70858, 624 | 0x54768c4b0c64ca6e, 0xa9942f5dcf7dfd09, 625 | 0xd3f93b35435d7c4c, 0xc47bc5014a1a6daf, 626 | 0x359ab6419ca1091b, 0xc30163d203c94b62, 627 | 0x79e0de63425dcf1d, 0x985915fc12f542e4, 628 | 0x3e6f5b7b17b2939d, 0xa705992ceecf9c42, 629 | 0x50c6ff782a838353, 0xa4f8bf5635246428, 630 | 0x871b7795e136be99, 0x28e2557b59846e3f, 631 | 0x331aeada2fe589cf, 0x3ff0d2c85def7621, 632 | 0xfed077a756b53a9, 0xd3e8495912c62894, 633 | 0x64712dd7abbbd95c, 0xbd8d794d96aacfb3, 634 | 0xecf0d7a0fc5583a0, 0xf41686c49db57244, 635 | 0x311c2875c522ced5, 0x7d633293366b828b, 636 | 0xae5dff9c02033197, 0xd9f57f830283fdfc, 637 | 0xd072df63c324fd7b, 0x4247cb9e59f71e6d, 638 | 0x52d9be85f074e608, 0x67902e276c921f8b, 639 | 0xba1cd8a3db53b6, 0x80e8a40eccd228a4, 640 | 0x6122cd128006b2cd, 0x796b805720085f81, 641 | 0xcbe3303674053bb0, 0xbedbfc4411068a9c, 642 | 0xee92fb5515482d44, 0x751bdd152d4d1c4a, 643 | 0xd262d45a78a0635d, 0x86fb897116c87c34, 644 | 0xd45d35e6ae3d4da0, 0x8974836059cca109, 645 | 0x2bd1a438703fc94b, 0x7b6306a34627ddcf, 646 | 0x1a3bc84c17b1d542, 0x20caba5f1d9e4a93, 647 | 0x547eb47b7282ee9c, 0xe99e619a4f23aa43, 648 | 0x6405fa00e2ec94d4, 0xde83bc408dd3dd04, 649 | 0x9624ab50b148d445, 0x3badd624dd9b0957, 650 | 0xe54ca5d70a80e5d6, 0x5e9fcf4ccd211f4c, 651 | 0x7647c3200069671f, 0x29ecd9f40041e073, 652 | 0xf468107100525890, 0x7182148d4066eeb4, 653 | 0xc6f14cd848405530, 0xb8ada00e5a506a7c, 654 | 0xa6d90811f0e4851c, 0x908f4a166d1da663, 655 | 0x9a598e4e043287fe, 0x40eff1e1853f29fd, 656 | 0xd12bee59e68ef47c, 0x82bb74f8301958ce, 657 | 0xe36a52363c1faf01, 0xdc44e6c3cb279ac1, 658 | 0x29ab103a5ef8c0b9, 0x7415d448f6b6f0e7, 659 | 0x111b495b3464ad21, 0xcab10dd900beec34, 660 | 0x3d5d514f40eea742, 0xcb4a5a3112a5112, 661 | 0x47f0e785eaba72ab, 0x59ed216765690f56, 662 | 0x306869c13ec3532c, 0x1e414218c73a13fb, 663 | 0xe5d1929ef90898fa, 0xdf45f746b74abf39, 664 | 0x6b8bba8c328eb783, 0x66ea92f3f326564, 665 | 0xc80a537b0efefebd, 0xbd06742ce95f5f36, 666 | 0x2c48113823b73704, 0xf75a15862ca504c5, 667 | 0x9a984d73dbe722fb, 0xc13e60d0d2e0ebba, 668 | 0x318df905079926a8, 0xfdf17746497f7052, 669 | 0xfeb6ea8bedefa633, 0xfe64a52ee96b8fc0, 670 | 0x3dfdce7aa3c673b0, 0x6bea10ca65c084e, 671 | 0x486e494fcff30a62, 0x5a89dba3c3efccfa, 672 | 0xf89629465a75e01c, 0xf6bbb397f1135823, 673 | 0x746aa07ded582e2c, 0xa8c2a44eb4571cdc, 674 | 0x92f34d62616ce413, 0x77b020baf9c81d17, 675 | 0xace1474dc1d122e, 0xd819992132456ba, 676 | 0x10e1fff697ed6c69, 0xca8d3ffa1ef463c1, 677 | 0xbd308ff8a6b17cb2, 0xac7cb3f6d05ddbde, 678 | 0x6bcdf07a423aa96b, 0x86c16c98d2c953c6, 679 | 0xe871c7bf077ba8b7, 0x11471cd764ad4972, 680 | 0xd598e40d3dd89bcf, 0x4aff1d108d4ec2c3, 681 | 0xcedf722a585139ba, 0xc2974eb4ee658828, 682 | 0x733d226229feea32, 0x806357d5a3f525f, 683 | 0xca07c2dcb0cf26f7, 0xfc89b393dd02f0b5, 684 | 0xbbac2078d443ace2, 0xd54b944b84aa4c0d, 685 | 0xa9e795e65d4df11, 0x4d4617b5ff4a16d5, 686 | 0x504bced1bf8e4e45, 0xe45ec2862f71e1d6, 687 | 0x5d767327bb4e5a4c, 0x3a6a07f8d510f86f, 688 | 0x890489f70a55368b, 0x2b45ac74ccea842e, 689 | 0x3b0b8bc90012929d, 0x9ce6ebb40173744, 690 | 0xcc420a6a101d0515, 0x9fa946824a12232d, 691 | 0x47939822dc96abf9, 0x59787e2b93bc56f7, 692 | 0x57eb4edb3c55b65a, 0xede622920b6b23f1, 693 | 0xe95fab368e45eced, 0x11dbcb0218ebb414, 694 | 0xd652bdc29f26a119, 0x4be76d3346f0495f, 695 | 0x6f70a4400c562ddb, 0xcb4ccd500f6bb952, 696 | 0x7e2000a41346a7a7, 0x8ed400668c0c28c8, 697 | 0x728900802f0f32fa, 0x4f2b40a03ad2ffb9, 698 | 0xe2f610c84987bfa8, 0xdd9ca7d2df4d7c9, 699 | 0x91503d1c79720dbb, 0x75a44c6397ce912a, 700 | 0xc986afbe3ee11aba, 0xfbe85badce996168, 701 | 0xfae27299423fb9c3, 0xdccd879fc967d41a, 702 | 0x5400e987bbc1c920, 0x290123e9aab23b68, 703 | 0xf9a0b6720aaf6521, 0xf808e40e8d5b3e69, 704 | 0xb60b1d1230b20e04, 0xb1c6f22b5e6f48c2, 705 | 0x1e38aeb6360b1af3, 0x25c6da63c38de1b0, 706 | 0x579c487e5a38ad0e, 0x2d835a9df0c6d851, 707 | 0xf8e431456cf88e65, 0x1b8e9ecb641b58ff, 708 | 0xe272467e3d222f3f, 0x5b0ed81dcc6abb0f, 709 | 0x98e947129fc2b4e9, 0x3f2398d747b36224, 710 | 0x8eec7f0d19a03aad, 0x1953cf68300424ac, 711 | 0x5fa8c3423c052dd7, 0x3792f412cb06794d, 712 | 0xe2bbd88bbee40bd0, 0x5b6aceaeae9d0ec4, 713 | 0xf245825a5a445275, 0xeed6e2f0f0d56712, 714 | 0x55464dd69685606b, 0xaa97e14c3c26b886, 715 | 0xd53dd99f4b3066a8, 0xe546a8038efe4029, 716 | 0xde98520472bdd033, 0x963e66858f6d4440, 717 | 0xdde7001379a44aa8, 0x5560c018580d5d52, 718 | 0xaab8f01e6e10b4a6, 0xcab3961304ca70e8, 719 | 0x3d607b97c5fd0d22, 0x8cb89a7db77c506a, 720 | 0x77f3608e92adb242, 0x55f038b237591ed3, 721 | 0x6b6c46dec52f6688, 0x2323ac4b3b3da015, 722 | 0xabec975e0a0d081a, 0x96e7bd358c904a21, 723 | 0x7e50d64177da2e54, 0xdde50bd1d5d0b9e9, 724 | 0x955e4ec64b44e864, 0xbd5af13bef0b113e, 725 | 0xecb1ad8aeacdd58e, 0x67de18eda5814af2, 726 | 0x80eacf948770ced7, 0xa1258379a94d028d, 727 | 0x96ee45813a04330, 0x8bca9d6e188853fc, 728 | 0x775ea264cf55347d, 0x95364afe032a819d, 729 | 0x3a83ddbd83f52204, 0xc4926a9672793542, 730 | 0x75b7053c0f178293, 0x5324c68b12dd6338, 731 | 0xd3f6fc16ebca5e03, 0x88f4bb1ca6bcf584, 732 | 0x2b31e9e3d06c32e5, 0x3aff322e62439fcf, 733 | 0x9befeb9fad487c2, 0x4c2ebe687989a9b3, 734 | 0xf9d37014bf60a10, 0x538484c19ef38c94, 735 | 0x2865a5f206b06fb9, 0xf93f87b7442e45d3, 736 | 0xf78f69a51539d748, 0xb573440e5a884d1b, 737 | 0x31680a88f8953030, 0xfdc20d2b36ba7c3d, 738 | 0x3d32907604691b4c, 0xa63f9a49c2c1b10f, 739 | 0xfcf80dc33721d53, 0xd3c36113404ea4a8, 740 | 0x645a1cac083126e9, 0x3d70a3d70a3d70a3, 741 | 0xcccccccccccccccc, 0x0, 742 | 0x0, 0x0, 743 | 0x0, 0x0, 744 | 0x0, 0x0, 745 | 0x0, 0x0, 746 | 0x0, 0x0, 747 | 0x0, 0x0, 748 | 0x0, 0x0, 749 | 0x0, 0x0, 750 | 0x0, 0x0, 751 | 0x0, 0x0, 752 | 0x0, 0x0, 753 | 0x0, 0x0, 754 | 0x0, 0x0, 755 | 0x0, 0x4000000000000000, 756 | 0x5000000000000000, 0xa400000000000000, 757 | 0x4d00000000000000, 0xf020000000000000, 758 | 0x6c28000000000000, 0xc732000000000000, 759 | 0x3c7f400000000000, 0x4b9f100000000000, 760 | 0x1e86d40000000000, 0x1314448000000000, 761 | 0x17d955a000000000, 0x5dcfab0800000000, 762 | 0x5aa1cae500000000, 0xf14a3d9e40000000, 763 | 0x6d9ccd05d0000000, 0xe4820023a2000000, 764 | 0xdda2802c8a800000, 0xd50b2037ad200000, 765 | 0x4526f422cc340000, 0x9670b12b7f410000, 766 | 0x3c0cdd765f114000, 0xa5880a69fb6ac800, 767 | 0x8eea0d047a457a00, 0x72a4904598d6d880, 768 | 0x47a6da2b7f864750, 0x999090b65f67d924, 769 | 0xfff4b4e3f741cf6d, 0xbff8f10e7a8921a4, 770 | 0xaff72d52192b6a0d, 0x9bf4f8a69f764490, 771 | 0x2f236d04753d5b4, 0x1d762422c946590, 772 | 0x424d3ad2b7b97ef5, 0xd2e0898765a7deb2, 773 | 0x63cc55f49f88eb2f, 0x3cbf6b71c76b25fb, 774 | 0x8bef464e3945ef7a, 0x97758bf0e3cbb5ac, 775 | 0x3d52eeed1cbea317, 0x4ca7aaa863ee4bdd, 776 | 0x8fe8caa93e74ef6a, 0xb3e2fd538e122b44, 777 | 0x60dbbca87196b616, 0xbc8955e946fe31cd, 778 | 0x6babab6398bdbe41, 0xc696963c7eed2dd1, 779 | 0xfc1e1de5cf543ca2, 0x3b25a55f43294bcb, 780 | 0x49ef0eb713f39ebe, 0x6e3569326c784337, 781 | 0x49c2c37f07965404, 0xdc33745ec97be906, 782 | 0x69a028bb3ded71a3, 0xc40832ea0d68ce0c, 783 | 0xf50a3fa490c30190, 0x792667c6da79e0fa, 784 | 0x577001b891185938, 0xed4c0226b55e6f86, 785 | 0x544f8158315b05b4, 0x696361ae3db1c721, 786 | 0x3bc3a19cd1e38e9, 0x4ab48a04065c723, 787 | 0x62eb0d64283f9c76, 0x3ba5d0bd324f8394, 788 | 0xca8f44ec7ee36479, 0x7e998b13cf4e1ecb, 789 | 0x9e3fedd8c321a67e, 0xc5cfe94ef3ea101e, 790 | 0xbba1f1d158724a12, 0x2a8a6e45ae8edc97, 791 | 0xf52d09d71a3293bd, 0x593c2626705f9c56, 792 | 0x6f8b2fb00c77836c, 0xb6dfb9c0f956447, 793 | 0x4724bd4189bd5eac, 0x58edec91ec2cb657, 794 | 0x2f2967b66737e3ed, 0xbd79e0d20082ee74, 795 | 0xecd8590680a3aa11, 0xe80e6f4820cc9495, 796 | 0x3109058d147fdcdd, 0xbd4b46f0599fd415, 797 | 0x6c9e18ac7007c91a, 0x3e2cf6bc604ddb0, 798 | 0x84db8346b786151c, 0xe612641865679a63, 799 | 0x4fcb7e8f3f60c07e, 0xe3be5e330f38f09d, 800 | 0x5cadf5bfd3072cc5, 0x73d9732fc7c8f7f6, 801 | 0x2867e7fddcdd9afa, 0xb281e1fd541501b8, 802 | 0x1f225a7ca91a4226, 0x3375788de9b06958, 803 | 0x52d6b1641c83ae, 0xc0678c5dbd23a49a, 804 | 0xf840b7ba963646e0, 0xb650e5a93bc3d898, 805 | 0xa3e51f138ab4cebe, 0xc66f336c36b10137, 806 | 0xb80b0047445d4184, 0xa60dc059157491e5, 807 | 0x87c89837ad68db2f, 0x29babe4598c311fb, 808 | 0xf4296dd6fef3d67a, 0x1899e4a65f58660c, 809 | 0x5ec05dcff72e7f8f, 0x76707543f4fa1f73, 810 | 0x6a06494a791c53a8, 0x487db9d17636892, 811 | 0x45a9d2845d3c42b6, 0xb8a2392ba45a9b2, 812 | 0x8e6cac7768d7141e, 0x3207d795430cd926, 813 | 0x7f44e6bd49e807b8, 0x5f16206c9c6209a6, 814 | 0x36dba887c37a8c0f, 0xc2494954da2c9789, 815 | 0xf2db9baa10b7bd6c, 0x6f92829494e5acc7, 816 | 0xcb772339ba1f17f9, 0xff2a760414536efb, 817 | 0xfef5138519684aba, 0x7eb258665fc25d69, 818 | 0xef2f773ffbd97a61, 0xaafb550ffacfd8fa, 819 | 0x95ba2a53f983cf38, 0xdd945a747bf26183, 820 | 0x94f971119aeef9e4, 0x7a37cd5601aab85d, 821 | 0xac62e055c10ab33a, 0x577b986b314d6009, 822 | 0xed5a7e85fda0b80b, 0x14588f13be847307, 823 | 0x596eb2d8ae258fc8, 0x6fca5f8ed9aef3bb, 824 | 0x25de7bb9480d5854, 0xaf561aa79a10ae6a, 825 | 0x1b2ba1518094da04, 0x90fb44d2f05d0842, 826 | 0x353a1607ac744a53, 0x42889b8997915ce8, 827 | 0x69956135febada11, 0x43fab9837e699095, 828 | 0x94f967e45e03f4bb, 0x1d1be0eebac278f5, 829 | 0x6462d92a69731732, 0x7d7b8f7503cfdcfe, 830 | 0x5cda735244c3d43e, 0x3a0888136afa64a7, 831 | 0x88aaa1845b8fdd0, 0x8aad549e57273d45, 832 | 0x36ac54e2f678864b, 0x84576a1bb416a7dd, 833 | 0x656d44a2a11c51d5, 0x9f644ae5a4b1b325, 834 | 0x873d5d9f0dde1fee, 0xa90cb506d155a7ea, 835 | 0x9a7f12442d588f2, 0xc11ed6d538aeb2f, 836 | 0x8f1668c8a86da5fa, 0xf96e017d694487bc, 837 | 0x37c981dcc395a9ac, 0x85bbe253f47b1417, 838 | 0x93956d7478ccec8e, 0x387ac8d1970027b2, 839 | 0x6997b05fcc0319e, 0x441fece3bdf81f03, 840 | 0xd527e81cad7626c3, 0x8a71e223d8d3b074, 841 | 0xf6872d5667844e49, 0xb428f8ac016561db, 842 | 0xe13336d701beba52, 0xecc0024661173473, 843 | 0x27f002d7f95d0190, 0x31ec038df7b441f4, 844 | 0x7e67047175a15271, 0xf0062c6e984d386, 845 | 0x52c07b78a3e60868, 0xa7709a56ccdf8a82, 846 | 0x88a66076400bb691, 0x6acff893d00ea435, 847 | 0x583f6b8c4124d43, 0xc3727a337a8b704a, 848 | 0x744f18c0592e4c5c, 0x1162def06f79df73, 849 | 0x8addcb5645ac2ba8, 0x6d953e2bd7173692, 850 | 0xc8fa8db6ccdd0437, 0x1d9c9892400a22a2, 851 | 0x2503beb6d00cab4b, 0x2e44ae64840fd61d, 852 | 0x5ceaecfed289e5d2, 0x7425a83e872c5f47, 853 | 0xd12f124e28f77719, 0x82bd6b70d99aaa6f, 854 | 0x636cc64d1001550b, 0x3c47f7e05401aa4e, 855 | 0x65acfaec34810a71, 0x7f1839a741a14d0d, 856 | 0x1ede48111209a050, 0x934aed0aab460432, 857 | 0xf81da84d5617853f, 0x36251260ab9d668e, 858 | 0xc1d72b7c6b426019, 0xb24cf65b8612f81f, 859 | 0xdee033f26797b627, 0x169840ef017da3b1, 860 | 0x8e1f289560ee864e, 0xf1a6f2bab92a27e2, 861 | 0xae10af696774b1db, 0xacca6da1e0a8ef29, 862 | 0x17fd090a58d32af3, 0xddfc4b4cef07f5b0, 863 | 0x4abdaf101564f98e, 0x9d6d1ad41abe37f1, 864 | 0x84c86189216dc5ed, 0x32fd3cf5b4e49bb4, 865 | 0x3fbc8c33221dc2a1, 0xfabaf3feaa5334a, 866 | 0x29cb4d87f2a7400e, 0x743e20e9ef511012, 867 | 0x914da9246b255416, 0x1ad089b6c2f7548e, 868 | 0xa184ac2473b529b1, 0xc9e5d72d90a2741e, 869 | 0x7e2fa67c7a658892, 0xddbb901b98feeab7, 870 | 0x552a74227f3ea565, 0xd53a88958f87275f, 871 | 0x8a892abaf368f137, 0x2d2b7569b0432d85, 872 | 0x9c3b29620e29fc73, 0x8349f3ba91b47b8f, 873 | 0x241c70a936219a73, 0xed238cd383aa0110, 874 | 0xf4363804324a40aa, 0xb143c6053edcd0d5, 875 | 0xdd94b7868e94050a, 0xca7cf2b4191c8326, 876 | 0xfd1c2f611f63a3f0, 0xbc633b39673c8cec, 877 | 0xd5be0503e085d813, 0x4b2d8644d8a74e18, 878 | 0xddf8e7d60ed1219e, 0xcabb90e5c942b503, 879 | 0x3d6a751f3b936243, 0xcc512670a783ad4, 880 | 0x27fb2b80668b24c5, 0xb1f9f660802dedf6, 881 | 0x5e7873f8a0396973, 0xdb0b487b6423e1e8, 882 | 0x91ce1a9a3d2cda62, 0x7641a140cc7810fb, 883 | 0xa9e904c87fcb0a9d, 0x546345fa9fbdcd44, 884 | 0xa97c177947ad4095, 0x49ed8eabcccc485d, 885 | 0x5c68f256bfff5a74, 0x73832eec6fff3111, 886 | 0xc831fd53c5ff7eab, 0xba3e7ca8b77f5e55, 887 | 0x28ce1bd2e55f35eb, 0x7980d163cf5b81b3, 888 | 0xd7e105bcc332621f, 0x8dd9472bf3fefaa7, 889 | 0xb14f98f6f0feb951, 0x6ed1bf9a569f33d3, 890 | 0xa862f80ec4700c8, 0xcd27bb612758c0fa, 891 | 0x8038d51cb897789c, 0xe0470a63e6bd56c3, 892 | 0x1858ccfce06cac74, 0xf37801e0c43ebc8, 893 | 0xd30560258f54e6ba, 0x47c6b82ef32a2069, 894 | 0x4cdc331d57fa5441, 0xe0133fe4adf8e952, 895 | 0x58180fddd97723a6, 0x570f09eaa7ea7648,}; 896 | 897 | // we start with a fast path 898 | // It was described in 899 | // Clinger WD. How to read floating point numbers accurately. 900 | // ACM SIGPLAN Notices. 1990 901 | #if (FLT_EVAL_METHOD != 1) && (FLT_EVAL_METHOD != 0) 902 | // we do not trust the divisor 903 | if (0 <= power && power <= 22 && i <= 9007199254740991) { 904 | #else 905 | if (-22 <= power && power <= 22 && i <= 9007199254740991) { 906 | #endif 907 | // convert the integer into a double. This is lossless since 908 | // 0 <= i <= 2^53 - 1. 909 | double d = double(i); 910 | // 911 | // The general idea is as follows. 912 | // If 0 <= s < 2^53 and if 10^0 <= p <= 10^22 then 913 | // 1) Both s and p can be represented exactly as 64-bit floating-point 914 | // values 915 | // (binary64). 916 | // 2) Because s and p can be represented exactly as floating-point values, 917 | // then s * p 918 | // and s / p will produce correctly rounded values. 919 | // 920 | if (power < 0) { 921 | d = d / power_of_ten[-power]; 922 | } else { 923 | d = d * power_of_ten[power]; 924 | } 925 | if (negative) { 926 | d = -d; 927 | } 928 | *success = true; 929 | return d; 930 | } 931 | // When 22 < power && power < 22 + 16, we could 932 | // hope for another, secondary fast path. It wa 933 | // described by David M. Gay in "Correctly rounded 934 | // binary-decimal and decimal-binary conversions." (1990) 935 | // If you need to compute i * 10^(22 + x) for x < 16, 936 | // first compute i * 10^x, if you know that result is exact 937 | // (e.g., when i * 10^x < 2^53), 938 | // then you can still proceed and do (i * 10^x) * 10^22. 939 | // Is this worth your time? 940 | // You need 22 < power *and* power < 22 + 16 *and* (i * 10^(x-22) < 2^53) 941 | // for this second fast path to work. 942 | // If you you have 22 < power *and* power < 22 + 16, and then you 943 | // optimistically compute "i * 10^(x-22)", there is still a chance that you 944 | // have wasted your time if i * 10^(x-22) >= 2^53. It makes the use cases of 945 | // this optimization maybe less common than we would like. Source: 946 | // http://www.exploringbinary.com/fast-path-decimal-to-floating-point-conversion/ 947 | // also used in RapidJSON: https://rapidjson.org/strtod_8h_source.html 948 | 949 | 950 | 951 | // The fast path has now failed, so we are failing back on the slower path. 952 | 953 | // In the slow path, we need to adjust i so that it is > 1<<63 which is always 954 | // possible, except if i == 0, so we handle i == 0 separately. 955 | if(i == 0) { 956 | return negative ? -0.0 : 0.0; 957 | } 958 | 959 | 960 | // We are going to need to do some 64-bit arithmetic to get a more precise product. 961 | // We use a table lookup approach. 962 | // It is safe because 963 | // power >= FASTFLOAT_SMALLEST_POWER 964 | // and power <= FASTFLOAT_LARGEST_POWER 965 | // We recover the mantissa of the power, it has a leading 1. It is always 966 | // rounded down. 967 | uint64_t factor_mantissa = mantissa_64[power - FASTFLOAT_SMALLEST_POWER]; 968 | 969 | 970 | // The exponent is 1024 + 63 + power 971 | // + floor(log(5**power)/log(2)). 972 | // The 1024 comes from the ieee64 standard. 973 | // The 63 comes from the fact that we use a 64-bit word. 974 | // 975 | // Computing floor(log(5**power)/log(2)) could be 976 | // slow. Instead we use a fast function. 977 | // 978 | // For power in (-400,350), we have that 979 | // (((152170 + 65536) * power ) >> 16); 980 | // is equal to 981 | // floor(log(5**power)/log(2)) + power when power >= 0 982 | // and it is equal to 983 | // ceil(log(5**-power)/log(2)) + power when power < 0 984 | // 985 | // 986 | // The 65536 is (1<<16) and corresponds to 987 | // (65536 * power) >> 16 ---> power 988 | // 989 | // ((152170 * power ) >> 16) is equal to 990 | // floor(log(5**power)/log(2)) 991 | // 992 | // Note that this is not magic: 152170/(1<<16) is 993 | // approximatively equal to log(5)/log(2). 994 | // The 1<<16 value is a power of two; we could use a 995 | // larger power of 2 if we wanted to. 996 | // 997 | int64_t exponent = (((152170 + 65536) * power) >> 16) + 1024 + 63; 998 | // We want the most significant bit of i to be 1. Shift if needed. 999 | int lz = leading_zeroes(i); 1000 | i <<= lz; 1001 | // We want the most significant 64 bits of the product. We know 1002 | // this will be non-zero because the most significant bit of i is 1003 | // 1. 1004 | value128 product = full_multiplication(i, factor_mantissa); 1005 | uint64_t lower = product.low; 1006 | uint64_t upper = product.high; 1007 | // We know that upper has at most one leading zero because 1008 | // both i and factor_mantissa have a leading one. This means 1009 | // that the result is at least as large as ((1<<63)*(1<<63))/(1<<64). 1010 | 1011 | // As long as the first 9 bits of "upper" are not "1", then we 1012 | // know that we have an exact computed value for the leading 1013 | // 55 bits because any imprecision would play out as a +1, in 1014 | // the worst case. 1015 | // Having 55 bits is necessary because 1016 | // we need 53 bits for the mantissa but we have to have one rounding bit and 1017 | // we can waste a bit if the most significant bit of the product is zero. 1018 | // We expect this next branch to be rarely taken (say 1% of the time). 1019 | // When (upper & 0x1FF) == 0x1FF, it can be common for 1020 | // lower + i < lower to be true (proba. much higher than 1%). 1021 | if (unlikely((upper & 0x1FF) == 0x1FF) && (lower + i < lower)) { 1022 | uint64_t factor_mantissa_low = 1023 | mantissa_128[power - FASTFLOAT_SMALLEST_POWER]; 1024 | // next, we compute the 64-bit x 128-bit multiplication, getting a 192-bit 1025 | // result (three 64-bit values) 1026 | product = full_multiplication(i, factor_mantissa_low); 1027 | uint64_t product_low = product.low; 1028 | uint64_t product_middle2 = product.high; 1029 | uint64_t product_middle1 = lower; 1030 | uint64_t product_high = upper; 1031 | uint64_t product_middle = product_middle1 + product_middle2; 1032 | if (product_middle < product_middle1) { 1033 | product_high++; // overflow carry 1034 | } 1035 | // we want to check whether mantissa *i + i would affect our result 1036 | // This does happen, e.g. with 7.3177701707893310e+15 1037 | if (((product_middle + 1 == 0) && ((product_high & 0x1FF) == 0x1FF) && 1038 | (product_low + i < product_low))) { // let us be prudent and bail out. 1039 | *success = false; 1040 | return 0; 1041 | } 1042 | upper = product_high; 1043 | lower = product_middle; 1044 | } 1045 | // The final mantissa should be 53 bits with a leading 1. 1046 | // We shift it so that it occupies 54 bits with a leading 1. 1047 | /////// 1048 | uint64_t upperbit = upper >> 63; 1049 | uint64_t mantissa = upper >> (upperbit + 9); 1050 | lz += int(1 ^ upperbit); 1051 | // Here we have mantissa < (1<<54). 1052 | 1053 | // We have to round to even. The "to even" part 1054 | // is only a problem when we are right in between two floats 1055 | // which we guard against. 1056 | // If we have lots of trailing zeros, we may fall right between two 1057 | // floating-point values. 1058 | if (unlikely((lower == 0) && ((upper & 0x1FF) == 0) && 1059 | ((mantissa & 3) == 1))) { 1060 | // if mantissa & 1 == 1 we might need to round up. 1061 | // 1062 | // Scenarios: 1063 | // 1. We are not in the middle. Then we should round up. 1064 | // 1065 | // 2. We are right in the middle. Whether we round up depends 1066 | // on the last significant bit: if it is "one" then we round 1067 | // up (round to even) otherwise, we do not. 1068 | // 1069 | // So if the last significant bit is 1, we can safely round up. 1070 | // Hence we only need to bail out if (mantissa & 3) == 1. 1071 | // Otherwise we may need more accuracy or analysis to determine whether 1072 | // we are exactly between two floating-point numbers. 1073 | // It can be triggered with 1e23. 1074 | // Note: because the factor_mantissa and factor_mantissa_low are 1075 | // almost always rounded down (except for small positive powers), 1076 | // almost always should round up. 1077 | *success = false; 1078 | return 0; 1079 | } 1080 | mantissa += mantissa & 1; 1081 | mantissa >>= 1; 1082 | // Here we have mantissa < (1<<53), unless there was an overflow 1083 | if (mantissa >= (1ULL << 53)) { 1084 | ////////// 1085 | // This will happen when parsing values such as 7.2057594037927933e+16 1086 | //////// 1087 | mantissa = (1ULL << 52); 1088 | lz--; // undo previous addition 1089 | } 1090 | mantissa &= ~(1ULL << 52); 1091 | uint64_t real_exponent = exponent - lz; 1092 | // we have to check that real_exponent is in range, otherwise we bail out 1093 | if (unlikely((real_exponent < 1) || (real_exponent > 2046))) { 1094 | *success = false; 1095 | return 0; 1096 | } 1097 | mantissa |= real_exponent << 52; 1098 | mantissa |= (((uint64_t)negative) << 63); 1099 | double d; 1100 | memcpy(&d, &mantissa, sizeof(d)); 1101 | *success = true; 1102 | return d; 1103 | } 1104 | // Return the null pointer on error 1105 | static const char * parse_float_strtod(const char *ptr, double *outDouble) { 1106 | char *endptr; 1107 | #if defined(FAST_DOUBLE_PARSER_SOLARIS) || defined(FAST_DOUBLE_PARSER_CYGWIN) 1108 | // workround for cygwin, solaris 1109 | *outDouble = cygwin_strtod_l(ptr, &endptr); 1110 | #elif defined(_WIN32) 1111 | static _locale_t c_locale = _create_locale(LC_ALL, "C"); 1112 | *outDouble = _strtod_l(ptr, &endptr, c_locale); 1113 | #elif defined(__PASE__) 1114 | *outDouble = strtod(ptr, &endptr); 1115 | #else 1116 | static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); 1117 | *outDouble = strtod_l(ptr, &endptr, c_locale); 1118 | #endif 1119 | // Some libraries will set errno = ERANGE when the value is subnormal, 1120 | // yet we may want to be able to parse subnormal values. 1121 | // However, we do not want to tolerate NAN or infinite values. 1122 | // There isno realistic application where you might need values so large than 1123 | // they can't fit in binary64. The maximal value is about 1.7976931348623157 1124 | // × 10^308 It is an unimaginable large number. There will never be any piece 1125 | // of engineering involving as many as 10^308 parts. It is estimated that 1126 | // there are about 10^80 atoms in the universe. The estimate for the total 1127 | // number of electrons is similar. Using a double-precision floating-point 1128 | // value, we can represent easily the number of atoms in the universe. We 1129 | // could also represent the number of ways you can pick any three individual 1130 | // atoms at random in the universe. 1131 | if (!std::isfinite(*outDouble)) { 1132 | return nullptr; 1133 | } 1134 | return endptr; 1135 | } 1136 | 1137 | // parse the number at p 1138 | // return the null pointer on error 1139 | WARN_UNUSED 1140 | really_inline const char * parse_number(const char *p, double *outDouble) { 1141 | const char *pinit = p; 1142 | bool found_minus = (*p == '-'); 1143 | bool negative = false; 1144 | if (found_minus) { 1145 | ++p; 1146 | negative = true; 1147 | if (!is_integer(*p)) { // a negative sign must be followed by an integer 1148 | return nullptr; 1149 | } 1150 | } 1151 | const char *const start_digits = p; 1152 | 1153 | uint64_t i; // an unsigned int avoids signed overflows (which are bad) 1154 | if (*p == '0') { // 0 cannot be followed by an integer 1155 | ++p; 1156 | if (is_integer(*p)) { 1157 | return nullptr; 1158 | } 1159 | i = 0; 1160 | } else { 1161 | if (!(is_integer(*p))) { // must start with an integer 1162 | return nullptr; 1163 | } 1164 | unsigned char digit = *p - '0'; 1165 | i = digit; 1166 | p++; 1167 | // the is_made_of_eight_digits_fast routine is unlikely to help here because 1168 | // we rarely see large integer parts like 123456789 1169 | while (is_integer(*p)) { 1170 | digit = *p - '0'; 1171 | // a multiplication by 10 is cheaper than an arbitrary integer 1172 | // multiplication 1173 | i = 10 * i + digit; // might overflow, we will handle the overflow later 1174 | ++p; 1175 | } 1176 | } 1177 | int64_t exponent = 0; 1178 | const char *first_after_period = NULL; 1179 | if (*p == '.') { 1180 | ++p; 1181 | first_after_period = p; 1182 | if (is_integer(*p)) { 1183 | unsigned char digit = *p - '0'; 1184 | ++p; 1185 | i = i * 10 + digit; // might overflow + multiplication by 10 is likely 1186 | // cheaper than arbitrary mult. 1187 | // we will handle the overflow later 1188 | } else { 1189 | return nullptr; 1190 | } 1191 | while (is_integer(*p)) { 1192 | unsigned char digit = *p - '0'; 1193 | ++p; 1194 | i = i * 10 + digit; // in rare cases, this will overflow, but that's ok 1195 | // because we have parse_highprecision_float later. 1196 | } 1197 | exponent = first_after_period - p; 1198 | } 1199 | int digit_count = 1200 | int(p - start_digits - 1); // used later to guard against overflows 1201 | if (('e' == *p) || ('E' == *p)) { 1202 | ++p; 1203 | bool neg_exp = false; 1204 | if ('-' == *p) { 1205 | neg_exp = true; 1206 | ++p; 1207 | } else if ('+' == *p) { 1208 | ++p; 1209 | } 1210 | if (!is_integer(*p)) { 1211 | return nullptr; 1212 | } 1213 | unsigned char digit = *p - '0'; 1214 | int64_t exp_number = digit; 1215 | p++; 1216 | if (is_integer(*p)) { 1217 | digit = *p - '0'; 1218 | exp_number = 10 * exp_number + digit; 1219 | ++p; 1220 | } 1221 | if (is_integer(*p)) { 1222 | digit = *p - '0'; 1223 | exp_number = 10 * exp_number + digit; 1224 | ++p; 1225 | } 1226 | while (is_integer(*p)) { 1227 | digit = *p - '0'; 1228 | if (exp_number < 0x100000000) { // we need to check for overflows 1229 | exp_number = 10 * exp_number + digit; 1230 | } 1231 | ++p; 1232 | } 1233 | exponent += (neg_exp ? -exp_number : exp_number); 1234 | } 1235 | // If we frequently had to deal with long strings of digits, 1236 | // we could extend our code by using a 128-bit integer instead 1237 | // of a 64-bit integer. However, this is uncommon. 1238 | if (unlikely((digit_count >= 19))) { // this is uncommon 1239 | // It is possible that the integer had an overflow. 1240 | // We have to handle the case where we have 0.0000somenumber. 1241 | const char *start = start_digits; 1242 | while (*start == '0' || (*start == '.')) { 1243 | start++; 1244 | } 1245 | // we over-decrement by one when there is a decimal separator 1246 | digit_count -= int(start - start_digits); 1247 | if (digit_count >= 19) { 1248 | // Chances are good that we had an overflow! 1249 | // We start anew. 1250 | // This will happen in the following examples: 1251 | // 10000000000000000000000000000000000000000000e+308 1252 | // 3.1415926535897932384626433832795028841971693993751 1253 | // 1254 | return parse_float_strtod(pinit, outDouble); 1255 | } 1256 | } 1257 | if (unlikely(exponent < FASTFLOAT_SMALLEST_POWER) || 1258 | (exponent > FASTFLOAT_LARGEST_POWER)) { 1259 | // this is almost never going to get called!!! 1260 | // exponent could be as low as 325 1261 | return parse_float_strtod(pinit, outDouble); 1262 | } 1263 | // from this point forward, exponent >= FASTFLOAT_SMALLEST_POWER and 1264 | // exponent <= FASTFLOAT_LARGEST_POWER 1265 | bool success = true; 1266 | *outDouble = compute_float_64(exponent, i, negative, &success); 1267 | if (!success) { 1268 | // we are almost never going to get here. 1269 | return parse_float_strtod(pinit, outDouble); 1270 | } 1271 | return p; 1272 | } 1273 | 1274 | } // namespace fast_double_parser 1275 | 1276 | #endif 1277 | -------------------------------------------------------------------------------- /script/table_generation.py: -------------------------------------------------------------------------------- 1 | def format(number): 2 | upper = number // (1<<64) 3 | lower = number % (1<<64) 4 | print(""+hex(upper)+","+hex(lower)+",") 5 | 6 | for q in range(-342,0): 7 | power5 = 5 ** -q 8 | z = 0 9 | while( (1<= -27): 12 | b = z + 127 13 | c = 2 ** b // power5 + 1 14 | format(c) 15 | else: 16 | b = 2 * z + 2 * 64 17 | c = 2 ** b // power5 + 1 18 | # truncate 19 | while(c >= (1<<128)): 20 | c //= 2 21 | format(c) 22 | 23 | for q in range(0,308+1): 24 | power5 = 5 ** q 25 | # move the most significant bit in position 26 | while(power5 < (1<<127)): 27 | power5 *= 2 28 | # *truncate* 29 | while(power5 >= (1<<128)): 30 | power5 //= 2 31 | format(power5) 32 | -------------------------------------------------------------------------------- /tests/bogus.cpp: -------------------------------------------------------------------------------- 1 | #include "fast_double_parser.h" 2 | // To detect issues such as 3 | // https://github.com/lemire/fast_double_parser/issues/42 4 | 5 | fast_double_parser::value128 my_full_multiplication(uint64_t value1, uint64_t value2) { 6 | return fast_double_parser::full_multiplication(value1, value2); 7 | } -------------------------------------------------------------------------------- /tests/rebogus.cpp: -------------------------------------------------------------------------------- 1 | #include "fast_double_parser.h" 2 | // To detect issues such as 3 | // https://github.com/lemire/fast_double_parser/issues/42 4 | 5 | fast_double_parser::value128 my_other_full_multiplication(uint64_t value1, uint64_t value2) { 6 | return fast_double_parser::full_multiplication(value1, value2); 7 | } -------------------------------------------------------------------------------- /tests/unit.cpp: -------------------------------------------------------------------------------- 1 | #include "fast_double_parser.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // ulp distance 10 | // Marc B. Reynolds, 2016-2019 11 | // Public Domain under http://unlicense.org, see link for details. 12 | // adapted by D. Lemire 13 | inline uint64_t f64_ulp_dist(double a, double b) { 14 | uint64_t ua, ub; 15 | memcpy(&ua, &a, sizeof(ua)); 16 | memcpy(&ub, &b, sizeof(ub)); 17 | if ((int64_t)(ub ^ ua) >= 0) 18 | return (int64_t)(ua - ub) >= 0 ? (ua - ub) : (ub - ua); 19 | return ua + ub + 0x80000000; 20 | } 21 | 22 | static inline uint64_t rng(uint64_t h) { 23 | h ^= h >> 33; 24 | h *= UINT64_C(0xff51afd7ed558ccd); 25 | h ^= h >> 33; 26 | h *= UINT64_C(0xc4ceb9fe1a85ec53); 27 | h ^= h >> 33; 28 | return h; 29 | } 30 | 31 | std::string randomfloats(uint64_t howmany) { 32 | std::stringstream out; 33 | uint64_t offset = 1190; 34 | for (size_t i = 1; i <= howmany; i++) { 35 | uint64_t x = rng(i + offset); 36 | double d; 37 | ::memcpy(&d, &x, sizeof(double)); 38 | // paranoid 39 | while (!std::isfinite(d)) { 40 | offset++; 41 | x = rng(i + offset); 42 | ::memcpy(&d, &x, sizeof(double)); 43 | } 44 | out << std::setprecision(DBL_DIG) << d << " "; 45 | } 46 | return out.str(); 47 | } 48 | 49 | void check(double d) { 50 | std::string s(64, '\0'); 51 | auto written = std::snprintf(&s[0], s.size(), "%.*e", DBL_DIG + 1, d); 52 | s.resize(written); 53 | double x; 54 | const char * isok = fast_double_parser::parse_number(s.data(), &x); 55 | if (!isok) { 56 | printf("fast_double_parser refused to parse %s\n", s.c_str()); 57 | throw std::runtime_error("fast_double_parser refused to parse"); 58 | } 59 | if(isok != s.data() + s.size()) throw std::runtime_error("does not point at the end"); 60 | if (d != x) { 61 | std::cerr << "fast_double_parser disagrees" << std::endl; 62 | printf("fast_double_parser: %.*e\n", DBL_DIG + 1, x); 63 | printf("reference: %.*e\n", DBL_DIG + 1, d); 64 | printf("string: %s\n", s.c_str()); 65 | printf("f64_ulp_dist = %d\n", (int)f64_ulp_dist(x, d)); 66 | throw std::runtime_error("fast_double_parser disagrees"); 67 | } 68 | } 69 | 70 | 71 | void check_string(std::string s) { 72 | double x; 73 | bool isok = fast_double_parser::parse_number(s.data(), &x); 74 | if (!isok) { 75 | printf("fast_double_parser refused to parse %s\n", s.c_str()); 76 | throw std::runtime_error("fast_double_parser refused to parse"); 77 | } 78 | #if defined(FAST_DOUBLE_PARSER_SOLARIS) || defined(FAST_DOUBLE_PARSER_CYGWIN) 79 | // workround for cygwin, solaris 80 | char *endptr; 81 | double d = cygwin_strtod_l(s.data(), &endptr); 82 | #elif defined(_WIN32) 83 | static _locale_t c_locale = _create_locale(LC_ALL, "C"); 84 | double d = _strtod_l(s.data(), nullptr, c_locale); 85 | #else 86 | static locale_t c_locale = newlocale(LC_ALL_MASK, "C", NULL); 87 | double d = strtod_l(s.data(), nullptr, c_locale); 88 | #endif 89 | if (d != x) { 90 | std::cerr << "fast_double_parser disagrees" << std::endl; 91 | printf("fast_double_parser: %.*e\n", DBL_DIG + 1, x); 92 | printf("reference: %.*e\n", DBL_DIG + 1, d); 93 | printf("string: %s\n", s.c_str()); 94 | printf("f64_ulp_dist = %d\n", (int)f64_ulp_dist(x, d)); 95 | throw std::runtime_error("fast_double_parser disagrees"); 96 | } 97 | } 98 | 99 | 100 | void unit_tests() { 101 | for (std::string s : {"7.3177701707893310e+15","1e23", "9007199254740995","7e23"}) { 102 | check_string(s); 103 | } 104 | for (double d : {-65.613616999999977, 7.2057594037927933e+16, 1.0e-308, 105 | 0.1e-308, 0.01e-307, 1.79769e+308, 2.22507e-308, 106 | -1.79769e+308, -2.22507e-308, 1e-308}) { 107 | check(d); 108 | } 109 | uint64_t offset = 1190; 110 | size_t howmany = 10000000; 111 | for (size_t i = 1; i <= howmany; i++) { 112 | if ((i % 10000) == 0) { 113 | printf("."); 114 | fflush(NULL); 115 | } 116 | uint64_t x = rng(i + offset); 117 | double d; 118 | ::memcpy(&d, &x, sizeof(double)); 119 | // paranoid 120 | while ((!std::isnormal(d)) || std::isnan(d) || std::isinf(d)) { 121 | offset++; 122 | x = rng(i + offset); 123 | ::memcpy(&d, &x, sizeof(double)); 124 | } 125 | check(d); 126 | } 127 | 128 | printf("Unit tests ok\n"); 129 | } 130 | static const double testing_power_of_ten[] = { 131 | 1e-307, 1e-306, 1e-305, 1e-304, 1e-303, 1e-302, 1e-301, 1e-300, 1e-299, 132 | 1e-298, 1e-297, 1e-296, 1e-295, 1e-294, 1e-293, 1e-292, 1e-291, 1e-290, 133 | 1e-289, 1e-288, 1e-287, 1e-286, 1e-285, 1e-284, 1e-283, 1e-282, 1e-281, 134 | 1e-280, 1e-279, 1e-278, 1e-277, 1e-276, 1e-275, 1e-274, 1e-273, 1e-272, 135 | 1e-271, 1e-270, 1e-269, 1e-268, 1e-267, 1e-266, 1e-265, 1e-264, 1e-263, 136 | 1e-262, 1e-261, 1e-260, 1e-259, 1e-258, 1e-257, 1e-256, 1e-255, 1e-254, 137 | 1e-253, 1e-252, 1e-251, 1e-250, 1e-249, 1e-248, 1e-247, 1e-246, 1e-245, 138 | 1e-244, 1e-243, 1e-242, 1e-241, 1e-240, 1e-239, 1e-238, 1e-237, 1e-236, 139 | 1e-235, 1e-234, 1e-233, 1e-232, 1e-231, 1e-230, 1e-229, 1e-228, 1e-227, 140 | 1e-226, 1e-225, 1e-224, 1e-223, 1e-222, 1e-221, 1e-220, 1e-219, 1e-218, 141 | 1e-217, 1e-216, 1e-215, 1e-214, 1e-213, 1e-212, 1e-211, 1e-210, 1e-209, 142 | 1e-208, 1e-207, 1e-206, 1e-205, 1e-204, 1e-203, 1e-202, 1e-201, 1e-200, 143 | 1e-199, 1e-198, 1e-197, 1e-196, 1e-195, 1e-194, 1e-193, 1e-192, 1e-191, 144 | 1e-190, 1e-189, 1e-188, 1e-187, 1e-186, 1e-185, 1e-184, 1e-183, 1e-182, 145 | 1e-181, 1e-180, 1e-179, 1e-178, 1e-177, 1e-176, 1e-175, 1e-174, 1e-173, 146 | 1e-172, 1e-171, 1e-170, 1e-169, 1e-168, 1e-167, 1e-166, 1e-165, 1e-164, 147 | 1e-163, 1e-162, 1e-161, 1e-160, 1e-159, 1e-158, 1e-157, 1e-156, 1e-155, 148 | 1e-154, 1e-153, 1e-152, 1e-151, 1e-150, 1e-149, 1e-148, 1e-147, 1e-146, 149 | 1e-145, 1e-144, 1e-143, 1e-142, 1e-141, 1e-140, 1e-139, 1e-138, 1e-137, 150 | 1e-136, 1e-135, 1e-134, 1e-133, 1e-132, 1e-131, 1e-130, 1e-129, 1e-128, 151 | 1e-127, 1e-126, 1e-125, 1e-124, 1e-123, 1e-122, 1e-121, 1e-120, 1e-119, 152 | 1e-118, 1e-117, 1e-116, 1e-115, 1e-114, 1e-113, 1e-112, 1e-111, 1e-110, 153 | 1e-109, 1e-108, 1e-107, 1e-106, 1e-105, 1e-104, 1e-103, 1e-102, 1e-101, 154 | 1e-100, 1e-99, 1e-98, 1e-97, 1e-96, 1e-95, 1e-94, 1e-93, 1e-92, 155 | 1e-91, 1e-90, 1e-89, 1e-88, 1e-87, 1e-86, 1e-85, 1e-84, 1e-83, 156 | 1e-82, 1e-81, 1e-80, 1e-79, 1e-78, 1e-77, 1e-76, 1e-75, 1e-74, 157 | 1e-73, 1e-72, 1e-71, 1e-70, 1e-69, 1e-68, 1e-67, 1e-66, 1e-65, 158 | 1e-64, 1e-63, 1e-62, 1e-61, 1e-60, 1e-59, 1e-58, 1e-57, 1e-56, 159 | 1e-55, 1e-54, 1e-53, 1e-52, 1e-51, 1e-50, 1e-49, 1e-48, 1e-47, 160 | 1e-46, 1e-45, 1e-44, 1e-43, 1e-42, 1e-41, 1e-40, 1e-39, 1e-38, 161 | 1e-37, 1e-36, 1e-35, 1e-34, 1e-33, 1e-32, 1e-31, 1e-30, 1e-29, 162 | 1e-28, 1e-27, 1e-26, 1e-25, 1e-24, 1e-23, 1e-22, 1e-21, 1e-20, 163 | 1e-19, 1e-18, 1e-17, 1e-16, 1e-15, 1e-14, 1e-13, 1e-12, 1e-11, 164 | 1e-10, 1e-9, 1e-8, 1e-7, 1e-6, 1e-5, 1e-4, 1e-3, 1e-2, 165 | 1e-1, 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 166 | 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 167 | 1e17, 1e18, 1e19, 1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 168 | 1e26, 1e27, 1e28, 1e29, 1e30, 1e31, 1e32, 1e33, 1e34, 169 | 1e35, 1e36, 1e37, 1e38, 1e39, 1e40, 1e41, 1e42, 1e43, 170 | 1e44, 1e45, 1e46, 1e47, 1e48, 1e49, 1e50, 1e51, 1e52, 171 | 1e53, 1e54, 1e55, 1e56, 1e57, 1e58, 1e59, 1e60, 1e61, 172 | 1e62, 1e63, 1e64, 1e65, 1e66, 1e67, 1e68, 1e69, 1e70, 173 | 1e71, 1e72, 1e73, 1e74, 1e75, 1e76, 1e77, 1e78, 1e79, 174 | 1e80, 1e81, 1e82, 1e83, 1e84, 1e85, 1e86, 1e87, 1e88, 175 | 1e89, 1e90, 1e91, 1e92, 1e93, 1e94, 1e95, 1e96, 1e97, 176 | 1e98, 1e99, 1e100, 1e101, 1e102, 1e103, 1e104, 1e105, 1e106, 177 | 1e107, 1e108, 1e109, 1e110, 1e111, 1e112, 1e113, 1e114, 1e115, 178 | 1e116, 1e117, 1e118, 1e119, 1e120, 1e121, 1e122, 1e123, 1e124, 179 | 1e125, 1e126, 1e127, 1e128, 1e129, 1e130, 1e131, 1e132, 1e133, 180 | 1e134, 1e135, 1e136, 1e137, 1e138, 1e139, 1e140, 1e141, 1e142, 181 | 1e143, 1e144, 1e145, 1e146, 1e147, 1e148, 1e149, 1e150, 1e151, 182 | 1e152, 1e153, 1e154, 1e155, 1e156, 1e157, 1e158, 1e159, 1e160, 183 | 1e161, 1e162, 1e163, 1e164, 1e165, 1e166, 1e167, 1e168, 1e169, 184 | 1e170, 1e171, 1e172, 1e173, 1e174, 1e175, 1e176, 1e177, 1e178, 185 | 1e179, 1e180, 1e181, 1e182, 1e183, 1e184, 1e185, 1e186, 1e187, 186 | 1e188, 1e189, 1e190, 1e191, 1e192, 1e193, 1e194, 1e195, 1e196, 187 | 1e197, 1e198, 1e199, 1e200, 1e201, 1e202, 1e203, 1e204, 1e205, 188 | 1e206, 1e207, 1e208, 1e209, 1e210, 1e211, 1e212, 1e213, 1e214, 189 | 1e215, 1e216, 1e217, 1e218, 1e219, 1e220, 1e221, 1e222, 1e223, 190 | 1e224, 1e225, 1e226, 1e227, 1e228, 1e229, 1e230, 1e231, 1e232, 191 | 1e233, 1e234, 1e235, 1e236, 1e237, 1e238, 1e239, 1e240, 1e241, 192 | 1e242, 1e243, 1e244, 1e245, 1e246, 1e247, 1e248, 1e249, 1e250, 193 | 1e251, 1e252, 1e253, 1e254, 1e255, 1e256, 1e257, 1e258, 1e259, 194 | 1e260, 1e261, 1e262, 1e263, 1e264, 1e265, 1e266, 1e267, 1e268, 195 | 1e269, 1e270, 1e271, 1e272, 1e273, 1e274, 1e275, 1e276, 1e277, 196 | 1e278, 1e279, 1e280, 1e281, 1e282, 1e283, 1e284, 1e285, 1e286, 197 | 1e287, 1e288, 1e289, 1e290, 1e291, 1e292, 1e293, 1e294, 1e295, 198 | 1e296, 1e297, 1e298, 1e299, 1e300, 1e301, 1e302, 1e303, 1e304, 199 | 1e305, 1e306, 1e307, 1e308}; 200 | 201 | 202 | void issue13() { 203 | std::string a = "0"; 204 | double x; 205 | bool ok = fast_double_parser::parse_number(a.c_str(), &x); 206 | if(!ok) throw std::runtime_error("could not parse zero."); 207 | if(x != 0) throw std::runtime_error("zero does not map to zero."); 208 | std::cout << "zero maps to zero" << std::endl; 209 | } 210 | 211 | void issue40() { 212 | //https://tools.ietf.org/html/rfc7159 213 | // A fraction part is a decimal point followed by one or more digits. 214 | std::string a = "0."; 215 | double x; 216 | bool ok = fast_double_parser::parse_number(a.c_str(), &x); 217 | if(ok) throw std::runtime_error("We should not parse '0.'"); 218 | } 219 | 220 | void issue32() { 221 | std::string a = "-0"; 222 | double x; 223 | bool ok = fast_double_parser::parse_number(a.c_str(), &x); 224 | if(!ok) throw std::runtime_error("could not parse -zero."); 225 | if(x != 0) throw std::runtime_error("-zero does not map to zero."); 226 | std::cout << "zero maps to zero" << std::endl; 227 | } 228 | 229 | void negative_subsubnormal_to_negative_zero() { 230 | std::string a = "-1e-999"; 231 | double x; 232 | bool ok = fast_double_parser::parse_number(a.c_str(), &x); 233 | if(!ok) throw std::runtime_error("could not parse -1e-999"); 234 | if(!std::signbit(x)) throw std::runtime_error("-1e-999 signbit is positive"); 235 | if(x != -0) throw std::runtime_error("-1e-999 is not -0"); 236 | std::cout << "-1e-999 parses to -0" << std::endl; 237 | } 238 | 239 | void issue50_fastpath() { 240 | std::string a = "-0.0"; 241 | double x; 242 | bool ok = fast_double_parser::parse_number(a.c_str(), &x); 243 | if(!ok) throw std::runtime_error("could not parse -0.0"); 244 | if(!std::signbit(x)) throw std::runtime_error("-0.0 signbit is positive"); 245 | std::cout << "-0.0 signbit is negative" << std::endl; 246 | } 247 | 248 | 249 | void issue50_off_fastpath() { 250 | std::string a = "-0.0e-22"; 251 | double x; 252 | bool ok = fast_double_parser::parse_number(a.c_str(), &x); 253 | if(!ok) throw std::runtime_error("could not parse -0.0-22"); 254 | if(!std::signbit(x)) throw std::runtime_error("-0.0-22 signbit is positive"); 255 | std::cout << "-0.0-22 signbit is negative" << std::endl; 256 | } 257 | 258 | void issue23() { 259 | std::string a = "0e+42949672970"; 260 | double x; 261 | bool ok = fast_double_parser::parse_number(a.c_str(), &x); 262 | if(!ok) throw std::runtime_error("could not parse zero."); 263 | if(x != 0) throw std::runtime_error("zero does not map to zero."); 264 | std::cout << "zero maps to zero" << std::endl; 265 | } 266 | 267 | void issue23_2() { 268 | std::string a = "5e0012"; 269 | double x; 270 | const char * ok = fast_double_parser::parse_number(a.c_str(), &x); 271 | if(!ok) throw std::runtime_error("could not parse zero."); 272 | if(ok != a.c_str() + a.size()) throw std::runtime_error("does not point at the end."); 273 | if(x != 5e12) throw std::runtime_error("cannot parse 5e0012."); 274 | std::cout << "can parse 5e0012" << std::endl; 275 | } 276 | 277 | void issue63() { 278 | std::string a = "1-4-abc"; 279 | double x; 280 | const char * ok = fast_double_parser::parse_number(a.c_str(), &x); 281 | if(!ok) throw std::runtime_error("1-4-abc"); 282 | if(ok != a.c_str() + 1) throw std::runtime_error("does not point at the next number"); 283 | if(x != 1) throw std::runtime_error("cannot parse 1."); 284 | std::cout << "1-4-abc" << std::endl; 285 | } 286 | 287 | void issue2093() { 288 | std::string a = "0.95000000000000000000"; 289 | double x; 290 | const char * ok = fast_double_parser::parse_number(a.c_str(), &x); 291 | if(!ok) throw std::runtime_error("0.95000000000000000000"); 292 | if(x != 0.95) throw std::runtime_error("cannot parse 0.95000000000000000000."); 293 | std::cout << "0.95000000000000000000" << std::endl; 294 | } 295 | 296 | 297 | 298 | inline void Assert(bool Assertion) { 299 | if (!Assertion) 300 | throw std::runtime_error("bug"); 301 | } 302 | bool basic_test_64bit(std::string vals, double val) { 303 | std::cout << " parsing " << vals << std::endl; 304 | double result_value; 305 | const char * ok = fast_double_parser::parse_number(vals.c_str(), & result_value); 306 | if (!ok) { 307 | std::cerr << " I could not parse " << vals << std::endl; 308 | return false; 309 | } 310 | if(ok != vals.c_str() + vals.size()) { 311 | std::cout << "gap is " << (ok - vals.c_str()) << std::endl; 312 | throw std::runtime_error("does not point at the end."); 313 | 314 | } 315 | if (std::isnan(val)) { 316 | if (!std::isnan(result_value)) { 317 | std::cerr << "not nan" << result_value << std::endl; 318 | return false; 319 | } 320 | } else if (result_value != val) { 321 | std::cerr << "I got " << std::hexfloat << result_value << " but I was expecting " << val 322 | << std::endl; 323 | std::cerr << std::dec; 324 | std::cerr << "string: " << vals << std::endl; 325 | return false; 326 | } 327 | std::cout << std::hexfloat << result_value << " == " << val << std::endl; 328 | std::cout << std::dec; 329 | 330 | return true; 331 | } 332 | 333 | int main() { 334 | const int evl_method = FLT_EVAL_METHOD; 335 | printf("FLT_EVAL_METHOD = %d\n", evl_method); 336 | bool is_pow_correct{1e-308 == std::pow(10,-308)}; 337 | if(!is_pow_correct) { 338 | printf("It appears that your system has bad floating-point support since 1e-308 differs from std::pow(10,-308).\n"); 339 | printf("Aborting further tests."); 340 | return EXIT_SUCCESS; 341 | } 342 | issue2093(); 343 | Assert(basic_test_64bit("1090544144181609348835077142190",0x1.b8779f2474dfbp+99)); 344 | Assert(basic_test_64bit("4503599627370496.5", 4503599627370496.5)); 345 | Assert(basic_test_64bit("4503599627370497.5", 4503599627370497.5)); 346 | Assert(basic_test_64bit("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044501477170144022721148195934182639518696390927032912960468522194496444440421538910330590478162701758282983178260792422137401728773891892910553144148156412434867599762821265346585071045737627442980259622449029037796981144446145705102663115100318287949527959668236039986479250965780342141637013812613333119898765515451440315261253813266652951306000184917766328660755595837392240989947807556594098101021612198814605258742579179000071675999344145086087205681577915435923018910334964869420614052182892431445797605163650903606514140377217442262561590244668525767372446430075513332450079650686719491377688478005309963967709758965844137894433796621993967316936280457084866613206797017728916080020698679408551343728867675409720757232455434770912461317493580281734466552734375)); 347 | Assert(basic_test_64bit("0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375", 0.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000022250738585072008890245868760858598876504231122409594654935248025624400092282356951787758888037591552642309780950434312085877387158357291821993020294379224223559819827501242041788969571311791082261043971979604000454897391938079198936081525613113376149842043271751033627391549782731594143828136275113838604094249464942286316695429105080201815926642134996606517803095075913058719846423906068637102005108723282784678843631944515866135041223479014792369585208321597621066375401613736583044193603714778355306682834535634005074073040135602968046375918583163124224521599262546494300836851861719422417646455137135420132217031370496583210154654068035397417906022589503023501937519773030945763173210852507299305089761582519159720757232455434770912461317493580281734466552734375)); 348 | issue63(); 349 | issue40(); 350 | issue23(); 351 | issue32(); 352 | issue50_fastpath(); 353 | issue50_off_fastpath(); 354 | issue23_2(); 355 | unit_tests(); 356 | for (int p = -306; p <= 308; p++) { 357 | if (p == 23) 358 | p++; 359 | printf("."); 360 | fflush(NULL); 361 | bool success; 362 | double d = fast_double_parser::compute_float_64(p, 1, false, &success); 363 | if (!success) { 364 | printf("failed to parse\n"); 365 | printf(" 10 ^ %d ", p); 366 | 367 | return EXIT_FAILURE; 368 | } 369 | if (d != testing_power_of_ten[p + 307]) { 370 | printf(" 10 ^ %d ", p); 371 | 372 | printf("bad parsing\n"); 373 | printf("got: %.*e\n", DBL_DIG + 1, d); 374 | printf("reference: %.*e\n", DBL_DIG + 1, testing_power_of_ten[p + 307]); 375 | 376 | return EXIT_FAILURE; 377 | } 378 | } 379 | for (int p = -306; p <= 308; p++) { 380 | double d; 381 | std::string s = "1e"+std::to_string(p); 382 | std::cout << "parsing " << s << std::endl; 383 | const char * isok = fast_double_parser::parse_number(s.c_str(), &d); 384 | if (!isok) { 385 | printf("fast_double_parser refused to parse %s\n", s.c_str()); 386 | throw std::runtime_error("fast_double_parser refused to parse"); 387 | } 388 | if(isok != s.c_str() + s.size()) throw std::runtime_error("does not point at the end"); 389 | if (d != testing_power_of_ten[p + 307]) { 390 | std::cerr << "fast_double_parser disagrees" << std::endl; 391 | printf("fast_double_parser: %.*e\n", DBL_DIG + 1, d); 392 | printf("reference: %.*e\n", DBL_DIG + 1, testing_power_of_ten[p + 307]); 393 | printf("string: %s\n", s.c_str()); 394 | throw std::runtime_error("fast_double_parser disagrees"); 395 | } 396 | } 397 | negative_subsubnormal_to_negative_zero(); 398 | std::cout << std::endl; 399 | std::cout << "All ok" << std::endl; 400 | printf("Good!\n"); 401 | return EXIT_SUCCESS; 402 | } 403 | --------------------------------------------------------------------------------