diff --git a/build.jam b/build.jam new file mode 100644 index 000000000..81d2c7580 --- /dev/null +++ b/build.jam @@ -0,0 +1,20 @@ +# Copyright René Ferdinand Rivera Morell 2023-2024 +# Copyright Matt Borland 2024 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +require-b2 5.2 ; + +project /boost/decimal + : common-requirements + include + ; + +explicit + [ alias boost_core ] + [ alias all : boost_decimal test ] + ; + +call-if : boost-library decimal + ; diff --git a/doc/Jamfile b/doc/Jamfile index 459f1723c..55a35f90c 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -1,10 +1,14 @@ # Copyright 2017, 2018 Peter Dimov +# Copyright 2024 Matt Borland # Distributed under the Boost Software License, Version 1.0. # https://www.boost.org/LICENSE_1_0.txt import asciidoctor ; -html decimal.html : decimal.adoc ; +html decimal.html : decimal.adoc + : /boost/boostlook//boostlook + decimal-docinfo-footer.html + ; install html_ : decimal.html : html ; diff --git a/doc/decimal/benchmarks.adoc b/doc/decimal/benchmarks.adoc index b2ff69d33..26001aec6 100644 --- a/doc/decimal/benchmarks.adoc +++ b/doc/decimal/benchmarks.adoc @@ -32,38 +32,70 @@ Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 35,581 +| 34,814 | 0.604 | `double` -| 58,848 +| 57,644 | 1.000 | `decimal32` -| 2,410,084 -| 40.954 +| 2,163,595 +| 37.534 | `decimal64` -| 4,233,175 -| 71.934 +| 2,633,923 +| 45.693 | `decimal128` -| 6,337,447 -| 107.692 +| 6,064,630 +| 105.208 | `decimal32_fast` -| 628,241 -| 10.676 +| 613,626 +| 10.645 | `decimal64_fast` -| 724,474 -| 12.311 +| 693,390 +| 12.029 | `decimal128_fast` -| 517,930 -| 8.801 +| 628,596 +| 10.905 | GCC `_Decimal32` | 893,375 -| 15.181 +| 15.498 | GCC `_Decimal64` | 496,127 -| 8.431 +| 8.607 | GCC `_Decimal128` | 1,143,636 -| 19.434 +| 19.840 +|=== + +=== x64 Windows Results + +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 182,707 +| 0.943 +| `double` +| 193,737 +| 1.000 +| `decimal32` +| 3,097,942 +| 15.990 +| `decimal64` +| 4,697,948 +| 24.249 +| `decimal128` +| 17,267,609 +| 89.129 +| `decimal32_fast` +| 809,847 +| 4.180 +| `decimal64_fast` +| 1,043,657 +| 5.387 +| `decimal128_fast` +| 888,053 +| 4.584 |=== === M1 macOS Results @@ -73,29 +105,29 @@ Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebr |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 146,976 -| 2.319 +| 131,803 +| 2.060 | `double` -| 63,382 +| 63,981 | 1.000 | `decimal32` -| 1,797,597 -| 28.361 +| 2,052,770 +| 32.084 | `decimal64` -| 2,799,376 -| 44.167 +| 2,701,290 +| 42.220 | `decimal128` -| 6,478,939 -| 102.220 +| 5,545,490 +| 86.674 | `decimal32_fast` -| 1,070,232 -| 16.885 +| 728,146 +| 11.381 | `decimal64_fast` -| 1,111,273 -| 17.533 +| 611,866 +| 9.563 | `decimal128_fast` -| 1,118,976 -| 17.654 +| 714,586 +| 11.169 |=== == Basic Operations @@ -103,6 +135,10 @@ Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebr The benchmark for these operations generates a random vector containing 20,000,000 elements and does operations `+`, `-`, `*`, `/` between `vec[i] and vec[i + 1]`. This is repeated 5 times to generate stable results. +As discussed in the design of the fast types the significand is stored in normalized form so that we do not have to worry about the effects of cohorts. +Unfortunately this means that `decimal128_fast` multiplication is always carried out internally at 256-bit size whereas `decimal128` contains heuristics in `operator*` to avoid 256-bit multiplication when it is not needed (i.e. the resultant significand is less than or equal to 128-bits). +This causes multiplication of `decimal128_fast` to be ~1.72x slower than `decimal128`, but all other operators leave us with a geometric average runtime under 1.00 for `decimal128_fast` / `decimal128` so we accept this tradeoff. + === x64 Linux Results Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 @@ -112,38 +148,38 @@ Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 54,566 -| 1.077 +| 55,811 +| 1.062 | `double` -| 50,640 +| 52,531 | 1.000 | `decimal32` -| 3,639,957 -| 71.879 +| 2,653,456 +| 50.512 | `decimal64` -| 4,172,318 -| 82.392 +| 3,254,833 +| 61.960 | `decimal128` -| 10,936,595 -| 215.968 +| 10,479,050 +| 199.483 | `decimal32_fast` -| 1,148,249 -| 22.675 +| 1,371,022 +| 26.100 | `decimal64_fast` -| 1,149,203 -| 22.694 +| 1,370,192 +| 26.083 | `decimal128_fast` -| 7,424,598 -| 146.615 +| 7,197,718 +| 137.018 | GCC `_Decimal32` | 2,997,658 -| 50.939 +| 57.065 | GCC `_Decimal64` | 2,129,898 -| 36.193 +| 40.546 | GCC `_Decimal128` | 3,056,979 -| 51.947 +| 58.194 |=== ==== Subtraction @@ -151,38 +187,38 @@ Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 48,654 -| 0.912 +| 53,362 +| 1.083 | `double` -| 53,348 +| 49,242 | 1.000 | `decimal32` -| 2,850,709 -| 53.436 +| 2,054,535 +| 41.723 | `decimal64` -| 3,493,936 -| 65.493 +| 2,507,709 +| 50.926 | `decimal128` -| 10,492,728 -| 196.685 +| 5,554,139 +| 112.793 | `decimal32_fast` -| 1,012,199 -| 18.974 +| 1,050,225 +| 21.328 | `decimal64_fast` -| 1,055,476 -| 19.785 +| 1,048,560 +| 21.294 | `decimal128_fast` -| 2,114,185 -| 39.630 +| 2,073,580 +| 42.110 | GCC `_Decimal32` | 2,006,964 -| 37.620 +| 40.757 | GCC `_Decimal64` | 1,324,796 -| 24.833 +| 26.904 | GCC `_Decimal128` | 2,783,553 -| 52.177 +| 56.528 |=== ==== Multiplication @@ -190,38 +226,38 @@ Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 53,405 -| 1.101 +| 53,469 +| 1.093 | `double` -| 48,497 +| 48,903 | 1.000 | `decimal32` -| 2,708,779 -| 55.855 +| 1,993,989 +| 40.774 | `decimal64` -| 2,761,465 -| 56.941 +| 2,766,602 +| 56.573 | `decimal128` -| 8,509,678 -| 175.468 +| 4,796,346 +| 98.079 | `decimal32_fast` -| 451,679 -| 9.313 +| 1,117,727 +| 22.856 | `decimal64_fast` -| 777,927 -| 16.041 +| 1,369,834 +| 28.011 | `decimal128_fast` -| 13,970,509 -| 288.070 +| 8,139,518 +| 166.442 | GCC `_Decimal32` | 2,507,998 -| 51.714 +| 51.285 | GCC `_Decimal64` | 2,414,864 -| 49.794 +| 49.381 | GCC `_Decimal128` | 6,248,956 -| 128.852 +| 127.783 |=== ==== Division @@ -229,72 +265,72 @@ Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 58,955 -| 0.755 +| 59,003 +| 0.756 | `double` -| 78,046 +| 78,078 | 1.000 | `decimal32` -| 2,907,134 -| 37.249 +| 2,250,186 +| 28.820 | `decimal64` -| 3,464,841 -| 44.394 +| 2,816,014 +| 36.067 | `decimal128` -| 18,202,742 -| 233.231 +| 18,320,634 +| 234.645 | `decimal32_fast` -| 1,092,346 -| 13.996 +| 1,123,428 +| 14.389 | `decimal64_fast` -| 1,207,648 -| 15.474 +| 1,258,004 +| 16.112 | `decimal128_fast` -| 1,208,184 -| 15.480 +| 1,243,024 +| 15.920 | GCC `_Decimal32` | 5,002,197 -| 64.093 +| 64.067 | GCC `_Decimal64` | 2,961,731 -| 37.900 +| 37.933 | GCC `_Decimal128` | 10,095,995 -| 129.360 +| 129.307 |=== -=== M1 macOS Results +=== x64 Windows Results -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 ==== Addition |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 16,685 -| 0.955 +| 67,019 +| 0.974 | `double` -| 17,476 +| 68,820 | 1.000 | `decimal32` -| 2,528,095 -| 144.661 +| 2,994,405 +| 43.511 | `decimal64` -| 2,713,507 -| 155.270 +| 4,531,755 +| 65.849 | `decimal128` -| 11,969,714 -| 684.923 +| 25,209,554 +| 366.311 | `decimal32_fast` -| 1,423,277 -| 81.442 +| 2,066,728 +| 30.031 | `decimal64_fast` -| 1,280,409 -| 73.267 +| 3,667,169 +| 53.286 | `decimal128_fast` -| 6,047,499 -| 346.046 +| 11,213,280 +| 162.936 |=== ==== Subtraction @@ -302,29 +338,29 @@ Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and home |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 16,302 -| 1.045 +| 60,912 +| 0.976 | `double` -| 17,033 +| 62,409 | 1.000 | `decimal32` -| 2,010,525 -| 118.037 +| 3,132,613 +| 50.194 | `decimal64` -| 2,237,729 -| 131.376 +| 3,864,498 +| 61.992 | `decimal128` -| 6,907,396 -| 405.530 +| 17,210,173 +| 275.764 | `decimal32_fast` -| 1,378,448 -| 80.928 +| 2,028,429 +| 32.502 | `decimal64_fast` -| 1,276,731 -| 74.956 +| 3,017,419 +| 48.349 | `decimal128_fast` -| 2,970,586 -| 174.401 +| 5,557,846 +| 89.055 |=== ==== Multiplication @@ -332,29 +368,29 @@ Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and home |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 16,499 -| 0.926 +| 60,742 +| 0.969 | `double` -| 17,821 +| 62,658 | 1.000 | `decimal32` -| 1,951,504 -| 109.506 +| 2,029,689 +| 32.393 | `decimal64` -| 2,480,528 -| 139.191 +| 8,805,524 +| 140.533 | `decimal128` -| 14,360,630 -| 805.826 +| 15,519,053 +| 247.689 | `decimal32_fast` -| 630,355 -| 35.371 +| 1,573,280 +| 25.109 | `decimal64_fast` -| 987,703 -| 55.424 +| 7,650,156 +| 122.094 | `decimal128_fast` -| 12,573,178 -| 705.526 +| 16,874,890 +| 269.317 |=== ==== Division @@ -362,224 +398,648 @@ Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and home |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 20,267 -| 0.841 +| 75,437 +| 0.936 | `double` -| 24,111 +| 80,559 | 1.000 | `decimal32` -| 1,757,506 -| 72.892 +| 2,832,016 +| 45.198 | `decimal64` -| 3,496,913 -| 145.033 +| 11,640,789 +| 185.783 | `decimal128` -| 20,017,989 -| 830.243 +| 32,470,044 +| 518.211 | `decimal32_fast` -| 846,727 -| 35.118 +| 1,660,332 +| 26.498 | `decimal64_fast` -| 2,484,985 -| 103.064 +| 11,266,972 +| 179.817 | `decimal128_fast` -| 2,490,175 -| 103.280 +| 11,201,820 +| 178.777 |=== -//// -These are not available for the built-ins so not deleting but also not incorporating +=== M1 macOS Results -== Selected Special Functions +Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 -The benchmark for these operations generates a random vector containing 2,000,000 elements and does operations `+`, `-`, `*`, `/` between `vec[i] and vec[i + 1]`. -This is repeated 5 times to generate stable results. +==== Addition -=== M1 macOS Results +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 43,056 +| 1.295 +| `double` +| 33,238 +| 1.000 +| `decimal32` +| 3,146,032 +| 94.652 +| `decimal64` +| 2,963,788 +| 89.169 +| `decimal128` +| 10,125,221 +| 304.628 +| `decimal32_fast` +| 1,685,360 +| 50.706 +| `decimal64_fast` +| 1,886,022 +| 56.743 +| `decimal128_fast` +| 6,893,049 +| 207.385 +|=== -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +==== Subtraction + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 43,013 +| 1.295 +| `double` +| 33,204 +| 1.000 +| `decimal32` +| 2,385,896 +| 71.586 +| `decimal64` +| 2,759,536 +| 83.108 +| `decimal128` +| 5,560,295 +| 167.459 +| `decimal32_fast` +| 1,228,630 +| 37.002 +| `decimal64_fast` +| 1,312,815 +| 39.538 +| `decimal128_fast` +| 2,869,005 +| 86.405 +|=== + +==== Multiplication + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 42,634 +| 1.293 +| `double` +| 32,970 +| 1.000 +| `decimal32` +| 2,826,351 +| 85.725 +| `decimal64` +| 3,268,243 +| 99.128 +| `decimal128` +| 4,654,643 +| 141.178 +| `decimal32_fast` +| 1,614,365 +| 48.965 +| `decimal64_fast` +| 2,417,646 +| 73.329 +| `decimal128_fast` +| 8,017,934 +| 243.189 +|=== -==== SQRT +==== Division |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 2021 -| 0.626 +| 46,030 +| 1.351 | `double` -| 3229 +| 34,078 | 1.000 | `decimal32` -| 4,826,066 -| 1494.601 +| 2,649,922 +| 77.760 | `decimal64` -| 7,780,637 -| 2409.612 +| 3,721,028 +| 109.192 | `decimal128` -| 100,269,145 -| 31052.693 +| 19,559,739 +| 573.970 +| `decimal32_fast` +| 1,436,099 +| 42.142 +| `decimal64_fast` +| 2,593,573 +| 76.107 +| `decimal128_fast` +| 2,594,426 +| 76.132 |=== == `` -For all the following the results compare against https://github.com/boostorg/charconv[boost.charconv] for 10'000'000 conversions. +Parsing and serializing number exactly is one of the key features of decimal floating point types, so we must compare the performance of ``. For all the following the results compare against STL provided `` for 20,000,000 conversions. +Since `` is fully implemented in software for each type the performance gap between built-in `float` and `double` vs `decimal32` and `decimal64` is significantly smaller (or the decimal performance is better) than the hardware vs software performance gap seen above for basic operations. -=== `from_chars` general +To run these benchmarks yourself you will need a compiler with complete implementation of `` and to run the benchmarks under C++17 or higher. +At the time of writing this is limited to: -==== M1 macOS Results +- GCC 11 or newer +- MSVC 19.24 or newer -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +These benchmarks are automatically disabled if your compiler does not provide feature complete `` or if the language standard is set to C++14. + +=== `from_chars` + +==== `from_chars` general + +===== x64 Linux Results + +Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 235,816 -| 0.953 +| 10,308,818 +| 0.551 | `double` -| 247,307 +| 18,692,513 | 1.000 | `decimal32` -| 366,682 -| 1.483 +| 3,301,003 +| 0.177 | `decimal64` -| 485,965 -| 1.965 -// Decimal128 was two orders of magnitude faster. I expect an issue -//| `decimal128` -//| 275,779,340 -//| 73267.60 +| 4,580,001 +| 0.245 +| `decimal32_fast` +| 3,321,788 +| 0.178 +| `decimal64_fast` +| 4,591,311 +| 0.246 |=== -NOTE: `decimal128` is currently absent due to results showing it is 2 orders of magnitude faster than the others. -This should not be the case so will be investigated. +===== x64 Windows Results -=== `from_chars` scientific +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 -==== M1 macOS Results +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 8,577,201 +| 0.410 +| `double` +| 20,903,459 +| 1.000 +| `decimal32` +| 4,602,771 +| 0.220 +| `decimal64` +| 5,332,730 +| 0.255 +| `decimal32_fast` +| 3,932,622 +| 0.188 +| `decimal64_fast` +| 5,614,476 +| 0.269 +|=== -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +===== M1 macOS Results + +Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebrew GCC 14.2.0 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 2,556,533 +| 0.965 +| `double` +| 2,648,485 +| 1.000 +| `decimal32` +| 3,201,545 +| 1.209 +| `decimal64` +| 4,775,487 +| 1.803 +| `decimal32_fast` +| 3,196,724 +| 1.207 +| `decimal64_fast` +| 4,762,636 +| 1.798 +|=== + +==== `from_chars` scientific + +===== x64 Linux Results + +Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 241,893 -| 0.975 +| 10,363,219 +| 0.554 | `double` -| 247,975 +| 18,677,179 | 1.000 | `decimal32` -| 358,189 -| 1.444 +| 3,296,877 +| 0.177 | `decimal64` -| 477,574 +| 4,500,127 +| 0.241 +| `decimal32_fast` +| 3,381,651 +| 0.181 +| `decimal64_fast` +| 4,496,194 +| 0.241 +|=== + +===== x64 Windows Results + +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 8,170,079 +| 0.439 +| `double` +| 18,626,905 +| 1.000 +| `decimal32` +| 3,927,882 +| 0.211 +| `decimal64` +| 5,668,246 +| 0.304 +| `decimal32_fast` +| 3,904,457 +| 0.210 +| `decimal64_fast` +| 5,302,174 +| 0.285 +|=== + +===== M1 macOS Results + +Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebrew GCC 14.2.0 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 2,651,707 +| 0.986 +| `double` +| 2,690,166 +| 1.000 +| `decimal32` +| 3,153,821 +| 1.172 +| `decimal64` +| 4,726,009 | 1.926 -// Decimal128 was two orders of magnitude faster. I expect an issue -//| `decimal128` -//| 275,779,340 -//| 73267.60 +| `decimal32_fast` +| 4,726,009 +| 1.757 +| `decimal64_fast` +| 4,693,387 +| 1.747 |=== -NOTE: `decimal128` is currently absent due to results showing it is 2 orders of magnitude faster than the others. -This should not be the case so will be investigated. +=== `to_chars` -=== `to_chars` general shortest representation +==== `to_chars` general shortest representation -==== M1 macOS Results +===== x64 Linux Results -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 316,300 -| 1.040 +| 2,839,146 +| 0.841 | `double` -| 304,272 +| 3,374,946 | 1.000 | `decimal32` -| 406,053 -| 1.335 +| 4,253,304 +| 1.260 | `decimal64` -| 678,451 -| 2.230 -| `decimal128` -| 6,309,346 -| 20.736 +| 6,885,679 +| 2.040 +| `decimal32_fast` +| 4,453,957 +| 1.320 +| `decimal64_fast` +| 7,827,910 +| 2.319 |=== -=== `to_chars` general 6-digits of precision +===== x64 Windows Results -==== M1 macOS Results +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 3,108,053 +| 0.823 +| `double` +| 3,774,811 +| 1.000 +| `decimal32` +| 6,127,529 +| 1.623 +| `decimal64` +| 8,582,256 +| 2.273 +| `decimal32_fast` +| 7,639,470 +| 2.024 +| `decimal64_fast` +| 11,564,222 +| 3.064 +|=== + +===== M1 macOS Results + +Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebrew GCC 14.2.0 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 323,867 -| 0.967 +| 2,917,920 +| 0.849 | `double` -| 334,989 +| 3,435,671 | 1.000 | `decimal32` -| 409,608 -| 1.223 +| 4,636,747 +| 1.350 | `decimal64` -| 702,339 -| 2.097 -| `decimal128` -| 6,305,521 -| 18.823 +| 5,680,800 +| 1.653 +| `decimal32_fast` +| 4,675,951 +| 1.361 +| `decimal64_fast` +| 5,900,272 +| 1.717 |=== -=== `to_chars` scientific shortest representation +==== `to_chars` general 6-digits of precision -==== M1 macOS Results +===== x64 Linux Results -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 286,330 -| 1.011 +| 5,226,353 +| 0.957 | `double` -| 283,287 +| 5,458,987 | 1.000 | `decimal32` -| 290,117 -| 1.024 +| 3,782,692 +| 0.693 | `decimal64` -| 499,637 -| 1.764 -| `decimal128` -| 3,096,910 -| 10.932 +| 5,368,162 +| 0.983 +| `decimal32_fast` +| 3,611,498 +| 0.662 +| `decimal64_fast` +| 6,025,340 +| 1.104 |=== -=== `to_chars` scientific 6-digits of precision +===== x64 Windows Results -==== M1 macOS Results +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 -Run using a Macbook pro with M1 pro chipset running macOS Sonoma 14.4.1 and homebrew Clang 18.1.4 +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 5,873,775 +| 0.929 +| `double` +| 6,322,448 +| 1.000 +| `decimal32` +| 5,493,981 +| 0.869 +| `decimal64` +| 7,849,419 +| 1.215 +| `decimal32_fast` +| 6,516,633 +| 1.031 +| `decimal64_fast` +| 8,065,516 +| 1.276 +|=== + +===== M1 macOS Results + +Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebrew GCC 14.2.0 |=== | Type | Runtime (us) | Ratio to `double` | `float` -| 258,710 -| 0.809 +| 6,320,719 +| 0.962 | `double` -| 319,676 +| 6,572,846 | 1.000 | `decimal32` -| 292,250 -| 0.914 +| 4,133,466 +| 0.629 | `decimal64` -| 516,399 -| 1.615 -| `decimal128` -| 3,108,380 -| 9.724 +| 6,106,989 +| 0.929 +| `decimal32_fast` +| 3,458,534 +| 0.526 +| `decimal64_fast` +| 5,997,442 +| 0.912 |=== -//// +==== `to_chars` scientific shortest representation + +===== x64 Linux Results + +Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 2,835,528 +| 0.849 +| `double` +| 3,338,216 +| 1.000 +| `decimal32` +| 2,887,451 +| 0.865 +| `decimal64` +| 5,218,195 +| 1.563 +| `decimal32_fast` +| 3,033,115 +| 0.909 +| `decimal64_fast` +| 6,103,323 +| 1.828 +|=== + +===== x64 Windows Results + +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 3,047,827 +| 0.814 +| `double` +| 3,742,344 +| 1.000 +| `decimal32` +| 4,103,661 +| 1.097 +| `decimal64` +| 6,721,570 +| 1.796 +| `decimal32_fast` +| 4,542,470 +| 1.214 +| `decimal64_fast` +| 8,694,813 +| 2.323 +|=== + +===== M1 macOS Results + +Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebrew GCC 14.2.0 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 2,814,527 +| 0.817 +| `double` +| 3,442,930 +| 1.000 +| `decimal32` +| 3,048,663 +| 0.885 +| `decimal64` +| 3,786,216 +| 1.010 +| `decimal32_fast` +| 2,813,360 +| 0.817 +| `decimal64_fast` +| 4,082,146 +| 1.186 +|=== + +==== `to_chars` scientific 6-digits of precision + +===== x64 Linux Results + +Run using an Intel i9-11900k chipset running RHEL 9.4 and GCC 11.4.1-3 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 4,686,460 +| 0.938 +| `double` +| 4,993,886 +| 1.000 +| `decimal32` +| 2,919,727 +| 0.585 +| `decimal64` +| 4,157,802 +| 0.833 +| `decimal32_fast` +| 3,052,228 +| 0.611 +| `decimal64_fast` +| 5,597,538 +| 1.121 +|=== + +===== x64 Windows Results + +Run using an Intel i9-11900k chipset running Windows 11 and Visual Studio 17.11.4 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 4,734,517 +| 0.970 +| `double` +| 4,880,384 +| 1.000 +| `decimal32` +| 3,879,496 +| 0.795 +| `decimal64` +| 5,614,452 +| 1.150 +| `decimal32_fast` +| 4,445,619 +| 0.911 +| `decimal64_fast` +| 7,375,520 +| 1.511 +|=== + +===== M1 macOS Results + +Run using a Macbook pro with M1 pro chipset running macOS Sonoma 15.0 and homebrew GCC 14.2.0 + +|=== +| Type | Runtime (us) | Ratio to `double` +| `float` +| 5,636,010 +| 0.952 +| `double` +| 5,922,301 +| 1.000 +| `decimal32` +| 3,048,058 +| 0.515 +| `decimal64` +| 5,140,604 +| 0.868 +| `decimal32_fast` +| 2,821,707 +| 0.476 +| `decimal64_fast` +| 5,525,549 +| 0.933 +|=== diff --git a/doc/decimal/conversions.adoc b/doc/decimal/conversions.adoc index 6a199b7e7..82ee90ae3 100644 --- a/doc/decimal/conversions.adoc +++ b/doc/decimal/conversions.adoc @@ -5,11 +5,12 @@ https://www.boost.org/LICENSE_1_0.txt //// [#conversions] -= Fast Type Conversions += Bit Conversions :idprefix: conversions_ -Since we have non-IEEE 754 compliant types we offer a set of functions that allow their conversion to and from the IEEE 754 compliant BID layout. -These functions allow lossless conversion with more compact storage. +IEEE 754 specifies two different encodings for decimal floating point types: Binary Integer Significand Field (BID), and Densely Packed Decimal Significand Field (DPD). +Internally this library is implemented in the BID format for the IEEE-754 compliant types. +Should the user want to capture the bit format in BID or convert to DPD we offer a family of conversion functions: `to_bid`, `from_bid`, `to_dpd`, and `from_dpd` that allow conversion to or from the bit strings regardless of encoding. [source, c++] ---- @@ -26,27 +27,87 @@ struct uint128 } // namespace detail -BOOST_DECIMAL_CXX20_CONSTEXPR std::uint32_t to_bid_d32(decimal32 val) noexcept; +// ----- BID Conversions ----- -BOOST_DECIMAL_CXX20_CONSTEXPR std::uint32_t to_bid_d32f(decimal32_fast val) noexcept; +constexpr std::uint32_t to_bid_d32(decimal32 val) noexcept; -BOOST_DECIMAL_CXX20_CONSTEXPR std::uint64_t to_bid_d64(decimal64 val) noexcept; +constexpr decimal32 from_bid_d32(std::uint32_t bits) noexcept; -BOOST_DECIMAL_CXX20_CONSTEXPR std::uint64_t to_bid_d64f(decimal64_fast val) noexcept; +constexpr std::uint32_t to_bid_d32f(decimal32_fast val) noexcept; -BOOST_DECIMAL_CXX20_CONSTEXPR detail::uint128 to_bid_d128(decimal128 val) noexcept; +constexpr decimal32_fast from_bid_d32f(std::uint32_t bits) noexcept; + +constexpr std::uint64_t to_bid_d64(decimal64 val) noexcept; + +constexpr decimal64 from_bid_d64(std::uint64_t bits) noexcept; + +constexpr std::uint64_t to_bid_d64f(decimal64_fast val) noexcept; + +constexpr decimal64_fast from_bid_d64f(std::uint64_t bits) noexcept; + +constexpr detail::uint128 to_bid_d128(decimal128 val) noexcept; + +constexpr decimal128 from_bid_d128(detail::uint128 bits) noexcept; + +// Automatic detection if your platform has built-in unsigned __int128 or not to enable/disable the overload +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr decimal128 from_bid_d128(unsigned __int128 bits) noexcept; + +#endif // BOOST_DECIMAL_HAS_INT128 + +constexpr detail::uint128 to_bid_d128f(decimal128_fast val) noexcept; + +constexpr decimal128 from_bid_d128f(detail::uint128 bits) noexcept; + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr decimal128 from_bid_d128f(unsigned __int128 bits) noexcept; + +#endif // BOOST_DECIMAL_HAS_INT128 template -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(T val) noexcept; +constexpr auto to_bid(T val) noexcept; template -BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(std::uint32_t bits) noexcept; +constexpr T from_bid(std::uint32_t bits) noexcept; template -BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(std::uint64_t bits) noexcept; +constexpr T from_bid(std::uint64_t bits) noexcept; template -BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(detail::uint128 bits) noexcept; +constexpr T from_bid(detail::uint128 bits) noexcept; + +// ----- DPD Conversions ----- + +constexpr std::uint32_t to_dpd_d32(decimal32 val) noexcept; + +constexpr std::uint32_t to_dpd_d32f(decimal32_fast val) noexcept; + +constexpr std::uint64_t to_dpd_d64(decimal64 val) noexcept; + +constexpr std::uint64_t to_dpd_d64f(decimal64_fast val) noexcept; + +constexpr detail::uint128 to_dpd_d128(decimal128 val) noexcept; + +constexpr detail::uint128 to_dpd_d128f(decimal128_fast val) noexcept; + +template +constexpr auto to_dpd(T val) noexcept; + +template +constexpr T from_dpd(std::uint32_t bits) noexcept; + +template +constexpr T from_dpd(std::uint64_t bits) noexcept; + +template +constexpr T from_dpd(detail::uint128 bits) noexcept; + +#ifdef BOOST_DECIMAL_HAS_INT128 +template +constexpr T from_dpd(unsigned __int128 bits) noexcept; +#endif } // namespace decimal } // namespace boost diff --git a/doc/decimal/examples.adoc b/doc/decimal/examples.adoc index fdee4e150..167a5c225 100644 --- a/doc/decimal/examples.adoc +++ b/doc/decimal/examples.adoc @@ -92,6 +92,12 @@ int main() return 0; } ---- +Output: +---- + Initial Value: 0.25 +Returned Value: 0.25 +---- + == Rounding Mode [source, c++] @@ -179,3 +185,35 @@ int main() return 0; } ---- + +== Bit Conversions +[source, c++] +---- +#include +#include +#include + +using namespace boost::decimal; + +int main() +{ + const decimal32_fast fast_type {5}; + const std::uint32_t BID_bits {to_bid(fast_type)}; + const std::uint32_t DPD_bits {to_dpd(fast_type)}; + + std::cout << std::hex + << "BID format: " << BID_bits << '\n' + << "DPD format: " << DPD_bits << std::endl; + + const decimal32 bid_decimal {from_bid(BID_bits)}; + const decimal32 dpd_decimal {from_dpd(DPD_bits)}; + + return !(bid_decimal == dpd_decimal); +} +---- +Output: +---- +BID format: 31fc4b40 +DPD format: 35f00000 +---- + diff --git a/examples/bit_conversions.cpp b/examples/bit_conversions.cpp new file mode 100644 index 000000000..6893166e4 --- /dev/null +++ b/examples/bit_conversions.cpp @@ -0,0 +1,25 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +using namespace boost::decimal; + +int main() +{ + const decimal32_fast fast_type {5}; + const std::uint32_t BID_bits {to_bid(fast_type)}; + const std::uint32_t DPD_bits {to_dpd(fast_type)}; + + std::cout << std::hex + << "BID format: " << BID_bits << '\n' + << "DPD format: " << DPD_bits << std::endl; + + const decimal32 bid_decimal {from_bid(BID_bits)}; + const decimal32 dpd_decimal {from_dpd(DPD_bits)}; + + return !(bid_decimal == dpd_decimal); +} diff --git a/examples/charconv.cpp b/examples/charconv.cpp index 867b55a62..d79df3ec3 100644 --- a/examples/charconv.cpp +++ b/examples/charconv.cpp @@ -18,7 +18,7 @@ int main() *r_to.ptr = '\0'; decimal64 return_value; - auto r_from = from_chars(buffer, buffer + std::strlen(buffer), return_value); + BOOST_DECIMAL_ATTRIBUTE_UNUSED auto r_from = from_chars(buffer, buffer + std::strlen(buffer), return_value); assert(r_from); assert(val == return_value); diff --git a/examples/literals.cpp b/examples/literals.cpp index 6361e26ff..3f6195421 100644 --- a/examples/literals.cpp +++ b/examples/literals.cpp @@ -17,8 +17,8 @@ int main() { using namespace boost::decimal; - const auto pi_32 {"3.141592653589793238"_DF}; - const auto pi_64 {"3.141592653589793238"_DD}; + BOOST_DECIMAL_ATTRIBUTE_UNUSED const auto pi_32 {"3.141592653589793238"_DF}; + BOOST_DECIMAL_ATTRIBUTE_UNUSED const auto pi_64 {"3.141592653589793238"_DD}; assert(float_equal(pi_32, static_cast(pi_64))); // Explicit conversion between decimal types assert(float_equal(pi_32, boost::decimal::numbers::pi_v)); // Constants available in numbers namespace diff --git a/examples/rounding_mode.cpp b/examples/rounding_mode.cpp index a994f36ad..63b79b937 100644 --- a/examples/rounding_mode.cpp +++ b/examples/rounding_mode.cpp @@ -7,9 +7,9 @@ int main() { - auto default_rounding_mode = boost::decimal::fegetround(); // Default is fe_dec_to_nearest_from_zero + BOOST_DECIMAL_ATTRIBUTE_UNUSED auto default_rounding_mode = boost::decimal::fegetround(); // Default is fe_dec_to_nearest_from_zero - auto new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_to_nearest); + BOOST_DECIMAL_ATTRIBUTE_UNUSED auto new_rounding_mode = boost::decimal::fesetround(boost::decimal::rounding_mode::fe_dec_to_nearest); assert(default_rounding_mode != new_rounding_mode); diff --git a/include/boost/decimal.hpp b/include/boost/decimal.hpp index eea3b535e..b878eb032 100644 --- a/include/boost/decimal.hpp +++ b/include/boost/decimal.hpp @@ -40,6 +40,7 @@ #include #include #include +#include #if defined(__clang__) && !defined(__GNUC__) # pragma clang diagnostic pop diff --git a/include/boost/decimal/bid_conversion.hpp b/include/boost/decimal/bid_conversion.hpp index cb84317b1..683c87682 100644 --- a/include/boost/decimal/bid_conversion.hpp +++ b/include/boost/decimal/bid_conversion.hpp @@ -20,149 +20,162 @@ namespace decimal { # pragma GCC diagnostic ignored "-Wconversion" #endif -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d32(decimal32 val) noexcept -> std::uint32_t +constexpr auto to_bid_d32(decimal32 val) noexcept -> std::uint32_t { - const auto bits {detail::bit_cast(val)}; - return bits; + return val.bits_; } -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d32(std::uint32_t bits) noexcept -> decimal32 +constexpr auto from_bid_d32(std::uint32_t bits) noexcept -> decimal32 { - const auto val {detail::bit_cast(bits)}; - return val; + return from_bits(bits); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d32f(decimal32_fast val) noexcept -> std::uint32_t +constexpr auto to_bid_d32f(decimal32_fast val) noexcept -> std::uint32_t { const decimal32 compliant_val {val}; - const auto bits {detail::bit_cast(compliant_val)}; - return bits; + return to_bid_d32(compliant_val); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d32f(std::uint32_t bits) noexcept -> decimal32_fast +constexpr auto from_bid_d32f(std::uint32_t bits) noexcept -> decimal32_fast { - const auto compliant_val {detail::bit_cast(bits)}; + const auto compliant_val {from_bid_d32(bits)}; const decimal32_fast val {compliant_val}; return val; } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d64(decimal64 val) noexcept -> std::uint64_t +constexpr auto to_bid_d64(decimal64 val) noexcept -> std::uint64_t { - const auto bits {detail::bit_cast(val)}; - return bits; + return val.bits_; } -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d64(std::uint64_t bits) noexcept -> decimal64 +constexpr auto from_bid_d64(std::uint64_t bits) noexcept -> decimal64 { - const auto val {detail::bit_cast(bits)}; - return val; + return from_bits(bits); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d64f(decimal64_fast val) noexcept -> std::uint64_t +constexpr auto to_bid_d64f(decimal64_fast val) noexcept -> std::uint64_t { const decimal64 compliant_val {val}; - const auto bits {detail::bit_cast(compliant_val)}; - return bits; + return to_bid_d64(compliant_val); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d64f(std::uint64_t bits) noexcept -> decimal64_fast +constexpr auto from_bid_d64f(std::uint64_t bits) noexcept -> decimal64_fast { - const auto compliant_val {detail::bit_cast(bits)}; + const auto compliant_val {from_bid_d64(bits)}; const decimal64_fast val {compliant_val}; return val; } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d128(decimal128 val) noexcept -> detail::uint128 +constexpr auto to_bid_d128(decimal128 val) noexcept -> detail::uint128 { - const auto bits {detail::bit_cast(val)}; - return bits; + return val.bits_; } -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d128(detail::uint128 bits) noexcept -> decimal128 +constexpr auto from_bid_d128(detail::uint128 bits) noexcept -> decimal128 { - const auto val {detail::bit_cast(bits)}; - return val; + return from_bits(bits); +} + +#ifdef BOOST_DECIMAL_HAS_INT128 +constexpr auto from_bid_d128(detail::uint128_t bits) noexcept -> decimal128 +{ + return from_bits(bits); } +#endif -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d128f(decimal128_fast val) noexcept -> detail::uint128 +constexpr auto to_bid_d128f(decimal128_fast val) noexcept -> detail::uint128 { const decimal128 compliant_val {val}; - const auto bits {detail::bit_cast(compliant_val)}; - return bits; + return to_bid_d128(compliant_val); +} + +constexpr auto from_bid_d128f(detail::uint128 bits) noexcept -> decimal128_fast +{ + const auto compliant_val {from_bid_d128(bits)}; + const decimal128_fast val {compliant_val}; + return val; } -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d128f(detail::uint128 bits) noexcept -> decimal128_fast +#ifdef BOOST_DECIMAL_HAS_INT128 +constexpr auto from_bid_d128f(detail::uint128_t bits) noexcept -> decimal128_fast { - const auto compliant_val {detail::bit_cast(bits)}; + const auto compliant_val {from_bid_d128(bits)}; const decimal128_fast val {compliant_val}; return val; } +#endif -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(decimal32 val) noexcept -> std::uint32_t +constexpr auto to_bid(decimal32 val) noexcept -> std::uint32_t { return to_bid_d32(val); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(decimal32_fast val) noexcept -> std::uint32_t +constexpr auto to_bid(decimal32_fast val) noexcept -> std::uint32_t { return to_bid_d32f(val); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(decimal64 val) noexcept -> std::uint64_t +constexpr auto to_bid(decimal64 val) noexcept -> std::uint64_t { return to_bid_d64(val); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(decimal64_fast val) noexcept -> std::uint64_t +constexpr auto to_bid(decimal64_fast val) noexcept -> std::uint64_t { return to_bid_d64f(val); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(decimal128 val) noexcept -> detail::uint128 +constexpr auto to_bid(decimal128 val) noexcept -> detail::uint128 { return to_bid_d128(val); } -BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(decimal128_fast val) noexcept -> detail::uint128 +constexpr auto to_bid(decimal128_fast val) noexcept -> detail::uint128 { return to_bid_d128f(val); } +template +constexpr auto to_bid(T val) noexcept +{ + return to_bid(val); +} + template -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid(std::uint32_t bits) noexcept +constexpr auto from_bid(std::uint32_t bits) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { return from_bid_d32f(bits); } template <> -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid(std::uint32_t bits) noexcept -> decimal32 +constexpr auto from_bid(std::uint32_t bits) noexcept -> decimal32 { return from_bid_d32(bits); } template -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid(std::uint64_t bits) noexcept +constexpr auto from_bid(std::uint64_t bits) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { return from_bid_d64f(bits); } template <> -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid(std::uint64_t bits) noexcept -> decimal64 +constexpr auto from_bid(std::uint64_t bits) noexcept -> decimal64 { return from_bid_d64(bits); } template -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid(detail::uint128 bits) noexcept +constexpr auto from_bid(detail::uint128 bits) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { return from_bid_d128f(bits); } template <> -BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid(detail::uint128 bits) noexcept -> decimal128 +constexpr auto from_bid(detail::uint128 bits) noexcept -> decimal128 { return from_bid_d128(bits); } diff --git a/include/boost/decimal/charconv.hpp b/include/boost/decimal/charconv.hpp index 001c76d28..1ad74ea85 100644 --- a/include/boost/decimal/charconv.hpp +++ b/include/boost/decimal/charconv.hpp @@ -914,12 +914,12 @@ BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* la template struct limits { - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_chars = boost::decimal::detail::max_string_length_v; + static constexpr int max_chars = boost::decimal::detail::max_string_length_v; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) -template BOOST_DECIMAL_ATTRIBUTE_UNUSED constexpr int limits::max_chars; +template constexpr int limits::max_chars; #endif diff --git a/include/boost/decimal/decimal128.hpp b/include/boost/decimal/decimal128.hpp index b0e0b2130..1da9b031b 100644 --- a/include/boost/decimal/decimal128.hpp +++ b/include/boost/decimal/decimal128.hpp @@ -228,6 +228,18 @@ BOOST_DECIMAL_EXPORT class decimal128 final friend constexpr auto not_finite(decimal128 rhs) noexcept -> bool; + friend constexpr auto to_bid_d128(decimal128 val) noexcept -> detail::uint128; + + friend constexpr auto from_bid_d128(detail::uint128 bits) noexcept -> decimal128; + + #ifdef BOOST_DECIMAL_HAS_INT128 + friend constexpr auto from_bid_d128(detail::uint128_t bits) noexcept -> decimal128; + #endif + + template + friend constexpr auto to_dpd_d128(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, detail::uint128); + public: // 3.2.4.1 construct/copy/destroy constexpr decimal128() noexcept = default; @@ -1682,15 +1694,9 @@ constexpr auto operator*(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 auto lhs_sig {lhs.full_significand()}; auto lhs_exp {lhs.biased_exponent()}; - const auto lhs_zeros {detail::remove_trailing_zeros(lhs_sig)}; - lhs_sig = lhs_zeros.trimmed_number; - lhs_exp += static_cast(lhs_zeros.number_of_removed_zeros); auto rhs_sig {rhs.full_significand()}; auto rhs_exp {rhs.biased_exponent()}; - const auto rhs_zeros {detail::remove_trailing_zeros(rhs_sig)}; - rhs_sig = rhs_zeros.trimmed_number; - rhs_exp += static_cast(rhs_zeros.number_of_removed_zeros); return detail::d128_mul_impl( lhs_sig, lhs_exp, lhs.isneg(), @@ -2181,45 +2187,45 @@ struct numeric_limits public: #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true; + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; // These members were deprecated in C++23 #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true; + static constexpr std::float_denorm_style has_denorm = std::denorm_present; + static constexpr bool has_denorm_loss = true; #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 34; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -6142; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 6145; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits::traps; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true; + static constexpr std::float_round_style round_style = std::round_indeterminate; + static constexpr bool is_iec559 = true; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 34; + static constexpr int digits10 = digits; + static constexpr int max_digits10 = digits; + static constexpr int radix = 10; + static constexpr int min_exponent = -6142; + static constexpr int min_exponent10 = min_exponent; + static constexpr int max_exponent = 6145; + static constexpr int max_exponent10 = max_exponent; + static constexpr bool traps = numeric_limits::traps; + static constexpr bool tinyness_before = true; // Member functions - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (min) () -> boost::decimal::decimal128 { return {1, min_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (max) () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto lowest () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent, true}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto epsilon () -> boost::decimal::decimal128 { return {1, -34}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto round_error () -> boost::decimal::decimal128 { return epsilon(); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto infinity () -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto quiet_NaN () -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_nan_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto signaling_NaN() -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::decimal128 { return {1, boost::decimal::detail::etiny_v}; } + static constexpr auto (min) () -> boost::decimal::decimal128 { return {1, min_exponent}; } + static constexpr auto (max) () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent}; } + static constexpr auto lowest () -> boost::decimal::decimal128 { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal128 { return {1, -34}; } + static constexpr auto round_error () -> boost::decimal::decimal128 { return epsilon(); } + static constexpr auto infinity () -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask); } + static constexpr auto quiet_NaN () -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_nan_mask); } + static constexpr auto signaling_NaN() -> boost::decimal::decimal128 { return boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask); } + static constexpr auto denorm_min () -> boost::decimal::decimal128 { return {1, boost::decimal::detail::etiny_v}; } }; } //namespace std diff --git a/include/boost/decimal/decimal128_fast.hpp b/include/boost/decimal/decimal128_fast.hpp index abf5f9280..655669f95 100644 --- a/include/boost/decimal/decimal128_fast.hpp +++ b/include/boost/decimal/decimal128_fast.hpp @@ -123,6 +123,10 @@ class decimal128_fast final friend constexpr auto not_finite(const decimal128_fast& val) noexcept -> bool; + template + friend constexpr auto to_dpd_d128(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, detail::uint128); + public: constexpr decimal128_fast() noexcept = default; @@ -909,9 +913,21 @@ constexpr auto operator*(decimal128_fast lhs, decimal128_fast rhs) noexcept -> d } #endif - return detail::d128_fast_mul_impl( - lhs.significand_, lhs.biased_exponent(), lhs.sign_, - rhs.significand_, rhs.biased_exponent(), rhs.sign_); + auto lhs_sig {lhs.full_significand()}; + auto lhs_exp {lhs.biased_exponent()}; + const auto lhs_zeros {detail::remove_trailing_zeros(lhs_sig)}; + lhs_sig = lhs_zeros.trimmed_number; + lhs_exp += static_cast(lhs_zeros.number_of_removed_zeros); + + auto rhs_sig {rhs.full_significand()}; + auto rhs_exp {rhs.biased_exponent()}; + const auto rhs_zeros {detail::remove_trailing_zeros(rhs_sig)}; + rhs_sig = rhs_zeros.trimmed_number; + rhs_exp += static_cast(rhs_zeros.number_of_removed_zeros); + + return detail::d128_mul_impl( + lhs_sig, lhs_exp, lhs.sign_, + rhs_sig, rhs_exp, rhs.sign_); } template @@ -1412,45 +1428,45 @@ struct numeric_limits public: #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true; + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; // These members were deprecated in C++23 #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true; + static constexpr std::float_denorm_style has_denorm = std::denorm_present; + static constexpr bool has_denorm_loss = true; #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 34; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -6142; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 6145; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits::traps; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true; + static constexpr std::float_round_style round_style = std::round_indeterminate; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 34; + static constexpr int digits10 = digits; + static constexpr int max_digits10 = digits; + static constexpr int radix = 10; + static constexpr int min_exponent = -6142; + static constexpr int min_exponent10 = min_exponent; + static constexpr int max_exponent = 6145; + static constexpr int max_exponent10 = max_exponent; + static constexpr bool traps = numeric_limits::traps; + static constexpr bool tinyness_before = true; // Member functions - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (min) () -> boost::decimal::decimal128_fast { return {1, min_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (max) () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto lowest () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent, true}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto epsilon () -> boost::decimal::decimal128_fast { return {1, -34}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto round_error () -> boost::decimal::decimal128_fast { return epsilon(); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto infinity () -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_inf, 0, false); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto quiet_NaN () -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_qnan, 0, false); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto signaling_NaN() -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_snan, 0, false); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::decimal128_fast { return {1, boost::decimal::detail::etiny_v}; } + static constexpr auto (min) () -> boost::decimal::decimal128_fast { return {1, min_exponent}; } + static constexpr auto (max) () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent}; } + static constexpr auto lowest () -> boost::decimal::decimal128_fast { return {boost::decimal::detail::uint128{UINT64_C(999'999'999'999'999), UINT64_C(9'999'999'999'999'999'999)}, max_exponent, true}; } + static constexpr auto epsilon () -> boost::decimal::decimal128_fast { return {1, -34}; } + static constexpr auto round_error () -> boost::decimal::decimal128_fast { return epsilon(); } + static constexpr auto infinity () -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_inf, 0, false); } + static constexpr auto quiet_NaN () -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_qnan, 0, false); } + static constexpr auto signaling_NaN() -> boost::decimal::decimal128_fast { return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_snan, 0, false); } + static constexpr auto denorm_min () -> boost::decimal::decimal128_fast { return {1, boost::decimal::detail::etiny_v}; } }; } diff --git a/include/boost/decimal/decimal32.hpp b/include/boost/decimal/decimal32.hpp index 4c8e0e6fc..00accccdd 100644 --- a/include/boost/decimal/decimal32.hpp +++ b/include/boost/decimal/decimal32.hpp @@ -212,6 +212,20 @@ BOOST_DECIMAL_EXPORT class decimal32 final // NOLINT(cppcoreguidelines-special-m -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_decimal_floating_point_v), bool>; + template + friend constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + + template + friend constexpr auto sequential_less_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + + friend constexpr auto to_bid_d32(decimal32 val) noexcept -> std::uint32_t; + + friend constexpr auto from_bid_d32(std::uint32_t bits) noexcept -> decimal32; + + template + friend constexpr auto to_dpd_d32(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint32_t); + public: // 3.2.2.1 construct/copy/destroy: constexpr decimal32() noexcept = default; @@ -1056,15 +1070,7 @@ constexpr auto decimal32::operator-=(Integer rhs) noexcept constexpr auto operator==(decimal32 lhs, decimal32 rhs) noexcept -> bool { - #ifndef BOOST_DECIMAL_FAST_MATH - if (isnan(lhs) || isnan(rhs)) - { - return false; - } - #endif - - return equal_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), - rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); + return equality_impl(lhs, rhs); } template @@ -1116,8 +1122,7 @@ constexpr auto operator<(decimal32 lhs, decimal32 rhs) noexcept -> bool } #endif - return less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), - rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); + return sequential_less_impl(lhs, rhs); } template @@ -1161,8 +1166,7 @@ constexpr auto operator<=(decimal32 lhs, decimal32 rhs) noexcept -> bool } #endif - return !less_parts_impl(rhs.full_significand(), rhs.biased_exponent(), rhs.isneg(), - lhs.full_significand(), lhs.biased_exponent(), lhs.isneg()); + return !sequential_less_impl(rhs, lhs); } template @@ -1227,8 +1231,7 @@ constexpr auto operator>(decimal32 lhs, decimal32 rhs) noexcept -> bool } #endif - return less_parts_impl(rhs.full_significand(), rhs.biased_exponent(), rhs.isneg(), - lhs.full_significand(), lhs.biased_exponent(), lhs.isneg()); + return sequential_less_impl(rhs, lhs); } template @@ -1268,8 +1271,7 @@ constexpr auto operator>=(decimal32 lhs, decimal32 rhs) noexcept -> bool } #endif - return !less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), - rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); + return !sequential_less_impl(lhs, rhs); } template @@ -2200,45 +2202,45 @@ struct numeric_limits public: #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true; + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; // These members were deprecated in C++23 #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true; + static constexpr std::float_denorm_style has_denorm = std::denorm_present; + static constexpr bool has_denorm_loss = true; #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 7; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -95; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 96; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits::traps; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true; + static constexpr std::float_round_style round_style = std::round_indeterminate; + static constexpr bool is_iec559 = true; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 7; + static constexpr int digits10 = digits; + static constexpr int max_digits10 = digits; + static constexpr int radix = 10; + static constexpr int min_exponent = -95; + static constexpr int min_exponent10 = min_exponent; + static constexpr int max_exponent = 96; + static constexpr int max_exponent10 = max_exponent; + static constexpr bool traps = numeric_limits::traps; + static constexpr bool tinyness_before = true; // Member functions - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (min) () -> boost::decimal::decimal32 { return {1, min_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (max) () -> boost::decimal::decimal32 { return {9'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto lowest () -> boost::decimal::decimal32 { return {-9'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto epsilon () -> boost::decimal::decimal32 { return {1, -7}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto round_error () -> boost::decimal::decimal32 { return epsilon(); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto infinity () -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_inf_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto quiet_NaN () -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_nan_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto signaling_NaN() -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_snan_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::decimal32 { return {1, boost::decimal::detail::etiny}; } + static constexpr auto (min) () -> boost::decimal::decimal32 { return {1, min_exponent}; } + static constexpr auto (max) () -> boost::decimal::decimal32 { return {9'999'999, max_exponent}; } + static constexpr auto lowest () -> boost::decimal::decimal32 { return {-9'999'999, max_exponent}; } + static constexpr auto epsilon () -> boost::decimal::decimal32 { return {1, -7}; } + static constexpr auto round_error () -> boost::decimal::decimal32 { return epsilon(); } + static constexpr auto infinity () -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_inf_mask); } + static constexpr auto quiet_NaN () -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_nan_mask); } + static constexpr auto signaling_NaN() -> boost::decimal::decimal32 { return boost::decimal::from_bits(boost::decimal::detail::d32_snan_mask); } + static constexpr auto denorm_min () -> boost::decimal::decimal32 { return {1, boost::decimal::detail::etiny}; } }; } // Namespace std diff --git a/include/boost/decimal/decimal32_fast.hpp b/include/boost/decimal/decimal32_fast.hpp index 8ac613880..348e84dcd 100644 --- a/include/boost/decimal/decimal32_fast.hpp +++ b/include/boost/decimal/decimal32_fast.hpp @@ -114,6 +114,10 @@ class decimal32_fast final -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_decimal_floating_point_v), bool>; + template + friend constexpr auto to_dpd_d32(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint32_t); + public: constexpr decimal32_fast() noexcept {} @@ -1434,47 +1438,47 @@ struct numeric_limits public: #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true; + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; // These members were deprecated in C++23 #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_absent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = false; + static constexpr std::float_denorm_style has_denorm = std::denorm_absent; + static constexpr bool has_denorm_loss = false; #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 7; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -95; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 96; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits::traps; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true; + static constexpr std::float_round_style round_style = std::round_indeterminate; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 7; + static constexpr int digits10 = digits; + static constexpr int max_digits10 = digits; + static constexpr int radix = 10; + static constexpr int min_exponent = -95; + static constexpr int min_exponent10 = min_exponent; + static constexpr int max_exponent = 96; + static constexpr int max_exponent10 = max_exponent; + static constexpr bool traps = numeric_limits::traps; + static constexpr bool tinyness_before = true; // Member functions - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (min) () -> boost::decimal::decimal32_fast { return {1, min_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (max) () -> boost::decimal::decimal32_fast { return {9'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto lowest () -> boost::decimal::decimal32_fast { return {-9'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto epsilon () -> boost::decimal::decimal32_fast { return {1, -7}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto round_error () -> boost::decimal::decimal32_fast { return epsilon(); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto infinity () -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_inf, UINT8_C((0))); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto quiet_NaN () -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_qnan, UINT8_C((0))); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto signaling_NaN() -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_snan, UINT8_C((0))); } + static constexpr auto (min) () -> boost::decimal::decimal32_fast { return {1, min_exponent}; } + static constexpr auto (max) () -> boost::decimal::decimal32_fast { return {9'999'999, max_exponent}; } + static constexpr auto lowest () -> boost::decimal::decimal32_fast { return {-9'999'999, max_exponent}; } + static constexpr auto epsilon () -> boost::decimal::decimal32_fast { return {1, -7}; } + static constexpr auto round_error () -> boost::decimal::decimal32_fast { return epsilon(); } + static constexpr auto infinity () -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_inf, UINT8_C((0))); } + static constexpr auto quiet_NaN () -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_qnan, UINT8_C((0))); } + static constexpr auto signaling_NaN() -> boost::decimal::decimal32_fast { return boost::decimal::direct_init(boost::decimal::detail::d32_fast_snan, UINT8_C((0))); } // With denorm absent returns the same value as min - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::decimal32_fast { return {1, min_exponent}; } + static constexpr auto denorm_min () -> boost::decimal::decimal32_fast { return {1, min_exponent}; } }; } // Namespace std diff --git a/include/boost/decimal/decimal64.hpp b/include/boost/decimal/decimal64.hpp index ac44a243a..95df42edd 100644 --- a/include/boost/decimal/decimal64.hpp +++ b/include/boost/decimal/decimal64.hpp @@ -219,6 +219,20 @@ BOOST_DECIMAL_EXPORT class decimal64 final // We can super easily combine this into a single op friend constexpr auto not_finite(decimal64 rhs) noexcept -> bool; + template + friend constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + + template + friend constexpr auto sequential_less_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + + friend constexpr auto to_bid_d64(decimal64 val) noexcept -> std::uint64_t; + + friend constexpr auto from_bid_d64(std::uint64_t bits) noexcept -> decimal64; + + template + friend constexpr auto to_dpd_d64(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint64_t); + public: // 3.2.3.1 construct/copy/destroy constexpr decimal64() noexcept = default; @@ -1622,16 +1636,7 @@ constexpr auto decimal64::operator%=(decimal64 rhs) noexcept -> decimal64& constexpr auto operator==(decimal64 lhs, decimal64 rhs) noexcept -> bool { - #ifndef BOOST_DECIMAL_FAST_MATH - // Check for IEEE requirement that nan != nan - if (isnan(lhs) || isnan(rhs)) - { - return false; - } - #endif - - return equal_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), - rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); + return equality_impl(lhs, rhs); } template @@ -1688,8 +1693,7 @@ constexpr auto operator<(decimal64 lhs, decimal64 rhs) noexcept -> bool } #endif - return less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), - rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); + return sequential_less_impl(lhs, rhs); } template @@ -2095,45 +2099,45 @@ struct numeric_limits public: #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true; + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; // These members were deprecated in C++23 #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true; + static constexpr std::float_denorm_style has_denorm = std::denorm_present; + static constexpr bool has_denorm_loss = true; #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 16; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -382; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 385; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits::traps; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true; + static constexpr std::float_round_style round_style = std::round_indeterminate; + static constexpr bool is_iec559 = true; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 16; + static constexpr int digits10 = digits; + static constexpr int max_digits10 = digits; + static constexpr int radix = 10; + static constexpr int min_exponent = -382; + static constexpr int min_exponent10 = min_exponent; + static constexpr int max_exponent = 385; + static constexpr int max_exponent10 = max_exponent; + static constexpr bool traps = numeric_limits::traps; + static constexpr bool tinyness_before = true; // Member functions - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (min) () -> boost::decimal::decimal64 { return {1, min_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (max) () -> boost::decimal::decimal64 { return {9'999'999'999'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto lowest () -> boost::decimal::decimal64 { return {-9'999'999'999'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto epsilon () -> boost::decimal::decimal64 { return {1, -16}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto round_error () -> boost::decimal::decimal64 { return epsilon(); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto infinity () -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_inf_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto quiet_NaN () -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_nan_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto signaling_NaN() -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_snan_mask); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::decimal64 { return {1, boost::decimal::detail::etiny_v}; } + static constexpr auto (min) () -> boost::decimal::decimal64 { return {1, min_exponent}; } + static constexpr auto (max) () -> boost::decimal::decimal64 { return {9'999'999'999'999'999, max_exponent}; } + static constexpr auto lowest () -> boost::decimal::decimal64 { return {-9'999'999'999'999'999, max_exponent}; } + static constexpr auto epsilon () -> boost::decimal::decimal64 { return {1, -16}; } + static constexpr auto round_error () -> boost::decimal::decimal64 { return epsilon(); } + static constexpr auto infinity () -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_inf_mask); } + static constexpr auto quiet_NaN () -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_nan_mask); } + static constexpr auto signaling_NaN() -> boost::decimal::decimal64 { return boost::decimal::from_bits(boost::decimal::detail::d64_snan_mask); } + static constexpr auto denorm_min () -> boost::decimal::decimal64 { return {1, boost::decimal::detail::etiny_v}; } }; } // Namespace std diff --git a/include/boost/decimal/decimal64_fast.hpp b/include/boost/decimal/decimal64_fast.hpp index 37e4712c8..1d3742dbb 100644 --- a/include/boost/decimal/decimal64_fast.hpp +++ b/include/boost/decimal/decimal64_fast.hpp @@ -119,6 +119,10 @@ class decimal64_fast final friend constexpr auto not_finite(decimal64_fast val) noexcept -> bool; + template + friend constexpr auto to_dpd_d64(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint64_t); + public: constexpr decimal64_fast() noexcept = default; @@ -1378,48 +1382,48 @@ struct numeric_limits public: #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = true; + static constexpr bool is_specialized = true; + static constexpr bool is_signed = true; + static constexpr bool is_integer = false; + static constexpr bool is_exact = false; + static constexpr bool has_infinity = true; + static constexpr bool has_quiet_NaN = true; + static constexpr bool has_signaling_NaN = true; // These members were deprecated in C++23 #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_present; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = true; + static constexpr std::float_denorm_style has_denorm = std::denorm_present; + static constexpr bool has_denorm_loss = true; #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_indeterminate; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 16; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = digits; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 10; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = -382; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = min_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 385; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = max_exponent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = numeric_limits::traps; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = true; + static constexpr std::float_round_style round_style = std::round_indeterminate; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = false; + static constexpr int digits = 16; + static constexpr int digits10 = digits; + static constexpr int max_digits10 = digits; + static constexpr int radix = 10; + static constexpr int min_exponent = -382; + static constexpr int min_exponent10 = min_exponent; + static constexpr int max_exponent = 385; + static constexpr int max_exponent10 = max_exponent; + static constexpr bool traps = numeric_limits::traps; + static constexpr bool tinyness_before = true; // Member functions - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (min) () -> boost::decimal::decimal64_fast { return {1, min_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (max) () -> boost::decimal::decimal64_fast { return {9'999'999'999'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto lowest () -> boost::decimal::decimal64_fast { return {-9'999'999'999'999'999, max_exponent}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto epsilon () -> boost::decimal::decimal64_fast { return {1, -16}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto round_error () -> boost::decimal::decimal64_fast { return epsilon(); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto infinity () -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( + static constexpr auto (min) () -> boost::decimal::decimal64_fast { return {1, min_exponent}; } + static constexpr auto (max) () -> boost::decimal::decimal64_fast { return {9'999'999'999'999'999, max_exponent}; } + static constexpr auto lowest () -> boost::decimal::decimal64_fast { return {-9'999'999'999'999'999, max_exponent}; } + static constexpr auto epsilon () -> boost::decimal::decimal64_fast { return {1, -16}; } + static constexpr auto round_error () -> boost::decimal::decimal64_fast { return epsilon(); } + static constexpr auto infinity () -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( boost::decimal::detail::d64_fast_inf, 0, false); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto quiet_NaN () -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( + static constexpr auto quiet_NaN () -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( boost::decimal::detail::d64_fast_qnan, 0, false); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto signaling_NaN() -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( + static constexpr auto signaling_NaN() -> boost::decimal::decimal64_fast { return boost::decimal::direct_init_d64( boost::decimal::detail::d64_fast_snan, 0, false); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::decimal64_fast { return {1, boost::decimal::detail::etiny_v}; } + static constexpr auto denorm_min () -> boost::decimal::decimal64_fast { return {1, boost::decimal::detail::etiny_v}; } }; } // namespace std diff --git a/include/boost/decimal/detail/attributes.hpp b/include/boost/decimal/detail/attributes.hpp index f97a3176f..2f44e2543 100644 --- a/include/boost/decimal/detail/attributes.hpp +++ b/include/boost/decimal/detail/attributes.hpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -19,102 +21,119 @@ namespace decimal { namespace detail { // Values from IEEE 754-2019 table 3.6 +namespace impl { + +template +constexpr auto storage_width_v() noexcept -> int +{ + return decimal_val_v < 64 ? 32 : + decimal_val_v < 128 ? 64 : 128; +} + +template +constexpr auto precision_v() noexcept -> int +{ + return decimal_val_v < 64 ? 7 : + decimal_val_v < 128 ? 16 : 34; +} + +template +constexpr auto bias_v() noexcept -> int +{ + return decimal_val_v < 64 ? 101 : + decimal_val_v < 128 ? 398 : 6176; +} + +template +constexpr auto max_biased_exp_v() noexcept -> int +{ + return decimal_val_v < 64 ? 191 : + decimal_val_v < 128 ? 767 : 12287; +} + +template +constexpr auto emax_v() noexcept -> int +{ + return decimal_val_v < 64 ? 96 : + decimal_val_v < 128 ? 384 : 6144; +} + +template +constexpr auto emin_v() noexcept -> int +{ + return decimal_val_v < 64 ? -95 : + decimal_val_v < 128 ? -383 : -6143; +} + +template +constexpr auto combination_field_width_v() noexcept -> int +{ + return decimal_val_v < 64 ? 11 : + decimal_val_v < 128 ? 13 : 17; +} + +template +constexpr auto trailing_significand_field_width_v() noexcept -> int +{ + return decimal_val_v < 64 ? 20 : + decimal_val_v < 128 ? 50 : 110; +} + +template < 128, bool> = true> +constexpr auto max_significand_v() noexcept +{ + return decimal_val_v < 64 ? 9'999'999 : 9'999'999'999'999'999; +} + +template >= 128, bool> = true> +constexpr auto max_significand_v() noexcept +{ + return std::is_same::value ? uint128{UINT64_C(0b1111111111'1111111111'1111111111'1111111111'111111), UINT64_MAX} : + uint128{UINT64_C(542101086242752), UINT64_C(4003012203950112767)}; +} + +template +constexpr auto max_string_length_v() noexcept -> int +{ + return decimal_val_v < 64 ? 15 : + decimal_val_v < 128 ? 25 : 41; +} + +} // namespace impl template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto storage_width_v = std::is_same::value || std::is_same::value ? 32 : 64; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto storage_width_v = 128; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto storage_width_v = 128; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto storage_width_v = impl::storage_width_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto precision_v = std::is_same::value || std::is_same::value ? 7 : 16; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto precision_v = 34; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto precision_v = 34; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto precision_v = impl::precision_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto bias_v = std::is_same::value || std::is_same::value ? 101 : 398; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto bias_v = 6176; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto bias_v = 6176; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto bias_v = impl::bias_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto max_biased_exp_v = std::is_same::value || std::is_same::value ? 191 : 767; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto max_biased_exp_v = 12287; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto max_biased_exp_v = 12287; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto max_biased_exp_v = impl::max_biased_exp_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto emax_v = std::is_same::value || std::is_same::value ? 96 : 384; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto emax_v = 6144; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto emax_v = 6144; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto emax_v = impl::emax_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto emin_v = std::is_same::value || std::is_same::value ? -95 : -383; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto emin_v = -6143; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto emin_v = -6143; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto emin_v = impl::emin_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto etiny_v = -bias_v; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto etiny_v = -impl::bias_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto combination_field_width_v = std::is_same::value || std::is_same::value ? 11 : 13; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto combination_field_width_v = 17; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto combination_field_width_v = 17; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto combination_field_width_v = impl::combination_field_width_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto trailing_significand_field_width_v = std::is_same::value || std::is_same::value ? 20 : 50; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto trailing_significand_field_width_v = 110; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto trailing_significand_field_width_v = 110; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto trailing_significand_field_width_v = impl::trailing_significand_field_width_v(); template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto max_significand_v = std::is_same::value || std::is_same::value ? 9'999'999 : 9'999'999'999'999'999; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto max_significand_v = - uint128{UINT64_C(0b1111111111'1111111111'1111111111'1111111111'111111), UINT64_MAX}; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto max_significand_v = - uint128{UINT64_C(542101086242752), UINT64_C(4003012203950112767)}; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto max_significand_v = impl::max_significand_v(); // sign + decimal digits + '.' + 'e' + '+/-' + max digits of exponent + null term template , bool> = true> -BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto max_string_length_v = std::is_same::value || std::is_same::value ? 15 : 25; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto max_string_length_v = 41; - -template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto max_string_length_v = 41; +BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto max_string_length_v = impl::max_string_length_v(); BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto storage_width {storage_width_v}; BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto precision {precision_v}; diff --git a/include/boost/decimal/detail/comparison.hpp b/include/boost/decimal/detail/comparison.hpp index 091b81501..f968185a0 100644 --- a/include/boost/decimal/detail/comparison.hpp +++ b/include/boost/decimal/detail/comparison.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -24,6 +25,51 @@ namespace boost { namespace decimal { +template +BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool +{ + using comp_type = typename DecimalType::significand_type; + + // Step 1: Check for NANs per IEEE 754 + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + // Step 3: Check signs + const auto lhs_neg {lhs.isneg()}; + const auto rhs_neg {rhs.isneg()}; + + if (lhs_neg != rhs_neg) + { + return false; + } + + // Step 4: Check the exponents + // If the difference is greater than we can represent in the significand than we can assume they are different + const auto lhs_exp {lhs.biased_exponent()}; + const auto rhs_exp {rhs.biased_exponent()}; + + auto lhs_sig {lhs.full_significand()}; + auto rhs_sig {rhs.full_significand()}; + + const auto delta_exp {lhs_exp - rhs_exp}; + + if (delta_exp > detail::precision_v || delta_exp < -detail::precision_v || + ((lhs_sig == static_cast(0)) ^ (rhs_sig == static_cast(0)))) + { + return false; + } + + // Step 5: Normalize the significand and compare + delta_exp >= 0 ? lhs_sig *= detail::pow10(static_cast(delta_exp)) : + rhs_sig *= detail::pow10(static_cast(-delta_exp)); + + return lhs_sig == rhs_sig; +} + template constexpr auto equal_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign, @@ -197,6 +243,82 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto fast_type_less_parts_impl(T lhs_sig, U return lhs_sign ? lhs_sig > rhs_sig : lhs_sig < rhs_sig; } +template +constexpr auto sequential_less_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool +{ + using comp_type = std::conditional_t::value, std::uint_fast64_t, + // GCC less than 10 in non-GNU mode, Clang < 10 and MinGW all have issues with the built-in u128, + // so use the emulated one + #if defined(BOOST_DECIMAL_HAS_INT128) + #if ( (defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 10) || (defined(__clang__) && __clang_major__ < 13) ) + detail::uint128 + # else + detail::uint128_t + # endif + #else + detail::uint128 + #endif + >; + + // Step 1: Handle our non-finite values in their own calling functions + + // Step 2: Check if they are bitwise equal: + /* + if (lhs.bits_ == rhs.bits_) + { + return false; + } + */ + + // Step 3: Decode and compare signs first: + const auto lhs_sign {lhs.isneg()}; + const auto rhs_sign {rhs.isneg()}; + + if (lhs_sign != rhs_sign) + { + return lhs_sign; + } + + // Step 4: Decode the significand and do a trivial comp + auto lhs_sig {static_cast(lhs.full_significand())}; + auto rhs_sig {static_cast(rhs.full_significand())}; + if (lhs_sig == static_cast(0) || rhs_sig == static_cast(0)) + { + return (lhs_sig == rhs_sig) ? false : (lhs_sig == static_cast(0) ? !rhs_sign : lhs_sign); + } + + // Step 5: Decode the exponent and see if we can even compare the significands + auto lhs_exp {lhs.biased_exponent()}; + auto rhs_exp {rhs.biased_exponent()}; + + const auto delta_exp {lhs_exp - rhs_exp}; + constexpr auto max_delta_diff {std::numeric_limits::digits10 - detail::precision_v}; + + if (delta_exp > max_delta_diff || delta_exp < -max_delta_diff) + { + return rhs_sign ? rhs_exp < lhs_exp : rhs_exp > lhs_exp; + } + + // Step 6: Approximate normalization if we need to and then get the answer + if (delta_exp >= 0) + { + lhs_sig *= detail::pow10(static_cast(delta_exp)); + lhs_exp -= delta_exp; + } + else + { + rhs_sig *= detail::pow10(static_cast(-delta_exp)); + rhs_exp += delta_exp; + } + + if (lhs_exp != rhs_exp) + { + return lhs_sign ? lhs_exp > rhs_exp : lhs_exp < rhs_exp; + } + + return lhs_sign ? lhs_sig > rhs_sig : lhs_sig < rhs_sig; +} + template constexpr auto less_parts_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign, diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index 7f6d26841..7507557a8 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -172,10 +172,16 @@ typedef unsigned __int128 uint128_t; #define BOOST_DECIMAL_PREVENT_MACRO_SUBSTITUTION -#if defined(___GNUC__) || defined(__clang__) -# define BOOST_DECIMAL_ATTRIBUTE_UNUSED __attribute__((__unused__)) +#if __has_cpp_attribute(maybe_unused) +# define BOOST_DECIMAL_ATTRIBUTE_UNUSED [[maybe_unused]] #else -# define BOOST_DECIMAL_ATTRIBUTE_UNUSED +# if defined(___GNUC__) || defined(__clang__) +# define BOOST_DECIMAL_ATTRIBUTE_UNUSED __attribute__((__unused__)) +# elif defined(_MSC_VER) +# define BOOST_DECIMAL_ATTRIBUTE_UNUSED __pragma(warning(suppress: 4189)) +# else +# define BOOST_DECIMAL_ATTRIBUTE_UNUSED +# endif #endif #if !defined(__cpp_if_constexpr) || (__cpp_if_constexpr < 201606L) @@ -264,7 +270,7 @@ typedef unsigned __int128 uint128_t; # define BOOST_DECIMAL_REDUCE_TEST_DEPTH #endif -#ifdef __clang__ +#if defined(__clang__) && __clang_major__ < 19 # define BOOST_DECIMAL_CLANG_STATIC static #else # define BOOST_DECIMAL_CLANG_STATIC diff --git a/include/boost/decimal/detail/emulated128.hpp b/include/boost/decimal/detail/emulated128.hpp index 574ebe585..b2c2d31e0 100644 --- a/include/boost/decimal/detail/emulated128.hpp +++ b/include/boost/decimal/detail/emulated128.hpp @@ -1342,45 +1342,45 @@ template <> struct numeric_limits { // Member constants - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_specialized = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_signed = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_integer = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_exact = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_infinity = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_quiet_NaN = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_signaling_NaN = false; + static constexpr bool is_specialized = true; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; // These members were deprecated in C++23 #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_denorm_style has_denorm = std::denorm_absent; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool has_denorm_loss = false; + static constexpr std::float_denorm_style has_denorm = std::denorm_absent; + static constexpr bool has_denorm_loss = false; #endif - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr std::float_round_style round_style = std::round_toward_zero; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_iec559 = false; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_bounded = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool is_modulo = true; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits = 128; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int digits10 = 38; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_digits10 = 0; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int radix = 2; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent = 0; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int min_exponent10 = 0; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent = 0; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr int max_exponent10 = 0; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool traps = std::numeric_limits::traps; - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr bool tinyness_before = false; + static constexpr std::float_round_style round_style = std::round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr int digits = 128; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool traps = std::numeric_limits::traps; + static constexpr bool tinyness_before = false; // Member functions - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (min) () -> boost::decimal::detail::uint128 { return 0; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto lowest () -> boost::decimal::detail::uint128 { return 0; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto (max) () -> boost::decimal::detail::uint128 { return {UINT64_MAX, UINT64_MAX}; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto epsilon () -> boost::decimal::detail::uint128 { return 0; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto round_error () -> boost::decimal::detail::uint128 { return 0; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto infinity () -> boost::decimal::detail::uint128 { return 0; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto quiet_NaN () -> boost::decimal::detail::uint128 { return 0; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto signaling_NaN() -> boost::decimal::detail::uint128 { return 0; } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto (min) () -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto lowest () -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto (max) () -> boost::decimal::detail::uint128 { return {UINT64_MAX, UINT64_MAX}; } + static constexpr auto epsilon () -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto round_error () -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto infinity () -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto quiet_NaN () -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto signaling_NaN() -> boost::decimal::detail::uint128 { return 0; } + static constexpr auto denorm_min () -> boost::decimal::detail::uint128 { return 0; } }; } // namespace std diff --git a/include/boost/decimal/detail/emulated256.hpp b/include/boost/decimal/detail/emulated256.hpp index 55df7630a..41973640c 100644 --- a/include/boost/decimal/detail/emulated256.hpp +++ b/include/boost/decimal/detail/emulated256.hpp @@ -551,26 +551,15 @@ constexpr uint256_t umul256_impl(std::uint64_t a_high, std::uint64_t a_low, std: const auto mid_product2 {static_cast(a_high) * b_low}; const auto high_product {static_cast(a_high) * b_high}; - std::uint64_t carry {}; + const auto mid_sum {mid_product1 + mid_product2}; + bool carry {mid_sum < mid_product1}; - const auto mid_combined {mid_product1 + mid_product2}; - if (mid_combined < mid_product1) - { - carry = 1; - } - - const auto mid_combined_high {mid_combined >> 64}; - const auto mid_combined_low {mid_combined << 64}; - - const auto low_sum {low_product + mid_combined_low}; - if (low_sum < low_product) - { - carry += 1; - } + const auto low_sum {low_product + (mid_sum << 64)}; + carry |= (low_sum < low_product); uint256_t result {}; result.low = low_sum; - result.high = high_product + mid_combined_high + carry; + result.high = high_product + (mid_sum >> 64) + carry; return result; } diff --git a/include/boost/decimal/detail/mul_impl.hpp b/include/boost/decimal/detail/mul_impl.hpp index 6855223e5..29a698cfc 100644 --- a/include/boost/decimal/detail/mul_impl.hpp +++ b/include/boost/decimal/detail/mul_impl.hpp @@ -165,28 +165,42 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto d64_mul_impl(T lhs_sig, U lhs_exp, boo return {res_sig_64, res_exp, sign}; } -template -constexpr auto d128_mul_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept -> ReturnType +template +constexpr auto d128_mul_impl(T1 lhs_sig, U1 lhs_exp, bool lhs_sign, + T2 rhs_sig, U2 rhs_exp, bool rhs_sign) noexcept -> ReturnType { bool sign {lhs_sign != rhs_sign}; - // Once we have the normalized significands and exponents all we have to do is - // multiply the significands and add the exponents - auto res_sig {detail::umul256(lhs_sig, rhs_sig)}; - auto res_exp {lhs_exp + rhs_exp}; - - const auto sig_dig {detail::num_digits(res_sig)}; + const auto lhs_dig {detail::num_digits(lhs_sig)}; + const auto rhs_dig {detail::num_digits(rhs_sig)}; - if (sig_dig > std::numeric_limits::digits10) + // If we can avoid it don't do 256 bit multiplication because it is slow + if (lhs_dig * rhs_dig <= std::numeric_limits::digits10) { - const auto digit_delta {sig_dig - std::numeric_limits::digits10}; - res_sig /= detail::uint256_t(pow10(detail::uint128(digit_delta))); - res_exp += digit_delta; + auto res_sig {lhs_sig * rhs_sig}; + auto res_exp {lhs_exp + rhs_exp}; + return {res_sig, res_exp, sign}; + } + else + { + // Once we have the normalized significands and exponents all we have to do is + // multiply the significands and add the exponents + auto res_sig {detail::umul256(lhs_sig, rhs_sig)}; + auto res_exp {lhs_exp + rhs_exp}; + + const auto sig_dig {detail::num_digits(res_sig)}; + + if (sig_dig > std::numeric_limits::digits10) + { + const auto digit_delta {sig_dig - std::numeric_limits::digits10}; + res_sig /= detail::uint256_t(pow10(detail::uint128(digit_delta))); + res_exp += digit_delta; + } + + BOOST_DECIMAL_ASSERT(res_sig.high == uint128(0, 0)); + return {res_sig.low, res_exp, sign}; } - - BOOST_DECIMAL_ASSERT(res_sig.high == uint128(0,0)); - return {res_sig.low, res_exp, sign}; } template +#include +#include +#include + +#ifndef BOOST_DECIMAL_BUILD_MODULE +#include +#include +#endif + +namespace boost { +namespace decimal { + +namespace detail { + +// See Table 3.4 +constexpr auto encode_dpd(std::uint8_t d1, std::uint8_t d2, std::uint8_t d3) -> std::uint16_t +{ + constexpr std::uint8_t b3_mask {0b0001}; + constexpr std::uint8_t b2_mask {0b0010}; + constexpr std::uint8_t b1_mask {0b0100}; + constexpr std::uint8_t b0_mask {0b1000}; + + const std::uint8_t b1[4] = { + static_cast((d1 & b0_mask) >> 3U), + static_cast((d1 & b1_mask) >> 2U), + static_cast((d1 & b2_mask) >> 1U), + static_cast((d1 & b3_mask)) + }; + BOOST_DECIMAL_ASSERT(b1[0] <= 1U && b1[1] <= 1U && b1[2] <= 1U && b1[3] <= 1); + + const std::uint8_t b2[4] = { + static_cast((d2 & b0_mask) >> 3U), + static_cast((d2 & b1_mask) >> 2U), + static_cast((d2 & b2_mask) >> 1U), + static_cast((d2 & b3_mask)) + }; + BOOST_DECIMAL_ASSERT(b2[0] <= 1U && b2[1] <= 1U && b2[2] <= 1U && b2[3] <= 1); + + const std::uint8_t b3[4] = { + static_cast((d3 & b0_mask) >> 3U), + static_cast((d3 & b1_mask) >> 2U), + static_cast((d3 & b2_mask) >> 1U), + static_cast((d3 & b3_mask)) + }; + BOOST_DECIMAL_ASSERT(b3[0] <= 1U && b3[1] <= 1U && b3[2] <= 1U && b3[3] <= 1); + + std::uint8_t result_b[10] {}; + + const auto table_val {(b1[0] << 2) + (b2[0] << 1) + b3[0]}; + BOOST_DECIMAL_ASSERT(table_val >= 0b000 && table_val <= 0b111); + + // Now that we have dissected the bits of d1, d2, and d3 we can use the lookup table from 3.4 to generate + // all possible combinations + switch (table_val) + { + case 0b000: + // b0, b1, b2 + result_b[0] = b1[1]; + result_b[1] = b1[2]; + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = b2[1]; + result_b[4] = b2[2]; + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(0); + + // b7, b8, b9 + result_b[7] = b3[1]; + result_b[8] = b3[2]; + result_b[9] = b3[3]; + break; + + case 0b001: + // b0, b1, b2 + result_b[0] = b1[1]; + result_b[1] = b1[2]; + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = b2[1]; + result_b[4] = b2[2]; + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(1); + + // b7, b8, b9 + result_b[9] = b3[3]; + break; + + case 0b010: + // b0, b1, b2 + result_b[0] = b1[1]; + result_b[1] = b1[2]; + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = b3[1]; + result_b[4] = b3[2]; + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(1); + + // b7, b8, b9 + result_b[7] = static_cast(0); + result_b[8] = static_cast(1); + result_b[9] = b3[3]; + break; + + case 0b011: + // b0, b1, b2 + result_b[0] = b1[1]; + result_b[1] = b1[2]; + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = static_cast(1); + result_b[4] = static_cast(0); + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(1); + + // b7, b8, b9 + result_b[7] = static_cast(1); + result_b[8] = static_cast(1); + result_b[9] = b3[3]; + break; + + case 0b100: + // b0, b1, b2 + result_b[0] = b3[1]; + result_b[1] = b3[2]; + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = b2[1]; + result_b[4] = b2[2]; + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(1); + + // b7, b8, b9 + result_b[7] = static_cast(1); + result_b[8] = static_cast(0); + result_b[9] = b3[3]; + break; + + case 0b101: + // b0, b1, b2 + result_b[0] = b2[1]; + result_b[1] = b2[2]; + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = static_cast(0); + result_b[4] = static_cast(1); + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(1); + + // b7, b8, b9 + result_b[7] = static_cast(1); + result_b[8] = static_cast(1); + result_b[9] = b3[3]; + break; + + case 0b110: + // b0, b1, b2 + result_b[0] = b3[1]; + result_b[1] = b3[2]; + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = static_cast(0); + result_b[4] = static_cast(0); + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(1); + + // b7, b8, b9 + result_b[7] = static_cast(1); + result_b[8] = static_cast(1); + result_b[9] = b3[3]; + break; + + case 0b111: + // b0, b1, b2 + result_b[0] = static_cast(0); + result_b[1] = static_cast(0); + result_b[2] = b1[3]; + + // b3, b4, b5 + result_b[3] = static_cast(1); + result_b[4] = static_cast(1); + result_b[5] = b2[3]; + + // b6 + result_b[6] = static_cast(1); + + // b7, b8, b9 + result_b[7] = static_cast(1); + result_b[8] = static_cast(1); + result_b[9] = b3[3]; + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + + // Now that we have the bit pattern of the result we need to write it into uint16_t and return the result + std::uint16_t result {}; + + for (std::uint16_t i {}; i < 10U; ++i) + { + result |= static_cast(result_b[i] << (9 - i)); + } + + return result; +} + +constexpr auto decode_dpd(std::uint32_t dpd_bits, std::uint8_t& d3, std::uint8_t& d2, std::uint8_t& d1) -> void +{ + // DPD decoding logic as per IEEE 754-2008 + std::uint8_t b[10] {}; + for (int i = 0; i < 10; ++i) + { + b[i] = static_cast((dpd_bits >> (9 - i)) & 0b1); + } + + // See table 3.3 for the flow of decoding + // Values are b6, b7, b8, b3, b4 + // 0XXXX + if (b[6] == 0U) + { + d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); + d2 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[5]); + d3 = static_cast((b[7] << 2U) + (b[8] << 1U) + b[9]); + } + // 100XX + else if (b[6] == 1U && b[7] == 0U && b[8] == 0U) + { + d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); + d2 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[5]); + d3 = static_cast(8U + b[9]); + } + // 101XX + else if (b[6] == 1U && b[7] == 0U && b[8] == 1U) + { + d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); + d2 = static_cast(8U + b[5]); + d3 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[9]); + } + // 110XX + else if (b[6] == 1U && b[7] == 1U && b[8] == 0U) + { + d1 = static_cast(8U + b[2]); + d2 = static_cast((b[3] << 2U) + (b[4] << 1U) + b[5]); + d3 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[9]); + } + // 11100 + else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 0U && b[4] == 0U) + { + d1 = static_cast(8U + b[2]); + d2 = static_cast(8U + b[5]); + d3 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[9]); + } + // 11101 + else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 0U && b[4] == 1U) + { + d1 = static_cast(8U + b[2]); + d2 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[5]); + d3 = static_cast(8U + b[9]); + } + // 11110 + else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 1U && b[4] == 0U) + { + d1 = static_cast((b[0] << 2U) + (b[1] << 1U) + b[2]); + d2 = static_cast(8U + b[5]); + d3 = static_cast(8U + b[9]); + } + // 11111 + else if (b[6] == 1U && b[7] == 1U && b[8] == 1U && b[3] == 1U && b[4] == 1U) + { + d1 = static_cast(8U + b[2]); + d2 = static_cast(8U + b[5]); + d3 = static_cast(8U + b[9]); + } + // LCOV_EXCL_START + else + { + BOOST_DECIMAL_UNREACHABLE; + } + // LCOV_EXCL_STOP +} + +} // namespace detail + +template +constexpr auto to_dpd_d32(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint32_t) +{ + static_assert(std::is_same::value || + std::is_same::value, "The input must be a 32-bit decimal type"); + + // In the non-finite cases the encodings are the same + // 3.5.2.a and 3.5.2.b + if (!isfinite(val)) + { + return to_bid(val); + } + + const auto sign {val.isneg()}; + const auto exp {val.unbiased_exponent()}; + const auto significand {val.full_significand()}; + + std::uint32_t dpd {}; + // Set the sign bit as applicable + if (sign) + { + dpd |= detail::d32_sign_mask; + } + + // Break the significand down into the 7 declets are needed + std::uint8_t d[std::numeric_limits::digits10] {}; + auto temp_sig {significand}; + for (int i = 6; i >= 0; --i) + { + d[i] = static_cast(temp_sig % 10U); + temp_sig /= 10U; + } + BOOST_DECIMAL_ASSERT(d[0] >= 0 && d[0] <= 9); + BOOST_DECIMAL_ASSERT(temp_sig == 0); + + // We now need to capture what the leading two bits of the exponent are, + // since they are stored in the combination field + constexpr std::uint32_t leading_two_exp_bits_mask {0b11000000}; + const auto leading_two_bits {(exp & leading_two_exp_bits_mask) >> 6U}; + BOOST_DECIMAL_ASSERT(leading_two_bits >= 0 && leading_two_bits <= 2); + constexpr std::uint32_t trailing_exp_bits_mask {0b00111111}; + const auto trailing_exp_bits {(exp & trailing_exp_bits_mask)}; + + + std::uint32_t combination_field_bits {}; + // Now based on what the value of d[0] and the leading bits of exp are we can set the value of the combination field + // See 3.5.2.c.1 + // If d0 is 8 or 9 then we follow section i + if (d[0] >= 8) + { + const auto d0_is_nine {d[0] == 9}; + switch (leading_two_bits) + { + case 0U: + combination_field_bits = d0_is_nine ? 0b11001 : 0b11000; + break; + case 1U: + combination_field_bits = d0_is_nine ? 0b11011 : 0b11010; + break; + case 2U: + combination_field_bits = d0_is_nine ? 0b11101 : 0b11100; + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + } + // If d0 is 0 to 7 then we follow section II + else + { + // In here the value of d[0] = 4*G2 + 2*G3 + G4 + const auto d0_mask {static_cast(d[0])}; + switch (leading_two_bits) + { + case 0U: + // 00XXX + combination_field_bits |= d0_mask; + break; + case 1U: + // 01XXX + combination_field_bits = 0b01000; + combination_field_bits |= d0_mask; + break; + case 2U: + // 10XXX + combination_field_bits = 0b10000; + combination_field_bits |= d0_mask; + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + } + + // The only thing we have left to compute now is the bit patterns of d[1] - d[6] + const auto declet_1 {static_cast(detail::encode_dpd(d[1], d[2], d[3]))}; + const auto declet_2 {static_cast(detail::encode_dpd(d[4], d[5], d[6]))}; + + // Now we can do final assembly of the number + dpd |= (combination_field_bits << 26U); + dpd |= (trailing_exp_bits << 20U); + dpd |= (declet_1 << 10U); + dpd |= declet_2; + + return dpd; +} + +template +constexpr auto from_dpd_d32(std::uint32_t dpd) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) +{ + static_assert(std::is_same::value || std::is_same::value, + "Target decimal type must be 32-bits"); + + // First we check for non-finite values + // Since they are in the same initial format as BID it's easy to check with our existing masks + if ((dpd & detail::d32_inf_mask) == detail::d32_inf_mask) + { + if ((dpd & detail::d32_snan_mask) == detail::d32_snan_mask) + { + return std::numeric_limits::signaling_NaN(); + } + else if ((dpd & detail::d32_nan_mask) == detail::d32_nan_mask) + { + return std::numeric_limits::quiet_NaN(); + } + else + { + return std::numeric_limits::infinity(); + } + } + + // The bit lengths are the same as used in the standard bid format + const auto sign {(dpd & detail::d32_sign_mask) != 0}; + const auto combination_field_bits {(dpd & detail::d32_combination_field_mask) >> 26U}; + const auto exponent_field_bits {(dpd & detail::d32_exponent_mask) >> 20U}; + const auto significand_bits {(dpd & detail::d32_significand_mask)}; + + // Case 1: 3.5.2.c.1.i + // Combination field bits are 110XX or 11110X + std::uint32_t d0 {}; + std::uint32_t leading_biased_exp_bits {}; + if (combination_field_bits >= 0b11000) + { + // d0 = 8 + G4 + // Must be equal to 8 or 9 + d0 = 8U + (combination_field_bits & 0b00001); + BOOST_DECIMAL_ASSERT(d0 == 8 || d0 == 9); + + // leading exp bits are 2*G2 + G3 + // Must be equal to 0, 1 or 2 + leading_biased_exp_bits = 2U * ((combination_field_bits & 0b00100) >> 2U) + ((combination_field_bits & 0b00010) >> 1U); + BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); + } + // Case 2: 3.5.2.c.1.ii + // Combination field bits are 0XXXX or 10XXX + else + { + // d0 = 4 * G2 + 2 * G3 + G4 + // Must be in the range 0-7 + d0 = combination_field_bits & 0b00111; + BOOST_DECIMAL_ASSERT(d0 <= 7); + + // Leading exp bits are 2 * G0 + G1 + // Must be equal to 0, 1 or 2 + leading_biased_exp_bits = (combination_field_bits & 0b11000) >> 3U; + BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); + } + + // Now that we have the bits we can calculate the exponents value + const auto complete_exp {(leading_biased_exp_bits << 6U) + exponent_field_bits}; + const auto exp {static_cast(complete_exp) - detail::bias_v}; + + // We can now decode the remainder of the significand to recover the value + std::uint8_t digits[7] {}; + digits[0] = static_cast(d0); + const auto significand_low {significand_bits & 0b1111111111}; + detail::decode_dpd(significand_low, digits[6], digits[5], digits[4]); + const auto significand_high {(significand_bits & 0b11111111110000000000) >> 10U}; + BOOST_DECIMAL_ASSERT(significand_high <= 0b1111111111); + detail::decode_dpd(significand_high, digits[3], digits[2], digits[1]); + + // Now we can assemble the significand + std::uint32_t significand {}; + for (std::uint32_t i {}; i < 7U; ++i) + { + significand += digits[i] * detail::pow10(6 - i); + } + + return DecimalType{significand, exp, sign}; +} + +template +constexpr auto to_dpd_d64(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, std::uint64_t) +{ + static_assert(std::is_same::value || + std::is_same::value, "The input must be a 64-bit decimal type"); + + // In the non-finite cases the encodings are the same + // 3.5.2.a and 3.5.2.b + if (!isfinite(val)) + { + return to_bid(val); + } + + const auto sign {val.isneg()}; + const auto exp {val.unbiased_exponent()}; + const auto significand {val.full_significand()}; + + std::uint64_t dpd {}; + // Set the sign bit as applicable + if (sign) + { + dpd |= detail::d64_sign_mask; + } + + std::uint8_t d[std::numeric_limits::digits10] {}; + auto temp_sig {significand}; + for (int i = 15; i >= 0; --i) + { + d[i] = static_cast(temp_sig % 10U); + temp_sig /= 10U; + } + BOOST_DECIMAL_ASSERT(d[0] >= 0 && d[0] <= 9); + BOOST_DECIMAL_ASSERT(temp_sig == 0); + + constexpr std::uint64_t leading_two_exp_bits_mask {0b1100000000}; + const auto leading_two_bits {(exp & leading_two_exp_bits_mask) >> 8U}; + BOOST_DECIMAL_ASSERT(leading_two_bits >= 0 && leading_two_bits <= 2); + constexpr std::uint64_t trailing_exp_bits_mask {0b0011111111}; + const auto trailing_exp_bits {(exp & trailing_exp_bits_mask)}; + + std::uint64_t combination_field_bits {}; + + // Now based on what the value of d[0] and the leading bits of exp are we can set the value of the combination field + // See 3.5.2.c.1 + // If d0 is 8 or 9 then we follow section i + if (d[0] >= 8) + { + const auto d0_is_nine {d[0] == 9}; + switch (leading_two_bits) + { + case 0U: + combination_field_bits = d0_is_nine ? 0b11001 : 0b11000; + break; + case 1U: + combination_field_bits = d0_is_nine ? 0b11011 : 0b11010; + break; + case 2U: + combination_field_bits = d0_is_nine ? 0b11101 : 0b11100; + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + } + // If d0 is 0 to 7 then we follow section II + else + { + // In here the value of d[0] = 4*G2 + 2*G3 + G4 + const auto d0_mask {static_cast(d[0])}; + switch (leading_two_bits) + { + case 0U: + // 00XXX + combination_field_bits |= d0_mask; + break; + case 1U: + // 01XXX + combination_field_bits = 0b01000; + combination_field_bits |= d0_mask; + break; + case 2U: + // 10XXX + combination_field_bits = 0b10000; + combination_field_bits |= d0_mask; + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + } + + // Write the now known combination field and trailing exp bits to the result + dpd |= (combination_field_bits << 58U); + dpd |= (trailing_exp_bits << 50U); + + // Now we need to encode all the declets + // Once we have the declet right it into the result + int offset {4}; + for (std::size_t i {1}; i < 15; i += 3U) + { + const auto declet {static_cast(detail::encode_dpd(d[i], d[i + 1], d[i + 2]))}; + dpd |= (declet << (10 * offset)); + --offset; + } + + return dpd; +} + +template +constexpr auto from_dpd_d64(std::uint64_t dpd) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) +{ + static_assert(std::is_same::value || std::is_same::value, + "Target decimal type must be 64-bits"); + + // First we check for non-finite values + // Since they are in the same initial format as BID it's easy to check with our existing masks + if ((dpd & detail::d64_inf_mask) == detail::d64_inf_mask) + { + if ((dpd & detail::d64_snan_mask) == detail::d64_snan_mask) + { + return std::numeric_limits::signaling_NaN(); + } + else if ((dpd & detail::d64_nan_mask) == detail::d64_nan_mask) + { + return std::numeric_limits::quiet_NaN(); + } + else + { + return std::numeric_limits::infinity(); + } + } + + // The bit lengths are the same as used in the standard bid format + const auto sign {(dpd & detail::d64_sign_mask) != 0}; + const auto combination_field_bits {(dpd & detail::d64_combination_field_mask) >> 58U}; + const auto exponent_field_bits {(dpd & detail::d64_exponent_mask) >> 50U}; + auto significand_bits {(dpd & detail::d64_significand_mask)}; + + // Case 1: 3.5.2.c.1.i + // Combination field bits are 110XX or 11110X + std::uint64_t d0 {}; + std::uint64_t leading_biased_exp_bits {}; + if (combination_field_bits >= 0b11000) + { + // d0 = 8 + G4 + // Must be equal to 8 or 9 + d0 = 8U + (combination_field_bits & 0b00001); + BOOST_DECIMAL_ASSERT(d0 == 8 || d0 == 9); + + // leading exp bits are 2*G2 + G3 + // Must be equal to 0, 1 or 2 + leading_biased_exp_bits = 2U * ((combination_field_bits & 0b00100) >> 2U) + ((combination_field_bits & 0b00010) >> 1U); + BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); + } + // Case 2: 3.5.2.c.1.ii + // Combination field bits are 0XXXX or 10XXX + else + { + // d0 = 4 * G2 + 2 * G3 + G4 + // Must be in the range 0-7 + d0 = combination_field_bits & 0b00111; + BOOST_DECIMAL_ASSERT(d0 <= 7); + + // Leading exp bits are 2 * G0 + G1 + // Must be equal to 0, 1 or 2 + leading_biased_exp_bits = (combination_field_bits & 0b11000) >> 3U; + BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); + } + + // Now that we have the bits we can calculate the exponents value + const auto complete_exp {(leading_biased_exp_bits << 8U) + exponent_field_bits}; + const auto exp {static_cast(complete_exp) - detail::bias_v}; + + // We can now decode the remainder of the significand to recover the value + std::uint8_t digits[16] {}; + digits[0] = static_cast(d0); + for (int i = 15; i > 0; i -= 3) + { + const auto declet_bits {static_cast(significand_bits & 0b1111111111)}; + significand_bits >>= 10U; + detail::decode_dpd(declet_bits, digits[i], digits[i - 1], digits[i - 2]); + } + + std::uint64_t significand {}; + for (std::uint64_t i {}; i < 16U; ++i) + { + significand += digits[i] * detail::pow10(15 - i); + } + + return DecimalType{significand, exp, sign}; +} + +template +constexpr auto to_dpd_d128(DecimalType val) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, DecimalType, detail::uint128) +{ + static_assert(std::is_same::value || + std::is_same::value, "The input must be a 128-bit decimal type"); + + // In the non-finite cases the encodings are the same + // 3.5.2.a and 3.5.2.b + if (!isfinite(val)) + { + return to_bid(val); + } + + const auto sign {val.isneg()}; + const auto exp {val.unbiased_exponent()}; + const auto significand {val.full_significand()}; + + detail::uint128 dpd {}; + + // Set the sign bit as applicable + if (sign) + { + dpd.high |= detail::d128_sign_mask.high; + } + + constexpr int num_digits {std::numeric_limits::digits10}; + std::uint8_t d[static_cast(num_digits)] {}; + auto temp_sig {significand}; + for (int i = num_digits - 1; i >= 0; --i) + { + d[i] = static_cast(temp_sig % 10U); + temp_sig /= 10U; + } + BOOST_DECIMAL_ASSERT(d[0] >= 0 && d[0] <= 9); + BOOST_DECIMAL_ASSERT(temp_sig == 0); + + constexpr std::uint64_t leading_two_exp_bits_mask {0b11000000000000}; + const auto leading_two_bits {(exp & leading_two_exp_bits_mask) >> 12U}; + constexpr std::uint64_t trailing_exp_bits_mask {0b00111111111111}; + const auto trailing_exp_bits {(exp & trailing_exp_bits_mask)}; + + std::uint64_t combination_field_bits {}; + + // Now based on what the value of d[0] and the leading bits of exp are we can set the value of the combination field + // See 3.5.2.c.1 + // If d0 is 8 or 9 then we follow section i + if (d[0] >= 8) + { + const auto d0_is_nine {d[0] == 9}; + switch (leading_two_bits) + { + case 0U: + combination_field_bits = d0_is_nine ? 0b11001 : 0b11000; + break; + case 1U: + combination_field_bits = d0_is_nine ? 0b11011 : 0b11010; + break; + case 2U: + combination_field_bits = d0_is_nine ? 0b11101 : 0b11100; + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + } + // If d0 is 0 to 7 then we follow section II + else + { + // In here the value of d[0] = 4*G2 + 2*G3 + G4 + const auto d0_mask {static_cast(d[0])}; + switch (leading_two_bits) + { + case 0U: + // 00XXX + combination_field_bits |= d0_mask; + break; + case 1U: + // 01XXX + combination_field_bits = 0b01000; + combination_field_bits |= d0_mask; + break; + case 2U: + // 10XXX + combination_field_bits = 0b10000; + combination_field_bits |= d0_mask; + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + } + + // Write the now know combination field and trailing exp bits to the result + dpd.high |= (combination_field_bits << 58U); + dpd.high |= (trailing_exp_bits << 46U); + + // Now we have to encode all 11 of the declets + int offset {10}; + for (std::size_t i {1}; i < num_digits - 1; i += 3U) + { + const auto declet {static_cast(detail::encode_dpd(d[i], d[i + 1], d[i + 2]))}; + dpd |= detail::uint128(declet << (10 * offset)); + --offset; + } + + return dpd; +} + +template +constexpr auto from_dpd_d128(detail::uint128 dpd) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) +{ + static_assert(std::is_same::value || std::is_same::value, + "Target decimal type must be 128-bits"); + + if ((dpd & detail::d128_inf_mask) == detail::d128_inf_mask) + { + if ((dpd & detail::d128_snan_mask) == detail::d128_snan_mask) + { + return std::numeric_limits::signaling_NaN(); + } + else if ((dpd & detail::d128_nan_mask) == detail::d128_nan_mask) + { + return std::numeric_limits::quiet_NaN(); + } + else + { + return std::numeric_limits::infinity(); + } + } + + // The bit lengths are the same as used in the standard bid format + const auto sign {(dpd.high & detail::d128_sign_mask.high) != 0}; + const auto combination_field_bits {(dpd.high & detail::d128_combination_field_mask.high) >> 58U}; + const auto exponent_field_bits {(dpd.high & detail::d128_exponent_mask.high) >> 46U}; + auto significand_bits {(dpd & detail::d128_significand_mask)}; + + // Case 1: 3.5.2.c.1.i + // Combination field bits are 110XX or 11110X + std::uint64_t d0 {}; + std::uint64_t leading_biased_exp_bits {}; + if (combination_field_bits >= 0b11000) + { + // d0 = 8 + G4 + // Must be equal to 8 or 9 + d0 = 8U + (combination_field_bits & 0b00001); + BOOST_DECIMAL_ASSERT(d0 == 8 || d0 == 9); + + // leading exp bits are 2*G2 + G3 + // Must be equal to 0, 1 or 2 + leading_biased_exp_bits = 2U * ((combination_field_bits & 0b00100) >> 2U) + ((combination_field_bits & 0b00010) >> 1U); + BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); + } + // Case 2: 3.5.2.c.1.ii + // Combination field bits are 0XXXX or 10XXX + else + { + // d0 = 4 * G2 + 2 * G3 + G4 + // Must be in the range 0-7 + d0 = combination_field_bits & 0b00111; + BOOST_DECIMAL_ASSERT(d0 <= 7); + + // Leading exp bits are 2 * G0 + G1 + // Must be equal to 0, 1 or 2 + leading_biased_exp_bits = (combination_field_bits & 0b11000) >> 3U; + BOOST_DECIMAL_ASSERT(leading_biased_exp_bits <= 2U); + } + + // Now that we have the bits we can calculate the exponents value + const auto complete_exp {(leading_biased_exp_bits << 12U) + exponent_field_bits}; + const auto exp {static_cast(complete_exp) - detail::bias_v}; + + // We can now decode the remainder of the significand to recover the value + constexpr auto num_digits {std::numeric_limits::digits10}; + std::uint8_t digits[static_cast(num_digits)] {}; + digits[0] = static_cast(d0); + for (int i = num_digits - 1; i > 0; i -= 3) + { + const auto declet_bits {static_cast(significand_bits & 0b1111111111)}; + significand_bits >>= 10U; + detail::decode_dpd(declet_bits, digits[i], digits[i - 1], digits[i - 2]); + } + + detail::uint128 significand {}; + for (int i {}; i < num_digits; ++i) + { + significand += static_cast(digits[i]) * detail::pow10(static_cast((num_digits - 1) - i)); + } + + return DecimalType{significand, exp, sign}; +} + +constexpr auto to_dpd(decimal32 val) noexcept -> std::uint32_t +{ + return to_dpd_d32(val); +} + +constexpr auto to_dpd(decimal32_fast val) noexcept -> std::uint32_t +{ + return to_dpd_d32(val); +} + +constexpr auto to_dpd(decimal64 val) noexcept -> std::uint64_t +{ + return to_dpd_d64(val); +} + +constexpr auto to_dpd(decimal64_fast val) noexcept -> std::uint64_t +{ + return to_dpd_d64(val); +} + +constexpr auto to_dpd(decimal128 val) noexcept -> detail::uint128 +{ + return to_dpd_d128(val); +} + +constexpr auto to_dpd(decimal128_fast val) noexcept -> detail::uint128 +{ + return to_dpd_d128(val); +} + +template +constexpr auto to_dpd(DecimalType val) noexcept +{ + static_assert(detail::is_decimal_floating_point_v, "Must be a decimal floating point type."); + return to_dpd(val); +} + +template +constexpr auto from_dpd(std::uint32_t bits) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) +{ + return from_dpd_d32(bits); +} + +template +constexpr auto from_dpd(std::uint64_t bits) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) +{ + return from_dpd_d64(bits); +} + +template +constexpr auto from_dpd(detail::uint128 bits) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) +{ + return from_dpd_d128(bits); +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +template +constexpr auto from_dpd(detail::uint128_t bits) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, DecimalType) +{ + const detail::uint128 converted_bits {bits}; + return from_dpd_d128(converted_bits); +} + +#endif // BOOST_DECIMAL_HAS_INT128 + +} // namespace decimal +} // namespace boost + +#endif // BOOST_DECIMAL_DPD_CONVERSION_HPP diff --git a/include/boost/decimal/numbers.hpp b/include/boost/decimal/numbers.hpp index d99b12fbe..7c2773713 100644 --- a/include/boost/decimal/numbers.hpp +++ b/include/boost/decimal/numbers.hpp @@ -10,224 +10,306 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include #endif -namespace boost { namespace decimal { namespace numbers { +namespace boost { +namespace decimal { +namespace numbers { + +namespace detail { + +template < 128, bool> = true> +constexpr auto e_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(2718281828459045235), -18}; +} + +template >= 128, bool> = true> +constexpr auto e_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(147358353192158), UINT64_C(5661142159003925334)}, -33}; +} + +template < 128, bool> = true> +constexpr auto log2e_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(1442695040888963407), -18}; +} + +template >= 128, bool> = true> +constexpr auto log2e_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(78208654878293), UINT64_C(16395798456599530402)}, -33}; +} + +template < 128, bool> = true> +constexpr auto log10e_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(4342944819032518277), -19}; +} + +template >= 128, bool> = true> +constexpr auto log10e_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(235431510388986), UINT64_C(2047877485384264674)}, -34}; +} + +template < 128, bool> = true> +constexpr auto log10_2_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(3010299956639811952), -19}; +} + +template >= 128, bool> = true> +constexpr auto log10_2_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(163188687641095), UINT64_C(3612628795761985410)}, -34}; +} + +template < 128, bool> = true> +constexpr auto pi_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(3141592653589793238), -18}; +} + +template >= 128, bool> = true> +constexpr auto pi_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(170306079004327), UINT64_C(13456286628489437068)}, -33}; +} + +template < 128, bool> = true> +constexpr auto pi_over_four_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(7853981633974483096), -19}; +} + +template >= 128, bool> = true> +constexpr auto pi_over_four_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(42576519751081932), UINT64_C(6764235707220873609)}, -38}; +} + +template < 128, bool> = true> +constexpr auto inv_pi_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(3183098861837906715), -19}; +} + +template >= 128, bool> = true> +constexpr auto inv_pi_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(172556135062039), UINT64_C(13820348844234745256)}, -34}; +} + +template < 128, bool> = true> +constexpr auto inv_sqrtpi_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(5641895835477562869), -19}; +} + +template >= 128, bool> = true> +constexpr auto inv_sqrtpi_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(305847786088084), UINT64_C(12695685840195063976)}, -34}; +} + +template < 128, bool> = true> +constexpr auto ln2_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(6931471805599453094), -19}; +} + +template >= 128, bool> = true> +constexpr auto ln2_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(375755839507647), UINT64_C(8395602002641374208)}, -34}; +} + +template < 128, bool> = true> +constexpr auto ln10_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(2302585092994045684), -18}; +} + +template >= 128, bool> = true> +constexpr auto ln10_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(124823388007844), UINT64_C(1462833818723808456)}, -33}; +} + +template < 128, bool> = true> +constexpr auto sqrt2_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(1414213562373095049), -18}; +} + +template >= 128, bool> = true> +constexpr auto sqrt2_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(76664670834168), UINT64_C(12987834932751794202)}, -33}; +} + +template < 128, bool> = true> +constexpr auto sqrt3_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(1732050807568877294), -18}; +} + +template >= 128, bool> = true> +constexpr auto sqrt3_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(93894662421072), UINT64_C(8437766544231453518)}, -33}; +} + +template < 128, bool> = true> +constexpr auto sqrt10_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(3162277660168379332), -18}; +} + +template >= 128, bool> = true> +constexpr auto sqrt10_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(171427415457846), UINT64_C(13450487317535253574)}, -33}; +} + +template < 128, bool> = true> +constexpr auto cbrt2_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(1259921049894873165), -18}; +} + +template >= 128, bool> = true> +constexpr auto cbrt2_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(68300456972811), UINT64_C(17628749411094165652)}, -33}; +} + +template < 128, bool> = true> +constexpr auto cbrt10_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(2154434690031883722), -18}; +} + +template >= 128, bool> = true> +constexpr auto cbrt10_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(116792138570535), UINT64_C(2467411419527284790)}, -33}; +} + +template < 128, bool> = true> +constexpr auto inv_sqrt2_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(7071067811865475244), -19}; +} + +template >= 128, bool> = true> +constexpr auto inv_sqrt2_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(383323354170843), UINT64_C(9598942442630316202)}, -34}; +} + +template < 128, bool> = true> +constexpr auto inv_sqrt3_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(5773502691896257645), -19}; +} + +template >= 128, bool> = true> +constexpr auto inv_sqrt3_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(312982208070241), UINT64_C(9679144407061960114)}, -34}; +} + +template < 128, bool> = true> +constexpr auto egamma_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(5772156649015328606), -19}; +} + +template >= 128, bool> = true> +constexpr auto egamma_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(312909238939453), UINT64_C(7916302232898517972)}, -34}; +} + +template < 128, bool> = true> +constexpr auto phi_v() noexcept -> DecimalType +{ + return DecimalType{UINT64_C(1618033988749894848), -18}; +} + +template >= 128, bool> = true> +constexpr auto phi_v() noexcept -> DecimalType +{ + return DecimalType{boost::decimal::detail::uint128{UINT64_C(87713798287901), UINT64_C(2061523135646567614)}, -33}; +} + +} // Namespace detail + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec e_v = detail::e_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec log2e_v = detail::log2e_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec log10e_v = detail::log10e_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec log10_2_v = detail::log10_2_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec pi_v = detail::pi_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec pi_over_four_v = detail::pi_over_four_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_pi_v = detail::inv_pi_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_sqrtpi_v = detail::inv_sqrtpi_v(); -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec e_v = Dec{UINT64_C(2718281828459045235), -18}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec ln2_v = detail::ln2_v(); + +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec ln10_v = detail::ln10_v(); -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 e_v = decimal128{detail::uint128{UINT64_C(147358353192158), - UINT64_C(5661142159003925334)}, -33}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec sqrt2_v = detail::sqrt2_v(); -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast e_v = decimal128_fast{detail::uint128{UINT64_C(147358353192158), - UINT64_C(5661142159003925334)}, -33}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec sqrt3_v = detail::sqrt3_v(); -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec log2e_v = Dec{UINT64_C(1442695040888963407), -18}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec sqrt10_v = detail::sqrt10_v(); -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 log2e_v = decimal128{detail::uint128{UINT64_C(78208654878293), - UINT64_C(16395798456599530402)}, -33}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec cbrt2_v = detail::cbrt2_v(); -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast log2e_v = decimal128_fast{detail::uint128{UINT64_C(78208654878293), - UINT64_C(16395798456599530402)}, -33}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec cbrt10_v = detail::cbrt10_v(); -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec log10e_v = Dec{UINT64_C(4342944819032518277), -19}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_sqrt2_v = detail::inv_sqrt2_v(); -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 log10e_v = decimal128{detail::uint128{UINT64_C(235431510388986), - UINT64_C(2047877485384264674)}, -34}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_sqrt3_v = detail::inv_sqrt3_v(); -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast log10e_v = decimal128_fast{detail::uint128{UINT64_C(235431510388986), - UINT64_C(2047877485384264674)}, -34}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec egamma_v = detail::egamma_v(); -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec log10_2_v = Dec{UINT64_C(3010299956639811952), -19}; +BOOST_DECIMAL_EXPORT template , bool> = true> +BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec phi_v = detail::phi_v(); -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 log10_2_v = decimal128{detail::uint128{UINT64_C(163188687641095), - UINT64_C(3612628795761985410)}, -34}; -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast log10_2_v = decimal128_fast{detail::uint128{UINT64_C(163188687641095), - UINT64_C(3612628795761985410)}, -34}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec pi_v = Dec{UINT64_C(3141592653589793238), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 pi_v = decimal128{detail::uint128{UINT64_C(170306079004327), - UINT64_C(13456286628489437068)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast pi_v = decimal128_fast{detail::uint128{UINT64_C(170306079004327), - UINT64_C(13456286628489437068)}, -33}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec pi_over_four_v = Dec{UINT64_C(7853981633974483096), -19}; - -// For extraction of the 128-bit approximate value of pi/4, see also: https://godbolt.org/z/fcjjGP7bY -// See also: -// N[Pi/4, 37] -// 0.7853981633974483096156608458198757210 -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 pi_over_four_v = decimal128{detail::uint128{UINT64_C(42576519751081932), - UINT64_C(6764235707220873609)}, -38}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast pi_over_four_v = decimal128_fast{detail::uint128{UINT64_C(42576519751081932), - UINT64_C(6764235707220873609)}, -38}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_pi_v = Dec{UINT64_C(3183098861837906715), -19}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 inv_pi_v = decimal128{detail::uint128{UINT64_C(172556135062039), - UINT64_C(13820348844234745256)}, -34}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast inv_pi_v = decimal128_fast{detail::uint128{UINT64_C(172556135062039), - UINT64_C(13820348844234745256)}, -34}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_sqrtpi_v = Dec{UINT64_C(5641895835477562869), -19}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 inv_sqrtpi_v = decimal128{detail::uint128{UINT64_C(305847786088084), - UINT64_C(12695685840195063976)}, -34}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast inv_sqrtpi_v = decimal128_fast{detail::uint128{UINT64_C(305847786088084), - UINT64_C(12695685840195063976)}, -34}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec ln2_v = Dec{UINT64_C(6931471805599453094), -19}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 ln2_v = decimal128{detail::uint128{UINT64_C(375755839507647), - UINT64_C(8395602002641374208)}, -34}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast ln2_v = decimal128_fast{detail::uint128{UINT64_C(375755839507647), - UINT64_C(8395602002641374208)}, -34}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec ln10_v = Dec{UINT64_C(2302585092994045684), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 ln10_v = decimal128{detail::uint128{UINT64_C(124823388007844), - UINT64_C(1462833818723808456)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast ln10_v = decimal128_fast{detail::uint128{UINT64_C(124823388007844), - UINT64_C(1462833818723808456)}, -33}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec sqrt2_v = Dec{UINT64_C(1414213562373095049), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 sqrt2_v = decimal128{detail::uint128{UINT64_C(76664670834168), - UINT64_C(12987834932751794202)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast sqrt2_v = decimal128_fast{detail::uint128{UINT64_C(76664670834168), - UINT64_C(12987834932751794202)}, -33}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec sqrt3_v = Dec{UINT64_C(1732050807568877294), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 sqrt3_v = decimal128{detail::uint128{UINT64_C(93894662421072), - UINT64_C(8437766544231453518)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast sqrt3_v = decimal128_fast{detail::uint128{UINT64_C(93894662421072), - UINT64_C(8437766544231453518)}, -33}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec sqrt10_v = Dec{UINT64_C(3162277660168379332), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 sqrt10_v = decimal128{detail::uint128{UINT64_C(171427415457846), - UINT64_C(13450487317535253574)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast sqrt10_v = decimal128_fast{detail::uint128{UINT64_C(171427415457846), - UINT64_C(13450487317535253574)}, -33}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec cbrt2_v = Dec{UINT64_C(1259921049894873165), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 cbrt2_v = decimal128{detail::uint128{UINT64_C(68300456972811), - UINT64_C(17628749411094165652)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast cbrt2_v = decimal128_fast{detail::uint128{UINT64_C(68300456972811), - UINT64_C(17628749411094165652)}, -33}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec cbrt10_v = Dec{UINT64_C(2154434690031883722), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 cbrt10_v = decimal128{detail::uint128{UINT64_C(116792138570535), - UINT64_C(2467411419527284790)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast cbrt10_v = decimal128_fast{detail::uint128{UINT64_C(116792138570535), - UINT64_C(2467411419527284790)}, -33}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_sqrt2_v = Dec{UINT64_C(7071067811865475244), -19}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 inv_sqrt2_v = decimal128{detail::uint128{UINT64_C(383323354170843), - UINT64_C(9598942442630316202)}, -34}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast inv_sqrt2_v = decimal128_fast{detail::uint128{UINT64_C(383323354170843), - UINT64_C(9598942442630316202)}, -34}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec inv_sqrt3_v = Dec{UINT64_C(5773502691896257645), -19}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 inv_sqrt3_v = decimal128{detail::uint128{UINT64_C(312982208070241), - UINT64_C(9679144407061960114)}, -34}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast inv_sqrt3_v = decimal128_fast{detail::uint128{UINT64_C(312982208070241), - UINT64_C(9679144407061960114)}, -34}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec egamma_v = Dec{UINT64_C(5772156649015328606), -19}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 egamma_v = decimal128{detail::uint128{UINT64_C(312909238939453), - UINT64_C(7916302232898517972)}, -34}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast egamma_v = decimal128_fast{detail::uint128{UINT64_C(312909238939453), - UINT64_C(7916302232898517972)}, -34}; - -BOOST_DECIMAL_EXPORT template , bool> = true> -BOOST_DECIMAL_CONSTEXPR_VARIABLE Dec phi_v = Dec{UINT64_C(1618033988749894848), -18}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128 phi_v = decimal128{detail::uint128{UINT64_C(87713798287901), - UINT64_C(2061523135646567614)}, -33}; - -BOOST_DECIMAL_EXPORT template <> -BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION decimal128_fast phi_v = decimal128_fast{detail::uint128{UINT64_C(87713798287901), - UINT64_C(2061523135646567614)}, -33}; +// Explicitly defaulted variables like the STL provides BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto e {e_v}; BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto log10_2 {log10_2_v}; diff --git a/test/Jamfile b/test/Jamfile index 083d458bf..17470c929 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -4,13 +4,14 @@ # Distributed under the Boost Software License, Version 1.0. # https://www.boost.org/LICENSE_1_0.txt +require-b2 5.0.1 ; +import-search /boost/config/checks ; +import config : requires ; +import modules ; import testing ; -import ../../config/checks/config : requires ; project : requirements - /boost/charconv//boost_charconv - gcc:-Wall gcc:-Wextra @@ -98,6 +99,7 @@ run test_decimal64_fast_stream.cpp ; run test_decimal64_stream.cpp ; run test_decimal128_basis.cpp ; run test_decimal_quantum.cpp ; +run test_dpd_conversions.cpp ; run test_edges_and_behave.cpp ; run test_edit_members.cpp ; run test_ellint_1.cpp ; @@ -113,7 +115,7 @@ run test_fixed_width_trunc.cpp ; run test_float_conversion.cpp ; run-fail test_fprintf.cpp ; run test_frexp_ldexp.cpp ; -run test_from_chars.cpp ; +run test_from_chars.cpp /boost/charconv//boost_charconv ; run test_git_issue_266.cpp ; run test_git_issue_271.cpp ; run test_hash.cpp ; @@ -142,3 +144,11 @@ run test_tgamma.cpp ; run test_to_chars.cpp ; run test_to_string.cpp ; run test_zeta.cpp ; + +# Run the examples too +run ../examples/adl.cpp ; +run ../examples/basic_construction.cpp ; +run ../examples/bit_conversions.cpp ; +run ../examples/charconv.cpp ; +run ../examples/literals.cpp ; +run ../examples/rounding_mode.cpp ; diff --git a/test/benchmarks.cpp b/test/benchmarks.cpp index 4b47be4c9..59a1ebbf8 100644 --- a/test/benchmarks.cpp +++ b/test/benchmarks.cpp @@ -4,7 +4,6 @@ // https://www.boost.org/LICENSE_1_0.txt #include -#include #include #include #include @@ -17,6 +16,15 @@ #ifdef BOOST_DECIMAL_RUN_BENCHMARKS +#if __cplusplus >= 201703L +#if __has_include() +# include +# if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L +# define BOOST_DECIMAL_BENCHMARK_CHARCONV +# endif +#endif +#endif + using namespace boost::decimal; using namespace std::chrono_literals; @@ -165,8 +173,10 @@ static BOOST_DECIMAL_NO_INLINE void init_input_data( std::vector& data ) } } +#ifdef BOOST_DECIMAL_BENCHMARK_CHARCONV + template ::value, bool> = true> -static BOOST_NOINLINE void test_boost_to_chars( std::vector const& data, bool general, char const* label, int precision, const char* type ) +static BOOST_DECIMAL_NO_INLINE void test_boost_to_chars( std::vector const& data, bool general, char const* label, int precision, const char* type ) { auto t1 = std::chrono::steady_clock::now(); @@ -191,11 +201,11 @@ static BOOST_NOINLINE void test_boost_to_chars( std::vector const& data, bool auto t2 = std::chrono::steady_clock::now(); - std::cout << "boost::decimal::to_chars<" << std::left << std::setw(11) << type << ">, " << label << ", " << precision << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << "boost::decimal::to_chars<" << std::left << std::setw(11) << type << ">, " << label << ", " << precision << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } template ::value, bool> = true> -static BOOST_NOINLINE void test_boost_to_chars( std::vector const& data, bool general, char const* label, int precision, const char* type ) +static BOOST_DECIMAL_NO_INLINE void test_boost_to_chars( std::vector const& data, bool general, char const* label, int precision, const char* type ) { auto t1 = std::chrono::steady_clock::now(); @@ -207,11 +217,11 @@ static BOOST_NOINLINE void test_boost_to_chars( std::vector const& data, bool for( auto x: data ) { - boost::charconv::chars_format fmt = general? boost::charconv::chars_format::general: boost::charconv::chars_format::scientific; + std::chars_format fmt = general ? std::chars_format::general : std::chars_format::scientific; auto r = precision == 0? - boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), x, fmt ): - boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), x, fmt, precision ); + std::to_chars( buffer, buffer + sizeof( buffer ), x, fmt ): + std::to_chars( buffer, buffer + sizeof( buffer ), x, fmt, precision ); s += static_cast( r.ptr - buffer ); s += static_cast( buffer[0] ); @@ -220,7 +230,7 @@ static BOOST_NOINLINE void test_boost_to_chars( std::vector const& data, bool auto t2 = std::chrono::steady_clock::now(); - std::cout << "boost::charconv::to_chars<" << std::left << std::setw(11) << type << ">, " << label << ", " << precision << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << " std::to_chars<" << std::left << std::setw(10) << type << ">, " << label << ", " << precision << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } @@ -254,7 +264,7 @@ BOOST_DECIMAL_NO_INLINE void init_from_chars_input_data( std::vector con for( auto const& x: data ) { T y; - auto r = boost::charconv::from_chars( x.data(), x.data() + x.size(), y, general? boost::charconv::chars_format::general: boost::charconv::chars_format::scientific ); + auto r = std::from_chars( x.data(), x.data() + x.size(), y, general ? std::chars_format::general : std::chars_format::scientific ); s = static_cast(r.ec); } @@ -307,7 +317,7 @@ BOOST_DECIMAL_NO_INLINE void test_boost_from_chars( std::vector con auto t2 = std::chrono::steady_clock::now(); - std::cout << "boost::charconv::from_chars<" << std::left << std::setw(11) << type << ">, " << label << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << " std::from_chars<" << std::left << std::setw(11) << type << ">, " << label << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } template ::value, bool> = true> @@ -329,7 +339,7 @@ BOOST_DECIMAL_NO_INLINE void test_boost_from_chars( std::vector con auto t2 = std::chrono::steady_clock::now(); - std::cout << "boost::decimal::from_chars<" << std::left << std::setw(11) << type << ">, " << label << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << "boost::decimal::from_chars<" << std::left << std::setw(11) << type << ">, " << label << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } template @@ -342,6 +352,8 @@ void test_from_chars(bool general, const char* type) test_boost_from_chars( data, general, label, type ); } +#endif + int main() { const auto float_vector = generate_random_vector(); @@ -409,7 +421,7 @@ int main() test_two_element_operation(dec64_fast_vector, std::divides<>(), "Division", "dec64_fast"); test_two_element_operation(dec64_fast_vector, std::divides<>(), "Division", "dec128_fast"); -/* +#if 0 std::cout << "\n===== sqrt =====\n"; test_one_element_operation(float_vector, (float(*)(float))std::sqrt, "sqrt", "float"); @@ -417,13 +429,15 @@ int main() test_one_element_operation(dec32_vector, (decimal32(*)(decimal32))sqrt, "sqrt", "decimal32"); test_one_element_operation(dec64_vector, (decimal64(*)(decimal64))sqrt, "sqrt", "decimal64"); test_one_element_operation(dec128_vector, (decimal128(*)(decimal128))sqrt, "sqrt", "decimal128"); - +#endif +#ifdef BOOST_DECIMAL_BENCHMARK_CHARCONV std::cout << "\n===== to_chars =====\n"; test_to_chars("float"); test_to_chars("double"); test_to_chars("decimal32"); test_to_chars("decimal64"); - test_to_chars("decimal128"); + test_to_chars("dec32_fast"); + test_to_chars("dec64_fast"); std::cout << "\n===== from_chars =====\n"; test_from_chars(false, "float"); @@ -434,9 +448,11 @@ int main() test_from_chars(true, "decimal32"); test_from_chars(false, "decimal64"); test_from_chars(true, "decimal64"); - test_from_chars(false, "decimal128"); - test_from_chars(true, "decimal128"); -*/ + test_from_chars(false, "dec32_fast"); + test_from_chars(true, "dec32_fast"); + test_from_chars(false, "dec64_fast"); + test_from_chars(true, "dec64_fast"); +#endif std::cout << std::endl; return 1; diff --git a/test/test_bid_conversions.cpp b/test/test_bid_conversions.cpp index 712366047..bf20e095b 100644 --- a/test/test_bid_conversions.cpp +++ b/test/test_bid_conversions.cpp @@ -18,7 +18,7 @@ void test() for (std::size_t i {}; i < 1024; ++i) { const T val {dist(rng)}; - const auto bits {to_bid(val)}; + const auto bits {to_bid(val)}; const T return_val {from_bid(bits)}; BOOST_TEST_EQ(val, return_val); } diff --git a/test/test_dpd_conversions.cpp b/test/test_dpd_conversions.cpp new file mode 100644 index 000000000..da927cca9 --- /dev/null +++ b/test/test_dpd_conversions.cpp @@ -0,0 +1,80 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +using namespace boost::decimal; + +template +T roundtrip(T val) +{ + const auto bits {to_dpd(val)}; + return from_dpd(bits); +} + +template +void test() +{ + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < 1024; ++i) + { + const T val {dist(rng)}; + const T return_val {roundtrip(val)}; + BOOST_TEST_EQ(val, return_val); + } + + // Non-finite values + BOOST_TEST(isinf(roundtrip(std::numeric_limits::infinity()))); + BOOST_TEST(isinf(roundtrip(-std::numeric_limits::infinity()))); + BOOST_TEST(isnan(roundtrip(std::numeric_limits::quiet_NaN()))); + BOOST_TEST(isnan(roundtrip(-std::numeric_limits::quiet_NaN()))); + BOOST_TEST(isnan(roundtrip(std::numeric_limits::signaling_NaN()))); + BOOST_TEST(isnan(roundtrip(-std::numeric_limits::signaling_NaN()))); +} + +template +void test_float_range() +{ + using float_type = std::conditional_t::value || + std::is_same::value, float, double>; + + std::mt19937_64 rng(42); + std::uniform_real_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < 1024; ++i) + { + const T val {dist(rng)}; + const T return_val {roundtrip(val)}; + BOOST_TEST_EQ(val, return_val); + } +} + +int main() +{ + test(); + test(); + + test_float_range(); + test_float_range(); + + test(); + test(); + + test_float_range(); + test_float_range(); + + test(); + test(); + + test_float_range(); + test_float_range(); + + return boost::report_errors(); +}