├── .clang-format ├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ ├── cross.yml │ └── os.yml ├── .gitignore ├── CMakeLists.txt ├── LICENSE-Apache2-LLVM ├── LICENSE-Boost ├── README.md ├── cmake └── dragonboxConfig.cmake ├── include └── dragonbox │ ├── dragonbox.h │ └── dragonbox_to_chars.h ├── other_files ├── Dragonbox.pdf ├── Dragonbox_old.pdf ├── unknown_win64_vc2019.html ├── unknown_win64_vc2019_randomdigit_time.png └── unknown_win64_vc2019_randomdigit_timedigit.png ├── source └── dragonbox_to_chars.cpp └── subproject ├── 3rdparty ├── grisu_exact │ ├── CMakeLists.txt │ ├── fp_to_chars.cpp │ ├── fp_to_chars.h │ └── grisu_exact.h ├── ryu │ ├── CMakeLists.txt │ └── ryu │ │ ├── common.h │ │ ├── d2s.c │ │ ├── d2s_full_table.h │ │ ├── d2s_intrinsics.h │ │ ├── digit_table.h │ │ ├── f2s.c │ │ ├── f2s_full_table.h │ │ ├── f2s_intrinsics.h │ │ └── ryu.h ├── schubfach │ ├── CMakeLists.txt │ ├── schubfach_32.cc │ ├── schubfach_32.h │ ├── schubfach_64.cc │ └── schubfach_64.h └── shaded_plots │ ├── example_shaded_plots.m │ ├── license.txt │ ├── plot_distribution.m │ ├── plot_distribution_prctile.m │ ├── plot_histogram_shaded.m │ └── plot_shaded.m ├── benchmark ├── CMakeLists.txt ├── include │ └── benchmark.h ├── matlab │ ├── plot_benchmarks.m │ ├── plot_digit_benchmark.m │ └── plot_uniform_benchmark.m ├── results │ ├── digits_benchmark_binary32_clang.png │ ├── digits_benchmark_binary32_msvc.png │ ├── digits_benchmark_binary64_clang.png │ ├── digits_benchmark_binary64_msvc.png │ ├── uniform_benchmark_binary32_clang.png │ ├── uniform_benchmark_binary32_msvc.png │ ├── uniform_benchmark_binary64_clang.png │ └── uniform_benchmark_binary64_msvc.png └── source │ ├── benchmark.cpp │ ├── dragonbox.cpp │ ├── grisu_exact.cpp │ ├── ryu.cpp │ └── schubfach.cpp ├── common ├── CMakeLists.txt ├── include │ ├── best_rational_approx.h │ ├── big_uint.h │ ├── continued_fractions.h │ ├── good_rational_approx.h │ ├── random_float.h │ └── rational_continued_fractions.h └── source │ └── big_uint.cpp ├── meta ├── CMakeLists.txt ├── results │ ├── binary32_generated_cache.txt │ └── binary64_generated_cache.txt └── source │ ├── generate_cache.cpp │ ├── live_test.cpp │ ├── perf_test.cpp │ └── sandbox.cpp ├── simple ├── CMakeLists.txt ├── README.md └── include │ └── simple_dragonbox.h └── test ├── CMakeLists.txt ├── results ├── binary32.csv ├── binary64.csv └── plot_required_bits.m └── source ├── constexpr.cpp ├── test_all_shorter_interval_cases.cpp ├── uniform_random_test.cpp ├── verify_cache_precision.cpp ├── verify_compressed_cache.cpp ├── verify_fast_multiplication.cpp ├── verify_log_computation.cpp └── verify_magic_division.cpp /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | Language: Cpp 3 | Standard: Latest 4 | AccessModifierOffset: -4 5 | AlwaysBreakTemplateDeclarations: true 6 | BreakBeforeBraces: Custom 7 | BraceWrapping: 8 | BeforeCatch: true 9 | BeforeElse: true 10 | ColumnLimit: 104 11 | FixNamespaceComments: false 12 | MaxEmptyLinesToKeep: 3 13 | NamespaceIndentation: All 14 | IndentWidth: 4 15 | IndentCaseLabels: false 16 | IndentPPDirectives: BeforeHash 17 | PointerAlignment: Left 18 | SortIncludes: false 19 | SortUsingDeclarations: false 20 | UseTab: Never -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | CMakeLists.txt eol=lf 4 | *.h eol=lf 5 | *.hpp eol=lf 6 | *.cc eol=lf 7 | *.cpp eol=lf 8 | *.cmake eol=lf 9 | *.m eol=lf 10 | 11 | *.png binary 12 | *.pdf binary 13 | *.csv binary 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | pull_request: 9 | branches: 10 | - master 11 | 12 | env: 13 | CC: /usr/bin/gcc-10 14 | CXX: /usr/bin/g++-10 15 | 16 | jobs: 17 | tests: 18 | runs-on: ubuntu-latest 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | 23 | - uses: friendlyanon/fetch-core-count@v1 24 | id: cores 25 | 26 | - name: Install GCC 10 27 | run: | 28 | sudo apt-get update 29 | sudo apt-get install -y gcc-10 g++-10 30 | 31 | - name: Configure 32 | run: cmake -Ssubproject/test -Bbuild/test 33 | -DCMAKE_BUILD_TYPE:STRING=Debug 34 | 35 | - name: Build 36 | run: cmake --build build/test --config Debug 37 | -j ${{ steps.cores.outputs.plus_one }} 38 | 39 | - name: Test 40 | run: | 41 | cd build/test 42 | ctest -C Debug --output-on-failure -j ${{ steps.cores.outputs.plus_one }} 43 | 44 | build_meta: 45 | runs-on: ubuntu-latest 46 | 47 | steps: 48 | - uses: actions/checkout@v3 49 | 50 | - uses: friendlyanon/fetch-core-count@v1 51 | id: cores 52 | 53 | - name: Install GCC 10 54 | run: | 55 | sudo apt-get update 56 | sudo apt-get install -y gcc-10 g++-10 57 | 58 | - name: Configure 59 | run: cmake -Ssubproject/meta -Bbuild/meta 60 | -DCMAKE_BUILD_TYPE:STRING=Debug 61 | 62 | - name: Build 63 | run: cmake --build build/meta --config Debug 64 | -j ${{ steps.cores.outputs.plus_one }} 65 | 66 | build_benchmark: 67 | runs-on: ubuntu-latest 68 | 69 | steps: 70 | - uses: actions/checkout@v3 71 | 72 | - uses: friendlyanon/fetch-core-count@v1 73 | id: cores 74 | 75 | - name: Install GCC 10 76 | run: | 77 | sudo apt-get update 78 | sudo apt-get install -y gcc-10 g++-10 79 | 80 | - name: Configure 81 | run: cmake -Ssubproject/benchmark -Bbuild/benchmark 82 | -DCMAKE_BUILD_TYPE:STRING=Debug 83 | 84 | - name: Build 85 | run: cmake --build build/benchmark --config Debug 86 | -j ${{ steps.cores.outputs.plus_one }} 87 | -------------------------------------------------------------------------------- /.github/workflows/cross.yml: -------------------------------------------------------------------------------- 1 | name: Cross-Compilation CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | 8 | workflow_dispatch: 9 | pull_request: 10 | branches: [master] 11 | 12 | jobs: 13 | tests: 14 | runs-on: ubuntu-latest 15 | 16 | strategy: 17 | fail-fast: false 18 | matrix: 19 | target: 20 | # Test big and little-endian architectures, 32-bit and 64-bit 21 | - arm64-unknown-linux-gnu 22 | - arm64eb-unknown-linux-gnu 23 | - mipsel-unknown-linux-gnu 24 | - mips-unknown-linux-gnu 25 | - riscv64-unknown-linux-gnu 26 | 27 | steps: 28 | - uses: actions/checkout@v3 29 | - name: Set up Python 30 | uses: actions/setup-python@v2 31 | with: 32 | python-version: 3.7 33 | 34 | - name: Install dependencies 35 | run: | 36 | python -m pip install --upgrade pip 37 | python -m pip install xcross==0.1.7 38 | 39 | - name: Configure 40 | run: xcross --target ${{ matrix.target }} 41 | cmake -Ssubproject/test -Bbuild/test 42 | -DCMAKE_BUILD_TYPE:STRING=Debug 43 | 44 | - name: Build 45 | run: xcross --target ${{ matrix.target }} 46 | cmake --build build/test --config Debug 47 | 48 | - name: Test 49 | run: | 50 | cd build/test 51 | xcross --target ${{ matrix.target }} run ./test_all_shorter_interval_cases 52 | xcross --target ${{ matrix.target }} run ./uniform_random_test 53 | xcross --target ${{ matrix.target }} run ./verify_compressed_cache 54 | xcross --target ${{ matrix.target }} run ./verify_fast_multiplication 55 | xcross --target ${{ matrix.target }} run ./verify_log_computation 56 | xcross --target ${{ matrix.target }} run ./verify_magic_division 57 | -------------------------------------------------------------------------------- /.github/workflows/os.yml: -------------------------------------------------------------------------------- 1 | name: OS Continuous Integration 2 | 3 | on: 4 | workflow_dispatch: 5 | pull_request: 6 | branches: [master] 7 | 8 | jobs: 9 | cross: 10 | name: DragonBox ${{matrix.platform}} 11 | strategy: 12 | matrix: 13 | platform: [ubuntu-latest, macos-latest, windows-latest] 14 | runs-on: ${{ matrix.platform }} 15 | 16 | steps: 17 | - uses: actions/checkout@v3 18 | - name: Configure 19 | run: cmake -Ssubproject/test -Bbuild/test -DCMAKE_BUILD_TYPE:STRING=Debug 20 | 21 | - name: Build 22 | run: cmake --build build/test --config Debug 23 | 24 | - name: Test 25 | run: | 26 | cd build/test 27 | ctest -C Debug --output-on-failure 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # TODO: clean 2 | 3 | # VS generated CMake settings 4 | CMakeSettings.json 5 | 6 | # CMake build directory 7 | build/ 8 | 9 | # Ignore CLion build folder everywhere 10 | **/cmake-build-*/ 11 | 12 | # Created by https://www.toptal.com/developers/gitignore/api/clion+all,visualstudio 13 | # Edit at https://www.toptal.com/developers/gitignore?templates=clion+all,visualstudio 14 | 15 | ### CLion+all ### 16 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 17 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 18 | 19 | # User-specific stuff 20 | .idea/**/workspace.xml 21 | .idea/**/tasks.xml 22 | .idea/**/usage.statistics.xml 23 | .idea/**/dictionaries 24 | .idea/**/shelf 25 | 26 | # Generated files 27 | .idea/**/contentModel.xml 28 | 29 | # Sensitive or high-churn files 30 | .idea/**/dataSources/ 31 | .idea/**/dataSources.ids 32 | .idea/**/dataSources.local.xml 33 | .idea/**/sqlDataSources.xml 34 | .idea/**/dynamic.xml 35 | .idea/**/uiDesigner.xml 36 | .idea/**/dbnavigator.xml 37 | 38 | # Gradle 39 | .idea/**/gradle.xml 40 | .idea/**/libraries 41 | 42 | # Gradle and Maven with auto-import 43 | # When using Gradle or Maven with auto-import, you should exclude module files, 44 | # since they will be recreated, and may cause churn. Uncomment if using 45 | # auto-import. 46 | # .idea/artifacts 47 | # .idea/compiler.xml 48 | # .idea/jarRepositories.xml 49 | # .idea/modules.xml 50 | # .idea/*.iml 51 | # .idea/modules 52 | # *.iml 53 | # *.ipr 54 | 55 | # CMake 56 | cmake-build-*/ 57 | 58 | # Mongo Explorer plugin 59 | .idea/**/mongoSettings.xml 60 | 61 | # File-based project format 62 | *.iws 63 | 64 | # IntelliJ 65 | out/ 66 | 67 | # mpeltonen/sbt-idea plugin 68 | .idea_modules/ 69 | 70 | # JIRA plugin 71 | atlassian-ide-plugin.xml 72 | 73 | # Cursive Clojure plugin 74 | .idea/replstate.xml 75 | 76 | # Crashlytics plugin (for Android Studio and IntelliJ) 77 | com_crashlytics_export_strings.xml 78 | crashlytics.properties 79 | crashlytics-build.properties 80 | fabric.properties 81 | 82 | # Editor-based Rest Client 83 | .idea/httpRequests 84 | 85 | # Android studio 3.1+ serialized cache file 86 | .idea/caches/build_file_checksums.ser 87 | 88 | ### CLion+all Patch ### 89 | # Ignores the whole .idea folder and all .iml files 90 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 91 | 92 | .idea/ 93 | 94 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 95 | 96 | *.iml 97 | modules.xml 98 | .idea/misc.xml 99 | *.ipr 100 | 101 | # Sonarlint plugin 102 | .idea/sonarlint 103 | 104 | ### VisualStudio ### 105 | ## Ignore Visual Studio temporary files, build results, and 106 | ## files generated by popular Visual Studio add-ons. 107 | ## 108 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 109 | 110 | # User-specific files 111 | *.rsuser 112 | *.suo 113 | *.user 114 | *.userosscache 115 | *.sln.docstates 116 | 117 | # User-specific files (MonoDevelop/Xamarin Studio) 118 | *.userprefs 119 | 120 | # Mono auto generated files 121 | mono_crash.* 122 | 123 | # Build results 124 | [Dd]ebug/ 125 | [Dd]ebugPublic/ 126 | [Rr]elease/ 127 | [Rr]eleases/ 128 | x64/ 129 | x86/ 130 | [Aa][Rr][Mm]/ 131 | [Aa][Rr][Mm]64/ 132 | bld/ 133 | [Bb]in/ 134 | [Oo]bj/ 135 | [Ll]og/ 136 | [Ll]ogs/ 137 | 138 | # Visual Studio 2015/2017 cache/options directory 139 | .vs/ 140 | # Uncomment if you have tasks that create the project's static files in wwwroot 141 | #wwwroot/ 142 | 143 | # Visual Studio 2017 auto generated files 144 | Generated\ Files/ 145 | 146 | # MSTest test Results 147 | [Tt]est[Rr]esult*/ 148 | [Bb]uild[Ll]og.* 149 | 150 | # NUnit 151 | *.VisualState.xml 152 | TestResult.xml 153 | nunit-*.xml 154 | 155 | # Build Results of an ATL Project 156 | [Dd]ebugPS/ 157 | [Rr]eleasePS/ 158 | dlldata.c 159 | 160 | # Benchmark Results 161 | BenchmarkDotNet.Artifacts/ 162 | 163 | # .NET Core 164 | project.lock.json 165 | project.fragment.lock.json 166 | artifacts/ 167 | 168 | # StyleCop 169 | StyleCopReport.xml 170 | 171 | # Files built by Visual Studio 172 | *_i.c 173 | *_p.c 174 | *_h.h 175 | *.ilk 176 | *.meta 177 | *.obj 178 | *.iobj 179 | *.pch 180 | *.pdb 181 | *.ipdb 182 | *.pgc 183 | *.pgd 184 | *.rsp 185 | *.sbr 186 | *.tlb 187 | *.tli 188 | *.tlh 189 | *.tmp 190 | *.tmp_proj 191 | *_wpftmp.csproj 192 | *.log 193 | *.vspscc 194 | *.vssscc 195 | .builds 196 | *.pidb 197 | *.svclog 198 | *.scc 199 | 200 | # Chutzpah Test files 201 | _Chutzpah* 202 | 203 | # Visual C++ cache files 204 | ipch/ 205 | *.aps 206 | *.ncb 207 | *.opendb 208 | *.opensdf 209 | *.sdf 210 | *.cachefile 211 | *.VC.db 212 | *.VC.VC.opendb 213 | 214 | # Visual Studio profiler 215 | *.psess 216 | *.vsp 217 | *.vspx 218 | *.sap 219 | 220 | # Visual Studio Trace Files 221 | *.e2e 222 | 223 | # TFS 2012 Local Workspace 224 | $tf/ 225 | 226 | # Guidance Automation Toolkit 227 | *.gpState 228 | 229 | # ReSharper is a .NET coding add-in 230 | _ReSharper*/ 231 | *.[Rr]e[Ss]harper 232 | *.DotSettings.user 233 | 234 | # TeamCity is a build add-in 235 | _TeamCity* 236 | 237 | # DotCover is a Code Coverage Tool 238 | *.dotCover 239 | 240 | # AxoCover is a Code Coverage Tool 241 | .axoCover/* 242 | !.axoCover/settings.json 243 | 244 | # Coverlet is a free, cross platform Code Coverage Tool 245 | coverage*[.json, .xml, .info] 246 | 247 | # Visual Studio code coverage results 248 | *.coverage 249 | *.coveragexml 250 | 251 | # NCrunch 252 | _NCrunch_* 253 | .*crunch*.local.xml 254 | nCrunchTemp_* 255 | 256 | # MightyMoose 257 | *.mm.* 258 | AutoTest.Net/ 259 | 260 | # Web workbench (sass) 261 | .sass-cache/ 262 | 263 | # Installshield output folder 264 | [Ee]xpress/ 265 | 266 | # DocProject is a documentation generator add-in 267 | DocProject/buildhelp/ 268 | DocProject/Help/*.HxT 269 | DocProject/Help/*.HxC 270 | DocProject/Help/*.hhc 271 | DocProject/Help/*.hhk 272 | DocProject/Help/*.hhp 273 | DocProject/Help/Html2 274 | DocProject/Help/html 275 | 276 | # Click-Once directory 277 | publish/ 278 | 279 | # Publish Web Output 280 | *.[Pp]ublish.xml 281 | *.azurePubxml 282 | # Note: Comment the next line if you want to checkin your web deploy settings, 283 | # but database connection strings (with potential passwords) will be unencrypted 284 | *.pubxml 285 | *.publishproj 286 | 287 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 288 | # checkin your Azure Web App publish settings, but sensitive information contained 289 | # in these scripts will be unencrypted 290 | PublishScripts/ 291 | 292 | # NuGet Packages 293 | *.nupkg 294 | # NuGet Symbol Packages 295 | *.snupkg 296 | # The packages folder can be ignored because of Package Restore 297 | **/[Pp]ackages/* 298 | # except build/, which is used as an MSBuild target. 299 | !**/[Pp]ackages/build/ 300 | # Uncomment if necessary however generally it will be regenerated when needed 301 | #!**/[Pp]ackages/repositories.config 302 | # NuGet v3's project.json files produces more ignorable files 303 | *.nuget.props 304 | *.nuget.targets 305 | 306 | # Microsoft Azure Build Output 307 | csx/ 308 | *.build.csdef 309 | 310 | # Microsoft Azure Emulator 311 | ecf/ 312 | rcf/ 313 | 314 | # Windows Store app package directories and files 315 | AppPackages/ 316 | BundleArtifacts/ 317 | Package.StoreAssociation.xml 318 | _pkginfo.txt 319 | *.appx 320 | *.appxbundle 321 | *.appxupload 322 | 323 | # Visual Studio cache files 324 | # files ending in .cache can be ignored 325 | *.[Cc]ache 326 | # but keep track of directories ending in .cache 327 | !?*.[Cc]ache/ 328 | 329 | # Others 330 | ClientBin/ 331 | ~$* 332 | *~ 333 | *.dbmdl 334 | *.dbproj.schemaview 335 | *.jfm 336 | *.pfx 337 | *.publishsettings 338 | orleans.codegen.cs 339 | 340 | # Including strong name files can present a security risk 341 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 342 | #*.snk 343 | 344 | # Since there are multiple workflows, uncomment next line to ignore bower_components 345 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 346 | #bower_components/ 347 | 348 | # RIA/Silverlight projects 349 | Generated_Code/ 350 | 351 | # Backup & report files from converting an old project file 352 | # to a newer Visual Studio version. Backup files are not needed, 353 | # because we have git ;-) 354 | _UpgradeReport_Files/ 355 | Backup*/ 356 | UpgradeLog*.XML 357 | UpgradeLog*.htm 358 | ServiceFabricBackup/ 359 | *.rptproj.bak 360 | 361 | # SQL Server files 362 | *.mdf 363 | *.ldf 364 | *.ndf 365 | 366 | # Business Intelligence projects 367 | *.rdl.data 368 | *.bim.layout 369 | *.bim_*.settings 370 | *.rptproj.rsuser 371 | *- [Bb]ackup.rdl 372 | *- [Bb]ackup ([0-9]).rdl 373 | *- [Bb]ackup ([0-9][0-9]).rdl 374 | 375 | # Microsoft Fakes 376 | FakesAssemblies/ 377 | 378 | # GhostDoc plugin setting file 379 | *.GhostDoc.xml 380 | 381 | # Node.js Tools for Visual Studio 382 | .ntvs_analysis.dat 383 | node_modules/ 384 | 385 | # Visual Studio 6 build log 386 | *.plg 387 | 388 | # Visual Studio 6 workspace options file 389 | *.opt 390 | 391 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 392 | *.vbw 393 | 394 | # Visual Studio LightSwitch build output 395 | **/*.HTMLClient/GeneratedArtifacts 396 | **/*.DesktopClient/GeneratedArtifacts 397 | **/*.DesktopClient/ModelManifest.xml 398 | **/*.Server/GeneratedArtifacts 399 | **/*.Server/ModelManifest.xml 400 | _Pvt_Extensions 401 | 402 | # Paket dependency manager 403 | .paket/paket.exe 404 | paket-files/ 405 | 406 | # FAKE - F# Make 407 | .fake/ 408 | 409 | # CodeRush personal settings 410 | .cr/personal 411 | 412 | # Python Tools for Visual Studio (PTVS) 413 | __pycache__/ 414 | *.pyc 415 | 416 | # Cake - Uncomment if you are using it 417 | # tools/** 418 | # !tools/packages.config 419 | 420 | # Tabs Studio 421 | *.tss 422 | 423 | # Telerik's JustMock configuration file 424 | *.jmconfig 425 | 426 | # BizTalk build output 427 | *.btp.cs 428 | *.btm.cs 429 | *.odx.cs 430 | *.xsd.cs 431 | 432 | # OpenCover UI analysis results 433 | OpenCover/ 434 | 435 | # Azure Stream Analytics local run output 436 | ASALocalRun/ 437 | 438 | # MSBuild Binary and Structured Log 439 | *.binlog 440 | 441 | # NVidia Nsight GPU debugger configuration file 442 | *.nvuser 443 | 444 | # MFractors (Xamarin productivity tool) working folder 445 | .mfractor/ 446 | 447 | # Local History for Visual Studio 448 | .localhistory/ 449 | 450 | # BeatPulse healthcheck temp database 451 | healthchecksdb 452 | 453 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 454 | MigrationBackup/ 455 | 456 | # Ionide (cross platform F# VS Code tools) working folder 457 | .ionide/ 458 | 459 | # End of https://www.toptal.com/developers/gitignore/api/clion+all,visualstudio 460 | /subproject/benchmark/results/uniform_benchmark_binary64.pdf 461 | /subproject/benchmark/results/uniform_benchmark_binary64.csv 462 | /subproject/benchmark/results/uniform_benchmark_binary32.pdf 463 | /subproject/benchmark/results/uniform_benchmark_binary32.csv 464 | /subproject/benchmark/results/digits_benchmark_binary64.pdf 465 | /subproject/benchmark/results/digits_benchmark_binary64.csv 466 | /subproject/benchmark/results/digits_benchmark_binary32.pdf 467 | /subproject/benchmark/results/digits_benchmark_binary32.csv 468 | /subproject/test/results/binary64_required_bits.pdf 469 | /subproject/test/results/binary32_required_bits.pdf 470 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(dragonbox 4 | VERSION 1.1.3 5 | LANGUAGES CXX) 6 | 7 | # ---- Includes ---- 8 | 9 | include(CMakePackageConfigHelpers) 10 | include(GNUInstallDirs) 11 | 12 | # ---- Warning guard ---- 13 | 14 | # Protect dependents from this project's warnings if the guard isn't disabled 15 | set(dragonbox_warning_guard "SYSTEM") 16 | if(dragonbox_INCLUDE_WITHOUT_SYSTEM) 17 | set(dragonbox_warning_guard "") 18 | endif() 19 | 20 | # ---- Declare library (dragonbox) ---- 21 | 22 | set(dragonbox_headers include/dragonbox/dragonbox.h) 23 | 24 | if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.19.0") 25 | add_library(dragonbox INTERFACE ${dragonbox_headers}) 26 | else() 27 | add_library(dragonbox INTERFACE) 28 | endif() 29 | add_library(dragonbox::dragonbox ALIAS dragonbox) 30 | 31 | target_include_directories(dragonbox 32 | ${dragonbox_warning_guard} 33 | INTERFACE 34 | "$") 35 | 36 | target_compile_features(dragonbox INTERFACE cxx_std_17) 37 | 38 | # ---- Declare library (dragonbox_to_chars) ---- 39 | 40 | if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.19.0") 41 | set(dragonbox_to_chars_headers include/dragonbox/dragonbox_to_chars.h) 42 | else() 43 | set(dragonbox_to_chars_headers 44 | ${dragonbox_headers} 45 | include/dragonbox/dragonbox_to_chars.h) 46 | endif() 47 | 48 | set(dragonbox_to_chars_sources source/dragonbox_to_chars.cpp) 49 | 50 | add_library(dragonbox_to_chars STATIC 51 | ${dragonbox_to_chars_headers} 52 | ${dragonbox_to_chars_sources}) 53 | add_library(dragonbox::dragonbox_to_chars ALIAS dragonbox_to_chars) 54 | 55 | target_include_directories(dragonbox_to_chars 56 | ${dragonbox_warning_guard} 57 | PUBLIC 58 | "$") 59 | 60 | target_link_libraries(dragonbox_to_chars PUBLIC dragonbox) 61 | 62 | target_compile_features(dragonbox_to_chars PUBLIC cxx_std_17) 63 | 64 | # ---- Install ---- 65 | 66 | option(DRAGONBOX_INSTALL_TO_CHARS 67 | "When invoked with --install, dragonbox_to_chars.h/.cpp are installed along with dragonbox.h" 68 | On) 69 | 70 | set(dragonbox_directory "dragonbox-${PROJECT_VERSION}") 71 | set(dragonbox_include_directory "${CMAKE_INSTALL_INCLUDEDIR}/${dragonbox_directory}") 72 | set(dragonbox_install_targets "dragonbox") 73 | 74 | if (DRAGONBOX_INSTALL_TO_CHARS) 75 | set(dragonbox_install_targets ${dragonbox_install_targets} dragonbox_to_chars) 76 | endif() 77 | 78 | install(TARGETS ${dragonbox_install_targets} 79 | EXPORT dragonboxTargets 80 | ARCHIVE # 81 | DESTINATION "${CMAKE_INSTALL_LIBDIR}" 82 | COMPONENT dragonbox_Development 83 | INCLUDES # 84 | DESTINATION "${dragonbox_include_directory}") 85 | 86 | set(dragonbox_install_cmakedir "${CMAKE_INSTALL_LIBDIR}/cmake/${dragonbox_directory}") 87 | 88 | write_basic_package_version_file( 89 | dragonboxConfigVersion.cmake 90 | VERSION ${PROJECT_VERSION} 91 | COMPATIBILITY SameMajorVersion 92 | ARCH_INDEPENDENT) 93 | 94 | install(EXPORT dragonboxTargets 95 | NAMESPACE dragonbox:: 96 | DESTINATION "${dragonbox_install_cmakedir}") 97 | 98 | install(FILES 99 | "${PROJECT_SOURCE_DIR}/cmake/dragonboxConfig.cmake" 100 | "${PROJECT_BINARY_DIR}/dragonboxConfigVersion.cmake" 101 | DESTINATION "${dragonbox_install_cmakedir}") 102 | 103 | if (DRAGONBOX_INSTALL_TO_CHARS) 104 | install(FILES ${dragonbox_to_chars_headers} 105 | DESTINATION "${dragonbox_include_directory}/dragonbox") 106 | else() 107 | install(FILES ${dragonbox_headers} 108 | DESTINATION "${dragonbox_include_directory}/dragonbox") 109 | endif() 110 | 111 | # ---- Subproject ---- 112 | 113 | option(DRAGONBOX_ENABLE_SUBPROJECT "Build subproject as well" OFF) 114 | 115 | if (DRAGONBOX_ENABLE_SUBPROJECT) 116 | add_subdirectory("subproject/benchmark") 117 | add_subdirectory("subproject/meta") 118 | add_subdirectory("subproject/test") 119 | endif() -------------------------------------------------------------------------------- /LICENSE-Boost: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /cmake/dragonboxConfig.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/dragonboxTargets.cmake") 2 | -------------------------------------------------------------------------------- /other_files/Dragonbox.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/other_files/Dragonbox.pdf -------------------------------------------------------------------------------- /other_files/Dragonbox_old.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/other_files/Dragonbox_old.pdf -------------------------------------------------------------------------------- /other_files/unknown_win64_vc2019_randomdigit_time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/other_files/unknown_win64_vc2019_randomdigit_time.png -------------------------------------------------------------------------------- /other_files/unknown_win64_vc2019_randomdigit_timedigit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/other_files/unknown_win64_vc2019_randomdigit_timedigit.png -------------------------------------------------------------------------------- /subproject/3rdparty/grisu_exact/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(grisu_exact LANGUAGES CXX) 4 | 5 | add_library(grisu_exact STATIC fp_to_chars.h fp_to_chars.cpp grisu_exact.h) 6 | add_library(grisu_exact::grisu_exact ALIAS grisu_exact) 7 | 8 | target_compile_features(grisu_exact PUBLIC cxx_std_17) 9 | 10 | target_include_directories(grisu_exact 11 | PUBLIC 12 | $) -------------------------------------------------------------------------------- /subproject/3rdparty/grisu_exact/fp_to_chars.cpp: -------------------------------------------------------------------------------- 1 | // The contents of this file is based on contents of: 2 | // 3 | // https://github.com/ulfjack/ryu/blob/master/ryu/common.h, 4 | // https://github.com/ulfjack/ryu/blob/master/ryu/d2s.c, and 5 | // https://github.com/ulfjack/ryu/blob/master/ryu/f2s.c, 6 | // 7 | // which are distributed under the following terms: 8 | //-------------------------------------------------------------------------------- 9 | // Copyright 2018 Ulf Adams 10 | // 11 | // The contents of this file may be used under the terms of the Apache License, 12 | // Version 2.0. 13 | // 14 | // (See accompanying file LICENSE-Apache or copy at 15 | // http://www.apache.org/licenses/LICENSE-2.0) 16 | // 17 | // Alternatively, the contents of this file may be used under the terms of 18 | // the Boost Software License, Version 1.0. 19 | // (See accompanying file LICENSE-Boost or copy at 20 | // https://www.boost.org/LICENSE_1_0.txt) 21 | // 22 | // Unless required by applicable law or agreed to in writing, this software 23 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 24 | // KIND, either express or implied. 25 | //-------------------------------------------------------------------------------- 26 | // Modifications Copyright 2020 Junekey Jeon 27 | // 28 | // Following modifications were made to the original contents: 29 | // - Put everything inside the namespace jkj::fp_to_chars_detail 30 | // - Combined decimalLength9 (from common.h) and decimalLength17 (from d2s.c) 31 | // into a single template function decimal_length 32 | // - Combined to_chars (from f2s.c) and to_chars (from d2s.c) into a 33 | // single template function fp_to_chars_impl 34 | // - Removed index counting statements; replaced them with pointer increments 35 | // - Removed usages of DIGIT_TABLE; replaced them with radix_100_table 36 | // 37 | // These modifications, together with other contents of this file may be used 38 | // under the same terms as the original contents. 39 | 40 | 41 | #include "fp_to_chars.h" 42 | 43 | namespace jkj { 44 | namespace fp_to_chars_detail { 45 | static constexpr char radix_100_table[] = { 46 | '0', '0', '0', '1', '0', '2', '0', '3', '0', '4', 47 | '0', '5', '0', '6', '0', '7', '0', '8', '0', '9', 48 | '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', 49 | '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', 50 | '2', '0', '2', '1', '2', '2', '2', '3', '2', '4', 51 | '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', 52 | '3', '0', '3', '1', '3', '2', '3', '3', '3', '4', 53 | '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', 54 | '4', '0', '4', '1', '4', '2', '4', '3', '4', '4', 55 | '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', 56 | '5', '0', '5', '1', '5', '2', '5', '3', '5', '4', 57 | '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', 58 | '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', 59 | '6', '5', '6', '6', '6', '7', '6', '8', '6', '9', 60 | '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', 61 | '7', '5', '7', '6', '7', '7', '7', '8', '7', '9', 62 | '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', 63 | '8', '5', '8', '6', '8', '7', '8', '8', '8', '9', 64 | '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', 65 | '9', '5', '9', '6', '9', '7', '9', '8', '9', '9' 66 | }; 67 | 68 | template 69 | static constexpr std::uint32_t decimal_length(UInt const v) { 70 | if constexpr (std::is_same_v) { 71 | // Function precondition: v is not a 10-digit number. 72 | // (f2s: 9 digits are sufficient for round-tripping.) 73 | // (d2fixed: We print 9-digit blocks.) 74 | assert(v < 1000000000); 75 | if (v >= 100000000) { return 9; } 76 | if (v >= 10000000) { return 8; } 77 | if (v >= 1000000) { return 7; } 78 | if (v >= 100000) { return 6; } 79 | if (v >= 10000) { return 5; } 80 | if (v >= 1000) { return 4; } 81 | if (v >= 100) { return 3; } 82 | if (v >= 10) { return 2; } 83 | return 1; 84 | } 85 | else { 86 | static_assert(std::is_same_v); 87 | // This is slightly faster than a loop. 88 | // The average output length is 16.38 digits, so we check high-to-low. 89 | // Function precondition: v is not an 18, 19, or 20-digit number. 90 | // (17 digits are sufficient for round-tripping.) 91 | assert(v < 100000000000000000L); 92 | if (v >= 10000000000000000L) { return 17; } 93 | if (v >= 1000000000000000L) { return 16; } 94 | if (v >= 100000000000000L) { return 15; } 95 | if (v >= 10000000000000L) { return 14; } 96 | if (v >= 1000000000000L) { return 13; } 97 | if (v >= 100000000000L) { return 12; } 98 | if (v >= 10000000000L) { return 11; } 99 | if (v >= 1000000000L) { return 10; } 100 | if (v >= 100000000L) { return 9; } 101 | if (v >= 10000000L) { return 8; } 102 | if (v >= 1000000L) { return 7; } 103 | if (v >= 100000L) { return 6; } 104 | if (v >= 10000L) { return 5; } 105 | if (v >= 1000L) { return 4; } 106 | if (v >= 100L) { return 3; } 107 | if (v >= 10L) { return 2; } 108 | return 1; 109 | } 110 | } 111 | 112 | template 113 | static char* fp_to_chars_impl(unsigned_fp_t v, char* buffer) 114 | { 115 | auto output = v.significand; 116 | auto const olength = decimal_length(output); 117 | 118 | // Print the decimal digits. 119 | // The following code is equivalent to: 120 | // for (uint32_t i = 0; i < olength - 1; ++i) { 121 | // const uint32_t c = output % 10; output /= 10; 122 | // result[index + olength - i] = (char) ('0' + c); 123 | // } 124 | // result[index] = '0' + output % 10; 125 | 126 | uint32_t i = 0; 127 | if constexpr (sizeof(Float) == 8) { 128 | // We prefer 32-bit operations, even on 64-bit platforms. 129 | // We have at most 17 digits, and uint32_t can store 9 digits. 130 | // If output doesn't fit into uint32_t, we cut off 8 digits, 131 | // so the rest will fit into uint32_t. 132 | if ((output >> 32) != 0) { 133 | // Expensive 64-bit division. 134 | const uint64_t q = output / 100000000; 135 | uint32_t output2 = ((uint32_t)output) - 100000000 * ((uint32_t)q); 136 | output = q; 137 | 138 | const uint32_t c = output2 % 10000; 139 | output2 /= 10000; 140 | const uint32_t d = output2 % 10000; 141 | const uint32_t c0 = (c % 100) << 1; 142 | const uint32_t c1 = (c / 100) << 1; 143 | const uint32_t d0 = (d % 100) << 1; 144 | const uint32_t d1 = (d / 100) << 1; 145 | memcpy(buffer + olength - i - 1, radix_100_table + c0, 2); 146 | memcpy(buffer + olength - i - 3, radix_100_table + c1, 2); 147 | memcpy(buffer + olength - i - 5, radix_100_table + d0, 2); 148 | memcpy(buffer + olength - i - 7, radix_100_table + d1, 2); 149 | i += 8; 150 | } 151 | } 152 | 153 | auto output2 = (uint32_t)output; 154 | while (output2 >= 10000) { 155 | #ifdef __clang__ // https://bugs.llvm.org/show_bug.cgi?id=38217 156 | const uint32_t c = output2 - 10000 * (output2 / 10000); 157 | #else 158 | const uint32_t c = output2 % 10000; 159 | #endif 160 | output2 /= 10000; 161 | const uint32_t c0 = (c % 100) << 1; 162 | const uint32_t c1 = (c / 100) << 1; 163 | memcpy(buffer + olength - i - 1, radix_100_table + c0, 2); 164 | memcpy(buffer + olength - i - 3, radix_100_table + c1, 2); 165 | i += 4; 166 | } 167 | if (output2 >= 100) { 168 | const uint32_t c = (output2 % 100) << 1; 169 | output2 /= 100; 170 | memcpy(buffer + olength - i - 1, radix_100_table + c, 2); 171 | i += 2; 172 | } 173 | if (output2 >= 10) { 174 | const uint32_t c = output2 << 1; 175 | // We can't use memcpy here: the decimal dot goes between these two digits. 176 | buffer[olength - i] = radix_100_table[c + 1]; 177 | buffer[0] = radix_100_table[c]; 178 | } 179 | else { 180 | buffer[0] = (char)('0' + output2); 181 | } 182 | 183 | // Print decimal point if needed. 184 | if (olength > 1) { 185 | buffer[1] = '.'; 186 | buffer += olength + 1; 187 | } 188 | else { 189 | ++buffer; 190 | } 191 | 192 | // Print the exponent. 193 | *buffer = 'E'; 194 | ++buffer; 195 | int32_t exp = v.exponent + (int32_t)olength - 1; 196 | if (exp < 0) { 197 | *buffer = '-'; 198 | ++buffer; 199 | exp = -exp; 200 | } 201 | if constexpr (sizeof(Float) == 8) { 202 | if (exp >= 100) { 203 | const int32_t c = exp % 10; 204 | memcpy(buffer, radix_100_table + 2 * (exp / 10), 2); 205 | buffer[2] = (char)('0' + c); 206 | buffer += 3; 207 | } 208 | else if (exp >= 10) { 209 | memcpy(buffer, radix_100_table + 2 * exp, 2); 210 | buffer += 2; 211 | } 212 | else { 213 | *buffer = (char)('0' + exp); 214 | ++buffer; 215 | } 216 | } 217 | else { 218 | if (exp >= 10) { 219 | memcpy(buffer, radix_100_table + 2 * exp, 2); 220 | buffer += 2; 221 | } 222 | else { 223 | *buffer = (char)('0' + exp); 224 | ++buffer; 225 | } 226 | } 227 | 228 | return buffer; 229 | } 230 | 231 | char* float_to_chars(unsigned_fp_t v, char* buffer) { 232 | return fp_to_chars_impl(v, buffer); 233 | } 234 | char* double_to_chars(unsigned_fp_t v, char* buffer) { 235 | return fp_to_chars_impl(v, buffer); 236 | } 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /subproject/3rdparty/grisu_exact/fp_to_chars.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #ifndef JKJ_FP_TO_CHARS 19 | #define JKJ_FP_TO_CHARS 20 | 21 | #include "grisu_exact.h" 22 | 23 | namespace jkj { 24 | namespace fp_to_chars_detail { 25 | char* float_to_chars(unsigned_fp_t v, char* buffer); 26 | char* double_to_chars(unsigned_fp_t v, char* buffer); 27 | } 28 | 29 | // Returns the next-to-end position 30 | template 34 | char* fp_to_chars_n(Float x, char* buffer, 35 | RoundingMode&& rounding_mode = {}, 36 | CorrectRoundingSearch&& crs = {}) 37 | { 38 | auto br = get_bit_representation(x); 39 | if (br.is_finite()) { 40 | if (br.is_negative()) { 41 | *buffer = '-'; 42 | ++buffer; 43 | } 44 | if (br.is_nonzero()) { 45 | if constexpr (sizeof(Float) == 4) { 46 | return fp_to_chars_detail::float_to_chars(grisu_exact(x, 47 | std::forward(rounding_mode), 48 | std::forward(crs)), buffer); 49 | } 50 | else { 51 | return fp_to_chars_detail::double_to_chars(grisu_exact(x, 52 | std::forward(rounding_mode), 53 | std::forward(crs)), buffer); 54 | } 55 | } 56 | else { 57 | std::memcpy(buffer, "0E0", 3); 58 | return buffer + 3; 59 | } 60 | } 61 | else { 62 | if ((br.f << (grisu_exact_detail::common_info::exponent_bits + 1)) != 0) 63 | { 64 | std::memcpy(buffer, "NaN", 3); 65 | return buffer + 3; 66 | } 67 | else { 68 | if (br.is_negative()) { 69 | *buffer = '-'; 70 | ++buffer; 71 | } 72 | std::memcpy(buffer, "Infinity", 8); 73 | return buffer + 8; 74 | } 75 | } 76 | } 77 | 78 | // Null-terminate and bypass the return value of fp_to_chars_n 79 | template 83 | char* fp_to_chars(Float x, char* buffer, 84 | RoundingMode&& rounding_mode = {}, 85 | CorrectRoundingSearch&& crs = {}) 86 | { 87 | auto ptr = fp_to_chars_n(x, buffer, 88 | std::forward(rounding_mode), 89 | std::forward(crs)); 90 | *ptr = '\0'; 91 | return ptr; 92 | } 93 | } 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /subproject/3rdparty/ryu/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(ryu_cmake LANGUAGES C) 4 | 5 | add_library(ryu STATIC 6 | ryu/ryu.h ryu/common.h ryu/f2s.c ryu/d2s.c 7 | ryu/f2s_intrinsics.h ryu/f2s_full_table.h 8 | ryu/d2s_intrinsics.h ryu/d2s_full_table.h 9 | ryu/digit_table.h) 10 | add_library(ryu::ryu ALIAS ryu) 11 | 12 | target_compile_features(ryu PUBLIC cxx_std_17) 13 | 14 | target_include_directories(ryu 15 | PUBLIC 16 | $) 17 | -------------------------------------------------------------------------------- /subproject/3rdparty/ryu/ryu/common.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | #ifndef RYU_COMMON_H 18 | #define RYU_COMMON_H 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #if defined(_M_IX86) || defined(_M_ARM) 25 | #define RYU_32_BIT_PLATFORM 26 | #endif 27 | 28 | // Returns the number of decimal digits in v, which must not contain more than 9 digits. 29 | static inline uint32_t decimalLength9(const uint32_t v) { 30 | // Function precondition: v is not a 10-digit number. 31 | // (f2s: 9 digits are sufficient for round-tripping.) 32 | // (d2fixed: We print 9-digit blocks.) 33 | assert(v < 1000000000); 34 | if (v >= 100000000) { return 9; } 35 | if (v >= 10000000) { return 8; } 36 | if (v >= 1000000) { return 7; } 37 | if (v >= 100000) { return 6; } 38 | if (v >= 10000) { return 5; } 39 | if (v >= 1000) { return 4; } 40 | if (v >= 100) { return 3; } 41 | if (v >= 10) { return 2; } 42 | return 1; 43 | } 44 | 45 | // Returns e == 0 ? 1 : [log_2(5^e)]; requires 0 <= e <= 3528. 46 | static inline int32_t log2pow5(const int32_t e) { 47 | // This approximation works up to the point that the multiplication overflows at e = 3529. 48 | // If the multiplication were done in 64 bits, it would fail at 5^4004 which is just greater 49 | // than 2^9297. 50 | assert(e >= 0); 51 | assert(e <= 3528); 52 | return (int32_t) ((((uint32_t) e) * 1217359) >> 19); 53 | } 54 | 55 | // Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528. 56 | static inline int32_t pow5bits(const int32_t e) { 57 | // This approximation works up to the point that the multiplication overflows at e = 3529. 58 | // If the multiplication were done in 64 bits, it would fail at 5^4004 which is just greater 59 | // than 2^9297. 60 | assert(e >= 0); 61 | assert(e <= 3528); 62 | return (int32_t) (((((uint32_t) e) * 1217359) >> 19) + 1); 63 | } 64 | 65 | // Returns e == 0 ? 1 : ceil(log_2(5^e)); requires 0 <= e <= 3528. 66 | static inline int32_t ceil_log2pow5(const int32_t e) { 67 | return log2pow5(e) + 1; 68 | } 69 | 70 | // Returns floor(log_10(2^e)); requires 0 <= e <= 1650. 71 | static inline uint32_t log10Pow2(const int32_t e) { 72 | // The first value this approximation fails for is 2^1651 which is just greater than 10^297. 73 | assert(e >= 0); 74 | assert(e <= 1650); 75 | return (((uint32_t) e) * 78913) >> 18; 76 | } 77 | 78 | // Returns floor(log_10(5^e)); requires 0 <= e <= 2620. 79 | static inline uint32_t log10Pow5(const int32_t e) { 80 | // The first value this approximation fails for is 5^2621 which is just greater than 10^1832. 81 | assert(e >= 0); 82 | assert(e <= 2620); 83 | return (((uint32_t) e) * 732923) >> 20; 84 | } 85 | 86 | static inline int copy_special_str(char * const result, const bool sign, const bool exponent, const bool mantissa) { 87 | if (mantissa) { 88 | memcpy(result, "NaN", 3); 89 | return 3; 90 | } 91 | if (sign) { 92 | result[0] = '-'; 93 | } 94 | if (exponent) { 95 | memcpy(result + sign, "Infinity", 8); 96 | return sign + 8; 97 | } 98 | memcpy(result + sign, "0E0", 3); 99 | return sign + 3; 100 | } 101 | 102 | static inline uint32_t float_to_bits(const float f) { 103 | uint32_t bits = 0; 104 | memcpy(&bits, &f, sizeof(float)); 105 | return bits; 106 | } 107 | 108 | static inline uint64_t double_to_bits(const double d) { 109 | uint64_t bits = 0; 110 | memcpy(&bits, &d, sizeof(double)); 111 | return bits; 112 | } 113 | 114 | #endif // RYU_COMMON_H 115 | -------------------------------------------------------------------------------- /subproject/3rdparty/ryu/ryu/digit_table.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | #ifndef RYU_DIGIT_TABLE_H 18 | #define RYU_DIGIT_TABLE_H 19 | 20 | // A table of all two-digit numbers. This is used to speed up decimal digit 21 | // generation by copying pairs of digits into the final output. 22 | static const char DIGIT_TABLE[200] = { 23 | '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', 24 | '1','0','1','1','1','2','1','3','1','4','1','5','1','6','1','7','1','8','1','9', 25 | '2','0','2','1','2','2','2','3','2','4','2','5','2','6','2','7','2','8','2','9', 26 | '3','0','3','1','3','2','3','3','3','4','3','5','3','6','3','7','3','8','3','9', 27 | '4','0','4','1','4','2','4','3','4','4','4','5','4','6','4','7','4','8','4','9', 28 | '5','0','5','1','5','2','5','3','5','4','5','5','5','6','5','7','5','8','5','9', 29 | '6','0','6','1','6','2','6','3','6','4','6','5','6','6','6','7','6','8','6','9', 30 | '7','0','7','1','7','2','7','3','7','4','7','5','7','6','7','7','7','8','7','9', 31 | '8','0','8','1','8','2','8','3','8','4','8','5','8','6','8','7','8','8','8','9', 32 | '9','0','9','1','9','2','9','3','9','4','9','5','9','6','9','7','9','8','9','9' 33 | }; 34 | 35 | #endif // RYU_DIGIT_TABLE_H 36 | -------------------------------------------------------------------------------- /subproject/3rdparty/ryu/ryu/f2s_full_table.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | #ifndef RYU_F2S_FULL_TABLE_H 18 | #define RYU_F2S_FULL_TABLE_H 19 | 20 | // This table is generated by PrintFloatLookupTable. 21 | #define FLOAT_POW5_INV_BITCOUNT 59 22 | #define FLOAT_POW5_BITCOUNT 61 23 | 24 | static uint64_t FLOAT_POW5_INV_SPLIT[55] = { 25 | 576460752303423489u, 461168601842738791u, 368934881474191033u, 295147905179352826u, 26 | 472236648286964522u, 377789318629571618u, 302231454903657294u, 483570327845851670u, 27 | 386856262276681336u, 309485009821345069u, 495176015714152110u, 396140812571321688u, 28 | 316912650057057351u, 507060240091291761u, 405648192073033409u, 324518553658426727u, 29 | 519229685853482763u, 415383748682786211u, 332306998946228969u, 531691198313966350u, 30 | 425352958651173080u, 340282366920938464u, 544451787073501542u, 435561429658801234u, 31 | 348449143727040987u, 557518629963265579u, 446014903970612463u, 356811923176489971u, 32 | 570899077082383953u, 456719261665907162u, 365375409332725730u, 292300327466180584u, 33 | 467680523945888934u, 374144419156711148u, 299315535325368918u, 478904856520590269u, 34 | 383123885216472215u, 306499108173177772u, 490398573077084435u, 392318858461667548u, 35 | 313855086769334039u, 502168138830934462u, 401734511064747569u, 321387608851798056u, 36 | 514220174162876889u, 411376139330301511u, 329100911464241209u, 526561458342785934u, 37 | 421249166674228747u, 336999333339382998u, 539198933343012796u, 431359146674410237u, 38 | 345087317339528190u, 552139707743245103u, 441711766194596083u 39 | }; 40 | static const uint64_t FLOAT_POW5_SPLIT[47] = { 41 | 1152921504606846976u, 1441151880758558720u, 1801439850948198400u, 2251799813685248000u, 42 | 1407374883553280000u, 1759218604441600000u, 2199023255552000000u, 1374389534720000000u, 43 | 1717986918400000000u, 2147483648000000000u, 1342177280000000000u, 1677721600000000000u, 44 | 2097152000000000000u, 1310720000000000000u, 1638400000000000000u, 2048000000000000000u, 45 | 1280000000000000000u, 1600000000000000000u, 2000000000000000000u, 1250000000000000000u, 46 | 1562500000000000000u, 1953125000000000000u, 1220703125000000000u, 1525878906250000000u, 47 | 1907348632812500000u, 1192092895507812500u, 1490116119384765625u, 1862645149230957031u, 48 | 1164153218269348144u, 1455191522836685180u, 1818989403545856475u, 2273736754432320594u, 49 | 1421085471520200371u, 1776356839400250464u, 2220446049250313080u, 1387778780781445675u, 50 | 1734723475976807094u, 2168404344971008868u, 1355252715606880542u, 1694065894508600678u, 51 | 2117582368135750847u, 1323488980084844279u, 1654361225106055349u, 2067951531382569187u, 52 | 1292469707114105741u, 1615587133892632177u, 2019483917365790221u 53 | }; 54 | 55 | #endif // RYU_F2S_FULL_TABLE_H 56 | -------------------------------------------------------------------------------- /subproject/3rdparty/ryu/ryu/f2s_intrinsics.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | #ifndef RYU_F2S_INTRINSICS_H 18 | #define RYU_F2S_INTRINSICS_H 19 | 20 | // Defines RYU_32_BIT_PLATFORM if applicable. 21 | #include "ryu/common.h" 22 | 23 | #if defined(RYU_FLOAT_FULL_TABLE) 24 | 25 | #include "ryu/f2s_full_table.h" 26 | 27 | #else 28 | 29 | #if defined(RYU_OPTIMIZE_SIZE) 30 | #include "ryu/d2s_small_table.h" 31 | #else 32 | #include "ryu/d2s_full_table.h" 33 | #endif 34 | #define FLOAT_POW5_INV_BITCOUNT (DOUBLE_POW5_INV_BITCOUNT - 64) 35 | #define FLOAT_POW5_BITCOUNT (DOUBLE_POW5_BITCOUNT - 64) 36 | 37 | #endif 38 | 39 | static inline uint32_t pow5factor_32(uint32_t value) { 40 | uint32_t count = 0; 41 | for (;;) { 42 | assert(value != 0); 43 | const uint32_t q = value / 5; 44 | const uint32_t r = value % 5; 45 | if (r != 0) { 46 | break; 47 | } 48 | value = q; 49 | ++count; 50 | } 51 | return count; 52 | } 53 | 54 | // Returns true if value is divisible by 5^p. 55 | static inline bool multipleOfPowerOf5_32(const uint32_t value, const uint32_t p) { 56 | return pow5factor_32(value) >= p; 57 | } 58 | 59 | // Returns true if value is divisible by 2^p. 60 | static inline bool multipleOfPowerOf2_32(const uint32_t value, const uint32_t p) { 61 | // __builtin_ctz doesn't appear to be faster here. 62 | return (value & ((1u << p) - 1)) == 0; 63 | } 64 | 65 | // It seems to be slightly faster to avoid uint128_t here, although the 66 | // generated code for uint128_t looks slightly nicer. 67 | static inline uint32_t mulShift32(const uint32_t m, const uint64_t factor, const int32_t shift) { 68 | assert(shift > 32); 69 | 70 | // The casts here help MSVC to avoid calls to the __allmul library 71 | // function. 72 | const uint32_t factorLo = (uint32_t)(factor); 73 | const uint32_t factorHi = (uint32_t)(factor >> 32); 74 | const uint64_t bits0 = (uint64_t)m * factorLo; 75 | const uint64_t bits1 = (uint64_t)m * factorHi; 76 | 77 | #if defined(RYU_32_BIT_PLATFORM) 78 | // On 32-bit platforms we can avoid a 64-bit shift-right since we only 79 | // need the upper 32 bits of the result and the shift value is > 32. 80 | const uint32_t bits0Hi = (uint32_t)(bits0 >> 32); 81 | uint32_t bits1Lo = (uint32_t)(bits1); 82 | uint32_t bits1Hi = (uint32_t)(bits1 >> 32); 83 | bits1Lo += bits0Hi; 84 | bits1Hi += (bits1Lo < bits0Hi); 85 | if (shift >= 64) { 86 | // s2f can call this with a shift value >= 64, which we have to handle. 87 | // This could now be slower than the !defined(RYU_32_BIT_PLATFORM) case. 88 | return (uint32_t)(bits1Hi >> (shift - 64)); 89 | } else { 90 | const int32_t s = shift - 32; 91 | return (bits1Hi << (32 - s)) | (bits1Lo >> s); 92 | } 93 | #else // RYU_32_BIT_PLATFORM 94 | const uint64_t sum = (bits0 >> 32) + bits1; 95 | const uint64_t shiftedSum = sum >> (shift - 32); 96 | assert(shiftedSum <= UINT32_MAX); 97 | return (uint32_t) shiftedSum; 98 | #endif // RYU_32_BIT_PLATFORM 99 | } 100 | 101 | static inline uint32_t mulPow5InvDivPow2(const uint32_t m, const uint32_t q, const int32_t j) { 102 | #if defined(RYU_FLOAT_FULL_TABLE) 103 | return mulShift32(m, FLOAT_POW5_INV_SPLIT[q], j); 104 | #elif defined(RYU_OPTIMIZE_SIZE) 105 | // The inverse multipliers are defined as [2^x / 5^y] + 1; the upper 64 bits from the double lookup 106 | // table are the correct bits for [2^x / 5^y], so we have to add 1 here. Note that we rely on the 107 | // fact that the added 1 that's already stored in the table never overflows into the upper 64 bits. 108 | uint64_t pow5[2]; 109 | double_computeInvPow5(q, pow5); 110 | return mulShift32(m, pow5[1] + 1, j); 111 | #else 112 | return mulShift32(m, DOUBLE_POW5_INV_SPLIT[q][1] + 1, j); 113 | #endif 114 | } 115 | 116 | static inline uint32_t mulPow5divPow2(const uint32_t m, const uint32_t i, const int32_t j) { 117 | #if defined(RYU_FLOAT_FULL_TABLE) 118 | return mulShift32(m, FLOAT_POW5_SPLIT[i], j); 119 | #elif defined(RYU_OPTIMIZE_SIZE) 120 | uint64_t pow5[2]; 121 | double_computePow5(i, pow5); 122 | return mulShift32(m, pow5[1], j); 123 | #else 124 | return mulShift32(m, DOUBLE_POW5_SPLIT[i][1], j); 125 | #endif 126 | } 127 | 128 | #endif // RYU_F2S_INTRINSICS_H 129 | -------------------------------------------------------------------------------- /subproject/3rdparty/ryu/ryu/ryu.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018 Ulf Adams 2 | // 3 | // The contents of this file may be used under the terms of the Apache License, 4 | // Version 2.0. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // http://www.apache.org/licenses/LICENSE-2.0) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | #ifndef RYU_H 18 | #define RYU_H 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | #include 25 | 26 | int d2s_buffered_n(double f, char* result); 27 | void d2s_buffered(double f, char* result); 28 | char* d2s(double f); 29 | 30 | int f2s_buffered_n(float f, char* result); 31 | void f2s_buffered(float f, char* result); 32 | char* f2s(float f); 33 | 34 | int d2fixed_buffered_n(double d, uint32_t precision, char* result); 35 | void d2fixed_buffered(double d, uint32_t precision, char* result); 36 | char* d2fixed(double d, uint32_t precision); 37 | 38 | int d2exp_buffered_n(double d, uint32_t precision, char* result); 39 | void d2exp_buffered(double d, uint32_t precision, char* result); 40 | char* d2exp(double d, uint32_t precision); 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif // RYU_H 47 | -------------------------------------------------------------------------------- /subproject/3rdparty/schubfach/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(schubfach LANGUAGES CXX) 4 | 5 | add_library(schubfach STATIC 6 | schubfach_32.cc schubfach_32.h schubfach_64.cc schubfach_64.h) 7 | add_library(schubfach::schubfach ALIAS schubfach) 8 | 9 | target_compile_features(schubfach PUBLIC cxx_std_17) 10 | 11 | target_include_directories(schubfach 12 | PUBLIC 13 | $) -------------------------------------------------------------------------------- /subproject/3rdparty/schubfach/schubfach_32.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Alexander Bolz 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #pragma once 7 | 8 | namespace schubfach { 9 | 10 | // char* output_end = Ftoa(buffer, value); 11 | // 12 | // Converts the given single-precision number into decimal form and stores the result in the given 13 | // buffer. 14 | // 15 | // The buffer must be large enough, i.e. >= FtoaMinBufferLength. 16 | // The output format is similar to printf("%g"). 17 | // The output is _not_ null-terminted. 18 | // 19 | // The output is optimal, i.e. the output string 20 | // 1. rounds back to the input number when read in (using round-to-nearest-even) 21 | // 2. is as short as possible, 22 | // 3. is as close to the input number as possible. 23 | // 24 | // Note: 25 | // This function may temporarily write up to FtoaMinBufferLength characters into the buffer. 26 | 27 | constexpr int FtoaMinBufferLength = 32; 28 | 29 | char* Ftoa(char* buffer, float value); 30 | 31 | } // namespace schubfach 32 | -------------------------------------------------------------------------------- /subproject/3rdparty/schubfach/schubfach_64.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Alexander Bolz 2 | // 3 | // Distributed under the Boost Software License, Version 1.0. 4 | // (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) 5 | 6 | #pragma once 7 | 8 | namespace schubfach { 9 | 10 | // char* output_end = Dtoa(buffer, value); 11 | // 12 | // Converts the given double-precision number into decimal form and stores the result in the given 13 | // buffer. 14 | // 15 | // The buffer must be large enough, i.e. >= DtoaMinBufferLength. 16 | // The output format is similar to printf("%g"). 17 | // The output is _not_ null-terminted. 18 | // 19 | // The output is optimal, i.e. the output string 20 | // 1. rounds back to the input number when read in (using round-to-nearest-even) 21 | // 2. is as short as possible, 22 | // 3. is as close to the input number as possible. 23 | // 24 | // Note: 25 | // This function may temporarily write up to DtoaMinBufferLength characters into the buffer. 26 | 27 | constexpr int DtoaMinBufferLength = 64; 28 | 29 | char* Dtoa(char* buffer, double value); 30 | 31 | } // namespace schubfach 32 | -------------------------------------------------------------------------------- /subproject/3rdparty/shaded_plots/example_shaded_plots.m: -------------------------------------------------------------------------------- 1 | %% Shaded line plot 2 | % Example showing the difference between the standard plot routine and the 3 | % shaded routine 4 | 5 | x = -2*pi:pi/100:2*pi; 6 | fx = sin(x); 7 | 8 | figure('Color','w'); 9 | subplot(1,2,1); 10 | hold on 11 | plot(x,fx); 12 | plot(2*x+pi/2,0.5*fx+0.1*x); 13 | hold off 14 | title('plot'); 15 | 16 | subplot(1,2,2); 17 | hold on 18 | plot_shaded(x,fx); 19 | plot_shaded(2*x+pi/2,0.5*fx+0.1*x); 20 | hold off 21 | title('plot\_shaded'); 22 | 23 | %% Histogram plot 24 | % Plots two histograms for two different distributions 25 | 26 | X1 = 3 + 2.0*randn([100000,1]); 27 | X2 = 12 + 4.0*randn([100000,1]); 28 | 29 | figure('Color','w'); 30 | hold on 31 | plot_histogram_shaded(X1,'Alpha',0.3); 32 | plot_histogram_shaded(X2); 33 | hold off 34 | title('plot\_histogram\_shaded'); 35 | 36 | 37 | %% Distribution plots 38 | % Show different plot routines to visualize measurement errors/noise 39 | 40 | X = 1:0.25:10; 41 | Y = sin(X)+0.25*X; 42 | Y_error = randn(1000,numel(Y)); 43 | Y_noisy = Y+Y_error.*repmat(0.1*X,[size(Y_error,1) 1]); 44 | 45 | 46 | figure('Color','w'); 47 | subplot(3,1,1); 48 | plot(X,Y,'LineWidth',1.5); 49 | title('plot (True value y=f(x))'); 50 | ylim([-1 5]); 51 | 52 | subplot(3,1,2); 53 | hold on 54 | plot(X,Y,'LineWidth',1.5); 55 | plot_distribution(X,Y_noisy); 56 | hold off 57 | title('plot\_distribution'); 58 | ylim([-1 5]); 59 | 60 | subplot(3,1,3); 61 | hold on 62 | plot(X,Y,'LineWidth',1.5); 63 | plot_distribution_prctile(X,Y_noisy,'Prctile',[25 50 75 90]); 64 | hold off 65 | title('plot\_distribution\_prctile'); 66 | ylim([-1 5]); 67 | 68 | 69 | -------------------------------------------------------------------------------- /subproject/3rdparty/shaded_plots/license.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018, John A. Onofrey 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution 13 | * Neither the name of Yale University nor the names of its 14 | contributors may be used to endorse or promote products derived from this 15 | software without specific prior written permission. 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /subproject/3rdparty/shaded_plots/plot_distribution.m: -------------------------------------------------------------------------------- 1 | function plot_distribution(varargin) 2 | % PLOT_DISTRIBUTION(X,Y) - Plots the mean with standard deviation errors as a shaded region in the open 3 | % figure window. 4 | % Inputs: 5 | % X: vector of n domain values (the x-axis). 6 | % Y: mxn matrix of distribution of range values (the y-axis), where m 7 | % corresponds to the number of data samples for each value n. 8 | % 9 | % PLOT_DISTRIBUTION(X,Y,...) 10 | % Parameter options include: 11 | % 'Alpha': the alpha value of the shaded region, default 0.15. 12 | % 'Color': the shaded region color. 13 | % 'LineWidth': the contour line width, default = 2.0. 14 | 15 | 16 | [X,Y,color_value,alpha_value,line_width] = parseinputs(varargin{:}); 17 | 18 | 19 | MU = mean(Y); 20 | SIGMA = std(Y); 21 | 22 | hold on 23 | % Create the polygons for the shaded region 24 | 25 | Ptop = MU+SIGMA; 26 | Pbot = MU-SIGMA; 27 | 28 | for i=1:numel(X)-1 29 | Px = [X(i) X(i+1) X(i+1) X(i)]; 30 | Py = [Ptop(i) Ptop(i+1) Pbot(i+1) Pbot(i)]; 31 | fill(Px,Py,color_value,'FaceAlpha',alpha_value,'EdgeColor','none'); 32 | end 33 | 34 | plot(X,MU,'LineWidth',line_width,'Color',color_value); 35 | hold off 36 | 37 | end 38 | 39 | 40 | 41 | 42 | function [X,Y,color_value,alpha_value,line_width] = parseinputs(varargin) 43 | 44 | % Check the number of input args 45 | minargs = 2; 46 | numopts = 3; 47 | maxargs = minargs + 2*numopts; 48 | narginchk(minargs,maxargs); 49 | 50 | ax = gca; 51 | 52 | % Set the defaults 53 | alpha_value = 0.15; 54 | color_value = ax.ColorOrder(ax.ColorOrderIndex,:); 55 | line_width = 2.0; 56 | 57 | 58 | % Get the inputs and check them 59 | X = varargin{1}; 60 | validateattributes(X,{'numeric'},{'vector','nonnan','finite'},mfilename,'X',2); 61 | Y = varargin{2}; 62 | validateattributes(Y,{'numeric'},{'2d','nonnan','finite','ncols',numel(X)},mfilename,'Y',2); 63 | 64 | 65 | if nargin > minargs 66 | for i=(minargs+1):2:nargin 67 | PNAME = varargin{i}; 68 | PVALUE = varargin{i+1}; 69 | 70 | PNAME = validatestring(PNAME,{'Alpha','Color','LineWidth','Prctile'},... 71 | mfilename,'ParameterName',i); 72 | 73 | switch PNAME 74 | case 'Alpha' 75 | validateattributes(PVALUE,{'numeric'},{'scalar','nonnan','finite','nonnegative','<=',1.0},mfilename,PNAME,i+1); 76 | alpha_value = PVALUE; 77 | case 'Color' 78 | validateattributes(PVALUE,{'numeric'},{'real','nonnegative','nonempty','vector','numel',3,'<=',1.0},mfilename,PNAME,i+1); 79 | color_value = PVALUE; 80 | case 'LineWidth' 81 | validateattributes(PVALUE,{'numeric'},{'scalar','finite','nonnegative'},... 82 | mfilename,PNAME,i+1); 83 | line_width = PVALUE; 84 | end 85 | end 86 | end 87 | 88 | end 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /subproject/3rdparty/shaded_plots/plot_distribution_prctile.m: -------------------------------------------------------------------------------- 1 | function plot_distribution_prctile(varargin) 2 | % PLOT_DISTRIBUTION_PRCTILE(X,Y) - Plots the median with percentile errors as a shaded region in the open 3 | % figure window. 4 | % Inputs: 5 | % X: vector of n domain values (the x-axis). 6 | % Y: mxn matrix of distribution of range values (the y-axis), where m 7 | % corresponds to the number of data samples for each value n. 8 | % 9 | % PLOT_DISTRIBUTION_PRCTILE(X,Y,...) 10 | % Parameter options include: 11 | % 'Alpha': the alpha value of the shaded region, default 0.15. 12 | % 'Color': the shaded region color. 13 | % 'LineWidth': the contour line width, default = 2.0. 14 | % 'Prctile': the percentile values to plot, default [50] (the middle 15 | % 50 percentile, or inter-quartile range). 16 | % 17 | 18 | 19 | [X,Y,prctile_value,color_value,alpha_value,line_width] = parseinputs(varargin{:}); 20 | 21 | p_value = 0.5*(100-sort(prctile_value)); 22 | 23 | hold on 24 | % Create the polygons for the shaded region 25 | %%%% This part is modified by Junekey Jeon %%%% 26 | % for j=1:numel(p_value) 27 | % Ptop = prctile(Y,100-p_value(j)); 28 | % Pbot = prctile(Y,p_value(j)); 29 | % for i=1:numel(X)-1 30 | % Px = [X(i) X(i+1) X(i+1) X(i)]; 31 | % Py = [Ptop(i) Ptop(i+1) Pbot(i+1) Pbot(i)]; 32 | % fill(Px,Py,color_value,'FaceAlpha',alpha_value,'EdgeColor','none'); 33 | % end 34 | % end 35 | % plot(X,median(Y),'LineWidth',line_width,'Color',color_value); 36 | Ptop = prctile(Y,100-p_value(1)); 37 | Pbot = prctile(Y,p_value(1)); 38 | block_alpha_value = 1- (1-alpha_value)^numel(p_value); 39 | for i=1:numel(X)-1 40 | Px = [X(i) X(i+1) X(i+1) X(i)]; 41 | Py = [Ptop(i) Ptop(i+1) Pbot(i+1) Pbot(i)]; 42 | fill(Px,Py,color_value,'FaceAlpha',block_alpha_value,'EdgeColor','none'); 43 | end 44 | for j=2:numel(p_value) 45 | Ptop1 = prctile(Y,100-p_value(j)); 46 | Ptop2 = prctile(Y,100-p_value(j-1)); 47 | Pbot1 = prctile(Y,p_value(j)); 48 | Pbot2 = prctile(Y,p_value(j-1)); 49 | block_alpha_value = 1- (1-alpha_value)^(numel(p_value) - j + 1); 50 | for i=1:numel(X)-1 51 | Px = [X(i) X(i+1) X(i+1) X(i)]; 52 | Py = [Ptop1(i) Ptop1(i+1) Ptop2(i+1) Ptop2(i)]; 53 | fill(Px,Py,color_value,'FaceAlpha',block_alpha_value,'EdgeColor','none'); 54 | Px = [X(i) X(i+1) X(i+1) X(i)]; 55 | Py = [Pbot1(i) Pbot1(i+1) Pbot2(i+1) Pbot2(i)]; 56 | fill(Px,Py,color_value,'FaceAlpha',block_alpha_value,'EdgeColor','none'); 57 | end 58 | end 59 | plot(X,median(Y),'--','LineWidth',line_width,'Color',color_value); 60 | hold off 61 | 62 | 63 | end 64 | 65 | 66 | 67 | 68 | 69 | function [X,Y,prctile_value,color_value,alpha_value,line_width] = parseinputs(varargin) 70 | 71 | % Check the number of input args 72 | minargs = 2; 73 | numopts = 4; 74 | maxargs = minargs + 2*numopts; 75 | narginchk(minargs,maxargs); 76 | 77 | ax = gca; 78 | 79 | % Set the defaults 80 | prctile_value = 50; 81 | alpha_value = 0.15; 82 | color_value = ax.ColorOrder(ax.ColorOrderIndex,:); 83 | line_width = 2.0; 84 | 85 | 86 | % Get the inputs and check them 87 | X = varargin{1}; 88 | validateattributes(X,{'numeric'},{'vector','nonnan','finite'},mfilename,'X',2); 89 | Y = varargin{2}; 90 | validateattributes(Y,{'numeric'},{'2d','nonnan','finite','ncols',numel(X)},mfilename,'Y',2); 91 | 92 | 93 | if nargin > minargs 94 | for i=3:2:nargin 95 | PNAME = varargin{i}; 96 | PVALUE = varargin{i+1}; 97 | 98 | PNAME = validatestring(PNAME,{'Alpha','Color','LineWidth','Prctile'},... 99 | mfilename,'ParameterName',i); 100 | 101 | switch PNAME 102 | case 'Alpha' 103 | validateattributes(PVALUE,{'numeric'},{'scalar','nonnan','finite','nonnegative','<=',1.0},mfilename,PNAME,i+1); 104 | alpha_value = PVALUE; 105 | case 'Color' 106 | validateattributes(PVALUE,{'numeric'},{'real','nonnegative','nonempty','vector','numel',3,'<=',1.0},mfilename,PNAME,i+1); 107 | color_value = PVALUE; 108 | case 'LineWidth' 109 | validateattributes(PVALUE,{'numeric'},{'scalar','finite','nonnegative'},... 110 | mfilename,PNAME,i+1); 111 | line_width = PVALUE; 112 | case 'Prctile' 113 | validateattributes(PVALUE,{'numeric'},{'vector','finite','nonnan','nonnegative','<=',100},... 114 | mfilename,PNAME,i+1); 115 | prctile_value = PVALUE; 116 | end 117 | end 118 | end 119 | 120 | end 121 | 122 | -------------------------------------------------------------------------------- /subproject/3rdparty/shaded_plots/plot_histogram_shaded.m: -------------------------------------------------------------------------------- 1 | function [bin_centers,hist_values,bin_edges] = plot_histogram_shaded(varargin) 2 | % [C,V,E] = PLOT_HISTOGRAM_SHADED(X) - Plots the histogram as a shaded line plot instead of bar plot. 3 | % Outputs: 4 | % C: the histogram bin centers. 5 | % V: the histogram values at each bin. 6 | % E: the histogram bin edges values. 7 | % 8 | % [C,V,E] = PLOT_HISTOGRAM_SHADED(X,_) 9 | % Optional values: 10 | % 'alpha': alpha value of the shaded region, default 0.2. 11 | % 'bins': number of bins, default []. 12 | % 'color': color of the plot, default [0 0 1]. 13 | % 'edges': the bins edges to use, default []. 14 | % 'normalization': the normalization type to use (see histogram doc), 15 | % default 'probability' 16 | % 17 | 18 | [X,n_bins,bin_edges,color_value,alpha_value,norm_type] = parseinputs(varargin{:}); 19 | 20 | 21 | 22 | if isempty(n_bins) && isempty(bin_edges) 23 | [hist_values,bin_edges] = histcounts(X,'Normalization',norm_type); 24 | else 25 | bin_param = n_bins; 26 | if ~isempty(bin_edges) 27 | bin_param = bin_edges; 28 | end 29 | [hist_values,bin_edges] = histcounts(X,bin_param,'Normalization',norm_type); 30 | end 31 | 32 | 33 | x_cat = cat(1,bin_edges,circshift(bin_edges,-1)); 34 | x_mean = mean(x_cat,1); 35 | bin_centers = x_mean(1:end-1); 36 | fprintf(1,'Plotting histogram with %d bins\n',numel(bin_centers)); 37 | 38 | % Use a shaded plot 39 | plot_shaded(bin_centers,hist_values,'Alpha',alpha_value,'Color',color_value,'LineWidth',1.5); 40 | 41 | 42 | end 43 | 44 | 45 | function [X,n_bins,bin_edges,color_value,alpha_value,norm_type] = parseinputs(varargin) 46 | 47 | % Get the number of input args 48 | minargs = 1; 49 | maxargs = minargs+5*2; 50 | narginchk(minargs,maxargs); 51 | 52 | ax = gca; 53 | 54 | % Set the defaults 55 | n_bins = []; 56 | bin_edges = []; 57 | color_value = ax.ColorOrder(ax.ColorOrderIndex,:); 58 | alpha_value = 0.2; 59 | norm_type = 'probability'; 60 | 61 | % Get the inputs and check them 62 | X = varargin{1}; 63 | validateattributes(X,{'numeric'},{'nonnan','finite'},mfilename,'X',1); 64 | 65 | if nargin > 1 66 | for i=2:2:nargin 67 | PNAME = varargin{i}; 68 | PVALUE = varargin{i+1}; 69 | 70 | PNAME = validatestring(PNAME,... 71 | {'alpha','bins','color','edges','normalization'},... 72 | mfilename,'ParameterName',i); 73 | 74 | switch PNAME 75 | case 'alpha' 76 | validateattributes(PVALUE,{'numeric'},{'real','nonnegative','finite','scalar'},... 77 | mfilename,PNAME,i+1); 78 | alpha_value = PVALUE; 79 | case 'bins' 80 | if ~isempty(PVALUE) 81 | validateattributes(PVALUE,{'numeric'},{'integer','positive','finite','scalar'},... 82 | mfilename,PNAME,i+1); 83 | n_bins = PVALUE; 84 | end 85 | case 'color' 86 | validateattributes(PVALUE,{'numeric'},{'real','nonnegative','nonempty','vector','numel',3,'<=',1.0},mfilename,PNAME,i+1); 87 | color_value = PVALUE; 88 | case 'edges' 89 | validateattributes(PVALUE,{'numeric'},{'real','nonempty','vector','nonnan'},mfilename,PNAME,i+1); 90 | bin_edges = PVALUE; 91 | case 'normalization' 92 | validateattributes(PVALUE,{'char'},{'nonempty'},mfilename,PNAME,i+1); 93 | norm_type = PVALUE; 94 | end 95 | end 96 | end 97 | 98 | end 99 | 100 | -------------------------------------------------------------------------------- /subproject/3rdparty/shaded_plots/plot_shaded.m: -------------------------------------------------------------------------------- 1 | function plot_shaded(varargin) 2 | % PLOT_SHADED(X,Y) - Plots the line with pretty shaded region under the line in the open 3 | % figure window. 4 | % Inputs: 5 | % X: vector of domain values. 6 | % Y: vector or range values. 7 | % 8 | % PLOT_SHADED(X,Y,...) 9 | % Parameter options include: 10 | % 'Alpha': the alpha value of the shaded region, default 0.15. 11 | % 'Color': the shaded region color. 12 | % 'LineWidth': the contour line width, default = 2.0. 13 | 14 | 15 | [X,Y,color_value,alpha_value,line_width] = parseinputs(varargin{:}); 16 | 17 | 18 | y_min = min(Y); 19 | y_max = max(Y); 20 | V_alpha = alpha_value*((Y-y_min)/y_max); 21 | F = [1 2 3 4]; 22 | 23 | hold on 24 | % Create patches for the shaded region 25 | for i=1:numel(X)-1 26 | V = [ X(i) y_min ; X(i) Y(i); X(i+1) Y(i+1); X(i+1) y_min ]; 27 | A = [0,V_alpha(i),V_alpha(i+1),0]'; 28 | patch('Faces',F,'Vertices',V,'FaceColor',color_value,... 29 | 'EdgeColor','none',... 30 | 'FaceVertexAlphaData',A,'FaceAlpha','interp','AlphaDataMapping','none'); 31 | end 32 | 33 | plot(X,Y,'LineWidth',line_width,'Color',color_value); 34 | hold off 35 | 36 | end 37 | 38 | 39 | 40 | 41 | function [X,Y,color_value,alpha_value,line_width] = parseinputs(varargin) 42 | 43 | % Check the number of input args 44 | minargs = 2; 45 | numopts = 3; 46 | maxargs = minargs + 2*numopts; 47 | narginchk(minargs,maxargs); 48 | 49 | ax = gca; 50 | 51 | % Set the defaults 52 | alpha_value = 0.15; 53 | color_value = ax.ColorOrder(ax.ColorOrderIndex,:); 54 | line_width = 2.0; 55 | 56 | 57 | % Get the inputs and check them 58 | X = varargin{1}; 59 | validateattributes(X,{'numeric'},{'vector','nonnan','finite'},mfilename,'X',2); 60 | Y = varargin{2}; 61 | validateattributes(Y,{'numeric'},{'vector','nonnan','finite','numel',numel(X)},mfilename,'Y',2); 62 | % Ensure that X and Y are column vectors 63 | X = X(:); 64 | Y = Y(:); 65 | 66 | if nargin > minargs 67 | for i=(minargs+1):2:nargin 68 | PNAME = varargin{i}; 69 | PVALUE = varargin{i+1}; 70 | 71 | PNAME = validatestring(PNAME,{'Alpha','Color','LineWidth','Prctile'},... 72 | mfilename,'ParameterName',i); 73 | 74 | switch PNAME 75 | case 'Alpha' 76 | validateattributes(PVALUE,{'numeric'},{'scalar','nonnan','finite','nonnegative','<=',1.0},mfilename,PNAME,i+1); 77 | alpha_value = PVALUE; 78 | case 'Color' 79 | validateattributes(PVALUE,{'numeric'},{'real','nonnegative','nonempty','vector','numel',3,'<=',1.0},mfilename,PNAME,i+1); 80 | color_value = PVALUE; 81 | case 'LineWidth' 82 | validateattributes(PVALUE,{'numeric'},{'scalar','finite','nonnegative'},... 83 | mfilename,PNAME,i+1); 84 | line_width = PVALUE; 85 | end 86 | end 87 | end 88 | 89 | end 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /subproject/benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(benchmark LANGUAGES CXX) 4 | 5 | include(FetchContent) 6 | if (NOT TARGET dragonbox) 7 | FetchContent_Declare(dragonbox SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../..") 8 | FetchContent_MakeAvailable(dragonbox) 9 | endif() 10 | if (NOT TARGET common) 11 | FetchContent_Declare(common SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../common") 12 | FetchContent_MakeAvailable(common) 13 | endif() 14 | if (NOT TARGET ryu) 15 | FetchContent_Declare(ryu SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../3rdparty/ryu") 16 | FetchContent_MakeAvailable(ryu) 17 | endif() 18 | if (NOT TARGET schubfach) 19 | FetchContent_Declare(schubfach SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../3rdparty/schubfach") 20 | FetchContent_MakeAvailable(schubfach) 21 | endif() 22 | if (NOT TARGET grisu_exact) 23 | FetchContent_Declare(grisu_exact SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../3rdparty/grisu_exact") 24 | FetchContent_MakeAvailable(grisu_exact) 25 | endif() 26 | 27 | set(benchmark_headers include/benchmark.h) 28 | 29 | set(benchmark_sources 30 | source/dragonbox.cpp 31 | source/grisu_exact.cpp 32 | source/benchmark.cpp 33 | source/ryu.cpp 34 | source/schubfach.cpp) 35 | 36 | add_executable(benchmark ${benchmark_headers} ${benchmark_sources}) 37 | 38 | target_compile_features(benchmark PRIVATE cxx_std_17) 39 | 40 | target_include_directories(benchmark 41 | PRIVATE 42 | $) 43 | 44 | target_link_libraries(benchmark 45 | PRIVATE 46 | ryu::ryu 47 | dragonbox::common 48 | dragonbox::dragonbox_to_chars 49 | grisu_exact::grisu_exact 50 | schubfach::schubfach) 51 | 52 | # ---- MSVC Specifics ---- 53 | if (MSVC) 54 | # See https://gitlab.kitware.com/cmake/cmake/-/issues/16478 55 | set_target_properties(benchmark PROPERTIES 56 | VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") 57 | endif() -------------------------------------------------------------------------------- /subproject/benchmark/include/benchmark.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #ifndef JKJ_BENCHMARK 19 | #define JKJ_BENCHMARK 20 | 21 | #include 22 | 23 | struct register_function_for_benchmark { 24 | register_function_for_benchmark() = default; 25 | 26 | register_function_for_benchmark( 27 | std::string_view name, 28 | void(*func)(float, char*)); 29 | 30 | register_function_for_benchmark( 31 | std::string_view name, 32 | void(*func)(double, char*)); 33 | 34 | register_function_for_benchmark( 35 | std::string_view name, 36 | void(*func_float)(float, char*), 37 | void(*func_double)(double, char*)); 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /subproject/benchmark/matlab/plot_benchmarks.m: -------------------------------------------------------------------------------- 1 | % Copyright 2020-2021 Junekey Jeon 2 | % 3 | % The contents of this file may be used under the terms of 4 | % the Apache License v2.0 with LLVM Exceptions. 5 | % 6 | % (See accompanying file LICENSE-Apache or copy at 7 | % https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | % 9 | % Alternatively, the contents of this file may be used under the terms of 10 | % the Boost Software License, Version 1.0. 11 | % (See accompanying file LICENSE-Boost or copy at 12 | % https://www.boost.org/LICENSE_1_0.txt) 13 | % 14 | % Unless required by applicable law or agreed to in writing, this software 15 | % is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | % KIND, either express or implied. 17 | 18 | addpath('../../3rdparty/shaded_plots'); 19 | plot_uniform_benchmark('../results/uniform_benchmark_binary32.csv', 32); 20 | plot_uniform_benchmark('../results/uniform_benchmark_binary64.csv', 64); 21 | avg32 = plot_digit_benchmark('../results/digits_benchmark_binary32.csv'); 22 | avg64 = plot_digit_benchmark('../results/digits_benchmark_binary64.csv'); -------------------------------------------------------------------------------- /subproject/benchmark/matlab/plot_digit_benchmark.m: -------------------------------------------------------------------------------- 1 | % Copyright 2020-2021 Junekey Jeon 2 | % 3 | % The contents of this file may be used under the terms of 4 | % the Apache License v2.0 with LLVM Exceptions. 5 | % 6 | % (See accompanying file LICENSE-Apache or copy at 7 | % https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | % 9 | % Alternatively, the contents of this file may be used under the terms of 10 | % the Boost Software License, Version 1.0. 11 | % (See accompanying file LICENSE-Boost or copy at 12 | % https://www.boost.org/LICENSE_1_0.txt) 13 | % 14 | % Unless required by applicable law or agreed to in writing, this software 15 | % is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | % KIND, either express or implied. 17 | 18 | function [avg] = plot_digit_benchmark( filename ) 19 | 20 | output_filename = replace(filename, '.csv', '.pdf'); 21 | 22 | samples_per_digits = csvread(filename,0,1,[0 1 0 1]); 23 | table = readtable(filename); 24 | max_digits = table{size(table,1),2}; 25 | number_of_algorithms = size(table,1) / samples_per_digits / max_digits; 26 | names = string(zeros(number_of_algorithms, 1)); 27 | for i=1:number_of_algorithms 28 | names(i) = string(table{samples_per_digits * i * max_digits,1}); 29 | end 30 | 31 | % algorithm index, digits, measured_time 32 | data = zeros(number_of_algorithms, max_digits, samples_per_digits); 33 | for algorithm_idx=1:number_of_algorithms 34 | for digits_idx=1:max_digits 35 | offset = ((algorithm_idx-1) * max_digits + (digits_idx-1)) * samples_per_digits; 36 | data(algorithm_idx, digits_idx,:) = table{offset+1:offset+samples_per_digits, 4}; 37 | end 38 | end 39 | 40 | color_array = {[.8 .06 .1],[.1 .7 .06],[.06 .1 .8],[.6 .2 .8],[.8 .9 0],[.5 .6 .7],[.8 .2 .6]}; 41 | avg = zeros(number_of_algorithms, max_digits); 42 | for algorithm_idx=1:number_of_algorithms 43 | for digits_idx=1:max_digits 44 | avg(algorithm_idx,digits_idx) = mean(data(algorithm_idx,digits_idx,:)); 45 | end 46 | end 47 | 48 | % we will call this function stdshade 49 | addpath(genpath('shaded_plots')); 50 | 51 | % plot 52 | fig = figure('Color','w'); 53 | fig_handles = zeros(number_of_algorithms,1); 54 | hold on 55 | for algorithm_idx=1:number_of_algorithms 56 | fig_handles(algorithm_idx) = plot(1:max_digits,squeeze(avg(algorithm_idx,:)), ... 57 | 'Color', color_array{algorithm_idx}, 'LineWidth', 1.2); 58 | end 59 | for algorithm_idx=1:number_of_algorithms 60 | plot_distribution_prctile(1:max_digits,squeeze(data(algorithm_idx,:,:))', ... 61 | 'Color', color_array{algorithm_idx}, 'Alpha', 0.05, ... 62 | 'Prctile', [30 50 70], 'LineWidth', 0.2); 63 | end 64 | legend(fig_handles,names); 65 | axis([1 max_digits -Inf Inf]); 66 | xlabel('Number of digits'); 67 | ylabel('Time (ns)'); 68 | h = gca; 69 | h.XTick = 1:max_digits; 70 | h.XGrid = 'on'; 71 | h.YGrid = 'on'; 72 | set(gca,'TickLabelInterpreter', 'latex'); 73 | set(gcf, 'Position', [100 100 1200 500]); 74 | orient(fig,'landscape'); 75 | exportgraphics(fig, output_filename, 'BackgroundColor', 'none'); 76 | hold off 77 | 78 | end 79 | -------------------------------------------------------------------------------- /subproject/benchmark/matlab/plot_uniform_benchmark.m: -------------------------------------------------------------------------------- 1 | % Copyright 2020-2021 Junekey Jeon 2 | % 3 | % The contents of this file may be used under the terms of 4 | % the Apache License v2.0 with LLVM Exceptions. 5 | % 6 | % (See accompanying file LICENSE-Apache or copy at 7 | % https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | % 9 | % Alternatively, the contents of this file may be used under the terms of 10 | % the Boost Software License, Version 1.0. 11 | % (See accompanying file LICENSE-Boost or copy at 12 | % https://www.boost.org/LICENSE_1_0.txt) 13 | % 14 | % Unless required by applicable law or agreed to in writing, this software 15 | % is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | % KIND, either express or implied. 17 | 18 | function [] = plot_uniform_benchmark( filename, bits ) 19 | 20 | output_filename = replace(filename, '.csv', '.pdf'); 21 | 22 | samples = csvread(filename,0,1,[0 1 0 1]); 23 | table = readtable(filename); 24 | number_of_algorithms = size(table,1) / samples; 25 | names = string(zeros(number_of_algorithms, 1)); 26 | for i=1:number_of_algorithms 27 | names(i) = string(table{samples * i,1}); 28 | end 29 | 30 | % algorithm index, measured_time 31 | measured_times = zeros(number_of_algorithms, samples); 32 | for algorithm_idx=1:number_of_algorithms 33 | offset = (algorithm_idx-1) * samples; 34 | measured_times(algorithm_idx,:) = table{offset+1:offset+samples, 3}; 35 | end 36 | bit_representations = sscanf(strjoin(string(table{1:samples, 2})', ' '), '%lu'); 37 | 38 | color_array = {[.8 .06 .1],[.1 .7 .06],[.06 .1 .8],[.6 .2 .8],[.8 .9 0],[.5 .6 .7],[.8 .2 .6]}; 39 | 40 | % compute statistics 41 | av = mean(measured_times'); 42 | st = std(measured_times'); 43 | me = median(measured_times'); 44 | 45 | decorated_names = names; 46 | for algorithm_idx=1:number_of_algorithms 47 | decorated_names(algorithm_idx) = sprintf('%s (avg: %.2f, std: %.2f, med: %.2f)', ... 48 | names(algorithm_idx), av(algorithm_idx), st(algorithm_idx), me(algorithm_idx)); 49 | end 50 | 51 | % sample data for plot 52 | maximum_plot_size = 10000; 53 | if samples > maximum_plot_size 54 | plot_size = maximum_plot_size; 55 | unit = floor(samples / maximum_plot_size); 56 | sampled_br = bit_representations(1:unit:unit*plot_size); 57 | sampled_mt = measured_times(:,1:unit:unit*plot_size); 58 | else 59 | plot_size = samples; 60 | sampled_br = bit_representations; 61 | sampled_mt = measured_times; 62 | end 63 | 64 | % plot 65 | fig = figure('Color','w'); 66 | fig_handles = zeros(number_of_algorithms,1); 67 | hold on 68 | sz = ones(plot_size, 1) * 0.4; 69 | for algorithm_idx=1:number_of_algorithms 70 | fig_handles(algorithm_idx) = scatter(sampled_br, ... 71 | sampled_mt(algorithm_idx,:), sz, '+', ... 72 | 'MarkerEdgeColor', color_array{algorithm_idx}, 'LineWidth', 0.1); 73 | end 74 | legend(fig_handles,decorated_names); 75 | xlabel('Bit representation'); 76 | ylabel('Time (ns)'); 77 | range = prctile(measured_times(:), 99.9); 78 | axis([0 uint64(2)^bits 0 range(1)]); 79 | if bits==32 80 | xticks([0 uint64(2)^30 uint64(2)^31 3*uint64(2)^30 uint64(2)^32]); 81 | xticklabels({'$0$','$2^{30}$','$2^{31}$','$3\times2^{30}$','$2^{32}$'}); 82 | elseif bits==64 83 | xticks([0 uint64(2)^62 uint64(2)^63 3*uint64(2)^62 uint64(2)^64]); 84 | xticklabels({'$0$','$2^{62}$','$2^{63}$','$3\times2^{62}$','$2^{64}$'}); 85 | end 86 | h = gca; 87 | h.XGrid = 'off'; 88 | h.YGrid = 'on'; 89 | set(gca,'TickLabelInterpreter', 'latex'); 90 | set(gcf, 'Position', [100 100 1200 500]); 91 | orient(fig,'landscape'); 92 | exportgraphics(fig, output_filename, 'BackgroundColor', 'none'); 93 | hold off 94 | 95 | end 96 | -------------------------------------------------------------------------------- /subproject/benchmark/results/digits_benchmark_binary32_clang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/digits_benchmark_binary32_clang.png -------------------------------------------------------------------------------- /subproject/benchmark/results/digits_benchmark_binary32_msvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/digits_benchmark_binary32_msvc.png -------------------------------------------------------------------------------- /subproject/benchmark/results/digits_benchmark_binary64_clang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/digits_benchmark_binary64_clang.png -------------------------------------------------------------------------------- /subproject/benchmark/results/digits_benchmark_binary64_msvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/digits_benchmark_binary64_msvc.png -------------------------------------------------------------------------------- /subproject/benchmark/results/uniform_benchmark_binary32_clang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/uniform_benchmark_binary32_clang.png -------------------------------------------------------------------------------- /subproject/benchmark/results/uniform_benchmark_binary32_msvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/uniform_benchmark_binary32_msvc.png -------------------------------------------------------------------------------- /subproject/benchmark/results/uniform_benchmark_binary64_clang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/uniform_benchmark_binary64_clang.png -------------------------------------------------------------------------------- /subproject/benchmark/results/uniform_benchmark_binary64_msvc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jk-jeon/dragonbox/6c7c925b571d54486b9ffae8d9d18a822801cbda/subproject/benchmark/results/uniform_benchmark_binary64_msvc.png -------------------------------------------------------------------------------- /subproject/benchmark/source/benchmark.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "benchmark.h" 19 | #include "random_float.h" 20 | #include "dragonbox/dragonbox_to_chars.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | template 33 | class benchmark_holder { 34 | public: 35 | static constexpr auto max_digits = std::size_t(std::numeric_limits::max_digits10); 36 | 37 | static benchmark_holder& get_instance() { 38 | static benchmark_holder inst; 39 | return inst; 40 | } 41 | 42 | // Generate random samples 43 | void prepare_samples(std::size_t number_of_general_samples, 44 | std::size_t number_of_digits_samples_per_digits) { 45 | samples_[0].resize(number_of_general_samples); 46 | for (auto& sample : samples_[0]) 47 | sample = uniformly_randomly_generate_general_float(rg_); 48 | 49 | for (unsigned int digits = 1; digits <= max_digits; ++digits) { 50 | samples_[digits].resize(number_of_digits_samples_per_digits); 51 | for (auto& sample : samples_[digits]) 52 | sample = randomly_generate_float_with_given_digits(digits, rg_); 53 | } 54 | } 55 | 56 | // { "name" : [(digits, [(sample, measured_time)])] } 57 | // Results for general samples is stored at the position digits=0 58 | using output_type = 59 | std::unordered_map>, max_digits + 1>>; 61 | void run(std::size_t number_of_iterations, std::string_view float_name, output_type& out) { 62 | assert(number_of_iterations >= 1); 63 | char buffer[40]; 64 | 65 | for (auto const& name_func_pair : name_func_pairs_) { 66 | auto [result_array_itr, is_inserted] = out.insert_or_assign( 67 | name_func_pair.first, 68 | std::array>, max_digits + 1>{}); 69 | 70 | for (unsigned int digits = 0; digits <= max_digits; ++digits) { 71 | (*result_array_itr).second[digits].resize(samples_[digits].size()); 72 | auto out_itr = (*result_array_itr).second[digits].begin(); 73 | 74 | if (digits == 0) { 75 | std::cout << "Benchmarking " << name_func_pair.first << " with uniformly random " 76 | << float_name << "'s...\n"; 77 | } 78 | else { 79 | std::cout << "Benchmarking " << name_func_pair.first 80 | << " with (approximately) uniformly random " << float_name << "'s of " 81 | << digits << " digits...\n"; 82 | } 83 | 84 | for (Float sample : samples_[digits]) { 85 | auto from = std::chrono::high_resolution_clock::now(); 86 | for (std::size_t i = 0; i < number_of_iterations; ++i) { 87 | name_func_pair.second(sample, buffer); 88 | } 89 | auto dur = std::chrono::high_resolution_clock::now() - from; 90 | 91 | *out_itr = { 92 | sample, 93 | double(std::chrono::duration_cast(dur).count()) / 94 | double(number_of_iterations)}; 95 | ++out_itr; 96 | } 97 | } 98 | } 99 | } 100 | 101 | output_type run(std::size_t number_of_iterations, std::string_view float_name) { 102 | output_type out; 103 | run(number_of_iterations, float_name, out); 104 | return out; 105 | } 106 | 107 | void register_function(std::string_view name, void (*func)(Float, char*)) { 108 | name_func_pairs_.emplace(name, func); 109 | } 110 | 111 | private: 112 | benchmark_holder() : rg_(generate_correctly_seeded_mt19937_64()) {} 113 | 114 | // Digits samples for [1] ~ [max_digits], general samples for [0] 115 | std::array, max_digits + 1> samples_; 116 | std::mt19937_64 rg_; 117 | std::unordered_map name_func_pairs_; 118 | }; 119 | 120 | register_function_for_benchmark::register_function_for_benchmark(std::string_view name, 121 | void (*func_float)(float, char*)) { 122 | benchmark_holder::get_instance().register_function(name, func_float); 123 | }; 124 | 125 | register_function_for_benchmark::register_function_for_benchmark(std::string_view name, 126 | void (*func_double)(double, char*)) { 127 | benchmark_holder::get_instance().register_function(name, func_double); 128 | }; 129 | 130 | register_function_for_benchmark::register_function_for_benchmark(std::string_view name, 131 | void (*func_float)(float, char*), 132 | void (*func_double)(double, char*)) { 133 | benchmark_holder::get_instance().register_function(name, func_float); 134 | benchmark_holder::get_instance().register_function(name, func_double); 135 | }; 136 | 137 | 138 | #define RUN_MATLAB 139 | #ifdef RUN_MATLAB 140 | #include 141 | 142 | void run_matlab() { 143 | struct launcher { 144 | ~launcher() { std::system("matlab -nosplash -r \"cd('matlab'); plot_benchmarks\""); } 145 | }; 146 | static launcher l; 147 | } 148 | #endif 149 | 150 | template 151 | static void benchmark_test(std::string_view float_name, std::size_t number_of_uniform_samples, 152 | std::size_t number_of_digits_samples_per_digits, 153 | std::size_t number_of_iterations) { 154 | auto& inst = benchmark_holder::get_instance(); 155 | std::cout << "Generating random samples...\n"; 156 | inst.prepare_samples(number_of_uniform_samples, number_of_digits_samples_per_digits); 157 | auto out = inst.run(number_of_iterations, float_name); 158 | 159 | std::cout << "Benchmarking done.\n" 160 | << "Now writing to files...\n"; 161 | 162 | // Write uniform benchmark results 163 | auto filename = std::string("results/uniform_benchmark_"); 164 | filename += float_name; 165 | filename += ".csv"; 166 | std::ofstream out_file{filename}; 167 | out_file << "number_of_samples," << number_of_uniform_samples << std::endl; 168 | ; 169 | out_file << "name,sample,bit_representation,time\n"; 170 | 171 | typename jkj::dragonbox::default_float_bit_carrier_conversion_traits::carrier_uint br; 172 | for (auto& name_result_pair : out) { 173 | for (auto const& data_time_pair : name_result_pair.second[0]) { 174 | std::memcpy(&br, &data_time_pair.first, sizeof(Float)); 175 | out_file << "\"" << name_result_pair.first << "\"," 176 | << "0x" << std::hex << std::setfill('0'); 177 | if constexpr (sizeof(Float) == 4) 178 | out_file << std::setw(8); 179 | else 180 | out_file << std::setw(16); 181 | out_file << br << std::dec << "," << data_time_pair.second << "\n"; 182 | } 183 | } 184 | out_file.close(); 185 | 186 | // Write digits benchmark results 187 | filename = std::string("results/digits_benchmark_"); 188 | filename += float_name; 189 | filename += ".csv"; 190 | out_file.open(filename); 191 | out_file << "number_of_samples_per_digits," << number_of_digits_samples_per_digits << std::endl; 192 | out_file << "name,digits,sample,time\n"; 193 | 194 | for (auto& name_result_pair : out) { 195 | for (unsigned int digits = 1; digits <= benchmark_holder::max_digits; ++digits) { 196 | for (auto const& data_time_pair : name_result_pair.second[digits]) { 197 | std::memcpy(&br, &data_time_pair.first, sizeof(Float)); 198 | out_file << "\"" << name_result_pair.first << "\"," << digits << "," 199 | << "0x" << std::hex << std::setfill('0'); 200 | if constexpr (sizeof(Float) == 4) 201 | out_file << std::setw(8); 202 | else 203 | out_file << std::setw(16); 204 | out_file << br << std::dec << "," << data_time_pair.second << "\n"; 205 | } 206 | } 207 | } 208 | out_file.close(); 209 | } 210 | 211 | int main() { 212 | constexpr bool benchmark_float = true; 213 | constexpr std::size_t number_of_uniform_benchmark_samples_float = 1000000; 214 | constexpr std::size_t number_of_digits_benchmark_samples_per_digits_float = 100000; 215 | constexpr std::size_t number_of_benchmark_iterations_float = 1000; 216 | 217 | constexpr bool benchmark_double = true; 218 | constexpr std::size_t number_of_uniform_benchmark_samples_double = 1000000; 219 | constexpr std::size_t number_of_digits_benchmark_samples_per_digits_double = 100000; 220 | constexpr std::size_t number_of_benchmark_iterations_double = 1000; 221 | 222 | if constexpr (benchmark_float) { 223 | std::cout << "[Running benchmark for binary32...]\n"; 224 | benchmark_test("binary32", number_of_uniform_benchmark_samples_float, 225 | number_of_digits_benchmark_samples_per_digits_float, 226 | number_of_benchmark_iterations_float); 227 | std::cout << "Done.\n\n\n"; 228 | } 229 | if constexpr (benchmark_double) { 230 | std::cout << "[Running benchmark for binary64...]\n"; 231 | benchmark_test("binary64", number_of_uniform_benchmark_samples_double, 232 | number_of_digits_benchmark_samples_per_digits_double, 233 | number_of_benchmark_iterations_double); 234 | std::cout << "Done.\n\n\n"; 235 | } 236 | 237 | #ifdef RUN_MATLAB 238 | run_matlab(); 239 | #endif 240 | } -------------------------------------------------------------------------------- /subproject/benchmark/source/dragonbox.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "benchmark.h" 19 | #include "dragonbox/dragonbox_to_chars.h" 20 | 21 | namespace { 22 | void dragonbox_float_to_chars(float x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); } 23 | void dragonbox_double_to_chars(double x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); } 24 | 25 | #if 1 26 | auto dummy = []() -> register_function_for_benchmark { 27 | return {"Dragonbox", dragonbox_float_to_chars, dragonbox_double_to_chars}; 28 | }(); 29 | #endif 30 | } 31 | -------------------------------------------------------------------------------- /subproject/benchmark/source/grisu_exact.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "benchmark.h" 19 | #include "fp_to_chars.h" 20 | 21 | namespace { 22 | void grisu_exact_float_to_chars(float x, char* buffer) { 23 | jkj::fp_to_chars(x, buffer, jkj::grisu_exact_rounding_modes::nearest_to_even{}, 24 | jkj::grisu_exact_correct_rounding::tie_to_even{}); 25 | } 26 | void grisu_exact_double_to_chars(double x, char* buffer) { 27 | jkj::fp_to_chars(x, buffer, jkj::grisu_exact_rounding_modes::nearest_to_even{}, 28 | jkj::grisu_exact_correct_rounding::tie_to_even{}); 29 | } 30 | 31 | #if 1 32 | auto dummy = []() -> register_function_for_benchmark { 33 | return {"Grisu-Exact", grisu_exact_float_to_chars, grisu_exact_double_to_chars}; 34 | }(); 35 | #endif 36 | } 37 | -------------------------------------------------------------------------------- /subproject/benchmark/source/ryu.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "benchmark.h" 19 | #include "ryu/ryu.h" 20 | 21 | namespace { 22 | #if 1 23 | auto dummy = []() -> register_function_for_benchmark { 24 | return {"Ryu", f2s_buffered, d2s_buffered}; 25 | }(); 26 | #endif 27 | } 28 | -------------------------------------------------------------------------------- /subproject/benchmark/source/schubfach.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "benchmark.h" 19 | #include "schubfach_32.h" 20 | #include "schubfach_64.h" 21 | 22 | namespace { 23 | void schubfach_32(float x, char* buf) { *schubfach::Ftoa(buf, x) = '\0'; } 24 | void schubfach_64(double x, char* buf) { *schubfach::Dtoa(buf, x) = '\0'; } 25 | 26 | #if 1 27 | auto dummy = []() -> register_function_for_benchmark { 28 | return {"Schubfach", schubfach_32, schubfach_64}; 29 | }(); 30 | #endif 31 | } 32 | -------------------------------------------------------------------------------- /subproject/common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(dragonbox_common LANGUAGES CXX) 4 | 5 | include(FetchContent) 6 | if (NOT TARGET dragonbox) 7 | FetchContent_Declare(dragonbox SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../..") 8 | FetchContent_MakeAvailable(dragonbox) 9 | endif() 10 | 11 | set(dragonbox_common_headers 12 | include/big_uint.h 13 | include/continued_fractions.h 14 | include/rational_continued_fractions.h 15 | include/best_rational_approx.h 16 | include/good_rational_approx.h 17 | include/random_float.h) 18 | 19 | set(dragonbox_common_sources source/big_uint.cpp) 20 | 21 | add_library(dragonbox_common STATIC 22 | ${dragonbox_common_headers} 23 | ${dragonbox_common_sources}) 24 | add_library(dragonbox::common ALIAS dragonbox_common) 25 | 26 | target_include_directories(dragonbox_common 27 | PUBLIC 28 | $) 29 | 30 | target_compile_features(dragonbox_common PUBLIC cxx_std_17) 31 | 32 | target_link_libraries(dragonbox_common PUBLIC dragonbox::dragonbox) -------------------------------------------------------------------------------- /subproject/common/include/best_rational_approx.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #ifndef JKJ_HEADER_BEST_RATIONAL_APPROX 19 | #define JKJ_HEADER_BEST_RATIONAL_APPROX 20 | 21 | #include "continued_fractions.h" 22 | #include 23 | #include 24 | 25 | namespace jkj { 26 | template 27 | struct best_rational_approx_output { 28 | unsigned_rational below; 29 | unsigned_rational above; 30 | }; 31 | 32 | // Find the best rational approximations from below and from above of denominators no more than 33 | // denominator_upper_bound for the given number x. 34 | template 35 | best_rational_approx_output 36 | find_best_rational_approx(PositiveNumber const& x, UInt const& denominator_upper_bound) { 37 | assert(denominator_upper_bound > 0); 38 | 39 | using uint_type = typename ContinuedFractionsImpl::uint_type; 40 | best_rational_approx_output ret_value; 41 | 42 | // Initialize a continued fractions calculator. 43 | ContinuedFractionsImpl cf{x}; 44 | 45 | // First, find the last convergent whose denominator is bounded above by the given upper 46 | // bound. 47 | unsigned_rational previous_convergent; 48 | unsigned_rational current_convergent; 49 | do { 50 | previous_convergent = cf.previous_convergent(); 51 | current_convergent = cf.current_convergent(); 52 | 53 | // Obtain the next convergent. 54 | if (!cf.update()) { 55 | // If there is no more convergents, we already obtained the perfect approximation. 56 | ret_value.below = cf.current_convergent(); 57 | ret_value.above = cf.current_convergent(); 58 | return ret_value; 59 | } 60 | } while (cf.current_denominator() <= denominator_upper_bound); 61 | 62 | // If the current convergent is of even index, 63 | // then the current convergent is the best approximation from below, 64 | // and the best approximation from above is the last semiconvergent. 65 | // If the current convergent is of odd index, then the other way around. 66 | // Note that cf.current_index() is one larger than the index of the current convergent, 67 | // so we need to reverse the parity. 68 | 69 | auto compute_bounds = [&](auto& major, auto& minor) { 70 | // The current convergent is the best approximation from below. 71 | major = current_convergent; 72 | 73 | // The best approximation from above is the last semiconvergent. 74 | using std::div; 75 | auto semiconvergent_coeff = 76 | div(denominator_upper_bound - previous_convergent.denominator, 77 | current_convergent.denominator) 78 | .quot; 79 | 80 | minor.numerator = 81 | previous_convergent.numerator + semiconvergent_coeff * current_convergent.numerator; 82 | minor.denominator = previous_convergent.denominator + 83 | semiconvergent_coeff * current_convergent.denominator; 84 | }; 85 | 86 | if (cf.current_index() % 2 == 1) { 87 | compute_bounds(ret_value.below, ret_value.above); 88 | } 89 | else { 90 | compute_bounds(ret_value.above, ret_value.below); 91 | } 92 | 93 | return ret_value; 94 | } 95 | } 96 | 97 | #endif -------------------------------------------------------------------------------- /subproject/common/include/big_uint.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #ifndef JKJ_HEADER_BIG_UINT 19 | #define JKJ_HEADER_BIG_UINT 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | namespace jkj { 28 | class big_uint { 29 | public: 30 | using element_type = std::uint64_t; 31 | static constexpr std::size_t element_number_of_bits = 64; 32 | 33 | private: 34 | // Least significant element first. 35 | std::vector elements; 36 | 37 | public: 38 | // elements is empty if and only if it represents 0. 39 | big_uint() = default; 40 | big_uint(element_type n) { 41 | if (n != 0) { 42 | elements.push_back(n); 43 | } 44 | } 45 | 46 | // Remove leading zeros. 47 | explicit big_uint(std::initializer_list list) 48 | : big_uint(list.begin(), list.end()) {} 49 | 50 | // Remove leading zeros. 51 | template ::iterator_category> 52 | big_uint(Iter first, Iter last) : elements(first, last) { 53 | auto effective_size = elements.size(); 54 | while (effective_size > 0) { 55 | if (elements[effective_size - 1] != 0) { 56 | break; 57 | } 58 | --effective_size; 59 | } 60 | elements.resize(effective_size); 61 | } 62 | 63 | element_type operator[](std::size_t idx) const { return elements[idx]; } 64 | 65 | bool is_zero() const noexcept { return elements.empty(); } 66 | bool is_even() const noexcept { 67 | if (elements.empty()) { 68 | return true; 69 | } 70 | else { 71 | return elements[0] % 2 == 0; 72 | } 73 | } 74 | 75 | friend std::size_t log2p1(big_uint const& n) noexcept; 76 | 77 | static big_uint power_of_2(std::size_t k); 78 | static big_uint pow(big_uint base, std::size_t k); 79 | 80 | // Repeat multiplying 2 until the number becomes bigger than or equal to the given number. 81 | // Returns the number of multiplications, which is ceil(log2(n/*this)). 82 | // Precondition: *this is not zero and n should be bigger than or equal to the current 83 | // number. Note that this function need not require &n != this. 84 | std::size_t multiply_2_until(big_uint const& n); 85 | 86 | // Repeat multiplying 2 while the number becomes less than or equal to the given number. 87 | // Returns the number of multiplications, which is floor(log2(n/*this)). 88 | // Precondition: *this is not zero and n should be bigger than or equal to the current 89 | // number. Note that this function need not require &n != this. 90 | std::size_t multiply_2_while(big_uint const& n); 91 | 92 | void multiply_2() &; 93 | void multiply_5() &; 94 | 95 | bool operator==(big_uint const& n) const noexcept { return elements == n.elements; } 96 | bool operator!=(big_uint const& n) const noexcept { return elements != n.elements; } 97 | bool operator==(element_type n) const noexcept { 98 | return (elements.size() == 0 && n == 0) || (elements.size() == 1 && elements[0] == n); 99 | } 100 | bool operator!=(element_type n) const noexcept { 101 | return (elements.size() == 0 && n != 0) || (elements.size() == 1 && elements[0] != n) || 102 | (elements.size() > 1); 103 | } 104 | 105 | private: 106 | int comparison_common(big_uint const& n) const noexcept; 107 | 108 | public: 109 | bool operator<(big_uint const& n) const noexcept { return comparison_common(n) < 0; } 110 | bool operator<=(big_uint const& n) const noexcept { return comparison_common(n) <= 0; } 111 | bool operator>(big_uint const& n) const noexcept { return comparison_common(n) > 0; } 112 | bool operator>=(big_uint const& n) const noexcept { return comparison_common(n) >= 0; } 113 | 114 | bool operator<(element_type n) const noexcept { 115 | return (elements.size() == 0 && n != 0) || (elements.size() == 1 && elements[0] < n); 116 | } 117 | bool operator<=(element_type n) const noexcept { 118 | return (elements.size() == 0 && n == 0) || (elements.size() == 1 && elements[0] <= n); 119 | } 120 | bool operator>(element_type n) const noexcept { 121 | return (elements.size() == 1 && elements[0] > n) || (elements.size() > 1); 122 | } 123 | bool operator>=(element_type n) const noexcept { 124 | return (elements.size() == 0 && n == 0) || (elements.size() == 1 && elements[0] >= n) || 125 | (elements.size() > 1); 126 | } 127 | 128 | big_uint& operator+=(big_uint const& n) &; 129 | big_uint& operator+=(element_type n) &; 130 | template 131 | big_uint operator+(T const& n) const { 132 | auto r = *this; 133 | return r += n; 134 | } 135 | friend big_uint operator+(element_type n, big_uint const& m) { return m + n; } 136 | big_uint& operator++() & { 137 | *this += 1; 138 | return *this; 139 | } 140 | big_uint operator++(int) & { 141 | auto temp = *this; 142 | *this += 1; 143 | return temp; 144 | } 145 | 146 | // Precondition: n should be strictly smaller than or equal to the current number 147 | big_uint& operator-=(big_uint const& n) &; 148 | big_uint operator-(big_uint const& n) const { 149 | auto r = *this; 150 | return r -= n; 151 | } 152 | 153 | // Precondition: *this should be nonzero 154 | big_uint& operator--() &; 155 | 156 | big_uint& operator*=(element_type n) &; 157 | big_uint operator*(element_type n) { 158 | auto r = *this; 159 | return r *= n; 160 | } 161 | friend big_uint operator*(big_uint const& x, big_uint const& y); 162 | big_uint& operator*=(big_uint const& y) & { 163 | auto result = *this * y; 164 | *this = result; 165 | return *this; 166 | } 167 | 168 | // Perform long division 169 | // *this becomes the remainder, returns the quotient 170 | // Precondition: n != 0 171 | big_uint long_division(big_uint const& n); 172 | 173 | big_uint operator/(big_uint const& n) const { 174 | auto temp = *this; 175 | return temp.long_division(n); 176 | } 177 | big_uint operator/(element_type n) const { 178 | auto temp = *this; 179 | return temp.long_division(n); 180 | } 181 | big_uint operator%(big_uint const& n) const { 182 | auto temp = *this; 183 | temp.long_division(n); 184 | return temp; 185 | } 186 | big_uint operator%(element_type n) const { 187 | auto temp = *this; 188 | temp.long_division(n); 189 | return temp; 190 | } 191 | 192 | // Convert the number into decimal, and returns the 193 | // array of (at most) 19 digits. 194 | std::vector to_decimal() const; 195 | }; 196 | 197 | std::size_t log2p1(big_uint const& n) noexcept; 198 | big_uint operator*(big_uint const& x, big_uint const& y); 199 | 200 | struct big_uint_div_t { 201 | big_uint quot; 202 | big_uint rem; 203 | }; 204 | inline big_uint_div_t div(big_uint const& x, big_uint const& y) { 205 | big_uint_div_t ret; 206 | ret.rem = x; 207 | ret.quot = ret.rem.long_division(y); 208 | return ret; 209 | } 210 | inline big_uint_div_t div(big_uint&& x, big_uint const& y) { 211 | big_uint_div_t ret; 212 | ret.rem = static_cast(x); 213 | ret.quot = ret.rem.long_division(y); 214 | return ret; 215 | } 216 | } 217 | 218 | #endif -------------------------------------------------------------------------------- /subproject/common/include/continued_fractions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #ifndef JKJ_HEADER_CONTINUED_FRACTIONS 19 | #define JKJ_HEADER_CONTINUED_FRACTIONS 20 | 21 | namespace jkj { 22 | // Continued fractions calculator for positive numbers. 23 | 24 | template 25 | struct unsigned_rational { 26 | UInt numerator = 0; 27 | UInt denominator = 0; 28 | 29 | unsigned_rational() = default; 30 | unsigned_rational(UInt const& numerator) : numerator{numerator}, denominator{1} {} 31 | unsigned_rational(UInt&& numerator) 32 | : numerator{static_cast(numerator)}, denominator{1} {} 33 | unsigned_rational(UInt const& numerator, UInt const& denominator) 34 | : numerator{numerator}, denominator{denominator} {} 35 | unsigned_rational(UInt&& numerator, UInt const& denominator) 36 | : numerator{static_cast(numerator)}, denominator{denominator} {} 37 | unsigned_rational(UInt const& numerator, UInt&& denominator) 38 | : numerator{numerator}, denominator{static_cast(denominator)} {} 39 | unsigned_rational(UInt&& numerator, UInt&& denominator) 40 | : numerator{static_cast(numerator)}, denominator{ 41 | static_cast(denominator)} {} 42 | 43 | friend bool operator<(unsigned_rational const& x, unsigned_rational const& y) { 44 | return x.numerator * y.denominator < y.numerator * x.denominator; 45 | } 46 | friend bool operator<=(unsigned_rational const& x, unsigned_rational const& y) { 47 | return x.numerator * y.denominator <= y.numerator * x.denominator; 48 | } 49 | friend bool operator>(unsigned_rational const& x, unsigned_rational const& y) { 50 | return x.numerator * y.denominator > y.numerator * x.denominator; 51 | } 52 | friend bool operator>=(unsigned_rational const& x, unsigned_rational const& y) { 53 | return x.numerator * y.denominator >= y.numerator * x.denominator; 54 | } 55 | friend bool operator==(unsigned_rational const& x, unsigned_rational const& y) { 56 | return x.numerator * y.denominator == y.numerator * x.denominator; 57 | } 58 | friend bool operator!=(unsigned_rational const& x, unsigned_rational const& y) { 59 | return x.numerator * y.denominator != y.numerator * x.denominator; 60 | } 61 | 62 | // Performs no reduction. 63 | friend unsigned_rational operator+(unsigned_rational const& x, unsigned_rational const& y) { 64 | return {x.numerator * y.denominator + y.numerator * x.denominator, 65 | x.denominator * y.denominator}; 66 | } 67 | // Performs no reduction. 68 | unsigned_rational& operator+=(unsigned_rational const& y) & { 69 | numerator *= y.denominator; 70 | numerator += y.numerator * denominator; 71 | denominator *= y.denominator; 72 | return *this; 73 | } 74 | // Performs no reduction. 75 | friend unsigned_rational operator-(unsigned_rational const& x, unsigned_rational const& y) { 76 | return {x.numerator * y.denominator - y.numerator * x.denominator, 77 | x.denominator * y.denominator}; 78 | } 79 | // Performs no reduction. 80 | unsigned_rational& operator-=(unsigned_rational const& y) & { 81 | numerator *= y.denominator; 82 | numerator -= y.numerator * denominator; 83 | denominator *= y.denominator; 84 | return *this; 85 | } 86 | // Performs no reduction. 87 | friend unsigned_rational operator*(unsigned_rational const& x, unsigned_rational const& y) { 88 | return {x.numerator * y.numerator, x.denominator * y.denominator}; 89 | } 90 | // Performs no reduction. 91 | unsigned_rational& operator*=(unsigned_rational const& y) & { 92 | numerator *= y.numerator; 93 | denominator *= y.denominator; 94 | return *this; 95 | } 96 | // Performs no reduction. 97 | friend unsigned_rational operator/(unsigned_rational const& x, unsigned_rational const& y) { 98 | return {x.numerator * y.denominator, x.denominator * y.numerator}; 99 | } 100 | // Performs no reduction. 101 | unsigned_rational& operator/=(unsigned_rational const& y) & { 102 | numerator *= y.denominator; 103 | denominator *= y.numerator; 104 | return *this; 105 | } 106 | }; 107 | 108 | template 109 | class continued_fractions { 110 | // The (-1)st coefficient is assumed to be 0. 111 | UInt current_coefficient_{0}; 112 | unsigned_rational current_convergent_{1, 0}; 113 | unsigned_rational previous_convergent_{0, 1}; 114 | int current_index_ = -1; 115 | bool terminated_ = false; 116 | 117 | protected: 118 | void set_terminate_flag() noexcept { terminated_ = true; } 119 | 120 | public: 121 | using uint_type = UInt; 122 | 123 | int current_index() const noexcept { return current_index_; } 124 | 125 | UInt const& current_coefficient() const noexcept { return current_coefficient_; } 126 | 127 | unsigned_rational const& current_convergent() const noexcept { 128 | return current_convergent_; 129 | } 130 | 131 | UInt const& current_numerator() const noexcept { return current_convergent().numerator; } 132 | 133 | UInt const& current_denominator() const noexcept { 134 | return current_convergent().denominator; 135 | } 136 | 137 | unsigned_rational const& previous_convergent() const noexcept { 138 | return previous_convergent_; 139 | } 140 | 141 | UInt const& previous_numerator() const noexcept { return previous_convergent().numerator; } 142 | 143 | UInt const& previous_denominator() const noexcept { 144 | return previous_convergent().denominator; 145 | } 146 | 147 | bool is_terminated() const noexcept { return terminated_; } 148 | 149 | // Do nothing if the procedure is terminated. 150 | // Returns true if the update is done, 151 | // and returns false if the procedure is already terminated. 152 | bool update() { 153 | if (!is_terminated()) { 154 | unsigned_rational new_output; 155 | current_coefficient_ = static_cast(*this).compute_next_coefficient(); 156 | 157 | unsigned_rational new_convergent{ 158 | previous_numerator() + current_coefficient_ * current_numerator(), 159 | previous_denominator() + current_coefficient_ * current_denominator()}; 160 | previous_convergent_ = static_cast&&>(current_convergent_); 161 | current_convergent_ = static_cast&&>(new_convergent); 162 | 163 | ++current_index_; 164 | 165 | return true; 166 | } 167 | else { 168 | return false; 169 | } 170 | } 171 | }; 172 | } 173 | 174 | #endif -------------------------------------------------------------------------------- /subproject/common/include/random_float.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #ifndef JKJ_HEADER_RANDOM_FLOAT 19 | #define JKJ_HEADER_RANDOM_FLOAT 20 | 21 | #include "dragonbox/dragonbox.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | // For correct seeding 30 | class repeating_seed_seq { 31 | public: 32 | using result_type = std::uint32_t; 33 | 34 | repeating_seed_seq() : stored_values{0} {} 35 | template 36 | repeating_seed_seq(InputIterator first, InputIterator last) : stored_values(first, last) {} 37 | template 38 | repeating_seed_seq(std::initializer_list list) : stored_values(list) {} 39 | 40 | repeating_seed_seq(std::random_device&& rd, std::size_t count) { 41 | stored_values.resize(count); 42 | for (auto& elem : stored_values) 43 | elem = rd(); 44 | } 45 | 46 | template 47 | void generate(RandomAccessIterator first, RandomAccessIterator last) { 48 | auto count = last - first; 49 | auto q = count / stored_values.size(); 50 | for (std::size_t i = 0; i < q; ++i) { 51 | std::copy_n(stored_values.cbegin(), stored_values.size(), first); 52 | first += stored_values.size(); 53 | } 54 | count -= q * stored_values.size(); 55 | std::copy_n(stored_values.cbegin(), count, first); 56 | } 57 | 58 | std::size_t size() const noexcept { return stored_values.size(); } 59 | 60 | template 61 | void param(OutputIterator first) const { 62 | std::copy(stored_values.begin(), stored_values.end(), first); 63 | } 64 | 65 | private: 66 | std::vector stored_values; 67 | }; 68 | 69 | inline std::mt19937_64 generate_correctly_seeded_mt19937_64() { 70 | repeating_seed_seq seed_seq{std::random_device{}, std::mt19937_64::state_size * 71 | std::mt19937_64::word_size / 72 | (sizeof(std::uint32_t) * 8)}; 73 | return std::mt19937_64{seed_seq}; 74 | } 75 | 76 | template 77 | Float uniformly_randomly_generate_finite_float(RandGen& rg) { 78 | using default_float_bit_carrier_conversion_traits = 79 | jkj::dragonbox::default_float_bit_carrier_conversion_traits; 80 | using format = typename default_float_bit_carrier_conversion_traits::format; 81 | using carrier_uint = typename default_float_bit_carrier_conversion_traits::carrier_uint; 82 | using format_traits = jkj::dragonbox::ieee754_binary_traits; 83 | using uniform_distribution = std::uniform_int_distribution; 84 | 85 | // Generate sign bit 86 | auto sign_bit = uniform_distribution{0, 1}(rg); 87 | 88 | // Generate exponent bits 89 | auto exponent_bits = uniform_distribution{0, (carrier_uint(1) << format::exponent_bits) - 2}(rg); 90 | 91 | // Generate significand bits 92 | auto significand_bits = 93 | uniform_distribution{0, (carrier_uint(1) << format_traits::significand_bits) - 1}(rg); 94 | 95 | auto bit_representation = (sign_bit << (format_traits::carrier_bits - 1)) | 96 | (exponent_bits << (format_traits::significand_bits)) | significand_bits; 97 | 98 | return default_float_bit_carrier_conversion_traits::carrier_to_float(bit_representation); 99 | } 100 | 101 | template 102 | Float uniformly_randomly_generate_general_float(RandGen& rg) { 103 | using default_float_bit_carrier_conversion_traits = 104 | jkj::dragonbox::default_float_bit_carrier_conversion_traits; 105 | using carrier_uint = typename default_float_bit_carrier_conversion_traits::carrier_uint; 106 | using uniform_distribution = std::uniform_int_distribution; 107 | 108 | // Generate sign bit 109 | auto bit_representation = uniform_distribution{0, std::numeric_limits::max()}(rg); 110 | return default_float_bit_carrier_conversion_traits::carrier_to_float(bit_representation); 111 | } 112 | 113 | template 114 | struct std_string_to_float; 115 | 116 | template <> 117 | struct std_string_to_float { 118 | float operator()(std::string const& str) const { return std::stof(str); } 119 | }; 120 | 121 | template <> 122 | struct std_string_to_float { 123 | double operator()(std::string const& str) const { return std::stod(str); } 124 | }; 125 | 126 | // This function tries to uniformly randomly generate a float number with the 127 | // given number of decimal digits, and the end-result is not perfectly bias-free. 128 | // However, I don't think there is an easy way to do it correctly. 129 | template 130 | Float randomly_generate_float_with_given_digits(unsigned int digits, RandGen& rg) { 131 | using conversion_traits = jkj::dragonbox::default_float_bit_carrier_conversion_traits; 132 | using carrier_uint = typename conversion_traits::carrier_uint; 133 | using signed_int_t = std::make_signed_t; 134 | 135 | assert(digits >= 1); 136 | assert(digits <= conversion_traits::format::decimal_significand_digits); 137 | 138 | // Generate sign uniformly randomly 139 | signed_int_t sign = std::uniform_int_distribution{0, 1}(rg) == 0 ? 1 : -1; 140 | 141 | 142 | // Try to generate significand uniformly randomly 143 | Float result; 144 | signed_int_t from = 0, to = 9; 145 | if (digits > 1) { 146 | from = 1; 147 | for (unsigned int e = 1; e < digits - 1; ++e) { 148 | from *= 10; 149 | } 150 | to = from * 10 - 1; 151 | } 152 | 153 | while (true) { 154 | auto significand = std::uniform_int_distribution{from, to}(rg); 155 | if (digits > 1) { 156 | significand *= 10; 157 | significand += std::uniform_int_distribution{1, 9}(rg); 158 | } 159 | 160 | // Generate exponent uniformly randomly 161 | auto exp = std::uniform_int_distribution{ 162 | std::numeric_limits::min_exponent10 - (int(digits) - 1), 163 | std::numeric_limits::max_exponent10 - (int(digits) - 1)}(rg); 164 | 165 | // Cook up 166 | auto str = std::to_string(sign * significand) + 'e' + std::to_string(exp); 167 | try { 168 | result = std_string_to_float{}(str); 169 | } 170 | catch (...) { 171 | continue; 172 | } 173 | 174 | if (!std::isfinite(result)) { 175 | continue; 176 | } 177 | 178 | // Discard if a shorter representation exists 179 | // We don't need to care about sign and correct rounding here 180 | if (from != 0) { 181 | auto roundtrip = jkj::dragonbox::to_decimal( 182 | result, jkj::dragonbox::policy::sign::ignore, 183 | jkj::dragonbox::policy::decimal_to_binary_rounding::nearest_to_even, 184 | jkj::dragonbox::policy::binary_to_decimal_rounding::do_not_care); 185 | if (roundtrip.significand <= carrier_uint(from * 10)) { 186 | continue; 187 | } 188 | } 189 | break; 190 | } 191 | 192 | return result; 193 | } 194 | 195 | #endif 196 | -------------------------------------------------------------------------------- /subproject/common/include/rational_continued_fractions.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #ifndef JKJ_HEADER_RATIONAL_CONTINUED_FRACTIONS 19 | #define JKJ_HEADER_RATIONAL_CONTINUED_FRACTIONS 20 | 21 | #include "continued_fractions.h" 22 | #include 23 | #include 24 | 25 | namespace jkj { 26 | template 27 | class rational_continued_fractions 28 | : public continued_fractions, UInt> { 29 | using crtp_base = continued_fractions, UInt>; 30 | friend crtp_base; 31 | 32 | UInt prev_error_; 33 | UInt curr_error_; 34 | 35 | UInt compute_next_coefficient() { 36 | using std::div; 37 | auto div_result = div(prev_error_, curr_error_); 38 | prev_error_ = static_cast(curr_error_); 39 | curr_error_ = static_cast(div_result.rem); 40 | 41 | if (curr_error_ == 0) { 42 | crtp_base::set_terminate_flag(); 43 | } 44 | 45 | return static_cast(div_result.quot); 46 | } 47 | 48 | public: 49 | rational_continued_fractions(unsigned_rational r) 50 | : prev_error_{static_cast(r.numerator)}, curr_error_{static_cast( 51 | r.denominator)} { 52 | assert(curr_error_ != 0); 53 | } 54 | }; 55 | } 56 | 57 | #endif -------------------------------------------------------------------------------- /subproject/meta/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(dragonbox_meta LANGUAGES CXX) 4 | 5 | include(FetchContent) 6 | if (NOT TARGET dragonbox) 7 | FetchContent_Declare(dragonbox SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../..") 8 | FetchContent_MakeAvailable(dragonbox) 9 | endif() 10 | if (NOT TARGET common) 11 | FetchContent_Declare(common SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../common") 12 | FetchContent_MakeAvailable(common) 13 | endif() 14 | if (NOT TARGET ryu) 15 | FetchContent_Declare(ryu SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../3rdparty/ryu") 16 | FetchContent_MakeAvailable(ryu) 17 | endif() 18 | 19 | function(meta_exe NAME) 20 | add_executable(${NAME} source/${NAME}.cpp) 21 | 22 | target_compile_features(${NAME} PRIVATE cxx_std_17) 23 | 24 | target_link_libraries(${NAME} PRIVATE ${ARGN}) 25 | 26 | # ---- MSVC Specifics ---- 27 | if (MSVC) 28 | # See https://gitlab.kitware.com/cmake/cmake/-/issues/16478 29 | set_target_properties(${NAME} PROPERTIES 30 | VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") 31 | endif() 32 | endfunction() 33 | 34 | meta_exe(generate_cache dragonbox::common) 35 | meta_exe(live_test dragonbox::common dragonbox::dragonbox_to_chars ryu::ryu) 36 | meta_exe(perf_test dragonbox::common dragonbox::dragonbox_to_chars) 37 | meta_exe(sandbox dragonbox::common dragonbox::dragonbox_to_chars) -------------------------------------------------------------------------------- /subproject/meta/results/binary32_generated_cache.txt: -------------------------------------------------------------------------------- 1 | static constexpr int min_k = -31; 2 | static constexpr int max_k = 46; 3 | static constexpr detail::array cache JKJ_STATIC_DATA_SECTION = { { 4 | UINT64_C(0x81ceb32c4b43fcf5), 5 | UINT64_C(0xa2425ff75e14fc32), 6 | UINT64_C(0xcad2f7f5359a3b3f), 7 | UINT64_C(0xfd87b5f28300ca0e), 8 | UINT64_C(0x9e74d1b791e07e49), 9 | UINT64_C(0xc612062576589ddb), 10 | UINT64_C(0xf79687aed3eec552), 11 | UINT64_C(0x9abe14cd44753b53), 12 | UINT64_C(0xc16d9a0095928a28), 13 | UINT64_C(0xf1c90080baf72cb2), 14 | UINT64_C(0x971da05074da7bef), 15 | UINT64_C(0xbce5086492111aeb), 16 | UINT64_C(0xec1e4a7db69561a6), 17 | UINT64_C(0x9392ee8e921d5d08), 18 | UINT64_C(0xb877aa3236a4b44a), 19 | UINT64_C(0xe69594bec44de15c), 20 | UINT64_C(0x901d7cf73ab0acda), 21 | UINT64_C(0xb424dc35095cd810), 22 | UINT64_C(0xe12e13424bb40e14), 23 | UINT64_C(0x8cbccc096f5088cc), 24 | UINT64_C(0xafebff0bcb24aaff), 25 | UINT64_C(0xdbe6fecebdedd5bf), 26 | UINT64_C(0x89705f4136b4a598), 27 | UINT64_C(0xabcc77118461cefd), 28 | UINT64_C(0xd6bf94d5e57a42bd), 29 | UINT64_C(0x8637bd05af6c69b6), 30 | UINT64_C(0xa7c5ac471b478424), 31 | UINT64_C(0xd1b71758e219652c), 32 | UINT64_C(0x83126e978d4fdf3c), 33 | UINT64_C(0xa3d70a3d70a3d70b), 34 | UINT64_C(0xcccccccccccccccd), 35 | UINT64_C(0x8000000000000000), 36 | UINT64_C(0xa000000000000000), 37 | UINT64_C(0xc800000000000000), 38 | UINT64_C(0xfa00000000000000), 39 | UINT64_C(0x9c40000000000000), 40 | UINT64_C(0xc350000000000000), 41 | UINT64_C(0xf424000000000000), 42 | UINT64_C(0x9896800000000000), 43 | UINT64_C(0xbebc200000000000), 44 | UINT64_C(0xee6b280000000000), 45 | UINT64_C(0x9502f90000000000), 46 | UINT64_C(0xba43b74000000000), 47 | UINT64_C(0xe8d4a51000000000), 48 | UINT64_C(0x9184e72a00000000), 49 | UINT64_C(0xb5e620f480000000), 50 | UINT64_C(0xe35fa931a0000000), 51 | UINT64_C(0x8e1bc9bf04000000), 52 | UINT64_C(0xb1a2bc2ec5000000), 53 | UINT64_C(0xde0b6b3a76400000), 54 | UINT64_C(0x8ac7230489e80000), 55 | UINT64_C(0xad78ebc5ac620000), 56 | UINT64_C(0xd8d726b7177a8000), 57 | UINT64_C(0x878678326eac9000), 58 | UINT64_C(0xa968163f0a57b400), 59 | UINT64_C(0xd3c21bcecceda100), 60 | UINT64_C(0x84595161401484a0), 61 | UINT64_C(0xa56fa5b99019a5c8), 62 | UINT64_C(0xcecb8f27f4200f3a), 63 | UINT64_C(0x813f3978f8940985), 64 | UINT64_C(0xa18f07d736b90be6), 65 | UINT64_C(0xc9f2c9cd04674edf), 66 | UINT64_C(0xfc6f7c4045812297), 67 | UINT64_C(0x9dc5ada82b70b59e), 68 | UINT64_C(0xc5371912364ce306), 69 | UINT64_C(0xf684df56c3e01bc7), 70 | UINT64_C(0x9a130b963a6c115d), 71 | UINT64_C(0xc097ce7bc90715b4), 72 | UINT64_C(0xf0bdc21abb48db21), 73 | UINT64_C(0x96769950b50d88f5), 74 | UINT64_C(0xbc143fa4e250eb32), 75 | UINT64_C(0xeb194f8e1ae525fe), 76 | UINT64_C(0x92efd1b8d0cf37bf), 77 | UINT64_C(0xb7abc627050305ae), 78 | UINT64_C(0xe596b7b0c643c71a), 79 | UINT64_C(0x8f7e32ce7bea5c70), 80 | UINT64_C(0xb35dbf821ae4f38c), 81 | UINT64_C(0xe0352f62a19e306f) 82 | } }; -------------------------------------------------------------------------------- /subproject/meta/source/generate_cache.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "big_uint.h" 19 | #include "continued_fractions.h" 20 | #include "dragonbox/dragonbox.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | template 27 | auto generate_cache(std::size_t cache_bits) { 28 | using impl = jkj::dragonbox::detail::impl; 29 | 30 | std::vector results; 31 | jkj::unsigned_rational target_number; 32 | for (int k = impl::min_k; k <= impl::max_k; ++k) { 33 | // (2f_c +- 1) * 2^beta * (2^(k - e_k - Q) * 5^k) 34 | // e_k = floor(k log2(10)) - Q + 1, so 35 | // k - e_k - Q = k - floor(k log2(10)) - 1. 36 | int exp_2 = k - jkj::dragonbox::detail::log::floor_log2_pow10(k) - 1; 37 | 38 | target_number.numerator = 1; 39 | target_number.denominator = 1; 40 | if (k >= 0) { 41 | target_number.numerator = jkj::big_uint::pow(5, k); 42 | } 43 | else { 44 | target_number.denominator = jkj::big_uint::pow(5, -k); 45 | } 46 | if (exp_2 >= 0) { 47 | target_number.numerator *= jkj::big_uint::power_of_2(exp_2); 48 | } 49 | else { 50 | target_number.denominator *= jkj::big_uint::power_of_2(-exp_2); 51 | } 52 | 53 | auto div_res = div(jkj::big_uint::power_of_2(cache_bits) * target_number.numerator, 54 | target_number.denominator); 55 | auto m = std::move(div_res.quot); 56 | if (!div_res.rem.is_zero()) { 57 | m += 1; 58 | } 59 | 60 | // Recheck that m is in the correct range. 61 | if (m < jkj::big_uint::power_of_2(cache_bits - 1) || 62 | m >= jkj::big_uint::power_of_2(cache_bits)) { 63 | throw std::logic_error{"Generated cache entry is not in the correct range"}; 64 | } 65 | 66 | results.push_back(std::move(m)); 67 | } 68 | 69 | return results; 70 | } 71 | 72 | #include 73 | #include 74 | #include 75 | 76 | int main() { 77 | std::cout << "[Generating cache...]\n"; 78 | 79 | auto write_file = [](std::ofstream& out, std::size_t cache_bits, auto type_tag, 80 | auto&& ieee_754_type_name_string, auto&& element_printer) { 81 | using impl_type = jkj::dragonbox::detail::impl; 82 | auto const cache_array = generate_cache(cache_bits); 83 | 84 | out << "static constexpr int min_k = " << std::dec << impl_type::min_k << ";\n"; 85 | out << "static constexpr int max_k = " << std::dec << impl_type::max_k << ";\n"; 86 | out << "static constexpr detail::array cache JKJ_STATIC_DATA_SECTION = { {"; 88 | for (int k = impl_type::min_k; k < impl_type::max_k; ++k) { 89 | auto idx = std::size_t(k - impl_type::min_k); 90 | out << "\n\t"; 91 | element_printer(out, cache_array[idx]); 92 | out << ","; 93 | } 94 | out << "\n\t"; 95 | element_printer(out, cache_array.back()); 96 | out << "\n} };"; 97 | }; 98 | 99 | std::ofstream out; 100 | 101 | try { 102 | out.open("results/binary32_generated_cache.txt"); 103 | write_file(out, 64, 104 | jkj::dragonbox::ieee754_binary_traits{}, 106 | "binary32", [](std::ofstream& out, jkj::big_uint const& value) { 107 | out << "UINT64_C(0x" << std::hex << std::setw(16) << std::setfill('0') 108 | << value[0] << ")"; 109 | }); 110 | out.close(); 111 | 112 | out.open("results/binary64_generated_cache.txt"); 113 | write_file(out, 128, 114 | jkj::dragonbox::ieee754_binary_traits{}, 116 | "binary64", [](std::ofstream& out, jkj::big_uint const& value) { 117 | out << "{UINT64_C(0x" << std::hex << std::setw(16) << std::setfill('0') 118 | << value[1] << "), UINT64_C(0x" << std::hex << std::setw(16) 119 | << std::setfill('0') << value[0] << ")}"; 120 | }); 121 | out.close(); 122 | } 123 | catch (std::logic_error const& ex) { 124 | std::cout << ex.what() << "\n"; 125 | return -1; 126 | } 127 | 128 | std::cout << "Done.\n\n\n"; 129 | } 130 | -------------------------------------------------------------------------------- /subproject/meta/source/live_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "dragonbox/dragonbox_to_chars.h" 19 | #include "random_float.h" 20 | #include "ryu/ryu.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | void reference_implementation(float x, char* buffer) { f2s_buffered(x, buffer); } 27 | void reference_implementation(double x, char* buffer) { d2s_buffered(x, buffer); } 28 | 29 | template 30 | static void live_test(std::streamsize hex_width) { 31 | char buffer1[41]; 32 | char buffer2[41]; 33 | 34 | while (true) { 35 | Float x; 36 | std::string x_str; 37 | while (true) { 38 | std::getline(std::cin, x_str); 39 | try { 40 | x = std_string_to_float{}(x_str); 41 | } 42 | catch (...) { 43 | std::cout << "Not a valid input; input again.\n"; 44 | continue; 45 | } 46 | break; 47 | } 48 | 49 | auto xx = jkj::dragonbox::make_float_bits(x); 50 | std::cout << " sign: " << (xx.is_negative() ? "-" : "+") << std::endl; 51 | std::cout << " exponent bits: " 52 | << "0x" << std::hex << std::setfill('0') << xx.extract_exponent_bits() << std::dec 53 | << " (value: " << xx.binary_exponent() << ")\n"; 54 | std::cout << " significand bits: " 55 | << "0x" << std::hex << std::setfill('0'); 56 | std::cout << std::setw(hex_width); 57 | std::cout << xx.extract_significand_bits() << " (value: 0x" << xx.binary_significand() 58 | << ")\n" 59 | << std::dec; 60 | 61 | jkj::dragonbox::to_chars(x, buffer1); 62 | reference_implementation(x, buffer2); 63 | std::cout << " Dragonbox output: " << buffer1 << "\n"; 64 | std::cout << " Reference output: " << buffer2 << "\n\n"; 65 | } 66 | } 67 | 68 | int main() { 69 | constexpr enum { test_float, test_double } test = test_float; 70 | 71 | if (test == test_float) { 72 | std::cout << "[Start live test for float's]\n"; 73 | live_test(8); 74 | } 75 | else if (test == test_double) { 76 | std::cout << "[Start live test for double's]\n"; 77 | live_test(16); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /subproject/meta/source/perf_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "dragonbox/dragonbox_to_chars.h" 19 | #include "random_float.h" 20 | #include 21 | #include 22 | 23 | template 24 | static void uniform_random_perf_test(std::size_t number_of_tests) 25 | { 26 | std::cout << "Generating random samples...\n"; 27 | auto rg = generate_correctly_seeded_mt19937_64(); 28 | std::vector samples(number_of_tests); 29 | for (auto& sample : samples) { 30 | sample = uniformly_randomly_generate_general_float(rg); 31 | } 32 | 33 | std::cout << "Performing test...\n"; 34 | char buffer[41]; 35 | auto from = std::chrono::steady_clock::now(); 36 | for (auto& sample : samples) { 37 | jkj::dragonbox::to_chars(sample, buffer); 38 | } 39 | auto dur = std::chrono::steady_clock::now() - from; 40 | std::cout << "Average time: " << 41 | double(std::chrono::duration_cast(dur).count()) / number_of_tests << "ns\n"; 42 | } 43 | 44 | template 45 | static void digit_perf_test(unsigned int digits, std::size_t number_of_tests) 46 | { 47 | std::cout << "Generating random samples...\n"; 48 | auto rg = generate_correctly_seeded_mt19937_64(); 49 | std::vector samples(number_of_tests); 50 | for (auto& sample : samples) { 51 | sample = randomly_generate_float_with_given_digits(digits, rg); 52 | } 53 | 54 | std::cout << "Performing test...\n"; 55 | char buffer[41]; 56 | auto from = std::chrono::steady_clock::now(); 57 | for (auto& sample : samples) { 58 | jkj::dragonbox::to_chars(sample, buffer); 59 | } 60 | auto dur = std::chrono::steady_clock::now() - from; 61 | std::cout << "Average time: " << 62 | double(std::chrono::duration_cast(dur).count()) / number_of_tests << "ns\n"; 63 | } 64 | 65 | int main() 66 | { 67 | constexpr bool run_uniform_random_float = true; 68 | constexpr std::size_t number_of_uniform_random_perf_tests_float = 100000000; 69 | 70 | constexpr bool run_uniform_random_double = true; 71 | constexpr std::size_t number_of_uniform_random_perf_tests_double = 100000000; 72 | 73 | constexpr bool run_digit_float = false; 74 | constexpr unsigned int digits_for_perf_test_float = 6; 75 | constexpr std::size_t number_of_digit_perf_tests_float = 40000000; 76 | 77 | constexpr bool run_digit_double = false; 78 | constexpr static unsigned int digits_for_perf_test_double = 17; 79 | constexpr static std::size_t number_of_digit_perf_tests_double = 6000000; 80 | 81 | 82 | if (run_uniform_random_float) { 83 | std::cout << "[Running the algorithm with uniformly randomly generated float inputs...]\n"; 84 | uniform_random_perf_test(number_of_uniform_random_perf_tests_float); 85 | std::cout << "Done.\n\n\n"; 86 | } 87 | if (run_uniform_random_double) { 88 | std::cout << "[Running the algorithm with uniformly randomly generated double inputs...]\n"; 89 | uniform_random_perf_test(number_of_uniform_random_perf_tests_double); 90 | std::cout << "Done.\n\n\n"; 91 | } 92 | if (run_digit_float) { 93 | std::cout << "[Running the algorithm with float inputs of digits = " 94 | << digits_for_perf_test_float << "...]\n"; 95 | digit_perf_test(digits_for_perf_test_float, number_of_digit_perf_tests_float); 96 | std::cout << "Done.\n\n\n"; 97 | } 98 | if (run_digit_double) { 99 | std::cout << "[Running the algorithm with double inputs of digits = " 100 | << digits_for_perf_test_double << "...]\n"; 101 | digit_perf_test(digits_for_perf_test_double, number_of_digit_perf_tests_double); 102 | std::cout << "Done.\n\n\n"; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /subproject/meta/source/sandbox.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "dragonbox/dragonbox_to_chars.h" 19 | 20 | int main() {} -------------------------------------------------------------------------------- /subproject/simple/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(dragonbox_simple LANGUAGES CXX) 4 | 5 | set(dragonbox_simple_headers include/simple_dragonbox.h) 6 | 7 | if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.19.0") 8 | add_library(dragonbox_simple INTERFACE ${dragonbox_simple_headers}) 9 | else() 10 | add_library(dragonbox_simple INTERFACE) 11 | endif() 12 | add_library(dragonbox::simple ALIAS dragonbox_simple) 13 | 14 | target_include_directories(dragonbox_simple 15 | INTERFACE 16 | $) 17 | 18 | target_compile_features(dragonbox_simple INTERFACE cxx_std_17) -------------------------------------------------------------------------------- /subproject/simple/README.md: -------------------------------------------------------------------------------- 1 | # Simple Dragonbox 2 | 3 | This is a simplified implementation of the algorithm, that closely follows the 4 | one in `include/dragonbox/dragonbox.h`, but aims to be shorter overall, use less 5 | C++ template indirection, and offer less flexibility and performance for the 6 | sake of simplicity. 7 | 8 | Simplifications over the implementation in `include/dragonbox/dragonbox.h` are 9 | made based primarily on the following assumptions: 10 | 11 | - No need to support `sign` policies (always uses `return_sign`). 12 | - No need to support `trailing_zeros` policies (always uses `remove`). 13 | - No need to support 128-bit compiler intrinsics (always uses portable 14 | implementation). 15 | - No need to support the `fast` digit-generation policy (always uses `compact`). 16 | - `if constexpr` is available (C++17). 17 | - `float` and `double` use [IEEE-754 binary32](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) and [IEEE-754 binary64](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) representations, 18 | respectively. 19 | - A modern compiler and standard library are available. 20 | 21 | Note the `cache`, `binary_to_decimal_rounding`, and `decimal_to_binary_rounding` 22 | policies are still supported. 23 | 24 | # Usage Examples 25 | 26 | Simple string generation from `float`/`double` (mirrors the interface and 27 | behavior of `jkj::dragonbox::to_chars`; see the [primary README](/README.md)): 28 | 29 | ```cpp 30 | #include "simple_dragonbox.h" 31 | 32 | constexpr int buffer_length = 1 + // for '\0' 33 | simple_dragonbox::max_output_string_length; 34 | double x = 1.234; // Also works for float 35 | char buffer[buffer_length]; 36 | 37 | // Null-terminate the buffer and return the pointer to the null character. 38 | // Hence, the length of the string is `end - buffer`. 39 | // `buffer` is now { '1', '.', '2', '3', '4', 'E', '0', '\0', (garbage) }. 40 | char* end = simple_dragonbox::to_chars(x, buffer); 41 | ``` 42 | 43 | Direct use of `simple_dragonbox::to_decimal` (mirrors the interface and 44 | behavior of `jkj::dragonbox::to_decimal`; see the [primary README](/README.md)): 45 | 46 | ```cpp 47 | #include "simple_dragonbox.h" 48 | double x = 1.234; // Also works for float 49 | 50 | // `x` must be a nonzero finite number. 51 | // `v` is a struct with three members: 52 | // significand : decimal significand (1234 in this case); 53 | // it is of type std::uint64_t for double, std::uint32_t for float 54 | // exponent : decimal exponent (-3 in this case); it is of type int 55 | // sign : as the name suggests; it is of type bool 56 | auto v = jkj::dragonbox::to_decimal(x); 57 | ``` 58 | 59 | Like `jkj::dragonbox::to_decimal`, `simple_dragonbox::to_decimal` only works 60 | with finite nonzero inputs. Its behavior when given infinities, NaNs, and ±0 is 61 | undefined. `simple_dragonbox::to_chars` works fine for any inputs. 62 | 63 | # Policies 64 | 65 | `simple_dragonbox` supports the same style of policy interface as 66 | `jkj::dragonbox`. For an introduction to `jkj::dragonbox`'s policy interface, 67 | see the [primary README](/README.md). For example, 68 | 69 | ```cpp 70 | simple_dragonbox::to_decimal(3.14, 71 | simple_dragonbox::policy::cache::compact, 72 | simple_dragonbox::policy::binary_to_decimal_rounding::to_odd); 73 | ``` 74 | 75 | `simple_dragonbox` supports a subset of the policies that `jkj::dragonbox` does. 76 | When supported, policies have the same meaning as the matching policy in 77 | `jkj::dragonbox` (see the [primary README](/README.md)). Supported policies 78 | include: 79 | 80 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_even` 81 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_odd` 82 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_plus_infinity` 83 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_minus_infinity` 84 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_zero` 85 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_away_from_zero` 86 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_even_static_boundary` 87 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_to_odd_static_boundary` 88 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_plus_infinity_static_boundary` 89 | - `simple_dragonbox::policy::decimal_to_binary_rounding::nearest_toward_minus_infinity_static_boundary` 90 | - `simple_dragonbox::policy::decimal_to_binary_rounding::toward_plus_infinity` 91 | - `simple_dragonbox::policy::decimal_to_binary_rounding::toward_minus_infinity` 92 | - `simple_dragonbox::policy::decimal_to_binary_rounding::toward_zero` 93 | - `simple_dragonbox::policy::decimal_to_binary_rounding::away_from_zero` 94 | 95 | - `simple_dragonbox::policy::binary_to_decimal_rounding::to_even` 96 | - `simple_dragonbox::policy::binary_to_decimal_rounding::to_odd` 97 | - `simple_dragonbox::policy::binary_to_decimal_rounding::away_from_zero` 98 | - `simple_dragonbox::policy::binary_to_decimal_rounding::toward_zero` 99 | - `simple_dragonbox::policy::binary_to_decimal_rounding::do_not_care` 100 | 101 | - `simple_dragonbox::policy::cache::full` 102 | - `simple_dragonbox::policy::cache::compact` 103 | 104 | # Internal direct API 105 | 106 | Internally, `simple_dragonbox` uses a more explicit, direct interface to express 107 | the various policies, via a class called `simple_dragonbox::detail::impl`. The 108 | `impl` class has four template parameters: `Float`, `BinaryRoundMode`, 109 | `DecimalRoundMode`, and `CacheType`, which correspond to the floating point 110 | type and three categories of policies above. These template parameters can be 111 | specified explicitly to instantiate a particular variant of the algorithm. The 112 | class has a single constructor that receives a value of type `Float` (e.g. 113 | `float` or `double`), and has methods `to_decimal()` and `to_chars()` for 114 | performing the desired conversions. 115 | 116 | For example, 117 | 118 | ```cpp 119 | namespace detail = simple_dragonbox::detail; 120 | using impl = detail::impl; 124 | 125 | char buffer[32]; 126 | auto x = impl(3.14); 127 | x.to_decimal(); // equivalent to `simple_dragonbox::to_decimal(3.14, policies...)` 128 | x.to_chars(buffer); // equivalent to `simple_dragonbox::to_chars(3.14, buf, policies...)` 129 | ``` 130 | -------------------------------------------------------------------------------- /subproject/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14 FATAL_ERROR) 2 | 3 | project(dragonboxTests LANGUAGES CXX) 4 | 5 | # ---- Add dependencies ---- 6 | 7 | # We want warnings in tests 8 | set(dragonbox_INCLUDE_WITHOUT_SYSTEM ON CACHE INTERNAL "") 9 | 10 | include(FetchContent) 11 | if (NOT TARGET dragonbox) 12 | FetchContent_Declare(dragonbox SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../..") 13 | FetchContent_MakeAvailable(dragonbox) 14 | endif() 15 | if (NOT TARGET common) 16 | FetchContent_Declare(common SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../common") 17 | FetchContent_MakeAvailable(common) 18 | endif() 19 | if (NOT TARGET simple) 20 | FetchContent_Declare(simple SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../simple") 21 | FetchContent_MakeAvailable(simple) 22 | endif() 23 | if (NOT TARGET ryu) 24 | FetchContent_Declare(ryu SOURCE_DIR "${CMAKE_CURRENT_LIST_DIR}/../3rdparty/ryu") 25 | FetchContent_MakeAvailable(ryu) 26 | endif() 27 | 28 | # ---- Tests ---- 29 | 30 | enable_testing() 31 | 32 | function(add_test NAME) 33 | cmake_parse_arguments(TEST "TO_CHARS;RYU" "NAME" "" ${ARGN}) 34 | if(TEST_TO_CHARS) 35 | set(dragonbox dragonbox::dragonbox_to_chars) 36 | else() 37 | set(dragonbox dragonbox::dragonbox) 38 | endif() 39 | 40 | add_executable(${NAME} source/${NAME}.cpp) 41 | 42 | target_link_libraries(${NAME} PRIVATE ${dragonbox} dragonbox::common dragonbox::simple) 43 | 44 | if(TEST_RYU) 45 | target_link_libraries(${NAME} PRIVATE ryu::ryu) 46 | endif() 47 | 48 | target_compile_features(${NAME} PRIVATE cxx_std_17) 49 | 50 | _add_test(NAME ${NAME} COMMAND ${NAME}) 51 | 52 | # ---- MSVC Specifics ---- 53 | if (MSVC) 54 | # See https://gitlab.kitware.com/cmake/cmake/-/issues/16478 55 | set_target_properties(${NAME} PROPERTIES 56 | VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}") 57 | endif() 58 | endfunction() 59 | 60 | add_test(verify_cache_precision) 61 | add_test(test_all_shorter_interval_cases TO_CHARS RYU) 62 | add_test(uniform_random_test TO_CHARS RYU) 63 | add_test(verify_compressed_cache) 64 | add_test(verify_fast_multiplication) 65 | add_test(verify_log_computation) 66 | add_test(verify_magic_division) 67 | 68 | option(DRAGONBOX_ENABLE_CONSTEXPR_TEST "Build constexpr test" OFF) 69 | if (DRAGONBOX_ENABLE_CONSTEXPR_TEST) 70 | add_test(constexpr) 71 | set_target_properties(constexpr PROPERTIES 72 | CXX_STANDARD 20 73 | CXX_STANDARD_REQUIRED ON 74 | ) 75 | endif() 76 | -------------------------------------------------------------------------------- /subproject/test/results/binary32.csv: -------------------------------------------------------------------------------- 1 | e,bits_for_multiplication,bits_for_integer_check 2 | -149,50,61 3 | -148,54,60 4 | -147,55,59 5 | -146,48,57 6 | -145,54,57 7 | -144,54,56 8 | -143,54,57 9 | -142,56,56 10 | -141,56,56 11 | -140,56,57 12 | -139,46,58 13 | -138,53,60 14 | -137,53,60 15 | -136,52,63 16 | -135,52,63 17 | -134,52,63 18 | -133,52,63 19 | -132,50,63 20 | -131,50,63 21 | -130,53,62 22 | -129,52,62 23 | -128,53,61 24 | -127,53,60 25 | -126,48,53 26 | -125,48,53 27 | -124,61,57 28 | -123,61,56 29 | -122,52,59 30 | -121,55,58 31 | -120,61,57 32 | -119,54,57 33 | -118,54,57 34 | -117,54,59 35 | -116,51,59 36 | -115,53,58 37 | -114,55,57 38 | -113,55,60 39 | -112,55,57 40 | -111,55,57 41 | -110,55,58 42 | -109,51,60 43 | -108,54,59 44 | -107,54,58 45 | -106,52,60 46 | -105,53,59 47 | -104,53,58 48 | -103,54,57 49 | -102,50,60 50 | -101,55,59 51 | -100,55,58 52 | -99,52,59 53 | -98,54,58 54 | -97,54,57 55 | -96,51,59 56 | -95,51,58 57 | -94,52,57 58 | -93,52,59 59 | -92,52,59 60 | -91,52,58 61 | -90,54,58 62 | -89,50,56 63 | -88,54,59 64 | -87,55,58 65 | -86,51,58 66 | -85,52,58 67 | -84,52,58 68 | -83,50,57 69 | -82,53,58 70 | -81,53,66 71 | -80,53,66 72 | -79,52,59 73 | -78,52,58 74 | -77,52,57 75 | -76,52,58 76 | -75,54,58 77 | -74,54,58 78 | -73,45,56 79 | -72,45,55 80 | -71,45,55 81 | -70,54,57 82 | -69,52,58 83 | -68,52,58 84 | -67,52,58 85 | -66,49,58 86 | -65,49,57 87 | -64,49,57 88 | -63,47,56 89 | -62,47,57 90 | -61,47,61 91 | -60,47,60 92 | -59,45,57 93 | -58,45,60 94 | -57,45,60 95 | -56,42,58 96 | -55,42,58 97 | -54,42,57 98 | -53,40,59 99 | -52,40,59 100 | -51,40,58 101 | -50,40,57 102 | -49,38,58 103 | -48,38,58 104 | -47,38,57 105 | -46,35,57 106 | -45,35,57 107 | -44,35,60 108 | -43,33,59 109 | -42,33,58 110 | -41,33,60 111 | -40,33,59 112 | -39,31,57 113 | -38,31,57 114 | -37,31,57 115 | -36,28,57 116 | -35,28,56 117 | -34,28,55 118 | -33,26,55 119 | -32,26,54 120 | -31,26,53 121 | -30,26,52 122 | -29,24,52 123 | -28,24,51 124 | -27,24,50 125 | -26,21,50 126 | -25,21,49 127 | -24,21,48 128 | -23,19,48 129 | -22,19,47 130 | -21,19,46 131 | -20,19,45 132 | -19,17,45 133 | -18,17,44 134 | -17,17,43 135 | -16,14,43 136 | -15,14,42 137 | -14,14,41 138 | -13,12,41 139 | -12,12,40 140 | -11,12,39 141 | -10,12,38 142 | -9,10,38 143 | -8,10,37 144 | -7,10,36 145 | -6,7,36 146 | -5,7,35 147 | -4,7,34 148 | -3,5,34 149 | -2,5,33 150 | -1,5,32 151 | 0,3,32 152 | 1,3,32 153 | 2,3,32 154 | 3,3,32 155 | 4,1,32 156 | 5,1,32 157 | 6,1,32 158 | 7,31,35 159 | 8,31,35 160 | 9,32,35 161 | 10,31,37 162 | 11,33,37 163 | 12,33,37 164 | 13,33,37 165 | 14,36,39 166 | 15,36,39 167 | 16,36,39 168 | 17,37,42 169 | 18,38,42 170 | 19,40,42 171 | 20,38,44 172 | 21,41,44 173 | 22,41,44 174 | 23,43,44 175 | 24,41,46 176 | 25,44,46 177 | 26,44,46 178 | 27,41,49 179 | 28,41,49 180 | 29,46,49 181 | 30,46,51 182 | 31,47,51 183 | 32,47,51 184 | 33,47,51 185 | 34,50,53 186 | 35,50,53 187 | 36,52,53 188 | 37,51,56 189 | 38,51,56 190 | 39,53,56 191 | 40,52,58 192 | 41,54,58 193 | 42,54,57 194 | 43,54,56 195 | 44,54,57 196 | 45,54,57 197 | 46,54,60 198 | 47,51,63 199 | 48,52,62 200 | 49,52,61 201 | 50,51,59 202 | 51,54,58 203 | 52,57,57 204 | 53,57,57 205 | 54,52,58 206 | 55,52,57 207 | 56,55,62 208 | 57,52,64 209 | 58,52,64 210 | 59,53,63 211 | 60,50,57 212 | 61,53,58 213 | 62,53,58 214 | 63,56,58 215 | 64,51,57 216 | 65,51,59 217 | 66,55,58 218 | 67,52,55 219 | 68,52,54 220 | 69,52,53 221 | 70,51,56 222 | 71,51,55 223 | 72,51,55 224 | 73,54,64 225 | 74,49,59 226 | 75,49,59 227 | 76,49,59 228 | 77,50,62 229 | 78,50,62 230 | 79,50,62 231 | 80,51,62 232 | 81,51,62 233 | 82,51,62 234 | 83,51,62 235 | 84,50,62 236 | 85,50,62 237 | 86,50,62 238 | 87,51,62 239 | 88,51,62 240 | 89,51,62 241 | 90,50,62 242 | 91,51,61 243 | 92,51,60 244 | 93,51,59 245 | 94,49,59 246 | 95,56,58 247 | 96,56,57 248 | 97,52,58 249 | 98,54,57 250 | 99,54,58 251 | 100,50,58 252 | 101,50,58 253 | 102,54,57 254 | 103,48,60 255 | 104,48,59 256 | -------------------------------------------------------------------------------- /subproject/test/results/plot_required_bits.m: -------------------------------------------------------------------------------- 1 | function [] = plot_required_bits(filename) 2 | 3 | table = readtable(filename); 4 | fig = figure('Color','w'); 5 | hold on 6 | mul_fig_handle = plot(table{:,1}, table{:,2}); 7 | int_fig_handle = plot(table{:,1}, table{:,3}); 8 | legend([mul_fig_handle int_fig_handle], ... 9 | ["For multiplication", "For integer check"]); 10 | axis([min(table{:,1}) max(table{:,1}) min(min(table{:,2:3})) max(max(table{:,2:3}))]); 11 | xlabel('$e$', 'Interpreter', 'latex'); 12 | ylabel('Upper bound on required number of bits'); 13 | h = gca; 14 | h.YTick = 0:8:max(max(table{:,2:3})); 15 | h.YGrid = 'on'; 16 | hold off 17 | 18 | end -------------------------------------------------------------------------------- /subproject/test/source/constexpr.cpp: -------------------------------------------------------------------------------- 1 | #include "dragonbox/dragonbox.h" 2 | 3 | constexpr auto x = jkj::dragonbox::to_decimal(3.1415); 4 | static_assert(x.significand == 31415); 5 | static_assert(x.exponent == -4); 6 | static_assert(!x.is_negative); 7 | 8 | constexpr auto y = jkj::dragonbox::to_decimal(123.); 9 | static_assert(y.significand == 123); 10 | static_assert(y.exponent == 0); 11 | static_assert(!y.is_negative); 12 | 13 | int main() { 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /subproject/test/source/test_all_shorter_interval_cases.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2024 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "dragonbox/dragonbox_to_chars.h" 19 | #include "simple_dragonbox.h" 20 | #include "ryu/ryu.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | static void reference_implementation(float x, char* buffer) { f2s_buffered(x, buffer); } 28 | static void reference_implementation(double x, char* buffer) { d2s_buffered(x, buffer); } 29 | 30 | template 31 | static bool test_all_shorter_interval_cases_impl(TestTarget&& test_target) { 32 | using conversion_traits = jkj::dragonbox::default_float_bit_carrier_conversion_traits; 33 | using ieee754_format_info = typename conversion_traits::format; 34 | using carrier_uint = typename conversion_traits::carrier_uint; 35 | 36 | char buffer1[64]; 37 | char buffer2[64]; 38 | 39 | bool success = true; 40 | for (int e = ieee754_format_info::min_exponent; e <= ieee754_format_info::max_exponent; ++e) { 41 | // Compose a floating-point number 42 | carrier_uint br = carrier_uint(e - ieee754_format_info::exponent_bias) 43 | << ieee754_format_info::significand_bits; 44 | auto x = conversion_traits::carrier_to_float(br); 45 | 46 | test_target(x, buffer1); 47 | reference_implementation(x, buffer2); 48 | 49 | std::string_view view1(buffer1); 50 | std::string_view view2(buffer2); 51 | 52 | if (view1 != view2) { 53 | std::cout << "Error detected! [Reference = " << buffer2 << ", Dragonbox = " << buffer1 54 | << "]\n"; 55 | success = false; 56 | } 57 | } 58 | 59 | if (success) { 60 | std::cout << "All cases are verified.\n"; 61 | } 62 | else { 63 | std::cout << "Error detected.\n"; 64 | } 65 | return success; 66 | } 67 | 68 | int main() { 69 | bool success = true; 70 | 71 | std::cout << "[Testing all shorter interval cases for binary32...]\n"; 72 | success &= test_all_shorter_interval_cases_impl( 73 | [](auto x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); }); 74 | std::cout << "Done.\n\n\n"; 75 | 76 | std::cout << "[Testing all shorter interval cases for binary32 (compact cache)...]\n"; 77 | success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { 78 | jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); 79 | }); 80 | std::cout << "Done.\n\n\n"; 81 | 82 | std::cout << "[Testing all shorter interval cases for binary32 (simplified impl)...]\n"; 83 | success &= test_all_shorter_interval_cases_impl( 84 | [](auto x, char* buffer) { jkj::simple_dragonbox::to_chars(x, buffer); }); 85 | std::cout << "Done.\n\n\n"; 86 | 87 | std::cout 88 | << "[Testing all shorter interval cases for binary32 (simplified impl, compact cache)...]\n"; 89 | success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { 90 | jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); 91 | }); 92 | std::cout << "Done.\n\n\n"; 93 | 94 | std::cout << "[Testing all shorter interval cases for binary64...]\n"; 95 | success &= test_all_shorter_interval_cases_impl( 96 | [](auto x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); }); 97 | std::cout << "Done.\n\n\n"; 98 | 99 | std::cout << "[Testing all shorter interval cases for binary64 (compact cache)...]\n"; 100 | success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { 101 | jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); 102 | }); 103 | std::cout << "Done.\n\n\n"; 104 | 105 | std::cout << "[Testing all shorter interval cases for binary64 (simplified impl)...]\n"; 106 | success &= test_all_shorter_interval_cases_impl( 107 | [](auto x, char* buffer) { jkj::simple_dragonbox::to_chars(x, buffer); }); 108 | std::cout << "Done.\n\n\n"; 109 | 110 | std::cout 111 | << "[Testing all shorter interval cases for binary64 (simplified impl, compact cache)...]\n"; 112 | success &= test_all_shorter_interval_cases_impl([](auto x, char* buffer) { 113 | jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); 114 | }); 115 | std::cout << "Done.\n\n\n"; 116 | 117 | if (!success) { 118 | return -1; 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /subproject/test/source/uniform_random_test.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2024 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "dragonbox/dragonbox_to_chars.h" 19 | #include "simple_dragonbox.h" 20 | #include "random_float.h" 21 | #include "ryu/ryu.h" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | static void reference_implementation(float x, char* buffer) { f2s_buffered(x, buffer); } 28 | static void reference_implementation(double x, char* buffer) { d2s_buffered(x, buffer); } 29 | 30 | template 31 | static bool uniform_random_test(std::size_t number_of_tests, TestTarget&& test_target) { 32 | char buffer1[64]; 33 | char buffer2[64]; 34 | auto rg = generate_correctly_seeded_mt19937_64(); 35 | bool success = true; 36 | for (std::size_t test_idx = 0; test_idx < number_of_tests; ++test_idx) { 37 | auto x = uniformly_randomly_generate_general_float(rg); 38 | 39 | // Check if the output is identical to the reference implementation (Ryu). 40 | test_target(x, buffer1); 41 | reference_implementation(x, buffer2); 42 | 43 | std::string_view view1(buffer1); 44 | std::string_view view2(buffer2); 45 | 46 | if (view1 != view2) { 47 | std::cout << "Error detected! [Reference = " << buffer2 << ", Dragonbox = " << buffer1 48 | << "]\n"; 49 | success = false; 50 | } 51 | } 52 | 53 | if (success) { 54 | std::cout << "Uniform random test with " << number_of_tests << " examples succeeded.\n"; 55 | } 56 | else { 57 | std::cout << "Error detected.\n"; 58 | } 59 | 60 | return success; 61 | } 62 | 63 | int main() { 64 | constexpr std::size_t number_of_uniform_random_tests_float = 10000000; 65 | constexpr bool run_float = true; 66 | constexpr bool run_float_with_compact_cache = true; 67 | constexpr bool run_simple_float = true; 68 | constexpr bool run_simpl_float_with_compact_cache = true; 69 | 70 | constexpr std::size_t number_of_uniform_random_tests_double = 10000000; 71 | constexpr bool run_double = true; 72 | constexpr bool run_double_with_compact_cache = true; 73 | constexpr bool run_simple_double = true; 74 | constexpr bool run_simple_double_with_compact_cache = true; 75 | 76 | bool success = true; 77 | 78 | if (run_float) { 79 | std::cout << "[Testing uniformly randomly generated binary32 inputs...]\n"; 80 | success &= 81 | uniform_random_test(number_of_uniform_random_tests_float, [](auto x, char* buffer) { 82 | jkj::dragonbox::to_chars(x, buffer); 83 | }); 84 | std::cout << "Done.\n\n\n"; 85 | } 86 | if (run_float_with_compact_cache) { 87 | std::cout << "[Testing uniformly randomly generated binary32 inputs (compact cache)...]\n"; 88 | success &= 89 | uniform_random_test(number_of_uniform_random_tests_float, [](auto x, char* buffer) { 90 | jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); 91 | }); 92 | std::cout << "Done.\n\n\n"; 93 | } 94 | if (run_simple_float) { 95 | std::cout << "[Testing uniformly randomly generated binary32 inputs (simplified impl)...]\n"; 96 | success &= 97 | uniform_random_test(number_of_uniform_random_tests_float, [](auto x, char* buffer) { 98 | jkj::simple_dragonbox::to_chars(x, buffer); 99 | }); 100 | std::cout << "Done.\n\n\n"; 101 | } 102 | if (run_simpl_float_with_compact_cache) { 103 | std::cout << "[Testing uniformly randomly generated binary32 inputs (simplified impl, compact " 104 | "cache)...]\n"; 105 | success &= uniform_random_test(number_of_uniform_random_tests_float, [](auto x, 106 | char* buffer) { 107 | jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); 108 | }); 109 | std::cout << "Done.\n\n\n"; 110 | } 111 | if (run_double) { 112 | std::cout << "[Testing uniformly randomly generated binary64 inputs...]\n"; 113 | success &= uniform_random_test( 114 | number_of_uniform_random_tests_double, 115 | [](auto x, char* buffer) { jkj::dragonbox::to_chars(x, buffer); }); 116 | std::cout << "Done.\n\n\n"; 117 | } 118 | if (run_double_with_compact_cache) { 119 | std::cout << "[Testing uniformly randomly generated binary64 inputs (compact cache)...]\n"; 120 | success &= uniform_random_test( 121 | number_of_uniform_random_tests_double, [](auto x, char* buffer) { 122 | jkj::dragonbox::to_chars(x, buffer, jkj::dragonbox::policy::cache::compact); 123 | }); 124 | std::cout << "Done.\n\n\n"; 125 | } 126 | if (run_simple_double) { 127 | std::cout << "[Testing uniformly randomly generated binary64 inputs (simplified impl)...]\n"; 128 | success &= uniform_random_test( 129 | number_of_uniform_random_tests_double, 130 | [](auto x, char* buffer) { jkj::simple_dragonbox::to_chars(x, buffer); }); 131 | std::cout << "Done.\n\n\n"; 132 | } 133 | if (run_simple_double_with_compact_cache) { 134 | std::cout << "[Testing uniformly randomly generated binary64 inputs with (simplified impl, " 135 | "compact cache)...]\n"; 136 | success &= uniform_random_test(number_of_uniform_random_tests_double, [](auto x, 137 | char* buffer) { 138 | jkj::simple_dragonbox::to_chars(x, buffer, jkj::simple_dragonbox::policy::cache::compact); 139 | }); 140 | std::cout << "Done.\n\n\n"; 141 | } 142 | 143 | if (!success) { 144 | return -1; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /subproject/test/source/verify_compressed_cache.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "dragonbox/dragonbox.h" 19 | #include "big_uint.h" 20 | #include "rational_continued_fractions.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | // We are trying to verify that an appropriate right-shift of phi_k * 5^a plus one 28 | // can be used instead of phi_(a+k). (Here, phi_k and phi_(a+k) are supposed to be the "tilde" ones; 29 | // tilde is omitted for simplicity.) Since phi_k is defined in terms of ceiling, what we get from 30 | // phi_k * 5^a will be phi_(a+k) + (error) for some nonnegative (error). 31 | // 32 | // For correct multiplication, the margin for binary32 is at least 33 | // 2^64 * 5091154818982829 / 12349290596248284087255008291061760 = 7.60..., 34 | // so we are safe if the error is up to 7. 35 | // The margin for binary64 is at least 36 | // 2^128 * 723173431431821867556830303887 / 37 | // 18550103527668669801949286474444582643081334006759269899933694558208 38 | // = 13.26..., so we are safe if the error is up to 13. 39 | // 40 | // For correct integer checks, the case b > n_max is fine because the only condition on the 41 | // recovered cache is a lower bound which must be already true for phi_(a+k). 42 | // For the case b <= n_max, we only need to check the upper bound 43 | // (recovered_cache) < 2^(Q-beta) * a/b + 2^(q-beta)/(floor(nmax/b) * b), 44 | // so we check it manually for each e. 45 | 46 | template 47 | struct recovered_cache_t { 48 | CacheEntryType value; 49 | bool success; 50 | }; 51 | 52 | template 53 | bool verify_compressed_cache(GetCache&& get_cache, ConvertToBigUInt&& convert_to_big_uint, 54 | std::size_t max_diff_for_multiplication) { 55 | using format = typename FormatTraits::format; 56 | using cache_holder_type = jkj::dragonbox::compressed_cache_holder; 57 | using impl = jkj::dragonbox::detail::impl; 58 | 59 | jkj::unsigned_rational unit; 60 | auto n_max = jkj::big_uint::power_of_2(format::significand_bits + 2); 61 | for (int e = format::min_exponent - format::significand_bits; 62 | e <= format::max_exponent - format::significand_bits; ++e) { 63 | int const k = impl::kappa - jkj::dragonbox::detail::log::floor_log10_pow2(e); 64 | 65 | auto const real_cache = jkj::dragonbox::policy::cache::full.get_cache(k); 66 | 67 | auto const recovered_cache = get_cache(k); 68 | if (!recovered_cache.success) { 69 | std::cout << " (e = " << e << ")\n"; 70 | return false; 71 | } 72 | 73 | auto const rc = convert_to_big_uint(recovered_cache.value); 74 | auto const diff = rc - convert_to_big_uint(real_cache); 75 | if (diff != 0) { 76 | if (diff > max_diff_for_multiplication) { 77 | std::cout << "Multiplication is no longer valid. (e = " << e << ")\n"; 78 | return false; 79 | } 80 | 81 | // For the case b <= n_max, integer check might be no longer valid. 82 | int const beta = e + jkj::dragonbox::detail::log::floor_log2_pow10(k); 83 | 84 | // unit = 2^(e + k - 1) * 5^k = a/b. 85 | unit.numerator = 1; 86 | unit.denominator = 1; 87 | if (k >= 0) { 88 | unit.numerator = jkj::big_uint::pow(5, k); 89 | } 90 | else { 91 | unit.denominator = jkj::big_uint::pow(5, -k); 92 | } 93 | if (e + k - 1 >= 0) { 94 | unit.numerator *= jkj::big_uint::power_of_2(e + k - 1); 95 | } 96 | else { 97 | unit.denominator *= jkj::big_uint::power_of_2(-e - k + 1); 98 | } 99 | 100 | if (unit.denominator <= n_max) { 101 | // Check (recovered_cache) < 2^(Q-beta) * a/b + 2^(q-beta)/(floor(nmax/b) * b), 102 | // or equivalently, 103 | // b * (recovered_cache) - 2^(Q-beta) * a < 2^(q-beta) / floor(nmax/b). 104 | auto const left_hand_side = 105 | unit.denominator * rc - 106 | jkj::big_uint::power_of_2(cache_holder_type::cache_bits - beta) * unit.numerator; 107 | 108 | if (left_hand_side * (n_max / unit.denominator) >= 109 | jkj::big_uint::power_of_2(FormatTraits::carrier_bits - beta)) { 110 | std::cout << "Integer check is no longer valid. (e = " << e << ")\n"; 111 | 112 | // This exceptional case is carefully examined, so okay. 113 | if (std::is_same::value && e == -10) { 114 | // The exceptional case only occurs when n is exactly n_max. 115 | if (left_hand_side * ((n_max - 1) / unit.denominator) >= 116 | jkj::big_uint::power_of_2(FormatTraits::carrier_bits - beta)) { 117 | return false; 118 | } 119 | std::cout << " This case has been carefully addressed.\n\n"; 120 | } 121 | else { 122 | return false; 123 | } 124 | } 125 | } 126 | } 127 | } 128 | 129 | return true; 130 | } 131 | 132 | int main() { 133 | bool success = true; 134 | 135 | std::cout << "[Verifying compressed cache for binary32...]\n"; 136 | { 137 | using cache_holder_type = 138 | jkj::dragonbox::compressed_cache_holder; 139 | 140 | if (verify_compressed_cache>( 142 | [](int k) { 143 | return recovered_cache_t{ 144 | cache_holder_type::get_cache(k), true}; 145 | }, 146 | [](cache_holder_type::cache_entry_type value) { return jkj::big_uint{value}; }, 7)) { 147 | std::cout << "Verification succeeded. No error detected.\n\n"; 148 | } 149 | else { 150 | std::cout << "\n"; 151 | success = false; 152 | } 153 | } 154 | 155 | std::cout << "[Verifying compressed cache for binary64...]\n"; 156 | { 157 | using cache_holder_type = 158 | jkj::dragonbox::compressed_cache_holder; 159 | 160 | if (verify_compressed_cache>( 162 | [](int k) { 163 | // Compute the base index. 164 | auto const cache_index = int(std::uint_least32_t(k - cache_holder_type::min_k) / 165 | cache_holder_type::compression_ratio); 166 | auto const kb = 167 | cache_index * cache_holder_type::compression_ratio + cache_holder_type::min_k; 168 | auto const offset = k - kb; 169 | 170 | // Get the base cache. 171 | auto const base_cache = cache_holder_type::cache[cache_index]; 172 | 173 | if (offset != 0) { 174 | // Obtain the corresponding power of 5. 175 | auto const pow5 = cache_holder_type::pow5_table[offset]; 176 | 177 | // Compute the required amount of bit-shifts. 178 | using jkj::dragonbox::detail::log::floor_log2_pow10; 179 | auto const alpha = floor_log2_pow10(k) - floor_log2_pow10(kb) - offset; 180 | assert(alpha > 0 && alpha < 64); 181 | 182 | // Try to recover the real cache. 183 | using jkj::dragonbox::detail::wuint::umul128; 184 | auto recovered_cache = umul128(base_cache.high(), pow5); 185 | auto const middle_low = umul128(base_cache.low(), pow5); 186 | 187 | recovered_cache += middle_low.high(); 188 | 189 | auto const high_to_middle = std::uint_least64_t( 190 | (recovered_cache.high() << (64 - alpha)) & UINT64_C(0xffffffffffffffff)); 191 | auto const middle_to_low = std::uint_least64_t( 192 | (recovered_cache.low() << (64 - alpha)) & UINT64_C(0xffffffffffffffff)); 193 | 194 | recovered_cache = {(recovered_cache.low() >> alpha) | high_to_middle, 195 | ((middle_low.low() >> alpha) | middle_to_low)}; 196 | recovered_cache = {recovered_cache.high(), 197 | std::uint_least64_t(recovered_cache.low() + 1)}; 198 | 199 | if (recovered_cache.low() == 0) { 200 | std::cout 201 | << "Overflow detected - taking the ceil requires addition-with-carry"; 202 | return recovered_cache_t{ 203 | recovered_cache, false}; 204 | } 205 | else { 206 | return recovered_cache_t{ 207 | recovered_cache, true}; 208 | } 209 | } 210 | else { 211 | return recovered_cache_t{base_cache, true}; 212 | } 213 | }, 214 | [](cache_holder_type::cache_entry_type value) { 215 | return jkj::big_uint{value.low(), value.high()}; 216 | }, 217 | 13)) { 218 | std::cout << "Verification succeeded. No error detected.\n\n"; 219 | } 220 | else { 221 | std::cout << "\n"; 222 | success = false; 223 | } 224 | } 225 | 226 | std::cout << "Done.\n\n\n"; 227 | return success ? 0 : -1; 228 | } 229 | -------------------------------------------------------------------------------- /subproject/test/source/verify_fast_multiplication.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2022 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "dragonbox/dragonbox.h" 19 | #include "big_uint.h" 20 | #include "continued_fractions.h" 21 | 22 | #include 23 | 24 | template 25 | static bool verify_fast_multiplication_xz(CachePolicy&& cache_policy) { 26 | using format = typename FormatTraits::format; 27 | using carrier_uint = typename FormatTraits::carrier_uint; 28 | using impl = jkj::dragonbox::detail::impl; 29 | using cache_holder_type = 30 | typename std::decay::type::template cache_holder_type; 31 | using multiplication_traits = jkj::dragonbox::multiplication_traits< 32 | FormatTraits, typename cache_holder_type::cache_entry_type, cache_holder_type::cache_bits>; 33 | 34 | 35 | constexpr auto four_fl = (carrier_uint(1) << (impl::significand_bits + 2)) - 1; 36 | constexpr auto two_fr = (carrier_uint(1) << (impl::significand_bits + 1)) + 1; 37 | 38 | using jkj::dragonbox::detail::log::floor_log10_pow2_minus_log10_4_over_3; 39 | using jkj::dragonbox::detail::log::floor_log2_pow10; 40 | 41 | bool success = true; 42 | 43 | for (int e = impl::min_exponent + 1 - impl::significand_bits; 44 | e <= impl::max_exponent - impl::significand_bits; ++e) { 45 | 46 | // Compute k and beta. 47 | int const k = -floor_log10_pow2_minus_log10_4_over_3(e); 48 | int const beta = e + floor_log2_pow10(k); 49 | 50 | // Load cache. 51 | auto const cache = cache_policy.template get_cache(k); 52 | 53 | // Compute the endpoints using the fast method. 54 | auto x_fast = 55 | multiplication_traits::compute_left_endpoint_for_shorter_interval_case(cache, beta); 56 | auto z_fast = 57 | multiplication_traits::compute_right_endpoint_for_shorter_interval_case(cache, beta); 58 | 59 | // Precisely compute the endpoints. 60 | jkj::unsigned_rational precise_multiplier{1, 1}; 61 | if (k >= 0) { 62 | precise_multiplier.numerator = jkj::big_uint::pow(5, k); 63 | } 64 | else { 65 | precise_multiplier.denominator = jkj::big_uint::pow(5, -k); 66 | } 67 | if (e + k >= 0) { 68 | precise_multiplier.numerator *= jkj::big_uint::power_of_2(e + k); 69 | } 70 | else { 71 | precise_multiplier.denominator *= jkj::big_uint::power_of_2(-e - k); 72 | } 73 | 74 | auto x_exact = (four_fl * precise_multiplier.numerator) / (4 * precise_multiplier.denominator); 75 | auto z_exact = (two_fr * precise_multiplier.numerator) / (2 * precise_multiplier.denominator); 76 | 77 | if (x_exact != x_fast) { 78 | std::cout << "(e = " << e << ") left endpoint is not correct; computed = " << x_fast 79 | << "; true_value = " << x_exact[0] << "\n"; 80 | success = false; 81 | } 82 | if (z_exact != z_fast) { 83 | std::cout << "(e = " << e << ") right endpoint is not correct; computed = " << z_fast 84 | << "; true_value = " << z_exact[0] << "\n"; 85 | success = false; 86 | } 87 | } 88 | 89 | if (success) { 90 | std::cout << "All cases are verified.\n"; 91 | } 92 | else { 93 | std::cout << "Error detected.\n"; 94 | } 95 | 96 | return success; 97 | } 98 | 99 | template 100 | static bool verify_fast_multiplication_yru(CachePolicy&& cache_policy) { 101 | using format = typename FormatTraits::format; 102 | using impl = jkj::dragonbox::detail::impl; 103 | using cache_holder_type = 104 | typename std::decay::type::template cache_holder_type; 105 | 106 | bool success = true; 107 | 108 | for (int k = impl::min_k; k <= impl::max_k; ++k) { 109 | auto const cache = cache_policy.template get_cache(k); 110 | 111 | // Since Q - p - beta - 2 >= q, it suffices to check that the lower half of the cache is not 112 | // 0. 113 | auto const lower_half = [cache] { 114 | if constexpr (std::is_same_v) { 115 | return std::uint32_t(cache); 116 | } 117 | else { 118 | return cache.low(); 119 | } 120 | }(); 121 | 122 | // If the lower half is zero, we need to check if the cache is precise. 123 | if (lower_half == 0) { 124 | if (k < 0 || 125 | k > jkj::dragonbox::detail::log::floor_log5_pow2(cache_holder_type::cache_bits)) { 126 | std::cout << "(k = " << k << ") computation might be incorrect\n"; 127 | success = false; 128 | } 129 | } 130 | } 131 | 132 | if (success) { 133 | std::cout << "All cases are verified.\n"; 134 | } 135 | else { 136 | std::cout << "Error detected.\n"; 137 | } 138 | 139 | return success; 140 | } 141 | 142 | int main() { 143 | bool success = true; 144 | 145 | std::cout << "[Verifying fast computation of xi and zi for the shorter interval case " 146 | "with full cache (binary32)...]\n"; 147 | success &= verify_fast_multiplication_xz< 148 | jkj::dragonbox::ieee754_binary_traits>( 149 | jkj::dragonbox::policy::cache::full); 150 | std::cout << "Done.\n\n\n"; 151 | 152 | std::cout << "[Verifying fast computation of xi and zi for the shorter interval case " 153 | "with compressed cache (binary32)...]\n"; 154 | success &= verify_fast_multiplication_xz< 155 | jkj::dragonbox::ieee754_binary_traits>( 156 | jkj::dragonbox::policy::cache::compact); 157 | std::cout << "Done.\n\n\n"; 158 | 159 | std::cout << "[Verifying fast computation of yru for the shorter interval case " 160 | "with full cache (binary32)...]\n"; 161 | success &= verify_fast_multiplication_yru< 162 | jkj::dragonbox::ieee754_binary_traits>( 163 | jkj::dragonbox::policy::cache::full); 164 | std::cout << "Done.\n\n\n"; 165 | 166 | std::cout << "[Verifying fast computation of yru for the shorter interval case " 167 | "with compressed cache (binary32)...]\n"; 168 | success &= verify_fast_multiplication_yru< 169 | jkj::dragonbox::ieee754_binary_traits>( 170 | jkj::dragonbox::policy::cache::compact); 171 | std::cout << "Done.\n\n\n"; 172 | 173 | std::cout << "[Verifying fast computation of xi and zi for the shorter interval case " 174 | "with full cache (binary64)...]\n"; 175 | success &= verify_fast_multiplication_xz< 176 | jkj::dragonbox::ieee754_binary_traits>( 177 | jkj::dragonbox::policy::cache::full); 178 | std::cout << "Done.\n\n\n"; 179 | 180 | std::cout << "[Verifying fast computation of xi and zi for the shorter interval case " 181 | "with compressed cache (binary64)...]\n"; 182 | success &= verify_fast_multiplication_xz< 183 | jkj::dragonbox::ieee754_binary_traits>( 184 | jkj::dragonbox::policy::cache::compact); 185 | std::cout << "Done.\n\n\n"; 186 | 187 | std::cout << "[Verifying fast computation of yru for the shorter interval case " 188 | "with full cache (binary64)...]\n"; 189 | success &= verify_fast_multiplication_yru< 190 | jkj::dragonbox::ieee754_binary_traits>( 191 | jkj::dragonbox::policy::cache::full); 192 | std::cout << "Done.\n\n\n"; 193 | 194 | std::cout << "[Verifying fast computation of yru for the shorter interval case " 195 | "with compressed cache (binary64)...]\n"; 196 | success &= verify_fast_multiplication_yru< 197 | jkj::dragonbox::ieee754_binary_traits>( 198 | jkj::dragonbox::policy::cache::compact); 199 | std::cout << "Done.\n\n\n"; 200 | 201 | if (!success) { 202 | return -1; 203 | } 204 | } 205 | -------------------------------------------------------------------------------- /subproject/test/source/verify_log_computation.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Junekey Jeon 2 | // 3 | // The contents of this file may be used under the terms of 4 | // the Apache License v2.0 with LLVM Exceptions. 5 | // 6 | // (See accompanying file LICENSE-Apache or copy at 7 | // https://llvm.org/foundation/relicensing/LICENSE.txt) 8 | // 9 | // Alternatively, the contents of this file may be used under the terms of 10 | // the Boost Software License, Version 1.0. 11 | // (See accompanying file LICENSE-Boost or copy at 12 | // https://www.boost.org/LICENSE_1_0.txt) 13 | // 14 | // Unless required by applicable law or agreed to in writing, this software 15 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | // KIND, either express or implied. 17 | 18 | #include "big_uint.h" 19 | #include "dragonbox/dragonbox.h" 20 | 21 | #include 22 | #include 23 | 24 | static std::int_least32_t floor_log10_pow2_precise(std::int_least32_t e) { 25 | using namespace jkj::dragonbox::detail::log; 26 | bool is_negative; 27 | if (e < 0) { 28 | is_negative = true; 29 | e = -e; 30 | } 31 | else { 32 | is_negative = false; 33 | } 34 | 35 | auto power_of_2 = jkj::big_uint::power_of_2(std::size_t(e)); 36 | auto power_of_10 = jkj::big_uint(1); 37 | std::int_least32_t k = 0; 38 | while (power_of_10 <= power_of_2) { 39 | power_of_10.multiply_5(); 40 | power_of_10.multiply_2(); 41 | ++k; 42 | } 43 | 44 | return is_negative ? -k : k - 1; 45 | } 46 | 47 | static std::int_least32_t floor_log10_pow2_minus_log10_4_over_3_precise(std::int_least32_t e) { 48 | e -= 2; 49 | 50 | if (e < 0) { 51 | e = -e; 52 | auto power_of_2 = jkj::big_uint::power_of_2(std::size_t(e)); 53 | auto power_of_10_times_3 = jkj::big_uint(3); 54 | std::int_least32_t k = 0; 55 | while (power_of_10_times_3 < power_of_2) { 56 | power_of_10_times_3.multiply_5(); 57 | power_of_10_times_3.multiply_2(); 58 | ++k; 59 | } 60 | return -k; 61 | } 62 | else { 63 | auto power_of_2_times_3 = jkj::big_uint::power_of_2(std::size_t(e)) * 3; 64 | auto power_of_10 = jkj::big_uint(1); 65 | std::int_least32_t k = 0; 66 | while (power_of_10 <= power_of_2_times_3) { 67 | power_of_10.multiply_5(); 68 | power_of_10.multiply_2(); 69 | ++k; 70 | } 71 | return k - 1; 72 | } 73 | } 74 | 75 | static std::int_least32_t floor_log2_pow10_precise(std::int_least32_t e) { 76 | bool is_negative; 77 | if (e < 0) { 78 | is_negative = true; 79 | e = -e; 80 | } 81 | else { 82 | is_negative = false; 83 | } 84 | 85 | auto power_of_10 = jkj::big_uint(1); 86 | for (std::int_least32_t i = 0; i < e; ++i) { 87 | power_of_10.multiply_5(); 88 | power_of_10.multiply_2(); 89 | } 90 | 91 | auto k = std::int_least32_t(log2p1(power_of_10)); 92 | 93 | return is_negative ? -k : k - 1; 94 | } 95 | 96 | static std::int_least32_t floor_log5_pow2_precise(std::int_least32_t e) { 97 | bool is_negative; 98 | if (e < 0) { 99 | is_negative = true; 100 | e = -e; 101 | } 102 | else { 103 | is_negative = false; 104 | } 105 | 106 | auto power_of_2 = jkj::big_uint::power_of_2(std::size_t(e)); 107 | auto power_of_5 = jkj::big_uint(1); 108 | std::int_least32_t k = 0; 109 | while (power_of_5 <= power_of_2) { 110 | power_of_5.multiply_5(); 111 | ++k; 112 | } 113 | 114 | return is_negative ? -k : k - 1; 115 | } 116 | 117 | static std::int_least32_t floor_log5_pow2_minus_log5_3_precise(std::int_least32_t e) { 118 | if (e >= 0) { 119 | auto power_of_2 = jkj::big_uint::power_of_2(std::size_t(e)); 120 | auto power_of_5_times_3 = jkj::big_uint(3); 121 | std::int_least32_t k = 0; 122 | while (power_of_5_times_3 <= power_of_2) { 123 | power_of_5_times_3.multiply_5(); 124 | ++k; 125 | } 126 | return k - 1; 127 | } 128 | else { 129 | e = -e; 130 | auto power_of_2_times_3 = jkj::big_uint::power_of_2(std::size_t(e)) * 3; 131 | auto power_of_5 = jkj::big_uint(1); 132 | std::int_least32_t k = 0; 133 | while (power_of_5 < power_of_2_times_3) { 134 | power_of_5.multiply_5(); 135 | ++k; 136 | } 137 | return -k; 138 | } 139 | } 140 | 141 | struct verify_result { 142 | std::int_least32_t min_exponent; 143 | std::int_least32_t max_exponent; 144 | }; 145 | 146 | template 147 | static verify_result verify(std::string_view name, std::size_t tier, 148 | PreciseCalculator&& precise_calculator) { 149 | // Compute the maximum possible exponent for ensuring no overflow. 150 | using info = FastCalculatorInfo; 151 | using intermediate_type = decltype(info::multiply); 152 | using return_type = typename info::default_return_type; 153 | 154 | constexpr auto max_intermediate_value = std::min( 155 | std::numeric_limits::max(), 156 | intermediate_type( 157 | (std::min(static_cast(std::numeric_limits::max()), 158 | intermediate_type(std::numeric_limits::max() >> info::shift)) * 159 | (intermediate_type(1) << info::shift)) + 160 | ((intermediate_type(1) << info::shift) - 1))); 161 | constexpr auto no_overflow_max_exponent = 162 | (max_intermediate_value + std::min(info::subtract, intermediate_type(0))) / info::multiply; 163 | 164 | constexpr auto min_intermediate_value = 165 | std::max(std::numeric_limits::min(), 166 | intermediate_type( 167 | (std::max(static_cast(std::numeric_limits::min()), 168 | intermediate_type((std::numeric_limits::min() + 169 | (intermediate_type(1) << (info::shift + 1)) - 2) >> 170 | info::shift)) * 171 | (intermediate_type(1) << info::shift)) - 172 | ((intermediate_type(1) << info::shift) - 1))); 173 | constexpr auto no_overflow_min_exponent = 174 | (min_intermediate_value + std::max(info::subtract, intermediate_type(0))) / 175 | info::multiply; // (negative) / (positive) computes the ceiling in C/C++. 176 | 177 | 178 | verify_result result{std::int_least32_t(no_overflow_min_exponent), 179 | std::int_least32_t(no_overflow_max_exponent)}; 180 | 181 | bool reach_upper_bound = false; 182 | bool reach_lower_bound = false; 183 | for (std::int_least32_t e = 0; e <= std::max(-no_overflow_min_exponent, no_overflow_max_exponent); 184 | ++e) { 185 | if (!reach_upper_bound) { 186 | auto true_value = precise_calculator(e); 187 | auto computed_value = (e * info::multiply - info::subtract) >> info::shift; 188 | if (computed_value != true_value) { 189 | std::cout << " - error with positive e (" 190 | << "e: " << e << ", true value: " << true_value 191 | << ", computed value: " << computed_value << ")\n"; 192 | 193 | reach_upper_bound = true; 194 | result.max_exponent = e - 1; 195 | } 196 | } 197 | 198 | if (!reach_lower_bound) { 199 | auto true_value = precise_calculator(-e); 200 | auto computed_value = static_cast( 201 | static_cast((-e * info::multiply - info::subtract) >> info::shift)); 202 | if (computed_value != true_value) { 203 | std::cout << " - error with negative e (" 204 | << "e: " << -e << ", true value: " << true_value 205 | << ", computed value: " << computed_value << ")\n"; 206 | 207 | reach_lower_bound = true; 208 | result.min_exponent = -e + 1; 209 | } 210 | } 211 | 212 | if (reach_upper_bound && reach_lower_bound) { 213 | break; 214 | } 215 | } 216 | 217 | std::cout << name << " (tier: " << tier << ") is correct for e in [" << result.min_exponent << ", " 218 | << result.max_exponent << "]\n\n"; 219 | 220 | return result; 221 | } 222 | 223 | template