diff --git a/.drone.jsonnet b/.drone.jsonnet index f016c10ba..813e1f5d5 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -159,9 +159,44 @@ local windows_pipeline(name, image, environment, arch = "amd64") = ), linux_pipeline( - "Linux 22.04 GCC 12 32 ASAN", + "Linux 22.04 GCC 12 32 ASAN 03", "cppalliance/droneubuntu2204:1", - { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03,11,14,17,20,2b', ADDRMD: '32' } + asan, + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '03', ADDRMD: '32' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 32 ASAN 11", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '11', ADDRMD: '32' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 32 ASAN 14", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '14', ADDRMD: '32' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 32 ASAN 17", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '17', ADDRMD: '32' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 32 ASAN 20", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '20', ADDRMD: '32' } + asan, + "g++-12-multilib", + ), + + linux_pipeline( + "Linux 22.04 GCC 12 32 ASAN 2b", + "cppalliance/droneubuntu2204:1", + { TOOLSET: 'gcc', COMPILER: 'g++-12', CXXSTD: '2b', ADDRMD: '32' } + asan, "g++-12-multilib", ), @@ -187,16 +222,30 @@ local windows_pipeline(name, image, environment, arch = "amd64") = ), linux_pipeline( - "Linux 24.04 GCC 14 32/64", + "Linux 24.04 GCC 14 32", + "cppalliance/droneubuntu2404:1", + { TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '03,11,14,17,20,23', ADDRMD: '32', CXXFLAGS: "-fexcess-precision=fast" }, + "g++-14-multilib", + ), + + linux_pipeline( + "Linux 24.04 GCC 14 64", + "cppalliance/droneubuntu2404:1", + { TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '03,11,14,17,20,23', ADDRMD: '64', CXXFLAGS: "-fexcess-precision=fast" }, + "g++-14-multilib", + ), + + linux_pipeline( + "Linux 24.04 GCC 14 GNU 32", "cppalliance/droneubuntu2404:1", - { TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '03,11,14,17,20,23', ADDRMD: '32,64', CXXFLAGS: "-fexcess-precision=fast" }, + { TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '03,11,14,17,20,23', ADDRMD: '32', CXXFLAGS: "-fexcess-precision=fast", CXXSTDDIALECT: "gnu" }, "g++-14-multilib", ), linux_pipeline( - "Linux 24.04 GCC 14 GNU 32/64", + "Linux 24.04 GCC 14 GNU 64", "cppalliance/droneubuntu2404:1", - { TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '03,11,14,17,20,23', ADDRMD: '32,64', CXXFLAGS: "-fexcess-precision=fast", CXXSTDDIALECT: "gnu" }, + { TOOLSET: 'gcc', COMPILER: 'g++-14', CXXSTD: '03,11,14,17,20,23', ADDRMD: '64', CXXFLAGS: "-fexcess-precision=fast", CXXSTDDIALECT: "gnu" }, "g++-14-multilib", ), @@ -358,16 +407,6 @@ local windows_pipeline(name, image, environment, arch = "amd64") = ["deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main"], ), - macos_pipeline( - "MacOS 10.15 Xcode 12.2 UBSAN", - { TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,17,2a' } + ubsan, - ), - - macos_pipeline( - "MacOS 10.15 Xcode 12.2 ASAN", - { TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,17,2a' } + asan, - ), - macos_pipeline( "MacOS 12.4 Xcode 13.4.1 UBSAN", { TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,17,20,2b' } + ubsan, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7c269c1f4..7e7674c5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -248,7 +248,7 @@ jobs: cxxstd: "03,11,14,17,20,2b" os: macos-14 - timeout-minutes: 120 + timeout-minutes: 180 runs-on: ${{matrix.os}} container: ${{matrix.container}} @@ -456,20 +456,30 @@ jobs: include: - toolset: msvc-14.2 cxxstd: "14,17,20,latest" - addrmd: 32,64 + addrmd: "32" os: windows-2019 - - toolset: msvc-14.3 + # B2 does not work with MSVC 17.10. Once it's updated we can re-enable these tests + # Still covered in drone + #- toolset: msvc-14.3 + # cxxstd: "14,17,20,latest" + # addrmd: "32" + # os: windows-2022 + - toolset: msvc-14.2 cxxstd: "14,17,20,latest" - addrmd: 32,64 - os: windows-2022 - - toolset: clang-win - cxxstd: "14,17,latest" - addrmd: "32" - os: windows-2022 - - toolset: clang-win - cxxstd: "14,17,latest" addrmd: "64" - os: windows-2022 + os: windows-2019 + #- toolset: msvc-14.3 + # cxxstd: "14,17,20,latest" + # addrmd: "64" + # os: windows-2022 + #- toolset: clang-win + # cxxstd: "14,17,latest" + # addrmd: "32" + # os: windows-2022 + #- toolset: clang-win + # cxxstd: "14,17,latest" + # addrmd: "64" + # os: windows-2022 - toolset: gcc cxxstd: "03,11,14,17,2a" addrmd: "64" diff --git a/doc/decimal.adoc b/doc/decimal.adoc index 428c64528..cd9906b12 100644 --- a/doc/decimal.adoc +++ b/doc/decimal.adoc @@ -20,6 +20,11 @@ include::decimal/generic_decimal.adoc[] include::decimal/decimal32.adoc[] include::decimal/decimal64.adoc[] include::decimal/decimal128.adoc[] +include::decimal/fast_types.adoc[] +include::decimal/decimal32_fast.adoc[] +include::decimal/decimal64_fast.adoc[] +include::decimal/decimal128_fast.adoc[] +include::decimal/conversions.adoc[] include::decimal/literals.adoc[] include::decimal/numbers.adoc[] include::decimal/cmath.adoc[] diff --git a/doc/decimal/benchmarks.adoc b/doc/decimal/benchmarks.adoc index 42408f8af..9a700ddae 100644 --- a/doc/decimal/benchmarks.adoc +++ b/doc/decimal/benchmarks.adoc @@ -32,20 +32,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` -| 8764 -| 1.577 +| 8587 +| 1.376 | `double` -| 5559 +| 6240 | 1.000 | `decimal32` -| 276,124 -| 49.672 +| 275,597 +| 44.166 | `decimal64` -| 355,999 -| 64.760 +| 296,929 +| 47.587 | `decimal128` -| 989,028 -| 177.915 +| 821,847 +| 131.706 +| `decimal32_fast` +| 99,664 +| 15.972 +| `decimal64_fast` +| 102,132 +| 16.367 +| `decimal128_fast` +| 146,302 +| 23.446 |=== == Basic Operations @@ -62,20 +71,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` -| 2113 -| 0.739 +| 2705 +| 0.859 | `double` -| 2860 +| 3148 | 1.000 | `decimal32` -| 353,836 -| 123.719 +| 351,505 +| 111.660 | `decimal64` -| 409,098 -| 143.041 +| 359,425 +| 114.176 | `decimal128` -| 2,418,039 -| 845.468 +| 1,446,674 +| 459.553 +| `decimal32_fast` +| 146,873 +| 46.656 +| `decimal64_fast` +| 139,294 +| 44.248 +| `decimal128_fast` +| 707,308 +| 224.685 |=== ==== Subtraction @@ -83,20 +101,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` -| 1782 -| 1.061 +| 3339 +| 2.014 | `double` -| 1680 +| 1658 | 1.000 | `decimal32` -| 293,927 -| 174.957 +| 267,646 +| 161.427 | `decimal64` -| 329,425 -| 196.086 +| 303,589 +| 183.106 | `decimal128` -| 1,527,261 -| 909.084 +| 954,211 +| 575.519 +| `decimal32_fast` +| 147,112 +| 88.729 +| `decimal64_fast` +| 145,606 +| 87.820 +| `decimal128_fast` +| 394,538 +| 2387.960 |=== ==== Multiplication @@ -104,20 +131,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` -| 1691 -| 0.979 +| 1646 +| 0.957 | `double` -| 1728 +| 1720 | 1.000 | `decimal32` -| 309,117 -| 178.887 +| 313,219 +| 182.104 | `decimal64` -| 408,010 -| 236.117 +| 583,818 +| 339.429 | `decimal128` -| 2,506,105 -| 1450.292 +| 1,881,936 +| 1094.149 +| `decimal32_fast` +| 86,093 +| 50.054 +| `decimal64_fast` +| 333,582 +| 193.943 +| `decimal128_fast` +| 1,269,429 +| 738.040 |=== ==== Division @@ -125,20 +161,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` -| 2058 -| 0.846 +| 2120 +| 0.547 | `double` -| 2434 +| 3874 | 1.000 | `decimal32` -| 304,852 -| 125.247 +| 307,337 +| 79.333 | `decimal64` -| 519,990 -| 213.636 +| 447,910 +| 115.620 | `decimal128` -| 3,534,909 -| 1452.304 +| 2,544,798 +| 656.892 +| `decimal32_fast` +| 105,796 +| 27.309 +| `decimal64_fast` +| 291,671 +| 75.289 +| `decimal128_fast` +| 302,003 +| 77.956 |=== == Selected Special Functions diff --git a/doc/decimal/cmath.adoc b/doc/decimal/cmath.adoc index a6f62fd96..ea8c0e349 100644 --- a/doc/decimal/cmath.adoc +++ b/doc/decimal/cmath.adoc @@ -458,3 +458,14 @@ constexpr boost::decimal::detail::uint128 frexpd128(decimal128 num, int* expptr) This function is very similar to https://en.cppreference.com/w/cpp/numeric/math/frexp[frexp], but returns the significand and an integral power of 10 since the `FLT_RADIX` of this type is 10. The significand is normalized to the number of digits of precision the type has (e.g. for decimal32 it is [1'000'000, 9'999'999]). + +=== trunc_to + +[source, c++] +---- +template +constexpr Decimal trunc_to(Decimal val, int precision = 0); +---- + +The function returns the decimal type with number of fractional digits equal to the value of precision. +`trunc_to` is similar to https://en.cppreference.com/w/cpp/numeric/math/trunc[trunc], and with the default precision argument of 0 it is identical. diff --git a/doc/decimal/config.adoc b/doc/decimal/config.adoc index 461aa0d2d..4c2997544 100644 --- a/doc/decimal/config.adoc +++ b/doc/decimal/config.adoc @@ -25,3 +25,6 @@ constexpr decimal64 half {5, -1}; std::complex test_val {half, half}; const auto res = std::acos(test_val); ---- + +- `BOOST_DECIMAL_FAST_MATH` performs optimizations similar to that of the `-ffast-math` compiler flag such as removing all checks for non-finite values. +This flag increases the performance of the basis operations (e.g. add, sub, mul, div, and comparisons) by up to 20%. diff --git a/doc/decimal/conversions.adoc b/doc/decimal/conversions.adoc new file mode 100644 index 000000000..6a199b7e7 --- /dev/null +++ b/doc/decimal/conversions.adoc @@ -0,0 +1,53 @@ +//// +Copyright 2024 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#conversions] += Fast Type 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. + +[source, c++] +---- +namespace boost { +namespace decimal { + +namespace detail { + +struct uint128 +{ + std::uint64_t hi; + std::uint64_t lo; +}; + +} // namespace detail + +BOOST_DECIMAL_CXX20_CONSTEXPR std::uint32_t to_bid_d32(decimal32 val) noexcept; + +BOOST_DECIMAL_CXX20_CONSTEXPR std::uint32_t to_bid_d32f(decimal32_fast val) noexcept; + +BOOST_DECIMAL_CXX20_CONSTEXPR std::uint64_t to_bid_d64(decimal64 val) noexcept; + +BOOST_DECIMAL_CXX20_CONSTEXPR std::uint64_t to_bid_d64f(decimal64_fast val) noexcept; + +BOOST_DECIMAL_CXX20_CONSTEXPR detail::uint128 to_bid_d128(decimal128 val) noexcept; + +template +BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid(T val) noexcept; + +template +BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(std::uint32_t bits) noexcept; + +template +BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(std::uint64_t bits) noexcept; + +template +BOOST_DECIMAL_CXX20_CONSTEXPR T from_bid(detail::uint128 bits) noexcept; + +} // namespace decimal +} // namespace boost +---- diff --git a/doc/decimal/decimal128.adoc b/doc/decimal/decimal128.adoc index 708dfc527..1ecbf5d99 100644 --- a/doc/decimal/decimal128.adoc +++ b/doc/decimal/decimal128.adoc @@ -10,7 +10,7 @@ https://www.boost.org/LICENSE_1_0.txt == Description -Decimal64 is the 128-bit version of the decimal interchange format, and has the following properties as defined in IEEE 754-2019 table 3.6 +Decimal128 is the 128-bit version of the decimal interchange format, and has the following properties as defined in IEEE 754-2019 table 3.6 - Storage width - 128 bits - Precision - 34 decimal digits (not bits like binary) diff --git a/doc/decimal/decimal128_fast.adoc b/doc/decimal/decimal128_fast.adoc new file mode 100644 index 000000000..8a24add07 --- /dev/null +++ b/doc/decimal/decimal128_fast.adoc @@ -0,0 +1,91 @@ +//// +Copyright 2024 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#decimal128_fast] += Decimal128_fast +:idprefix: decimal128_fast_ + +== Description + +`decimal128_fast` has the same ranges of values and representations as `decimal128_fast` but with greater performance. +The performance changes by being non-IEEE 754 compliant so that the value does not have to be decoded from bits, but is instead directly represented internal to the type. +As is often the case this trades space for time by having greater storage width requirements. + +- Storage width - at least 168 bits (`uint128` + `std::uint_fast32_t` + `bool`) +- Precision - 34 decimal digits (not bits like binary) +- Max exponent - 6145 +- Max Value - 9.99999...e6145 +- Smallest normalized value - 1.0000...e-6142 +- Smallest subnormal - 1e-6176 + +[source, c++] +---- +#include + +namespace boost { +namespace decimal { + +// Paragraph numbers are from ISO/IEC DTR 24733 + +// 3.2.4.1 construct/copy/destroy +constexpr decimal128_fast() noexcept = default; + +// 3.2.4.2 Conversion form floating-point type +template +explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_fast(Float val) noexcept; + +// 3.2.4.3 Conversion from integral type +template +explicit constexpr decimal128_fast(Integer val) noexcept; + +template +constexpr decimal128_fast(Integral1 coeff, Integral2 exp, bool sign = false) noexcept; + +template +constexpr decimal128_fast& operator=(const Integeral& RHS) noexcept; + +// 3.2.4.4 Conversion to integral type +explicit constexpr operator int() const noexcept; +explicit constexpr operator unsigned() const noexcept; +explicit constexpr operator long() const noexcept; +explicit constexpr operator unsigned long() const noexcept; +explicit constexpr operator long long() const noexcept; +explicit constexpr operator unsigned long long() const noexcept; +explicit constexpr operator std::int8_t() const noexcept; +explicit constexpr operator std::uint8_t() const noexcept; +explicit constexpr operator std::int16_t() const noexcept; +explicit constexpr operator std::uint16_t() const noexcept; + +// 3.2.4.5 increment and decrement operators: +constexpr decimal128_fast& operator++(); +constexpr decimal128_fast operator++(int); +constexpr decimal128_fast& operator--(); +constexpr decimal128_fast operator--(int); + +// 3.2.4.6 compound assignment: +constexpr decimal128_fast& operator+=(RHS rhs); +constexpr decimal128_fast& operator-=(RHS rhs); +constexpr decimal128_fast& operator*=(RHS rhs); +constexpr decimal128_fast& operator/=(RHS rhs); + +// 3.2.6 Conversion to floating-point type +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept; +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept; +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept; + +// The following are available assuming a C++23 compiler that provides the header +explicit constexpr operator std::float16_t() const noexcept; +explicit constexpr operator std::float32_t() const noexcept; +explicit constexpr operator std::float64_t() const noexcept; +explicit constexpr operator std::bfloat16_t() const noexcept; + +explicit constexpr operator decimal32() const noexcept; +explicit constexpr operator decimal64() const noexcept; + +} //namespace decimal +} //namespace boost + +---- diff --git a/doc/decimal/decimal32_fast.adoc b/doc/decimal/decimal32_fast.adoc new file mode 100644 index 000000000..c1e8af44e --- /dev/null +++ b/doc/decimal/decimal32_fast.adoc @@ -0,0 +1,91 @@ +//// +Copyright 2023 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#decimal32_fast] += Decimal32_fast +:idprefix: decimal32_fast_ + +== Description + +`decimal32_fast` has the same ranges of values and representations as `decimal32` but with greater performance. +The performance changes by being non-IEEE 754 compliant so that the value does not have to be decoded from bits, but is instead directly represented internal to the type. +As is often the case this trades space for time by having greater storage width requirements. + +- Storage width - At least 48bits (`std::uint_fast32_t` + `std::uint_fast8_t` + `bool`) +- Precision - 7 decimal digits (not bits like binary) +- Max exponent - 96 +- Max Value - 9.999999e96 +- Smallest normalized value - 1.000000e-95 +- Smallest subnormal - 1e-101 + +[source, c++] +---- +#include + +namespace boost { +namespace decimal { + +// Paragraph numbers are from ISO/IEC DTR 24733 + +// 3.2.2.1 construct/copy/destroy +constexpr decimal32_fast() noexcept = default; + +// 3.2.2.2 Conversion form floating-point type +template +explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_fast(Float val) noexcept; + +// 3.2.2.3 Conversion from integral type +template +explicit constexpr decimal32_fast(Integer val) noexcept; + +template +constexpr decimal32_fast(Integral1 coeff, Integral2 exp, bool sign = false) noexcept; + +template +constexpr decimal32_fast& operator=(const Integeral& RHS) noexcept; + +// 3.2.2.4 Conversion to integral type +explicit constexpr operator int() const noexcept; +explicit constexpr operator unsigned() const noexcept; +explicit constexpr operator long() const noexcept; +explicit constexpr operator unsigned long() const noexcept; +explicit constexpr operator long long() const noexcept; +explicit constexpr operator unsigned long long() const noexcept; +explicit constexpr operator std::int8_t() const noexcept; +explicit constexpr operator std::uint8_t() const noexcept; +explicit constexpr operator std::int16_t() const noexcept; +explicit constexpr operator std::uint16_t() const noexcept; + +// 3.2.2.5 increment and decrement operators: +constexpr decimal32_fast& operator++(); +constexpr decimal32_fast operator++(int); +constexpr decimal32_fast& operator--(); +constexpr decimal32_fast operator--(int); + +// 3.2.2.6 compound assignment: +constexpr decimal32_fast& operator+=(RHS rhs); +constexpr decimal32_fast& operator-=(RHS rhs); +constexpr decimal32_fast& operator*=(RHS rhs); +constexpr decimal32_fast& operator/=(RHS rhs); + +// 3.2.6 Conversion to floating-point type +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept; +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept; +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept; + +// The following are available assuming a C++23 compiler that provides the header +explicit constexpr operator std::float16_t() const noexcept; +explicit constexpr operator std::float32_t() const noexcept; +explicit constexpr operator std::float64_t() const noexcept; +explicit constexpr operator std::bfloat16_t() const noexcept; + +explicit constexpr operator decimal64() const noexcept; +explicit constexpr operator decimal128() const noexcept; + +} //namespace decimal +} //namespace boost + +---- diff --git a/doc/decimal/decimal64_fast.adoc b/doc/decimal/decimal64_fast.adoc new file mode 100644 index 000000000..99919ded3 --- /dev/null +++ b/doc/decimal/decimal64_fast.adoc @@ -0,0 +1,91 @@ +//// +Copyright 2023 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#decimal64_fast] += Decimal64_fast +:idprefix: decimal64_fast_ + +== Description + +`decimal64_fast` has the same ranges of values and representations as `decimal64` but with greater performance. +The performance changes by being non-IEEE 754 compliant so that the value does not have to be decoded from bits, but is instead directly represented internal to the type. +As is often the case this trades space for time by having greater storage width requirements. + +- Storage width - At least 88 bits (`std::uint_fast64_t` + `std::uint_fast_16_t` + `bool`) +- Precision - 16 decimal digits (not bits like binary) +- Max exponent - 385 +- Max Value - 9.999999999999999e385 +- Smallest normalized value - 1.000000000000000e-382 +- Smallest subnormal - 1e-398 + +[source, c++] +---- +#include + +namespace boost { +namespace decimal { + +// Paragraph numbers are from ISO/IEC DTR 24733 + +// 3.2.3.1 construct/copy/destroy +constexpr decimal64_fast() noexcept = default; + +// 3.2.2.2 Conversion form floating-point type +template +explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_fast(Float val) noexcept; + +// 3.2.3.3 Conversion from integral type +template +explicit constexpr decimal64_fast(Integer val) noexcept; + +template +constexpr decimal64_fast(Integral1 coeff, Integral2 exp, bool sign = false) noexcept; + +template +constexpr decimal64_fast& operator=(const Integeral& RHS) noexcept; + +// 3.2.3.4 Conversion to integral type +explicit constexpr operator int() const noexcept; +explicit constexpr operator unsigned() const noexcept; +explicit constexpr operator long() const noexcept; +explicit constexpr operator unsigned long() const noexcept; +explicit constexpr operator long long() const noexcept; +explicit constexpr operator unsigned long long() const noexcept; +explicit constexpr operator std::int8_t() const noexcept; +explicit constexpr operator std::uint8_t() const noexcept; +explicit constexpr operator std::int16_t() const noexcept; +explicit constexpr operator std::uint16_t() const noexcept; + +// 3.2.3.5 increment and decrement operators: +constexpr decimal64_fast& operator++(); +constexpr decimal64_fast operator++(int); +constexpr decimal64_fast& operator--(); +constexpr decimal64_fast operator--(int); + +// 3.2.3.6 compound assignment: +constexpr decimal64_fast& operator+=(RHS rhs); +constexpr decimal64_fast& operator-=(RHS rhs); +constexpr decimal64_fast& operator*=(RHS rhs); +constexpr decimal64_fast& operator/=(RHS rhs); + +// 3.2.6 Conversion to floating-point type +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept; +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept; +explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept; + +// The following are available assuming a C++23 compiler that provides the header +explicit constexpr operator std::float16_t() const noexcept; +explicit constexpr operator std::float32_t() const noexcept; +explicit constexpr operator std::float64_t() const noexcept; +explicit constexpr operator std::bfloat16_t() const noexcept; + +explicit constexpr operator decimal32() const noexcept; +explicit constexpr operator decimal128() const noexcept; + +} //namespace decimal +} //namespace boost + +---- diff --git a/doc/decimal/fast_types.adoc b/doc/decimal/fast_types.adoc new file mode 100644 index 000000000..4073ed509 --- /dev/null +++ b/doc/decimal/fast_types.adoc @@ -0,0 +1,14 @@ +//// +Copyright 2024 Matt Borland +Distributed under the Boost Software License, Version 1.0. +https://www.boost.org/LICENSE_1_0.txt +//// + +[#fast_types] += Fast Types +:idprefix: fast_types_ + +Now that we have seen the three basic types as specified in IEEE-754 there are three additional adjacent types: `decimal32_fast`, `decimal64_fast`, and `decimal128_fast`. +These types yield identical computational results, but with ~3x faster performance. +These types make the classic tradeoff of space for time as they require more storage width than the IEEE 754 conformant type. + diff --git a/include/boost/decimal.hpp b/include/boost/decimal.hpp index 441d6260d..b8aa912ea 100644 --- a/include/boost/decimal.hpp +++ b/include/boost/decimal.hpp @@ -27,7 +27,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -38,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 new file mode 100644 index 000000000..cb84317b1 --- /dev/null +++ b/include/boost/decimal/bid_conversion.hpp @@ -0,0 +1,177 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_BID_CONVERSION_HPP +#define BOOST_DECIMAL_BID_CONVERSION_HPP + +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace decimal { + +#if defined(__GNUC__) && __GNUC__ == 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif + +BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d32(decimal32 val) noexcept -> std::uint32_t +{ + const auto bits {detail::bit_cast(val)}; + return bits; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d32(std::uint32_t bits) noexcept -> decimal32 +{ + const auto val {detail::bit_cast(bits)}; + return val; +} + +BOOST_DECIMAL_CXX20_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; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d32f(std::uint32_t bits) noexcept -> decimal32_fast +{ + const auto compliant_val {detail::bit_cast(bits)}; + const decimal32_fast val {compliant_val}; + return val; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d64(decimal64 val) noexcept -> std::uint64_t +{ + const auto bits {detail::bit_cast(val)}; + return bits; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d64(std::uint64_t bits) noexcept -> decimal64 +{ + const auto val {detail::bit_cast(bits)}; + return val; +} + +BOOST_DECIMAL_CXX20_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; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d64f(std::uint64_t bits) noexcept -> decimal64_fast +{ + const auto compliant_val {detail::bit_cast(bits)}; + const decimal64_fast val {compliant_val}; + return val; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bid_d128(decimal128 val) noexcept -> detail::uint128 +{ + const auto bits {detail::bit_cast(val)}; + return bits; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d128(detail::uint128 bits) noexcept -> decimal128 +{ + const auto val {detail::bit_cast(bits)}; + return val; +} + +BOOST_DECIMAL_CXX20_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; +} + +BOOST_DECIMAL_CXX20_CONSTEXPR auto from_bid_d128f(detail::uint128 bits) noexcept -> decimal128_fast +{ + const auto compliant_val {detail::bit_cast(bits)}; + const decimal128_fast val {compliant_val}; + return val; +} + +BOOST_DECIMAL_CXX20_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 +{ + return to_bid_d32f(val); +} + +BOOST_DECIMAL_CXX20_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 +{ + return to_bid_d64f(val); +} + +BOOST_DECIMAL_CXX20_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 +{ + return to_bid_d128f(val); +} + +template +BOOST_DECIMAL_CXX20_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 +{ + return from_bid_d32(bits); +} + +template +BOOST_DECIMAL_CXX20_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 +{ + return from_bid_d64(bits); +} + +template +BOOST_DECIMAL_CXX20_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 +{ + return from_bid_d128(bits); +} + +#if defined(__GNUC__) && __GNUC__ == 7 +# pragma GCC diagnostic pop +#endif + +} // namespace decimal +} // namespace boost + +#endif //BOOST_BID_CONVERSION_HPP diff --git a/include/boost/decimal/charconv.hpp b/include/boost/decimal/charconv.hpp index 789ea0640..dd8693150 100644 --- a/include/boost/decimal/charconv.hpp +++ b/include/boost/decimal/charconv.hpp @@ -103,11 +103,21 @@ BOOST_DECIMAL_EXPORT constexpr auto from_chars(const char* first, const char* la return detail::from_chars_general_impl(first, last, value, fmt); } +BOOST_DECIMAL_EXPORT constexpr auto from_chars(const char* first, const char* last, decimal64_fast& value, chars_format fmt = chars_format::general) noexcept +{ + return detail::from_chars_general_impl(first, last, value, fmt); +} + BOOST_DECIMAL_EXPORT constexpr auto from_chars(const char* first, const char* last, decimal128& value, chars_format fmt = chars_format::general) noexcept { return detail::from_chars_general_impl(first, last, value, fmt); } +BOOST_DECIMAL_EXPORT constexpr auto from_chars(const char* first, const char* last, decimal128_fast& value, chars_format fmt = chars_format::general) noexcept +{ + return detail::from_chars_general_impl(first, last, value, fmt); +} + // --------------------------------------------------------------------------------------------------------------------- // to_chars and implementation // --------------------------------------------------------------------------------------------------------------------- @@ -237,7 +247,7 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_scientific_impl(char* first, char* last, c int exp {}; auto significand {frexp10(value, &exp)}; - using uint_type = std::conditional_t::value, uint128, std::uint64_t>; + using uint_type = std::conditional_t::value || std::is_same::value, uint128, std::uint64_t>; auto significand_digits = num_digits(significand); exp += significand_digits - 1; bool append_zeros = false; @@ -468,7 +478,8 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const } } - using uint_type = std::conditional_t::value, uint128, std::uint64_t>; + using uint_type = std::conditional_t::value || + std::is_same::value, uint128, std::uint64_t>; auto r = to_chars_integer_impl(first, last, significand, 10); if (BOOST_DECIMAL_UNLIKELY(!r)) @@ -574,7 +585,8 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_fixed_impl(char* first, char* last, const template BOOST_DECIMAL_CONSTEXPR auto to_chars_hex_impl(char* first, char* last, const TargetDecimalType& value, int precision = -1) noexcept -> to_chars_result { - using Unsigned_Integer = std::conditional_t::value, std::uint64_t, uint128>; + using Unsigned_Integer = std::conditional_t::value || + std::is_same::value, uint128, std::uint64_t>; if (signbit(value)) { @@ -712,8 +724,8 @@ BOOST_DECIMAL_CONSTEXPR auto to_chars_impl(char* first, char* last, TargetDecima } auto abs_value = abs(value); - constexpr auto max_fractional_value = std::is_same::value ? TargetDecimalType{1, 7} : - std::is_same::value ? TargetDecimalType{1, 16} : + constexpr auto max_fractional_value = std::is_same::value || std::is_same::value ? TargetDecimalType{1, 7} : + std::is_same::value || std::is_same::value ? TargetDecimalType{1, 16} : TargetDecimalType{1, 34}; constexpr auto min_fractional_value = TargetDecimalType{1, -4}; @@ -823,6 +835,26 @@ BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* la return detail::to_chars_impl(first, last, value, fmt, precision); } +BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* last, decimal64_fast value) noexcept -> to_chars_result +{ + return detail::to_chars_impl(first, last, value); +} + +BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* last, decimal64_fast value, chars_format fmt) noexcept -> to_chars_result +{ + return detail::to_chars_impl(first, last, value, fmt); +} + +BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* last, decimal64_fast value, chars_format fmt, int precision) noexcept -> to_chars_result +{ + if (precision < 0) + { + precision = 6; + } + + return detail::to_chars_impl(first, last, value, fmt, precision); +} + BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* last, decimal128 value) noexcept -> to_chars_result { return detail::to_chars_impl(first, last, value); @@ -843,6 +875,26 @@ BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* la return detail::to_chars_impl(first, last, value, fmt, precision); } +BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* last, decimal128_fast value) noexcept -> to_chars_result +{ + return detail::to_chars_impl(first, last, value); +} + +BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* last, decimal128_fast value, chars_format fmt) noexcept -> to_chars_result +{ + return detail::to_chars_impl(first, last, value, fmt); +} + +BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR auto to_chars(char* first, char* last, decimal128_fast value, chars_format fmt, int precision) noexcept -> to_chars_result +{ + if (precision < 0) + { + precision = 6; + } + + return detail::to_chars_impl(first, last, value, fmt, precision); +} + template struct limits { diff --git a/include/boost/decimal/cmath.hpp b/include/boost/decimal/cmath.hpp index acc5e9ffe..a6d285c88 100644 --- a/include/boost/decimal/cmath.hpp +++ b/include/boost/decimal/cmath.hpp @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,8 @@ #include #include #include +#include +#include #include // Macros from 3.6.2 @@ -83,56 +86,108 @@ namespace boost { namespace decimal { +// Overloads for all the functions that are implemented individually as friends + BOOST_DECIMAL_EXPORT constexpr auto scalbn(decimal32 num, int expval) noexcept -> decimal32 { return scalbnd32(num, expval); } +BOOST_DECIMAL_EXPORT constexpr auto scalbn(decimal32_fast num, int expval) noexcept -> decimal32_fast +{ + return scalbnd32f(num, expval); +} + BOOST_DECIMAL_EXPORT constexpr auto scalbn(decimal64 num, int expval) noexcept -> decimal64 { return scalbnd64(num, expval); } +BOOST_DECIMAL_EXPORT constexpr auto scalbn(decimal64_fast num, int expval) noexcept -> decimal64_fast +{ + return scalbnd64f(num, expval); +} + BOOST_DECIMAL_EXPORT constexpr auto scalbn(decimal128 num, int expval) noexcept -> decimal128 { return scalbnd128(num, expval); } +BOOST_DECIMAL_EXPORT constexpr auto scalbn(decimal128_fast num, int expval) noexcept -> decimal128_fast +{ + return scalbnd128f(num, expval); +} + BOOST_DECIMAL_EXPORT constexpr auto scalbln(decimal32 num, long expval) noexcept -> decimal32 { return scalblnd32(num, expval); } +BOOST_DECIMAL_EXPORT constexpr auto scalbln(decimal32_fast num, long expval) noexcept -> decimal32_fast +{ + return scalblnd32f(num, expval); +} + BOOST_DECIMAL_EXPORT constexpr auto scalbln(decimal64 num, long expval) noexcept -> decimal64 { return scalblnd64(num, expval); } +BOOST_DECIMAL_EXPORT constexpr auto scalbln(decimal64_fast num, long expval) noexcept -> decimal64_fast +{ + return scalblnd64f(num, expval); +} + BOOST_DECIMAL_EXPORT constexpr auto scalbln(decimal128 num, long expval) noexcept -> decimal128 { return scalblnd128(num, expval); } +BOOST_DECIMAL_EXPORT constexpr auto scalbln(decimal128_fast num, long expval) noexcept -> decimal128_fast +{ + return scalblnd128f(num, expval); +} + BOOST_DECIMAL_EXPORT constexpr auto copysign(decimal32 mag, decimal32 sgn) noexcept -> decimal32 { return copysignd32(mag, sgn); } +BOOST_DECIMAL_EXPORT constexpr auto copysign(decimal32_fast mag, decimal32_fast sgn) noexcept -> decimal32_fast +{ + return copysignd32f(mag, sgn); +} + BOOST_DECIMAL_EXPORT constexpr auto copysign(decimal64 mag, decimal64 sgn) noexcept -> decimal64 { return copysignd64(mag, sgn); } +BOOST_DECIMAL_EXPORT constexpr auto copysign(decimal64_fast mag, decimal64_fast sgn) noexcept -> decimal64_fast +{ + return copysignd64f(mag, sgn); +} + BOOST_DECIMAL_EXPORT constexpr auto copysign(decimal128 mag, decimal128 sgn) noexcept -> decimal128 { return copysignd128(mag, sgn); } +BOOST_DECIMAL_EXPORT constexpr auto copysign(decimal128_fast mag, decimal128_fast sgn) noexcept -> decimal128_fast +{ + return copysignd128f(mag, sgn); +} + BOOST_DECIMAL_EXPORT constexpr auto samequantum(decimal32 lhs, decimal32 rhs) noexcept -> bool { return samequantumd32(lhs, rhs); } +BOOST_DECIMAL_EXPORT constexpr auto samequantum(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool +{ + return samequantumd32f(lhs, rhs); +} + BOOST_DECIMAL_EXPORT constexpr auto samequantum(decimal64 lhs, decimal64 rhs) noexcept -> bool { return samequantumd64(lhs, rhs); @@ -143,11 +198,21 @@ BOOST_DECIMAL_EXPORT constexpr auto samequantum(decimal128 lhs, decimal128 rhs) return samequantumd128(lhs, rhs); } +BOOST_DECIMAL_EXPORT constexpr auto samequantum(decimal128_fast lhs, decimal128_fast rhs) noexcept -> bool +{ + return samequantumd128f(lhs, rhs); +} + BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal32 x) noexcept -> int { return quantexpd32(x); } +BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal32_fast x) noexcept -> int +{ + return quantexpd32f(x); +} + BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal64 x) noexcept -> int { return quantexpd64(x); @@ -158,11 +223,21 @@ BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal128 x) noexcept -> int return quantexpd128(x); } +BOOST_DECIMAL_EXPORT constexpr auto quantexp(decimal128_fast x) noexcept -> int +{ + return quantexpd128f(x); +} + BOOST_DECIMAL_EXPORT constexpr auto quantize(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { return quantized32(lhs, rhs); } +BOOST_DECIMAL_EXPORT constexpr auto quantize(decimal32_fast lhs, decimal32_fast rhs) noexcept -> decimal32_fast +{ + return quantized32f(lhs, rhs); +} + BOOST_DECIMAL_EXPORT constexpr auto quantize(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { return quantized64(lhs, rhs); @@ -173,6 +248,11 @@ BOOST_DECIMAL_EXPORT constexpr auto quantize(decimal128 lhs, decimal128 rhs) noe return quantized128(lhs, rhs); } +BOOST_DECIMAL_EXPORT constexpr auto quantize(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast +{ + return quantized128f(lhs, rhs); +} + } // namespace decimal } // namespace boost diff --git a/include/boost/decimal/cstdlib.hpp b/include/boost/decimal/cstdlib.hpp index 5e03e9997..67c68a25b 100644 --- a/include/boost/decimal/cstdlib.hpp +++ b/include/boost/decimal/cstdlib.hpp @@ -1,4 +1,4 @@ -// Copyright 2023 Matt Borland +// Copyright 2023 - 2024 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -32,7 +32,8 @@ namespace detail { template inline auto strtod_calculation(const char* str, char** endptr, char* buffer, std::size_t str_length) noexcept -> TargetDecimalType { - using significand_type = std::conditional_t::value, detail::uint128, std::uint64_t>; + using significand_type = std::conditional_t::value || + std::is_same::value, detail::uint128, std::uint64_t>; std::memcpy(buffer, str, str_length); convert_string_to_c_locale(buffer); @@ -199,6 +200,16 @@ BOOST_DECIMAL_EXPORT inline auto wcstod32(const wchar_t* str, wchar_t** endptr) return detail::wcstod_impl(str, endptr); } +BOOST_DECIMAL_EXPORT inline auto strtod32f(const char* str, char** endptr) noexcept -> decimal32_fast +{ + return detail::strtod_impl(str, endptr); +} + +BOOST_DECIMAL_EXPORT inline auto wcstod32f(const wchar_t* str, wchar_t** endptr) noexcept -> decimal32_fast +{ + return detail::wcstod_impl(str, endptr); +} + BOOST_DECIMAL_EXPORT inline auto strtod64(const char* str, char** endptr) noexcept -> decimal64 { return detail::strtod_impl(str, endptr); @@ -209,6 +220,16 @@ BOOST_DECIMAL_EXPORT inline auto wcstod64(const wchar_t* str, wchar_t** endptr) return detail::wcstod_impl(str, endptr); } +BOOST_DECIMAL_EXPORT inline auto strtod64f(const char* str, char** endptr) noexcept -> decimal64_fast +{ + return detail::strtod_impl(str, endptr); +} + +BOOST_DECIMAL_EXPORT inline auto wcstod64f(const wchar_t* str, wchar_t** endptr) noexcept -> decimal64_fast +{ + return detail::wcstod_impl(str, endptr); +} + BOOST_DECIMAL_EXPORT inline auto strtod128(const char* str, char** endptr) noexcept -> decimal128 { return detail::strtod_impl(str, endptr); @@ -219,6 +240,16 @@ BOOST_DECIMAL_EXPORT inline auto wcstod128(const wchar_t* str, wchar_t** endptr) return detail::wcstod_impl(str, endptr); } +BOOST_DECIMAL_EXPORT inline auto strtod128f(const char* str, char** endptr) noexcept -> decimal128_fast +{ + return detail::strtod_impl(str, endptr); +} + +BOOST_DECIMAL_EXPORT inline auto wcstod128f(const wchar_t* str, wchar_t** endptr) noexcept -> decimal128_fast +{ + return detail::wcstod_impl(str, endptr); +} + } // namespace decimal } // namespace boost diff --git a/include/boost/decimal/decimal128.hpp b/include/boost/decimal/decimal128.hpp index 0e3861575..aab6380a9 100644 --- a/include/boost/decimal/decimal128.hpp +++ b/include/boost/decimal/decimal128.hpp @@ -33,6 +33,10 @@ #include #include #include +#include +#include +#include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -128,6 +132,8 @@ BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 d128_big_combination_field_mask {UINT64 struct decimal128_components { + using sig_type = uint128; + uint128 sig {}; std::int32_t exp {}; bool sign {}; @@ -201,23 +207,6 @@ BOOST_DECIMAL_EXPORT class decimal128 final -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_decimal_floating_point_v), bool>; - template - constexpr auto d128_add_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept - -> detail::decimal128_components; - - template - constexpr auto d128_sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, - bool abs_lhs_bigger) noexcept -> detail::decimal128_components; - - 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 -> detail::decimal128_components; - - friend constexpr auto d128_generic_div_imp(detail::decimal128_components lhs, detail::decimal128_components rhs, - detail::decimal128_components& q) noexcept -> void; - friend constexpr auto d128_div_impl(decimal128 lhs, decimal128 rhs, decimal128& q, decimal128& r) noexcept -> void; friend constexpr auto d128_mod_impl(decimal128 lhs, decimal128 rhs, const decimal128& q, decimal128& r) noexcept -> void; @@ -893,6 +882,7 @@ template , b #endif BOOST_DECIMAL_CXX20_CONSTEXPR decimal128::decimal128(Float val) noexcept { + #ifndef BOOST_DECIMAL_FAST_MATH if (val != val) { *this = from_bits(detail::d128_nan_mask); @@ -902,6 +892,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal128::decimal128(Float val) noexcept *this = from_bits(detail::d128_inf_mask); } else + #endif { const auto components {detail::ryu::floating_point_to_fd128(val)}; @@ -1150,11 +1141,13 @@ constexpr auto operator-(decimal128 rhs) noexcept-> decimal128 constexpr auto operator==(decimal128 lhs, decimal128 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()); @@ -1195,6 +1188,7 @@ constexpr auto operator!=(Integer lhs, decimal128 rhs) noexcept constexpr auto operator<(decimal128 lhs, decimal128 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs) || (!lhs.isneg() && rhs.isneg())) { @@ -1206,15 +1200,18 @@ constexpr auto operator<(decimal128 lhs, decimal128 rhs) noexcept -> bool } else if (isfinite(lhs) && isinf(rhs)) { - if (!rhs.isneg()) - { - return true; - } - else - { - return false; - } + return !rhs.isneg(); + } + #else + if (!lhs.isneg() && rhs.isneg()) + { + return false; } + else if (lhs.isneg() && !rhs.isneg()) + { + return true; + } + #endif return less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); @@ -1231,20 +1228,24 @@ template constexpr auto operator<(Integer lhs, decimal128 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !less_impl(rhs, lhs) && lhs != rhs; } constexpr auto operator<=(decimal128 lhs, decimal128 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(rhs < lhs); } @@ -1253,10 +1254,12 @@ template constexpr auto operator<=(decimal128 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs)) { return false; } + #endif return !(rhs < lhs); } @@ -1265,10 +1268,12 @@ template constexpr auto operator<=(Integer lhs, decimal128 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !(rhs < lhs); } @@ -1294,10 +1299,12 @@ constexpr auto operator>(Integer lhs, decimal128 rhs) noexcept constexpr auto operator>=(decimal128 lhs, decimal128 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(lhs < rhs); } @@ -1306,10 +1313,12 @@ template constexpr auto operator>=(decimal128 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs)) { return false; } + #endif return !(lhs < rhs); } @@ -1318,10 +1327,12 @@ template constexpr auto operator>=(Integer lhs, decimal128 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !(lhs < rhs); } @@ -1420,220 +1431,9 @@ std::ostream& operator<<( std::ostream& os, boost::decimal::detail::uint128_t v # pragma warning(disable: 4127) // If constexpr macro only works for C++17 and above #endif -template -constexpr auto d128_add_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept -> detail::decimal128_components -{ - const bool sign {lhs_sign}; - - auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; - - if (delta_exp > detail::precision_v + 1) - { - // If the difference in exponents is more than the digits of accuracy - // we return the larger of the two - // - // e.g. 1e20 + 1e-20 = 1e20 - - return {lhs_sig, lhs_exp, lhs_sign}; - } - else if (delta_exp == detail::precision_v + 1) - { - // Only need to see if we need to add one to the - // significand of the bigger value - // - // e.g. 1.234567e5 + 9.876543e-2 = 1.234568e5 - - BOOST_DECIMAL_IF_CONSTEXPR (std::numeric_limits::digits10 > std::numeric_limits::digits10) - { - if (rhs_sig >= detail::uint128 {UINT64_C(0xF684DF56C3E0), UINT64_C(0x1BC6C73200000000)}) - { - ++lhs_sig; - } - - return {lhs_sig, lhs_exp, lhs_sign}; - } - else - { - return {lhs_sig, lhs_exp, lhs_sign}; - } - } - - // The two numbers can be added together without special handling - // - // If we can add to the lhs sig rather than dividing we can save some precision - // 64-bit sign int can have 19 digits, and our normalized significand has 16 - - if (delta_exp <= 3) - { - lhs_sig *= detail::pow10(static_cast(delta_exp)); - lhs_exp -= delta_exp; - delta_exp = 0; - } - else - { - lhs_sig *= 1000; - delta_exp -= 3; - lhs_exp -= 3; - } - - while (delta_exp > 1) - { - rhs_sig /= detail::pow10(static_cast(delta_exp - 1)); - delta_exp = 1; - } - - if (delta_exp == 1) - { - detail::fenv_round(rhs_sig, rhs_sign); - } - - // Convert both of the significands to unsigned types, so we can use intrinsics - // in the uint128 implementation - const auto unsigned_lhs_sig {detail::make_positive_unsigned(lhs_sig)}; - const auto unsigned_rhs_sig {detail::make_positive_unsigned(rhs_sig)}; - const auto new_sig {static_cast(unsigned_lhs_sig + unsigned_rhs_sig)}; - const auto new_exp {lhs_exp}; - - #ifdef BOOST_DECIMAL_DEBUG_ADD_128 - std::cerr << "Res Sig: " << static_cast(new_sig) - << "\nRes Exp: " << new_exp - << "\nRes Neg: " << sign << std::endl; - #endif - - return {new_sig, new_exp, sign}; -} - -template -constexpr auto d128_sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, - bool abs_lhs_bigger) noexcept -> detail::decimal128_components -{ - auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; - - if (delta_exp > detail::precision_v + 1) - { - // If the difference in exponents is more than the digits of accuracy - // we return the larger of the two - // - // e.g. 1e20 - 1e-20 = 1e20 - return abs_lhs_bigger ? detail::decimal128_components{detail::shrink_significand(lhs_sig, lhs_exp), lhs_exp, false} : - detail::decimal128_components{detail::shrink_significand(rhs_sig, rhs_exp), rhs_exp, true}; - } - - // The two numbers can be subtracted together without special handling - - auto& sig_bigger {abs_lhs_bigger ? lhs_sig : rhs_sig}; - auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp}; - auto& sig_smaller {abs_lhs_bigger ? rhs_sig : lhs_sig}; - auto& smaller_sign {abs_lhs_bigger ? rhs_sign : lhs_sign}; - - if (delta_exp == 1) - { - sig_bigger *= 10; - --delta_exp; - --exp_bigger; - } - else if (delta_exp >= 2) - { - sig_bigger *= 100; - delta_exp -= 2; - exp_bigger -= 2; - } - - while (delta_exp > 1) - { - sig_smaller /= detail::pow10(static_cast(delta_exp - 1)); - delta_exp = 1; - } - - if (delta_exp == 1) - { - detail::fenv_round(sig_smaller, smaller_sign); - } - - auto signed_sig_lhs {detail::make_signed_value(lhs_sig, lhs_sign)}; - auto signed_sig_rhs {detail::make_signed_value(rhs_sig, rhs_sign)}; - - // Both of the significands are less than 9'999'999'999'999'999, so we can safely - // cast them to signed 64-bit ints to calculate the new significand - detail::int128 new_sig {}; // NOLINT : Value is never used but can't leave uninitialized in constexpr function - - if (rhs_sign && !lhs_sign) - { - new_sig = signed_sig_lhs + signed_sig_rhs; - } - else - { - new_sig = signed_sig_lhs - signed_sig_rhs; - } - - const auto new_exp {abs_lhs_bigger ? lhs_exp : rhs_exp}; - const auto new_sign {new_sig < 0}; - const auto res_sig {detail::make_positive_unsigned(new_sig)}; - - return {res_sig, new_exp, new_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 -> detail::decimal128_components -{ - 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)}; - - 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; - } - - if (res_sig == 0) - { - sign = false; - } - - return {res_sig.low, res_exp, sign}; -} - -constexpr auto d128_generic_div_impl(detail::decimal128_components lhs, detail::decimal128_components rhs, - detail::decimal128_components& q) noexcept -> void -{ - bool sign {lhs.sign != rhs.sign}; - - const auto big_sig_lhs {detail::uint256_t(lhs.sig) * detail::uint256_t(pow10(detail::uint128(detail::precision_v)))}; - lhs.exp -= detail::precision_v; - - auto res_sig {big_sig_lhs / detail::uint256_t(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; - } - - if (res_sig == 0) - { - sign = false; - } - - // Let the constructor handle shrinking it back down and rounding correctly - q = detail::decimal128_components{res_sig.low, res_exp, sign}; -} - constexpr auto d128_div_impl(decimal128 lhs, decimal128 rhs, decimal128& q, decimal128& r) noexcept -> void { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal128 zero {0, 0}; constexpr decimal128 nan {boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask)}; @@ -1678,6 +1478,9 @@ constexpr auto d128_div_impl(decimal128 lhs, decimal128 rhs, decimal128& q, deci default: static_cast(rhs); } + #else + static_cast(r); + #endif auto sig_lhs {lhs.full_significand()}; auto exp_lhs {lhs.biased_exponent()}; @@ -1698,7 +1501,7 @@ constexpr auto d128_div_impl(decimal128 lhs, decimal128 rhs, decimal128& q, deci detail::decimal128_components rhs_components {sig_rhs, exp_rhs, rhs.isneg()}; detail::decimal128_components q_components {}; - d128_generic_div_impl(lhs_components, rhs_components, q_components); + detail::d128_generic_div_impl(lhs_components, rhs_components, q_components); q = decimal128(q_components.sig, q_components.exp, q_components.sign); } @@ -1717,6 +1520,7 @@ constexpr auto d128_mod_impl(decimal128 lhs, decimal128 rhs, const decimal128& q constexpr auto operator+(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal128 zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -1724,6 +1528,7 @@ constexpr auto operator+(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 { return res; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && rhs.isneg()) @@ -1757,7 +1562,8 @@ constexpr auto operator+(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 << "\nrhs exp: " << rhs_exp << std::endl; #endif - const auto result {d128_add_impl(lhs_sig, lhs_exp, lhs.isneg(), + const auto result {detail::d128_add_impl( + lhs_sig, lhs_exp, lhs.isneg(), rhs_sig, rhs_exp, rhs.isneg())}; return {result.sig, result.exp, result.sign}; @@ -1767,10 +1573,12 @@ template constexpr auto operator+(decimal128 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && (rhs < 0)) @@ -1808,14 +1616,16 @@ constexpr auto operator+(decimal128 lhs, Integer rhs) noexcept if (!lhs_components.sign && rhs_components.sign) { - result = d128_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign, - abs_lhs_bigger); + result = detail::d128_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger); } else { - result = d128_add_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign); + result = detail::d128_add_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign); } return decimal128(result.sig, result.exp, result.sign); @@ -1831,6 +1641,7 @@ constexpr auto operator+(Integer lhs, decimal128 rhs) noexcept // NOLINTNEXTLINE : If subtraction is actually addition than use operator+ and vice versa constexpr auto operator-(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal128 zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -1838,6 +1649,7 @@ constexpr auto operator-(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 { return res; } + #endif if (!lhs.isneg() && rhs.isneg()) { @@ -1854,9 +1666,10 @@ constexpr auto operator-(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 auto exp_rhs {rhs.biased_exponent()}; detail::normalize(sig_rhs, exp_rhs); - const auto result {d128_sub_impl(sig_lhs, exp_lhs, lhs.isneg(), - sig_rhs, exp_rhs, rhs.isneg(), - abs_lhs_bigger)}; + const auto result {detail::d128_sub_impl( + sig_lhs, exp_lhs, lhs.isneg(), + sig_rhs, exp_rhs, rhs.isneg(), + abs_lhs_bigger)}; return {result.sig, result.exp, result.sign}; } @@ -1865,10 +1678,12 @@ template constexpr auto operator-(decimal128 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(lhs) || isnan(lhs)) { return lhs; } + #endif if (!lhs.isneg() && (rhs < 0)) { @@ -1888,9 +1703,10 @@ constexpr auto operator-(decimal128 lhs, Integer rhs) noexcept auto unsigned_sig_rhs {detail::make_positive_unsigned(sig_rhs)}; auto rhs_components {detail::decimal128_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; - const auto result {d128_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign, - abs_lhs_bigger)}; + const auto result {detail::d128_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger)}; return {result.sig, result.exp, result.sign}; } @@ -1899,10 +1715,12 @@ template constexpr auto operator-(Integer lhs, decimal128 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(rhs) || isnan(rhs)) { return rhs; } + #endif if (lhs >= 0 && rhs.isneg()) { @@ -1922,15 +1740,17 @@ constexpr auto operator-(Integer lhs, decimal128 rhs) noexcept detail::normalize(sig_rhs, exp_rhs); auto rhs_components {detail::decimal128_components{sig_rhs, exp_rhs, rhs.isneg()}}; - const auto result {d128_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign, - abs_lhs_bigger)}; + const auto result {detail::d128_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger)}; return {result.sig, result.exp, result.sign}; } constexpr auto operator*(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal128 zero {0, 0}; const auto non_finite {detail::check_non_finite(lhs, rhs)}; @@ -1938,6 +1758,7 @@ constexpr auto operator*(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 { return non_finite; } + #endif auto lhs_sig {lhs.full_significand()}; auto lhs_exp {lhs.biased_exponent()}; @@ -1951,8 +1772,9 @@ constexpr auto operator*(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 rhs_sig = rhs_zeros.trimmed_number; rhs_exp += static_cast(rhs_zeros.number_of_removed_zeros); - const auto result {d128_mul_impl(lhs_sig, lhs_exp, lhs.isneg(), - rhs_sig, rhs_exp, rhs.isneg())}; + const auto result {detail::d128_mul_impl( + lhs_sig, lhs_exp, lhs.isneg(), + rhs_sig, rhs_exp, rhs.isneg())}; return {result.sig, result.exp, result.sign}; } @@ -1961,10 +1783,12 @@ template constexpr auto operator*(decimal128 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif auto lhs_sig {lhs.full_significand()}; auto lhs_exp {lhs.biased_exponent()}; @@ -1981,8 +1805,9 @@ constexpr auto operator*(decimal128 lhs, Integer rhs) noexcept auto unsigned_sig_rhs {detail::make_positive_unsigned(rhs_sig)}; auto rhs_components {detail::decimal128_components{unsigned_sig_rhs, rhs_exp, (rhs < 0)}}; - const auto result {d128_mul_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign)}; + const auto result {detail::d128_mul_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign)}; return {result.sig, result.exp, result.sign}; } @@ -2007,6 +1832,7 @@ template constexpr auto operator/(decimal128 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal128 zero {0, 0}; constexpr decimal128 nan {boost::decimal::from_bits(boost::decimal::detail::d128_snan_mask)}; @@ -2032,6 +1858,7 @@ constexpr auto operator/(decimal128 lhs, Integer rhs) noexcept { return sign ? -inf : inf; } + #endif auto lhs_sig {lhs.full_significand()}; auto lhs_exp {lhs.biased_exponent()}; @@ -2044,7 +1871,7 @@ constexpr auto operator/(decimal128 lhs, Integer rhs) noexcept detail::decimal128_components rhs_components {rhs_sig, rhs_exp, rhs < 0}; detail::decimal128_components q_components {}; - d128_generic_div_impl(lhs_components, rhs_components, q_components); + detail::d128_generic_div_impl(lhs_components, rhs_components, q_components); return decimal128(q_components.sig, q_components.exp, q_components.sign); } @@ -2053,6 +1880,7 @@ template constexpr auto operator/(Integer lhs, decimal128 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal128 zero {0, 0}; constexpr decimal128 inf {boost::decimal::from_bits(boost::decimal::detail::d128_inf_mask)}; @@ -2076,6 +1904,7 @@ constexpr auto operator/(Integer lhs, decimal128 rhs) noexcept default: static_cast(lhs); } + #endif auto rhs_sig {rhs.full_significand()}; auto rhs_exp {rhs.biased_exponent()}; @@ -2085,7 +1914,7 @@ constexpr auto operator/(Integer lhs, decimal128 rhs) noexcept detail::decimal128_components rhs_components {rhs_sig, rhs_exp, rhs.isneg()}; detail::decimal128_components q_components {}; - d128_generic_div_impl(lhs_components, rhs_components, q_components); + detail::d128_generic_div_impl(lhs_components, rhs_components, q_components); return decimal128(q_components.sig, q_components.exp, q_components.sign); } @@ -2225,6 +2054,7 @@ constexpr auto decimal128::operator%=(decimal128 rhs) noexcept -> decimal128& // The samequantum functions raise no exception. constexpr auto samequantumd128(decimal128 lhs, decimal128 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH const auto lhs_fp {fpclassify(lhs)}; const auto rhs_fp {fpclassify(rhs)}; @@ -2236,6 +2066,7 @@ constexpr auto samequantumd128(decimal128 lhs, decimal128 rhs) noexcept -> bool { return false; } + #endif return lhs.unbiased_exponent() == rhs.unbiased_exponent(); } @@ -2245,10 +2076,12 @@ constexpr auto samequantumd128(decimal128 lhs, decimal128 rhs) noexcept -> bool // Otherwise, a domain error occurs and INT_MIN is returned. constexpr auto quantexpd128(decimal128 x) noexcept -> int { + #ifndef BOOST_DECIMAL_FAST_MATH if (!isfinite(x)) { return INT_MIN; } + #endif return static_cast(x.unbiased_exponent()); } @@ -2266,6 +2099,7 @@ constexpr auto quantexpd128(decimal128 x) noexcept -> int // The quantize functions do not signal underflow. constexpr auto quantized128(decimal128 lhs, decimal128 rhs) noexcept -> decimal128 { + #ifndef BOOST_DECIMAL_FAST_MATH // Return the correct type of nan if (isnan(lhs)) { @@ -2285,6 +2119,7 @@ constexpr auto quantized128(decimal128 lhs, decimal128 rhs) noexcept -> decimal1 { return lhs; } + #endif return {lhs.full_significand(), rhs.biased_exponent(), lhs.isneg()}; } @@ -2397,12 +2232,14 @@ constexpr auto copysignd128(decimal128 mag, decimal128 sgn) noexcept -> decimal1 constexpr auto scalblnd128(decimal128 num, long exp) noexcept -> decimal128 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal128 zero {0, 0}; if (num == zero || exp == 0 || isinf(num) || isnan(num)) { return num; } + #endif num.edit_exponent(num.biased_exponent() + exp); diff --git a/include/boost/decimal/decimal128_fast.hpp b/include/boost/decimal/decimal128_fast.hpp new file mode 100644 index 000000000..d90c1f2d5 --- /dev/null +++ b/include/boost/decimal/decimal128_fast.hpp @@ -0,0 +1,1549 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BOOST_DECIMAL_DECIMAL128_FAST_HPP +#define BOOST_DECIMAL_DECIMAL128_FAST_HPP + +namespace boost { +namespace decimal { + +namespace detail { + +BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_inf = std::numeric_limits::max(); +BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_qnan = std::numeric_limits::max() - 1; +BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d128_fast_snan = std::numeric_limits::max() - 2; + +struct decimal128_fast_components +{ + using sig_type = uint128; + + uint128 sig; + std::int32_t exp; + bool sign; +}; + +} // namespace detail + +class decimal128_fast final +{ +public: + using significand_type = detail::uint128; + using exponent_type = std::uint_fast32_t; + +private: + // Instead of having to encode and decode at every operation + // we store the constituent pieces directly + + significand_type significand_ {}; + exponent_type exponent_ {}; + bool sign_ {}; + + constexpr auto isneg() const noexcept -> bool + { + return sign_; + } + + constexpr auto full_significand() const noexcept -> significand_type + { + return significand_; + } + + constexpr auto unbiased_exponent() const noexcept -> exponent_type + { + return exponent_; + } + + constexpr auto biased_exponent() const noexcept -> std::int32_t + { + return static_cast(exponent_) - detail::bias_v; + } + + template + friend constexpr auto to_integral_128(Decimal val) noexcept + BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_integral_v, TargetType, TargetType); + + template + friend BOOST_DECIMAL_CXX20_CONSTEXPR auto to_float(Decimal val) noexcept + BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_floating_point_v, TargetType, TargetType); + + template + friend constexpr auto to_decimal(Decimal val) noexcept -> TargetType; + + friend constexpr auto d128f_div_impl(decimal128_fast lhs, decimal128_fast rhs, decimal128_fast& q, decimal128_fast& r) noexcept -> void; + + // Equality template between any integer type and decimal128 + template + friend constexpr auto mixed_equality_impl(Decimal lhs, Integer rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_integral_v), bool>; + + template + friend constexpr auto mixed_decimal_equality_impl(Decimal1 lhs, Decimal2 rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && + detail::is_decimal_floating_point_v), bool>; + + // Template to compare operator< for any integer type and decimal128 + template + friend constexpr auto less_impl(Decimal lhs, Integer rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_integral_v), bool>; + + template + friend constexpr auto mixed_decimal_less_impl(Decimal1 lhs, Decimal2 rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && + detail::is_decimal_floating_point_v), bool>; + + template + friend constexpr auto ilogb(T d) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, T, int); + + template + friend constexpr auto logb(T num) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T); + +public: + constexpr decimal128_fast() noexcept = default; + + #ifdef BOOST_DECIMAL_HAS_CONCEPTS + template + #else + template && detail::is_integral_v, bool> = true> + #endif + constexpr decimal128_fast(T1 coeff, T2 exp, bool sign = false) noexcept; + + #ifdef BOOST_DECIMAL_HAS_CONCEPTS + template + #else + template , bool> = true> + #endif + constexpr decimal128_fast(Integer val) noexcept; + + #ifdef BOOST_DECIMAL_HAS_CONCEPTS + template + #else + template , bool> = true> + #endif + explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_fast(Float val) noexcept; + + friend constexpr auto direct_init_d128(significand_type significand, exponent_type exponent, bool sign) noexcept -> decimal128_fast; + + // Classification functions + friend constexpr auto signbit(decimal128_fast val) noexcept -> bool; + friend constexpr auto isinf(decimal128_fast val) noexcept -> bool; + friend constexpr auto isnan(decimal128_fast val) noexcept -> bool; + friend constexpr auto issignaling(decimal128_fast val) noexcept -> bool; + friend constexpr auto isnormal(decimal128_fast val) noexcept -> bool; + + // Comparison operators + friend constexpr auto operator==(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool; + friend constexpr auto operator!=(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool; + friend constexpr auto operator<(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool; + friend constexpr auto operator<=(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool; + friend constexpr auto operator>(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool; + friend constexpr auto operator>=(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool; + + // Mixed comparison operators + template + friend constexpr auto operator==(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator==(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator!=(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator!=(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<=(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<=(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>=(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>=(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + #ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + friend constexpr auto operator<=>(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> std::partial_ordering; + + template + friend constexpr auto operator<=>(const decimal128_fast& lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering); + + template + friend constexpr auto operator<=>(Integer lhs, const decimal128_fast& rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering); + #endif + + // Unary arithmetic operators + friend constexpr auto operator+(decimal128_fast rhs) noexcept -> decimal128_fast; + friend constexpr auto operator-(decimal128_fast rhs) noexcept -> decimal128_fast; + + // Binary arithmetic operators + friend constexpr auto operator+(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast; + friend constexpr auto operator-(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast; + friend constexpr auto operator*(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast; + friend constexpr auto operator/(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast; + friend constexpr auto operator%(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast; + + // Mixed type binary arithmetic operators + template + friend constexpr auto operator+(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + template + friend constexpr auto operator+(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + template + friend constexpr auto operator-(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + template + friend constexpr auto operator-(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + template + friend constexpr auto operator*(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + template + friend constexpr auto operator*(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + template + friend constexpr auto operator/(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + template + friend constexpr auto operator/(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast); + + // Compound Arithmetic Operators + constexpr auto operator+=(decimal128_fast rhs) noexcept -> decimal128_fast&; + + template + constexpr auto operator+=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&); + + constexpr auto operator-=(decimal128_fast rhs) noexcept -> decimal128_fast&; + + template + constexpr auto operator-=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&); + + constexpr auto operator*=(decimal128_fast rhs) noexcept -> decimal128_fast&; + + template + constexpr auto operator*=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&); + + constexpr auto operator/=(decimal128_fast rhs) noexcept -> decimal128_fast&; + + template + constexpr auto operator/=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&); + + // Conversions + explicit constexpr operator bool() const noexcept; + explicit constexpr operator int() const noexcept; + explicit constexpr operator unsigned() const noexcept; + explicit constexpr operator long() const noexcept; + explicit constexpr operator unsigned long() const noexcept; + explicit constexpr operator long long() const noexcept; + explicit constexpr operator unsigned long long() const noexcept; + explicit constexpr operator std::int8_t() const noexcept; + explicit constexpr operator std::uint8_t() const noexcept; + explicit constexpr operator std::int16_t() const noexcept; + explicit constexpr operator std::uint16_t() const noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + explicit constexpr operator detail::int128_t() const noexcept; + explicit constexpr operator detail::uint128_t() const noexcept; + #endif + + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept; + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept; + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept; + + #ifdef BOOST_DECIMAL_HAS_FLOAT16 + explicit constexpr operator std::float16_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT32 + explicit constexpr operator std::float32_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT64 + explicit constexpr operator std::float64_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_BRAINFLOAT16 + explicit constexpr operator std::bfloat16_t() const noexcept; + #endif + + template , bool> = true> + explicit constexpr operator Decimal() const noexcept; + + // functions that are better as friends + template + friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type; + + friend constexpr auto copysignd128f(decimal128_fast mag, decimal128_fast sgn) noexcept -> decimal128_fast; + friend constexpr auto scalblnd128f(decimal128_fast num, long exp) noexcept -> decimal128_fast; + friend constexpr auto scalbnd128f(decimal128_fast num, int exp) noexcept -> decimal128_fast; + friend constexpr auto fmad128f(decimal128_fast x, decimal128_fast y, decimal128 z) noexcept -> decimal128; + + // Decimal functions + // 3.6.4 Same Quantum + friend constexpr auto samequantumd128f(decimal128_fast lhs, decimal128_fast rhs) noexcept -> bool; + + // 3.6.5 Quantum exponent + friend constexpr auto quantexpd128f(decimal128_fast x) noexcept -> int; + + // 3.6.6 Quantize + friend constexpr auto quantized128f(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast; +}; + +#ifdef BOOST_DECIMAL_HAS_CONCEPTS +template +#else +template && detail::is_integral_v, bool>> +#endif +constexpr decimal128_fast::decimal128_fast(T1 coeff, T2 exp, bool sign) noexcept +{ + // Older compilers have issues with conversions from __uint128, so we skip all that and use our uint128 + #if defined(BOOST_DECIMAL_HAS_INT128) && (!defined(__GNUC__) || (defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 10)) && (!defined(__clang__) || (defined(__clang__) && __clang_major__ < 13)) + using Unsigned_Integer_1 = detail::make_unsigned_t; + using Unsigned_Integer = std::conditional_t::value, detail::uint128, Unsigned_Integer_1>; + #else + using Unsigned_Integer = detail::make_unsigned_t; + #endif + + using Basis_Unsigned_Integer = std::conditional_t::digits10 < std::numeric_limits::digits10, significand_type, Unsigned_Integer>; + + const bool isneg {coeff < static_cast(0) || sign}; + sign_ = isneg; + auto unsigned_coeff {static_cast(detail::make_positive_unsigned(coeff))}; + + // Normalize the significand in the constructor, so we don't have + // to calculate the number of digits for operationss + detail::normalize(unsigned_coeff, exp, sign); + + significand_ = unsigned_coeff; + + // Normalize the handling of 0 + if (significand_ == detail::uint128{UINT64_C(0), UINT64_C(0)}) + { + exp = 0; + } + + const auto biased_exp {static_cast(exp + detail::bias_v)}; + + if (biased_exp > detail::max_biased_exp_v) + { + significand_ = detail::d128_fast_inf; + } + else + { + exponent_ = biased_exp; + } +} + +#ifdef BOOST_DECIMAL_HAS_CONCEPTS +template +#else +template , bool>> +#endif +constexpr decimal128_fast::decimal128_fast(Integer val) noexcept +{ + using ConversionType = std::conditional_t::value, std::int32_t, Integer>; + *this = decimal128_fast{static_cast(val), 0, false}; +} + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#ifdef BOOST_DECIMAL_HAS_CONCEPTS +template +#else +template , bool>> +#endif +BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_fast::decimal128_fast(Float val) noexcept +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (val != val) + { + significand_ = detail::d128_fast_qnan; + } + else if (val == std::numeric_limits::infinity() || val == -std::numeric_limits::infinity()) + { + significand_ = detail::d128_fast_inf; + } + else + #endif + { + const auto components {detail::ryu::floating_point_to_fd128(val)}; + *this = decimal128_fast {components.mantissa, components.exponent, components.sign}; + } +} + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +constexpr auto direct_init_d128(decimal128_fast::significand_type significand, decimal128_fast::exponent_type exponent, bool sign) noexcept -> decimal128_fast +{ + decimal128_fast val {}; + val.significand_ = significand; + val.exponent_ = exponent; + val.sign_ = sign; + + return val; +} + +constexpr auto signbit(decimal128_fast val) noexcept -> bool +{ + return val.sign_; +} + +constexpr auto isinf(decimal128_fast val) noexcept -> bool +{ + return val.significand_ == detail::d128_fast_inf; +} + +constexpr auto isnan(decimal128_fast val) noexcept -> bool +{ + return val.significand_ == detail::d128_fast_qnan || + val.significand_ == detail::d128_fast_snan; +} + +constexpr auto issignaling(decimal128_fast val) noexcept -> bool +{ + return val.significand_ == detail::d128_fast_snan; +} + +constexpr auto isnormal(decimal128_fast val) noexcept -> bool +{ + if (val.exponent_ <= static_cast(detail::precision_v - 1)) + { + return false; + } + + return (val.significand_ != 0) && isfinite(val); +} + +constexpr auto operator==(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + return lhs.sign_ == rhs.sign_ && + lhs.exponent_ == rhs.exponent_ && + lhs.significand_ == rhs.significand_; +} + +template +constexpr auto operator==(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return mixed_equality_impl(lhs, rhs); +} + +template +constexpr auto operator==(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return mixed_equality_impl(rhs, lhs); +} + +constexpr auto operator!=(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool +{ + return !(lhs == rhs); +} + +template +constexpr auto operator!=(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return !(lhs == rhs); +} + +template +constexpr auto operator!=(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return !(lhs == rhs); +} + +constexpr auto operator<(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool +{ +#ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs) || + (!lhs.isneg() && rhs.isneg())) + { + return false; + } + else if (lhs.isneg() && !rhs.isneg()) + { + return true; + } + else if (isfinite(lhs) && isinf(rhs)) + { + return !signbit(rhs); + } + else if (isinf(lhs) && isfinite(rhs)) + { + return signbit(rhs); + } +#else + if (!lhs.isneg() && rhs.isneg()) + { + return false; + } + else if (lhs.isneg() && !rhs.isneg()) + { + return true; + } +#endif + + if (lhs.significand_ == 0 || rhs.significand_ == 0) + { + return lhs.significand_ == 0 ? !rhs.sign_ : lhs.sign_; + } + + if (lhs.exponent_ != rhs.exponent_) + { + return lhs.sign_ ? lhs.exponent_ > rhs.exponent_ : lhs.exponent_ < rhs.exponent_; + } + + return lhs.sign_ ? lhs.significand_ > rhs.significand_ : lhs.significand_ < rhs.significand_; +} + +template +constexpr auto operator<(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return less_impl(lhs, rhs); +} + +template +constexpr auto operator<(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(rhs)) + { + return false; + } + #endif + + return !less_impl(rhs, lhs) && lhs != rhs; +} + +constexpr auto operator<=(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + return !(rhs < lhs); +} + +template +constexpr auto operator<=(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs)) + { + return false; + } + #endif + + return !(rhs < lhs); +} + +template +constexpr auto operator<=(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(rhs)) + { + return false; + } + #endif + + return !(rhs < lhs); +} + +constexpr auto operator>(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool +{ + return rhs < lhs; +} + +template +constexpr auto operator>(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return rhs < lhs; +} + +template +constexpr auto operator>(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return rhs < lhs; +} + +constexpr auto operator>=(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + return !(lhs < rhs); +} + +template +constexpr auto operator>=(decimal128_fast lhs, Integer rhs) noexcept +BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs)) + { + return false; + } + #endif + + return !(lhs < rhs); +} + +template +constexpr auto operator>=(Integer lhs, decimal128_fast rhs) noexcept +BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(rhs)) + { + return false; + } + #endif + + return !(lhs < rhs); +} + +#ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + +constexpr auto operator<=>(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> std::partial_ordering +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +template +constexpr auto operator<=>(const decimal128_fast& lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering) +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +template +constexpr auto operator<=>(Integer lhs, const decimal128_fast& rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering) +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +#endif + +constexpr auto operator+(decimal128_fast rhs) noexcept -> decimal128_fast +{ + return rhs; +} + +constexpr auto operator-(decimal128_fast rhs) noexcept -> decimal128_fast +{ + rhs.sign_ = !rhs.sign_; + return rhs; +} + +constexpr auto operator+(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal128_fast zero {0, 0}; + + const auto res {detail::check_non_finite(lhs, rhs)}; + if (res != zero) + { + return res; + } + #endif + + bool lhs_bigger {lhs > rhs}; + if (lhs.isneg() && rhs.isneg()) + { + lhs_bigger = !lhs_bigger; + } + + // Ensure that lhs is always the larger for ease of impl + if (!lhs_bigger) + { + detail::swap(lhs, rhs); + } + + if (!lhs.isneg() && rhs.isneg()) + { + return lhs - abs(rhs); + } + + const auto result {detail::d128_add_impl( + lhs.significand_, lhs.biased_exponent(), lhs.sign_, + rhs.significand_, rhs.biased_exponent(), rhs.sign_)}; + + return {result.sig, result.exp, result.sign}; +}; + +template +constexpr auto operator+(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isinf(lhs)) + { + return lhs; + } + #endif + + bool lhs_bigger {lhs > rhs}; + if (lhs.isneg() && (rhs < 0)) + { + lhs_bigger = !lhs_bigger; + } + bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)}; + + auto lhs_components {detail::decimal128_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}}; + + auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; + std::int32_t exp_rhs {0}; + detail::normalize(sig_rhs, exp_rhs); + auto unsigned_sig_rhs = detail::make_positive_unsigned(sig_rhs); + auto rhs_components {detail::decimal128_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; + + if (!lhs_bigger) + { + detail::swap(lhs_components, rhs_components); + lhs_bigger = !lhs_bigger; + abs_lhs_bigger = !abs_lhs_bigger; + } + + detail::decimal128_fast_components result {}; + + #ifdef BOOST_DECIMAL_DEBUG_ADD + std::cerr << "Lhs sig: " << lhs_components.sig + << "\nLhs exp: " << lhs_components.exp + << "\nRhs sig: " << rhs_components.sig + << "\nRhs exp: " << rhs_components.exp << std::endl; + #endif + + if (!lhs_components.sign && rhs_components.sign) + { + result = detail::d128_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger); + } + else + { + result = detail::d128_add_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign); + } + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator+(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + return rhs + lhs; +} + +constexpr auto operator-(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal128_fast zero {0, 0}; + + const auto res {detail::check_non_finite(lhs, rhs)}; + if (res != zero) + { + return res; + } + #endif + + if (!lhs.isneg() && rhs.isneg()) + { + return lhs + (-rhs); + } + + const bool abs_lhs_bigger {abs(lhs) > abs(rhs)}; + + const auto result {detail::d128_sub_impl( + lhs.significand_, lhs.biased_exponent(), lhs.sign_, + rhs.significand_, rhs.biased_exponent(), rhs.sign_, + abs_lhs_bigger + )}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator-(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isinf(lhs) || isnan(lhs)) + { + return lhs; + } + #endif + + if (!lhs.isneg() && (rhs < 0)) + { + return lhs + detail::make_positive_unsigned(rhs); + } + + const bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)}; + + auto lhs_components {detail::decimal128_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}}; + + auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; + std::int32_t exp_rhs {0}; + detail::normalize(sig_rhs, exp_rhs); + auto unsigned_sig_rhs {detail::make_positive_unsigned(sig_rhs)}; + auto rhs_components {detail::decimal128_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; + + const auto result {detail::d128_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger)}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator-(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isinf(rhs) || isnan(rhs)) + { + return rhs; + } + #endif + + if (lhs >= 0 && rhs.isneg()) + { + return lhs + (-rhs); + } + + const bool abs_lhs_bigger {detail::make_positive_unsigned(lhs) > abs(rhs)}; + + auto sig_lhs {static_cast(detail::make_positive_unsigned(lhs))}; + std::int32_t exp_lhs {0}; + detail::normalize(sig_lhs, exp_lhs); + auto unsigned_sig_lhs {detail::make_positive_unsigned(sig_lhs)}; + auto lhs_components {detail::decimal128_fast_components{unsigned_sig_lhs, exp_lhs, (lhs < 0)}}; + + auto rhs_components {detail::decimal128_fast_components{rhs.significand_, rhs.biased_exponent(), rhs.isneg()}}; + + const auto result {detail::d128_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger)}; + + return {result.sig, result.exp, result.sign}; +} + +constexpr auto operator*(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal128_fast zero {0, 0}; + + const auto non_finite {detail::check_non_finite(lhs, rhs)}; + if (non_finite != zero) + { + return non_finite; + } + #endif + + // TODO(mborland): Is trimming the zeros really necessary? Doesn't seem like it + 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); + + const auto result {detail::d128_mul_impl( + lhs_sig, lhs_exp, lhs.isneg(), + rhs_sig, rhs_exp, rhs.isneg())}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator*(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isinf(lhs)) + { + return lhs; + } + #endif + + 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 lhs_components {detail::decimal128_fast_components{lhs_sig, lhs_exp, lhs.isneg()}}; + + auto rhs_sig {static_cast(detail::make_positive_unsigned(rhs))}; + std::int32_t rhs_exp {0}; + 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); + auto unsigned_sig_rhs {detail::make_positive_unsigned(rhs_sig)}; + auto rhs_components {detail::decimal128_fast_components{unsigned_sig_rhs, rhs_exp, (rhs < 0)}}; + + const auto result {detail::d128_mul_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign)}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator*(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + return rhs * lhs; +} + +constexpr auto d128f_div_impl(decimal128_fast lhs, decimal128_fast rhs, decimal128_fast& q, decimal128_fast& r) noexcept -> void +{ + #ifndef BOOST_DECIMAL_FAST_MATH + // Check pre-conditions + constexpr decimal128_fast zero {0, 0}; + constexpr decimal128_fast nan {boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_qnan, 0, false)}; + constexpr decimal128_fast inf {boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_inf, 0, false)}; + + const bool sign {lhs.isneg() != rhs.isneg()}; + + const auto lhs_fp {fpclassify(lhs)}; + const auto rhs_fp {fpclassify(rhs)}; + + if (lhs_fp == FP_NAN || rhs_fp == FP_NAN) + { + q = nan; + r = nan; + return; + } + + switch (lhs_fp) + { + case FP_INFINITE: + q = sign ? -inf : inf; + r = zero; + return; + case FP_ZERO: + q = sign ? -zero : zero; + r = sign ? -zero : zero; + return; + default: + static_cast(lhs); + } + + switch (rhs_fp) + { + case FP_ZERO: + q = inf; + r = zero; + return; + case FP_INFINITE: + q = sign ? -zero : zero; + r = lhs; + return; + default: + static_cast(rhs); + } + #else + static_cast(r); + #endif + + #ifdef BOOST_DECIMAL_DEBUG + std::cerr << "sig lhs: " << sig_lhs + << "\nexp lhs: " << exp_lhs + << "\nsig rhs: " << sig_rhs + << "\nexp rhs: " << exp_rhs << std::endl; + #endif + + detail::decimal128_fast_components lhs_components {lhs.significand_, lhs.biased_exponent(), lhs.isneg()}; + detail::decimal128_fast_components rhs_components {rhs.significand_, rhs.biased_exponent(), rhs.isneg()}; + detail::decimal128_fast_components q_components {}; + + detail::d128_generic_div_impl(lhs_components, rhs_components, q_components); + + q = decimal128_fast(q_components.sig, q_components.exp, q_components.sign); +} + +constexpr auto d128f_mod_impl(decimal128_fast lhs, decimal128_fast rhs, const decimal128_fast& q, decimal128_fast& r) -> void +{ + constexpr decimal128_fast zero {0, 0}; + + auto q_trunc {q > zero ? floor(q) : ceil(q)}; + r = lhs - (decimal128_fast(q_trunc) * rhs); +}; + +constexpr auto operator/(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast +{ + decimal128_fast q {}; + decimal128_fast r {}; + d128f_div_impl(lhs, rhs, q, r); + + return q; +}; + +template +constexpr auto operator/(decimal128_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + // Check pre-conditions + constexpr decimal128_fast zero {0, 0}; + constexpr decimal128_fast nan {boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_qnan, 0, false)}; + constexpr decimal128_fast inf {boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_inf, 0, false)}; + + const bool sign {lhs.isneg() != (rhs < 0)}; + + const auto lhs_fp {fpclassify(lhs)}; + + switch (lhs_fp) + { + case FP_NAN: + return nan; + case FP_INFINITE: + return inf; + case FP_ZERO: + return sign ? -zero : zero; + default: + static_cast(lhs); + } + + if (rhs == 0) + { + return sign ? -inf : inf; + } + #endif + + detail::decimal128_fast_components lhs_components {lhs.significand_, lhs.biased_exponent(), lhs.isneg()}; + + auto rhs_sig {detail::make_positive_unsigned(rhs)}; + std::int32_t rhs_exp {}; + detail::decimal128_fast_components rhs_components {rhs_sig, rhs_exp, rhs < 0}; + detail::decimal128_fast_components q_components {}; + + detail::d128_generic_div_impl(lhs_components, rhs_components, q_components); + + return {q_components.sig, q_components.exp, q_components.sign}; +} + +template +constexpr auto operator/(Integer lhs, decimal128_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + // Check pre-conditions + constexpr decimal128_fast zero {0, 0}; + constexpr decimal128_fast nan {boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_qnan, 0, false)}; + constexpr decimal128_fast inf {boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_inf, 0, false)}; + + const bool sign {(lhs < 0) != rhs.isneg()}; + + const auto rhs_fp {fpclassify(rhs)}; + + if (rhs_fp == FP_NAN) + { + return nan; + } + + switch (rhs_fp) + { + case FP_INFINITE: + return sign ? -zero : zero; + case FP_ZERO: + return sign ? -inf : inf; + default: + static_cast(lhs); + } + #endif + + detail::decimal128_fast_components lhs_components {detail::make_positive_unsigned(lhs), 0, lhs < 0}; + detail::decimal128_fast_components rhs_components {rhs.significand_, rhs.biased_exponent(), rhs.isneg()}; + detail::decimal128_fast_components q_components {}; + + detail::d128_generic_div_impl(lhs_components, rhs_components, q_components); + + return {q_components.sig, q_components.exp, q_components.sign}; +} + +constexpr auto operator%(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast +{ + decimal128_fast q {}; + decimal128_fast r {}; + d128f_div_impl(lhs, rhs, q, r); + d128f_mod_impl(lhs, rhs, q, r); + + return r; +}; + +constexpr auto decimal128_fast::operator+=(decimal128_fast rhs) noexcept -> decimal128_fast& +{ + *this = *this + rhs; + return *this; +} + +template +constexpr auto decimal128_fast::operator+=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&) +{ + *this = *this + rhs; + return *this; +} + +constexpr auto decimal128_fast::operator-=(decimal128_fast rhs) noexcept -> decimal128_fast& +{ + *this = *this - rhs; + return *this; +} + +template +constexpr auto decimal128_fast::operator-=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&) +{ + *this = *this - rhs; + return *this; +} + +constexpr auto decimal128_fast::operator*=(decimal128_fast rhs) noexcept -> decimal128_fast& +{ + *this = *this * rhs; + return *this; +} + +template +constexpr auto decimal128_fast::operator*=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&) +{ + *this = *this * rhs; + return *this; +} + +constexpr auto decimal128_fast::operator/=(decimal128_fast rhs) noexcept -> decimal128_fast& +{ + *this = *this / rhs; + return *this; +} + +template +constexpr auto decimal128_fast::operator/=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_fast&) +{ + *this = *this / rhs; + return *this; +} + +constexpr decimal128_fast::operator bool() const noexcept +{ + constexpr decimal128_fast zero {0, 0}; + return *this != zero; +} + +constexpr decimal128_fast::operator int() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator unsigned() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator long() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator unsigned long() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator long long() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator unsigned long long() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator std::int8_t() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator std::uint8_t() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator std::int16_t() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator std::uint16_t() const noexcept +{ + return to_integral_128(*this); +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr decimal128_fast::operator detail::int128_t() const noexcept +{ + return to_integral_128(*this); +} + +constexpr decimal128_fast::operator detail::uint128_t() const noexcept +{ + return to_integral_128(*this); +} + +#endif // BOOST_DECIMAL_HAS_INT128 + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_fast::operator float() const noexcept +{ + return to_float(*this); +} + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_fast::operator double() const noexcept +{ + return to_float(*this); +} + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal128_fast::operator long double() const noexcept +{ + return to_float(*this); +} + +#ifdef BOOST_DECIMAL_HAS_FLOAT16 +constexpr decimal128_fast::operator std::float16_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_FLOAT32 +constexpr decimal128_fast::operator std::float32_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_FLOAT64 +constexpr decimal128_fast::operator std::float64_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_BRAINFLOAT16 +constexpr decimal128_fast::operator std::bfloat16_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif + +template , bool>> +constexpr decimal128_fast::operator Decimal() const noexcept +{ + return to_decimal(*this); +} + +constexpr auto copysignd128f(decimal128_fast mag, decimal128_fast sgn) noexcept -> decimal128_fast +{ + mag.sign_ = sgn.sign_; + return mag; +} + +constexpr auto scalblnd128f(decimal128_fast num, long exp) noexcept -> decimal128_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal128_fast zero {0, 0}; + + if (num == zero || exp == 0 || isinf(num) || isnan(num)) + { + return num; + } + #endif + + num.exponent_ = static_cast(static_cast(num.biased_exponent()) + exp); + + return num; +} + +constexpr auto scalbnd128f(decimal128_fast num, int exp) noexcept -> decimal128_fast +{ + return scalblnd128f(num, static_cast(exp)); +} + +// 3.6.4 +// Effects: determines if the quantum exponents of x and y are the same. +// If both x and y are NaN, or infinity, they have the same quantum exponents; +// if exactly one operand is infinity or exactly one operand is NaN, they do not have the same quantum exponents. +// The samequantum functions raise no exception. +constexpr auto samequantumd128f(decimal128_fast lhs, decimal128_fast rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + const auto lhs_fp {fpclassify(lhs)}; + const auto rhs_fp {fpclassify(rhs)}; + + if ((lhs_fp == FP_NAN && rhs_fp == FP_NAN) || (lhs_fp == FP_INFINITE && rhs_fp == FP_INFINITE)) + { + return true; + } + if ((lhs_fp == FP_NAN || rhs_fp == FP_INFINITE) || (rhs_fp == FP_NAN || lhs_fp == FP_INFINITE)) + { + return false; + } + #endif + + return lhs.unbiased_exponent() == rhs.unbiased_exponent(); +} + +// 3.6.5 +// Effects: if x is finite, returns its quantum exponent. +// Otherwise, a domain error occurs and INT_MIN is returned. +constexpr auto quantexpd128f(decimal128_fast x) noexcept -> int +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (!isfinite(x)) + { + return INT_MIN; + } + #endif + + return static_cast(x.unbiased_exponent()); +} + +// 3.6.6 +// Returns: a number that is equal in value (except for any rounding) and sign to x, +// and which has an exponent set to be equal to the exponent of y. +// If the exponent is being increased, the value is correctly rounded according to the current rounding mode; +// if the result does not have the same value as x, the "inexact" floating-point exception is raised. +// If the exponent is being decreased and the significand of the result has more digits than the type would allow, +// the "invalid" floating-point exception is raised and the result is NaN. +// If one or both operands are NaN the result is NaN. +// Otherwise, if only one operand is infinity, the "invalid" floating-point exception is raised and the result is NaN. +// If both operands are infinity, the result is DEC_INFINITY, with the same sign as x, converted to the type of x. +// The quantize functions do not signal underflow. +constexpr auto quantized128f(decimal128_fast lhs, decimal128_fast rhs) noexcept -> decimal128_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + // Return the correct type of nan + if (isnan(lhs)) + { + return lhs; + } + else if (isnan(rhs)) + { + return rhs; + } + + // If one is infinity then return a signaling NAN + if (isinf(lhs) != isinf(rhs)) + { + return boost::decimal::direct_init_d128(boost::decimal::detail::d128_fast_qnan, 0, false); + } + else if (isinf(lhs) && isinf(rhs)) + { + return lhs; + } + #endif + + return {lhs.full_significand(), rhs.biased_exponent(), lhs.isneg()}; +} + +} // namespace decimal +} // namespace boost + +namespace std { + +BOOST_DECIMAL_EXPORT template<> +#ifdef _MSC_VER +class numeric_limits +#else +struct numeric_limits +#endif +{ + +#ifdef _MSC_VER + 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; + + // 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; + #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; + + // 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}; } +}; + +} + +#endif //BOOST_DECIMAL_DECIMAL128_FAST_HPP diff --git a/include/boost/decimal/decimal32.hpp b/include/boost/decimal/decimal32.hpp index d6a38356d..8e22ea15f 100644 --- a/include/boost/decimal/decimal32.hpp +++ b/include/boost/decimal/decimal32.hpp @@ -828,6 +828,7 @@ constexpr auto operator-(decimal32 rhs) noexcept-> decimal32 // NOLINTNEXTLINE : If addition is actually subtraction than change operator and vice versa constexpr auto operator+(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal32 zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -835,6 +836,7 @@ constexpr auto operator+(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { return res; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && rhs.isneg()) @@ -870,10 +872,12 @@ template constexpr auto operator+(decimal32 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && (rhs < 0)) @@ -961,6 +965,7 @@ constexpr auto decimal32::operator+=(Decimal rhs) noexcept // NOLINTNEXTLINE : If subtraction is actually addition than use operator+ and vice versa constexpr auto operator-(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal32 zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -968,6 +973,7 @@ constexpr auto operator-(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { return res; } + #endif if (!lhs.isneg() && rhs.isneg()) { @@ -995,10 +1001,12 @@ template constexpr auto operator-(decimal32 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(lhs) || isnan(lhs)) { return lhs; } + #endif if (!lhs.isneg() && (rhs < 0)) { @@ -1029,10 +1037,12 @@ template constexpr auto operator-(Integer lhs, decimal32 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(rhs) || isnan(rhs)) { return rhs; } + #endif if (lhs >= 0 && rhs.isneg()) { @@ -1139,6 +1149,7 @@ constexpr auto operator!=(Integer lhs, decimal32 rhs) noexcept constexpr auto operator<(decimal32 lhs, decimal32 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs) || (!lhs.isneg() && rhs.isneg())) { @@ -1150,15 +1161,18 @@ constexpr auto operator<(decimal32 lhs, decimal32 rhs) noexcept -> bool } else if (isfinite(lhs) && isinf(rhs)) { - if (!signbit(rhs)) - { - return true; - } - else - { - return false; - } + return !rhs.isneg(); } + #else + if (!lhs.isneg() && rhs.isneg()) + { + return false; + } + else if (lhs.isneg() && !rhs.isneg()) + { + return true; + } + #endif return less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); @@ -1175,20 +1189,24 @@ template constexpr auto operator<(Integer lhs, decimal32 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !less_impl(rhs, lhs) && lhs != rhs; } constexpr auto operator<=(decimal32 lhs, decimal32 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(rhs < lhs); } @@ -1197,10 +1215,12 @@ template constexpr auto operator<=(decimal32 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs)) { return false; } + #endif return !(rhs < lhs); } @@ -1209,10 +1229,12 @@ template constexpr auto operator<=(Integer lhs, decimal32 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !(rhs < lhs); } @@ -1226,10 +1248,12 @@ template constexpr auto operator>(decimal32 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs)) { return false; } + #endif return rhs < lhs; } @@ -1238,20 +1262,24 @@ template constexpr auto operator>(Integer lhs, decimal32 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return rhs < lhs; } constexpr auto operator>=(decimal32 lhs, decimal32 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(lhs < rhs); } @@ -1260,10 +1288,12 @@ template constexpr auto operator>=(decimal32 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs)) { return false; } + #endif return !(lhs < rhs); } @@ -1272,10 +1302,12 @@ template constexpr auto operator>=(Integer lhs, decimal32 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !(lhs < rhs); } @@ -1442,6 +1474,7 @@ template , b #endif BOOST_DECIMAL_CXX20_CONSTEXPR decimal32::decimal32(Float val) noexcept { + #ifndef BOOST_DECIMAL_FAST_MATH if (val != val) { *this = boost::decimal::from_bits(boost::decimal::detail::d32_nan_mask); @@ -1451,6 +1484,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal32::decimal32(Float val) noexcept *this = boost::decimal::from_bits(boost::decimal::detail::d32_inf_mask); } else + #endif { const auto components {detail::ryu::floating_point_to_fd128(val)}; @@ -1460,11 +1494,13 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal32::decimal32(Float val) noexcept << "\nSign: " << components.sign << std::endl; #endif + #ifndef BOOST_DECIMAL_FAST_MATH if (components.exponent > detail::emax) { *this = boost::decimal::from_bits(boost::decimal::detail::d32_inf_mask); } else + #endif { *this = decimal32 {components.mantissa, components.exponent, components.sign}; } @@ -1609,6 +1645,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR auto to_bits(decimal32 rhs) noexcept -> std::uint3 constexpr auto operator*(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal32 zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -1616,6 +1653,7 @@ constexpr auto operator*(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { return res; } + #endif auto sig_lhs {lhs.full_significand()}; auto exp_lhs {lhs.biased_exponent()}; @@ -1634,10 +1672,12 @@ template constexpr auto operator*(decimal32 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif auto sig_lhs {lhs.full_significand()}; auto exp_lhs {lhs.biased_exponent()}; @@ -1688,6 +1728,7 @@ constexpr auto decimal32::operator*=(Decimal rhs) noexcept constexpr auto div_impl(decimal32 lhs, decimal32 rhs, decimal32& q, decimal32& r) noexcept -> void { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal32 zero {0, 0}; constexpr decimal32 nan {boost::decimal::from_bits(boost::decimal::detail::d32_snan_mask)}; @@ -1732,6 +1773,9 @@ constexpr auto div_impl(decimal32 lhs, decimal32 rhs, decimal32& q, decimal32& r default: static_cast(rhs); } + #else + static_cast(r); + #endif auto sig_lhs {lhs.full_significand()}; auto exp_lhs {lhs.biased_exponent()}; @@ -1779,6 +1823,7 @@ template constexpr auto operator/(decimal32 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal32 zero {0, 0}; constexpr decimal32 nan {boost::decimal::from_bits(boost::decimal::detail::d32_snan_mask)}; @@ -1804,6 +1849,7 @@ constexpr auto operator/(decimal32 lhs, Integer rhs) noexcept { return sign ? -inf : inf; } + #endif auto sig_lhs {lhs.full_significand()}; auto exp_lhs {lhs.biased_exponent()}; @@ -1823,6 +1869,7 @@ template constexpr auto operator/(Integer lhs, decimal32 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal32 zero {0, 0}; constexpr decimal32 nan {boost::decimal::from_bits(boost::decimal::detail::d32_snan_mask)}; @@ -1846,6 +1893,7 @@ constexpr auto operator/(Integer lhs, decimal32 rhs) noexcept default: static_cast(lhs); } + #endif auto sig_rhs {rhs.full_significand()}; auto exp_rhs {rhs.biased_exponent()}; @@ -2061,6 +2109,7 @@ constexpr auto operator~(decimal32 lhs) noexcept -> decimal32 // The samequantum functions raise no exception. constexpr auto samequantumd32(decimal32 lhs, decimal32 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH const auto lhs_fp {fpclassify(lhs)}; const auto rhs_fp {fpclassify(rhs)}; @@ -2072,6 +2121,7 @@ constexpr auto samequantumd32(decimal32 lhs, decimal32 rhs) noexcept -> bool { return false; } + #endif return lhs.unbiased_exponent() == rhs.unbiased_exponent(); } @@ -2081,10 +2131,12 @@ constexpr auto samequantumd32(decimal32 lhs, decimal32 rhs) noexcept -> bool // Otherwise, a domain error occurs and INT_MIN is returned. constexpr auto quantexpd32(decimal32 x) noexcept -> int { + #ifndef BOOST_DECIMAL_FAST_MATH if (!isfinite(x)) { return INT_MIN; } + #endif return static_cast(x.unbiased_exponent()); } @@ -2102,6 +2154,7 @@ constexpr auto quantexpd32(decimal32 x) noexcept -> int // The quantize functions do not signal underflow. constexpr auto quantized32(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { + #ifndef BOOST_DECIMAL_FAST_MATH // Return the correct type of nan if (isnan(lhs)) { @@ -2121,18 +2174,21 @@ constexpr auto quantized32(decimal32 lhs, decimal32 rhs) noexcept -> decimal32 { return lhs; } + #endif return {lhs.full_significand(), rhs.biased_exponent(), lhs.isneg()}; } constexpr auto scalblnd32(decimal32 num, long exp) noexcept -> decimal32 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal32 zero {0, 0}; if (num == zero || exp == 0 || isinf(num) || isnan(num)) { return num; } + #endif num.edit_exponent(num.biased_exponent() + exp); diff --git a/include/boost/decimal/decimal32_fast.hpp b/include/boost/decimal/decimal32_fast.hpp index 13ef20659..502e9dd5d 100644 --- a/include/boost/decimal/decimal32_fast.hpp +++ b/include/boost/decimal/decimal32_fast.hpp @@ -42,6 +42,7 @@ class decimal32_fast final { public: using significand_type = std::uint_fast32_t; + using exponent_type = std::uint_fast8_t; private: // In regular decimal32 we have to decode the 24 bits of the significand and the 8 bits of the exp @@ -57,12 +58,12 @@ class decimal32_fast final return sign_; } - constexpr auto full_significand() const noexcept -> std::uint_fast32_t + constexpr auto full_significand() const noexcept -> significand_type { return significand_; } - constexpr auto unbiased_exponent() const noexcept -> std::uint_fast8_t + constexpr auto unbiased_exponent() const noexcept -> exponent_type { return exponent_; } @@ -83,8 +84,9 @@ class decimal32_fast final friend constexpr auto to_integral(Decimal val) noexcept BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_integral_v, TargetType, TargetType); - template - friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type; + template + friend BOOST_DECIMAL_CXX20_CONSTEXPR auto to_float(Decimal val) noexcept + BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_floating_point_v, TargetType, TargetType); template friend constexpr auto to_decimal(Decimal val) noexcept -> TargetType; @@ -296,45 +298,75 @@ class decimal32_fast final explicit constexpr operator detail::uint128_t() const noexcept; #endif + // 3.2.6 Conversion to floating-point type + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept; + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept; + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept; + + #ifdef BOOST_DECIMAL_HAS_FLOAT16 + explicit constexpr operator std::float16_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT32 + explicit constexpr operator std::float32_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT64 + explicit constexpr operator std::float64_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_BRAINFLOAT16 + explicit constexpr operator std::bfloat16_t() const noexcept; + #endif + + + // Conversion to other decimal type template , bool> = true> explicit constexpr operator Decimal() const noexcept; friend constexpr auto direct_init(std::uint_fast32_t significand, std::uint_fast8_t exponent, bool sign) noexcept -> decimal32_fast; + + // or extensions that need to be friends + template + friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type; + + friend constexpr auto copysignd32f(decimal32_fast mag, decimal32_fast sgn) noexcept -> decimal32_fast; + friend constexpr auto scalbnd32f(decimal32_fast num, int exp) noexcept -> decimal32_fast; + friend constexpr auto scalblnd32f(decimal32_fast num, long exp) noexcept -> decimal32_fast; + friend constexpr auto fmad32f(decimal32_fast x, decimal32_fast y, decimal32_fast z) noexcept -> decimal32_fast; + + template + friend constexpr auto ilogb(T d) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, T, int); + + template + friend constexpr auto logb(T num) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T); + + // Specific decimal functionality + friend constexpr auto samequantumd32f(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool; + friend constexpr auto quantexpd32f(decimal32_fast x) noexcept -> int; + friend constexpr auto quantized32f(decimal32_fast lhs, decimal32_fast rhs) noexcept -> decimal32_fast; }; template && detail::is_integral_v, bool>> constexpr decimal32_fast::decimal32_fast(T1 coeff, T2 exp, bool sign) noexcept { + // Older compilers have issues with conversions from __uint128, so we skip all that and use our uint128 + #if defined(BOOST_DECIMAL_HAS_INT128) && (!defined(__GNUC__) || (defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 10)) && (!defined(__clang__) || (defined(__clang__) && __clang_major__ < 13)) + using Unsigned_Integer_1 = detail::make_unsigned_t; + using Unsigned_Integer = std::conditional_t::value, detail::uint128, Unsigned_Integer_1>; + #else using Unsigned_Integer = detail::make_unsigned_t; + #endif + + using Basis_Unsigned_Integer = std::conditional_t::digits10 < std::numeric_limits::digits10, significand_type, Unsigned_Integer>; const bool isneg {coeff < static_cast(0) || sign}; sign_ = isneg; - Unsigned_Integer unsigned_coeff {detail::make_positive_unsigned(coeff)}; - - auto unsigned_coeff_digits {detail::num_digits(unsigned_coeff)}; - const bool reduced {unsigned_coeff_digits > detail::precision_v}; - - // Strip digits and round as required - if (reduced) - { - const auto digits_to_remove {static_cast(unsigned_coeff_digits - (detail::precision_v + 1))}; + auto unsigned_coeff {static_cast(detail::make_positive_unsigned(coeff))}; - #if defined(__GNUC__) && !defined(__clang__) - # pragma GCC diagnostic push - # pragma GCC diagnostic ignored "-Wconversion" - #endif + // Normalize in the constructor, so we never have to worry about it again + detail::normalize(unsigned_coeff, exp, sign); - unsigned_coeff /= static_cast(detail::pow10(digits_to_remove)); - - #if defined(__GNUC__) && !defined(__clang__) - # pragma GCC diagnostic pop - #endif - - exp += static_cast(digits_to_remove); - exp += static_cast(detail::fenv_round(unsigned_coeff, isneg)); - } - - significand_ = static_cast(unsigned_coeff); + significand_ = static_cast(unsigned_coeff); // Normalize the handling of zeros if (significand_ == UINT32_C(0)) @@ -343,13 +375,15 @@ constexpr decimal32_fast::decimal32_fast(T1 coeff, T2 exp, bool sign) noexcept } auto biased_exp {static_cast(exp + detail::bias)}; - if (biased_exp > std::numeric_limits::max()) + + // Decimal32 exponent holds 8 bits + if (biased_exp > UINT32_C(0xFF)) { significand_ = detail::d32_fast_inf; } else { - exponent_ = static_cast(biased_exp); + exponent_ = static_cast(biased_exp); } } @@ -371,6 +405,7 @@ constexpr decimal32_fast::decimal32_fast(Integer val) noexcept template , bool>> BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_fast::decimal32_fast(Float val) noexcept { + #ifndef BOOST_DECIMAL_FAST_MATH if (val != val) { significand_ = detail::d32_fast_qnan; @@ -380,6 +415,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_fast::decimal32_fast(Float val) noexcept significand_ = detail::d32_fast_inf; } else + #endif { const auto components {detail::ryu::floating_point_to_fd128(val)}; *this = decimal32_fast {components.mantissa, components.exponent, components.sign}; @@ -434,13 +470,16 @@ constexpr auto isnormal(decimal32_fast val) noexcept -> bool constexpr auto operator==(decimal32_fast lhs, decimal32_fast 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 lhs.sign_ == rhs.sign_ && + lhs.exponent_ == rhs.exponent_ && + lhs.significand_ == rhs.significand_; } constexpr auto operator!=(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool @@ -450,6 +489,7 @@ constexpr auto operator!=(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bo constexpr auto operator<(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs) || (!lhs.isneg() && rhs.isneg())) { @@ -467,17 +507,47 @@ constexpr auto operator<(decimal32_fast lhs, decimal32_fast rhs) noexcept -> boo { return signbit(rhs); } + #else + if (!lhs.isneg() && rhs.isneg()) + { + return false; + } + else if (lhs.isneg() && !rhs.isneg()) + { + return true; + } + #endif + + if (lhs.significand_ == 0 || rhs.significand_ == 0) + { + if (lhs.significand_ == 0 && rhs.significand_ == 0) + { + return false; + } + return lhs.significand_ == 0 ? !rhs.sign_ : lhs.sign_; + } + + if (lhs.sign_ != rhs.sign_) + { + return lhs.sign_; + } - return less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), - rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); + if (lhs.exponent_ != rhs.exponent_) + { + return lhs.sign_ ? lhs.exponent_ > rhs.exponent_ : lhs.exponent_ < rhs.exponent_; + } + + return lhs.sign_ ? lhs.significand_ > rhs.significand_ : lhs.significand_ < rhs.significand_; } constexpr auto operator<=(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(rhs < lhs); } @@ -489,10 +559,12 @@ constexpr auto operator>(decimal32_fast lhs, decimal32_fast rhs) noexcept -> boo constexpr auto operator>=(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(lhs < rhs); } @@ -536,49 +608,77 @@ template constexpr auto operator<(Integer lhs, decimal32_fast rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH return isnan(rhs) ? false : !less_impl(rhs, lhs) && lhs != rhs; + #else + return !less_impl(rhs, lhs) && lhs != rhs; + #endif } template constexpr auto operator<=(decimal32_fast lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH return isnan(lhs) ? false : !(rhs < lhs); + #else + return !(rhs < lhs); + #endif } template constexpr auto operator<=(Integer lhs, decimal32_fast rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH return isnan(rhs) ? false : !(rhs < lhs); + #else + return !(rhs < lhs); + #endif } template constexpr auto operator>(decimal32_fast lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH return isnan(lhs) ? false : rhs < lhs; + #else + return rhs < lhs; + #endif } template constexpr auto operator>(Integer lhs, decimal32_fast rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH return isnan(rhs) ? false : rhs < lhs; + #else + return rhs < lhs; + #endif } template constexpr auto operator>=(decimal32_fast lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH return isnan(lhs) ? false : !(lhs < rhs); + #else + return !(lhs < rhs); + #endif } template constexpr auto operator>=(Integer lhs, decimal32_fast rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH return isnan(rhs) ? false : !(lhs < rhs); + #else + return !(lhs < rhs); + #endif } #ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR @@ -656,6 +756,7 @@ constexpr auto operator-(decimal32_fast rhs) noexcept -> decimal32_fast constexpr auto operator+(decimal32_fast lhs, decimal32_fast rhs) noexcept -> decimal32_fast { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal32_fast zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -663,6 +764,7 @@ constexpr auto operator+(decimal32_fast lhs, decimal32_fast rhs) noexcept -> dec { return res; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && rhs.isneg()) @@ -681,15 +783,10 @@ constexpr auto operator+(decimal32_fast lhs, decimal32_fast rhs) noexcept -> dec return lhs - abs(rhs); } - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); - - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::normalize(sig_rhs, exp_rhs); - - const auto result {detail::add_impl(sig_lhs, exp_lhs, lhs.isneg(), sig_rhs, exp_rhs, rhs.isneg())}; + const auto result {detail::add_impl( + lhs.significand_, lhs.biased_exponent(), lhs.sign_, + rhs.significand_, rhs.biased_exponent(), rhs.sign_ + )}; return {result.sig, result.exp, result.sign}; } @@ -698,10 +795,12 @@ template constexpr auto operator+(decimal32_fast lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32_fast) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && (rhs < 0)) @@ -710,21 +809,17 @@ constexpr auto operator+(decimal32_fast lhs, Integer rhs) noexcept } bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)}; - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); + auto lhs_components {detail::decimal32_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}}; - auto lhs_components {detail::decimal32_fast_components{sig_lhs, exp_lhs, lhs.isneg()}}; auto sig_rhs {rhs}; std::int32_t exp_rhs {0}; detail::normalize(sig_rhs, exp_rhs); - auto unsigned_sig_rhs = detail::shrink_significand(detail::make_positive_unsigned(sig_rhs), exp_rhs); + auto unsigned_sig_rhs = detail::shrink_significand(detail::make_positive_unsigned(sig_rhs), exp_rhs); auto rhs_components {detail::decimal32_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; if (!lhs_bigger) { detail::swap(lhs_components, rhs_components); - lhs_bigger = !lhs_bigger; abs_lhs_bigger = !abs_lhs_bigger; } @@ -754,6 +849,7 @@ constexpr auto operator+(Integer lhs, decimal32_fast rhs) noexcept constexpr auto operator-(decimal32_fast lhs, decimal32_fast rhs) noexcept -> decimal32_fast { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal32_fast zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -761,6 +857,7 @@ constexpr auto operator-(decimal32_fast lhs, decimal32_fast rhs) noexcept -> dec { return res; } + #endif if (!lhs.isneg() && rhs.isneg()) { @@ -769,17 +866,11 @@ constexpr auto operator-(decimal32_fast lhs, decimal32_fast rhs) noexcept -> dec const bool abs_lhs_bigger {abs(lhs) > abs(rhs)}; - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); - - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::normalize(sig_rhs, exp_rhs); - - const auto result {detail::sub_impl(sig_lhs, exp_lhs, lhs.isneg(), - sig_rhs, exp_rhs, rhs.isneg(), - abs_lhs_bigger)}; + const auto result {detail::sub_impl( + lhs.significand_, lhs.biased_exponent(), lhs.sign_, + rhs.significand_, rhs.biased_exponent(), rhs.sign_, + abs_lhs_bigger + )}; return {result.sig, result.exp, result.sign}; } @@ -788,10 +879,12 @@ template constexpr auto operator-(decimal32_fast lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32_fast) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(lhs) || isnan(lhs)) { return lhs; } + #endif if (!lhs.isneg() && (rhs < 0)) { @@ -800,20 +893,18 @@ constexpr auto operator-(decimal32_fast lhs, Integer rhs) noexcept const bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)}; - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); - auto lhs_components {detail::decimal32_fast_components{sig_lhs, exp_lhs, lhs.isneg()}}; + auto lhs_components {detail::decimal32_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}}; auto sig_rhs {rhs}; std::int32_t exp_rhs {0}; detail::normalize(sig_rhs, exp_rhs); - auto unsigned_sig_rhs = detail::shrink_significand(detail::make_positive_unsigned(sig_rhs), exp_rhs); + auto unsigned_sig_rhs = detail::shrink_significand(detail::make_positive_unsigned(sig_rhs), exp_rhs); auto rhs_components {detail::decimal32_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; - const auto result {detail::sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign, - abs_lhs_bigger)}; + const auto result {detail::sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger)}; return {result.sig, result.exp, result.sign}; } @@ -822,10 +913,12 @@ template constexpr auto operator-(Integer lhs, decimal32_fast rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32_fast) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(rhs) || isnan(rhs)) { return rhs; } + #endif if (lhs >= 0 && rhs.isneg()) { @@ -840,20 +933,20 @@ constexpr auto operator-(Integer lhs, decimal32_fast rhs) noexcept auto unsigned_sig_lhs = detail::shrink_significand(detail::make_positive_unsigned(sig_lhs), exp_lhs); auto lhs_components {detail::decimal32_fast_components{unsigned_sig_lhs, exp_lhs, (lhs < 0)}}; - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::normalize(sig_rhs, exp_rhs); - auto rhs_components {detail::decimal32_fast_components{sig_rhs, exp_rhs, rhs.isneg()}}; + auto rhs_components {detail::decimal32_fast_components{rhs.significand_, rhs.biased_exponent(), rhs.isneg()}}; - const auto result {detail::sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign, - abs_lhs_bigger)}; + const auto result {detail::sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger + )}; return {result.sig, result.exp, result.sign}; } constexpr auto operator*(decimal32_fast lhs, decimal32_fast rhs) noexcept -> decimal32_fast { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal32_fast zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -861,16 +954,12 @@ constexpr auto operator*(decimal32_fast lhs, decimal32_fast rhs) noexcept -> dec { return res; } + #endif - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); - - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::normalize(sig_rhs, exp_rhs); - - const auto result {detail::mul_impl(sig_lhs, exp_lhs, lhs.isneg(), sig_rhs, exp_rhs, rhs.isneg())}; + const auto result {detail::mul_impl( + lhs.significand_, lhs.biased_exponent(), lhs.sign_, + rhs.significand_, rhs.biased_exponent(), rhs.sign_ + )}; return {result.sig, result.exp, result.sign}; } @@ -879,15 +968,14 @@ template constexpr auto operator*(decimal32_fast lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32_fast) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); - auto lhs_components {detail::decimal32_fast_components{sig_lhs, exp_lhs, lhs.isneg()}}; + auto lhs_components {detail::decimal32_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.sign_}}; auto sig_rhs {rhs}; std::int32_t exp_rhs {0}; @@ -896,9 +984,9 @@ constexpr auto operator*(decimal32_fast lhs, Integer rhs) noexcept auto rhs_components {detail::decimal32_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; const auto result {detail::mul_impl( - lhs_components.sig, lhs_components.exp, lhs_components.sign, - rhs_components.sig, rhs_components.exp, rhs_components.sign - )}; + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign + )}; return {result.sig, result.exp, result.sign}; } @@ -912,6 +1000,7 @@ constexpr auto operator*(Integer lhs, decimal32_fast rhs) noexcept constexpr auto div_impl(decimal32_fast lhs, decimal32_fast rhs, decimal32_fast& q, decimal32_fast& r) noexcept -> void { + #ifndef BOOST_DECIMAL_FAST_MATH const bool sign {lhs.isneg() != rhs.isneg()}; constexpr decimal32_fast zero {0, 0}; @@ -955,14 +1044,9 @@ constexpr auto div_impl(decimal32_fast lhs, decimal32_fast rhs, decimal32_fast& default: static_cast(rhs); } - - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); - - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::normalize(sig_rhs, exp_rhs); + #else + static_cast(r); + #endif #ifdef BOOST_DECIMAL_DEBUG std::cerr << "sig lhs: " << sig_lhs @@ -971,13 +1055,13 @@ constexpr auto div_impl(decimal32_fast lhs, decimal32_fast rhs, decimal32_fast& << "\nexp rhs: " << exp_rhs << std::endl; #endif - detail::decimal32_components lhs_components {static_cast(sig_lhs), exp_lhs, lhs.isneg()}; - detail::decimal32_components rhs_components {static_cast(sig_rhs), exp_rhs, rhs.isneg()}; - detail::decimal32_components q_components {}; - - generic_div_impl(lhs_components, rhs_components, q_components); + // We promote to uint64 since the significands are currently 32-bits + // By appending enough zeros to the LHS we end up finding what we need anyway + const auto big_sig_lhs {static_cast(lhs.significand_) * detail::pow10(static_cast(detail::precision_v))}; + const auto res_sig {big_sig_lhs / static_cast(rhs.significand_)}; + const auto res_exp {(lhs.biased_exponent() - detail::precision_v) - rhs.biased_exponent()}; - q = decimal32_fast(q_components.sig, q_components.exp, q_components.sign); + q = decimal32_fast(res_sig, res_exp, lhs.sign_ != rhs.sign_); } constexpr auto mod_impl(decimal32_fast lhs, decimal32_fast rhs, const decimal32_fast& q, decimal32_fast& r) noexcept -> void @@ -1002,6 +1086,7 @@ template constexpr auto operator/(decimal32_fast lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32_fast) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal32_fast zero {0, 0}; constexpr decimal32_fast nan {direct_init(detail::d32_fast_qnan, UINT8_C(0), false)}; @@ -1027,14 +1112,11 @@ constexpr auto operator/(decimal32_fast lhs, Integer rhs) noexcept { return sign ? -inf : inf; } + #endif - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::normalize(sig_lhs, exp_lhs); - - const detail::decimal32_fast_components lhs_components {sig_lhs, exp_lhs, lhs.isneg()}; + const detail::decimal32_fast_components lhs_components {lhs.significand_, lhs.biased_exponent(), lhs.sign_}; std::int32_t exp_rhs {}; - const detail::decimal32_fast_components rhs_components {detail::shrink_significand(detail::make_positive_unsigned(rhs), exp_rhs), exp_rhs, rhs < 0}; + const detail::decimal32_fast_components rhs_components {detail::shrink_significand(detail::make_positive_unsigned(rhs), exp_rhs), exp_rhs, rhs < 0}; detail::decimal32_fast_components q_components {}; detail::generic_div_impl(lhs_components, rhs_components, q_components); @@ -1046,6 +1128,7 @@ template constexpr auto operator/(Integer lhs, decimal32_fast rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal32_fast) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal32_fast zero {0, 0}; constexpr decimal32_fast nan {direct_init(detail::d32_fast_qnan, UINT8_C(0), false)}; @@ -1069,15 +1152,12 @@ constexpr auto operator/(Integer lhs, decimal32_fast rhs) noexcept default: static_cast(lhs); } - - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::normalize(sig_rhs, exp_rhs); + #endif std::int32_t lhs_exp {}; - const auto lhs_sig {detail::make_positive_unsigned(detail::shrink_significand(lhs, lhs_exp))}; + const auto lhs_sig {detail::make_positive_unsigned(detail::shrink_significand(lhs, lhs_exp))}; const detail::decimal32_fast_components lhs_components {lhs_sig, lhs_exp, lhs < 0}; - const detail::decimal32_fast_components rhs_components {sig_rhs, exp_rhs, rhs.isneg()}; + const detail::decimal32_fast_components rhs_components {rhs.significand_, rhs.biased_exponent(), rhs.isneg()}; detail::decimal32_fast_components q_components {}; detail::generic_div_impl(lhs_components, rhs_components, q_components); @@ -1251,12 +1331,154 @@ constexpr decimal32_fast::operator detail::uint128_t() const noexcept #endif +BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_fast::operator float() const noexcept +{ + return to_float(*this); +} + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_fast::operator double() const noexcept +{ + return to_float(*this); +} + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal32_fast::operator long double() const noexcept +{ + // TODO(mborland): Don't have an exact way of converting to various long doubles + return static_cast(to_float(*this)); +} + +#ifdef BOOST_DECIMAL_HAS_FLOAT16 +constexpr decimal32_fast::operator std::float16_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_FLOAT32 +constexpr decimal32_fast::operator std::float32_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_FLOAT64 +constexpr decimal32_fast::operator std::float64_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_BRAINFLOAT16 +constexpr decimal32_fast::operator std::bfloat16_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif + template , bool>> constexpr decimal32_fast::operator Decimal() const noexcept { return to_decimal(*this); } +constexpr auto scalblnd32f(decimal32_fast num, long exp) noexcept -> decimal32_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal32_fast zero {0, 0}; + + if (num == zero || exp == 0 || isinf(num) || isnan(num)) + { + return num; + } + #endif + + num = decimal32_fast(num.significand_, num.biased_exponent() + exp, num.sign_); + + return num; +} + +constexpr auto scalbnd32f(decimal32_fast num, int expval) noexcept -> decimal32_fast +{ + return scalblnd32f(num, static_cast(expval)); +} + +constexpr auto copysignd32f(decimal32_fast mag, decimal32_fast sgn) noexcept -> decimal32_fast +{ + mag.sign_ = sgn.sign_; + return mag; +} + +// Effects: determines if the quantum exponents of x and y are the same. +// If both x and y are NaN, or infinity, they have the same quantum exponents; +// if exactly one operand is infinity or exactly one operand is NaN, they do not have the same quantum exponents. +// The samequantum functions raise no exception. +constexpr auto samequantumd32f(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + const auto lhs_fp {fpclassify(lhs)}; + const auto rhs_fp {fpclassify(rhs)}; + + if ((lhs_fp == FP_NAN && rhs_fp == FP_NAN) || (lhs_fp == FP_INFINITE && rhs_fp == FP_INFINITE)) + { + return true; + } + if ((lhs_fp == FP_NAN || rhs_fp == FP_INFINITE) || (rhs_fp == FP_NAN || lhs_fp == FP_INFINITE)) + { + return false; + } + #endif + + return lhs.unbiased_exponent() == rhs.unbiased_exponent(); +} + +// Effects: if x is finite, returns its quantum exponent. +// Otherwise, a domain error occurs and INT_MIN is returned. +constexpr auto quantexpd32f(decimal32_fast x) noexcept -> int +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (!isfinite(x)) + { + return INT_MIN; + } + #endif + + return static_cast(x.unbiased_exponent()); +} + +// Returns: a number that is equal in value (except for any rounding) and sign to x, +// and which has an exponent set to be equal to the exponent of y. +// If the exponent is being increased, the value is correctly rounded according to the current rounding mode; +// if the result does not have the same value as x, the "inexact" floating-point exception is raised. +// If the exponent is being decreased and the significand of the result has more digits than the type would allow, +// the "invalid" floating-point exception is raised and the result is NaN. +// If one or both operands are NaN the result is NaN. +// Otherwise, if only one operand is infinity, the "invalid" floating-point exception is raised and the result is NaN. +// If both operands are infinity, the result is DEC_INFINITY, with the same sign as x, converted to the type of x. +// The quantize functions do not signal underflow. +constexpr auto quantized32f(decimal32_fast lhs, decimal32_fast rhs) noexcept -> decimal32_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + // Return the correct type of nan + if (isnan(lhs)) + { + return lhs; + } + else if (isnan(rhs)) + { + return rhs; + } + + // If one is infinity then return a signaling NAN + if (isinf(lhs) != isinf(rhs)) + { + return direct_init(detail::d32_fast_snan, UINT8_C(0)); + } + else if (isinf(lhs) && isinf(rhs)) + { + return lhs; + } + #endif + + return {lhs.full_significand(), rhs.biased_exponent(), lhs.isneg()}; +} + } // namespace decimal } // namespace boost @@ -1284,12 +1506,12 @@ struct numeric_limits // 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; + 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; #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_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; @@ -1312,7 +1534,9 @@ struct numeric_limits 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))); } - BOOST_DECIMAL_ATTRIBUTE_UNUSED static constexpr auto denorm_min () -> boost::decimal::decimal32_fast { return {1, boost::decimal::detail::etiny}; } + + // 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}; } }; } // Namespace std diff --git a/include/boost/decimal/decimal64.hpp b/include/boost/decimal/decimal64.hpp index fcf0458f3..da2ce21bd 100644 --- a/include/boost/decimal/decimal64.hpp +++ b/include/boost/decimal/decimal64.hpp @@ -34,6 +34,10 @@ #include #include #include +#include +#include +#include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -118,6 +122,8 @@ BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint64_t d64_construct_significand_mask = struct decimal64_components { + using sig_type = std::uint64_t; + std::uint64_t sig; std::int32_t exp; bool sign; @@ -193,23 +199,6 @@ BOOST_DECIMAL_EXPORT class decimal64 final -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_decimal_floating_point_v), bool>; - template - friend constexpr auto d64_add_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept - -> detail::decimal64_components; - - template - friend constexpr auto d64_sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, - bool abs_lhs_bigger) noexcept -> detail::decimal64_components; - - template - friend constexpr auto d64_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 -> detail::decimal64_components; - - friend constexpr auto d64_generic_div_impl(detail::decimal64_components lhs, detail::decimal64_components rhs, - detail::decimal64_components& q) noexcept -> void; - friend constexpr auto d64_div_impl(decimal64 lhs, decimal64 rhs, decimal64& q, decimal64& r) noexcept -> void; friend constexpr auto d64_mod_impl(decimal64 lhs, decimal64 rhs, const decimal64& q, decimal64& r) noexcept -> void; @@ -784,6 +773,7 @@ template , b #endif BOOST_DECIMAL_CXX20_CONSTEXPR decimal64::decimal64(Float val) noexcept { + #ifndef BOOST_DECIMAL_FAST_MATH if (val != val) { *this = from_bits(detail::d64_nan_mask); @@ -793,6 +783,7 @@ BOOST_DECIMAL_CXX20_CONSTEXPR decimal64::decimal64(Float val) noexcept *this = from_bits(detail::d64_inf_mask); } else + #endif { const auto components {detail::ryu::floating_point_to_fd128(val)}; @@ -1106,245 +1097,9 @@ constexpr auto operator-(decimal64 rhs) noexcept-> decimal64 return rhs; } -template -constexpr auto d64_add_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept -> detail::decimal64_components -{ - const bool sign {lhs_sign}; - - auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; - - if (delta_exp > detail::precision_v + 1) - { - // If the difference in exponents is more than the digits of accuracy - // we return the larger of the two - // - // e.g. 1e20 + 1e-20 = 1e20 - - return {lhs_sig, lhs_exp, lhs_sign}; - } - else if (delta_exp == detail::precision_v + 1) - { - // Only need to see if we need to add one to the - // significand of the bigger value - // - // e.g. 1.234567e5 + 9.876543e-2 = 1.234568e5 - - if (rhs_sig >= UINT64_C(5'000'000'000'000'000)) - { - ++lhs_sig; - return {lhs_sig, lhs_exp, lhs_sign}; - } - else - { - return {lhs_sig, lhs_exp, lhs_sign}; - } - } - - // The two numbers can be added together without special handling - // - // If we can add to the lhs sig rather than dividing we can save some precision - // 64-bit sign int can have 19 digits, and our normalized significand has 16 - if (delta_exp <= 3) - { - while (delta_exp > 0) - { - lhs_sig *= 10; - --delta_exp; - --lhs_exp; - } - } - else - { - lhs_sig *= 1000; - delta_exp -= 3; - lhs_exp -= 3; - } - - while (delta_exp > 1) - { - rhs_sig /= 10; - --delta_exp; - } - - if (delta_exp == 1) - { - detail::fenv_round(rhs_sig, rhs_sign); - } - - // Both of the significands are well under 64-bits, so we can fit them into int64_t without issue - const auto new_sig {static_cast(lhs_sig) + static_cast(rhs_sig)}; - const auto new_exp {lhs_exp}; - const auto res_sig {detail::make_positive_unsigned(new_sig)}; - - #ifdef BOOST_DECIMAL_DEBUG_ADD - std::cerr << "Res Sig: " << new_sig - << "\nRes Exp: " << new_exp - << "\nRes Neg: " << sign << std::endl; - #endif - - return {res_sig, new_exp, sign}; -} - -template -constexpr auto d64_sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, - bool abs_lhs_bigger) noexcept -> detail::decimal64_components -{ - auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; - auto signed_sig_lhs {detail::make_signed_value(lhs_sig, lhs_sign)}; - auto signed_sig_rhs {detail::make_signed_value(rhs_sig, rhs_sign)}; - - if (delta_exp > detail::precision_v + 1) - { - // If the difference in exponents is more than the digits of accuracy - // we return the larger of the two - // - // e.g. 1e20 - 1e-20 = 1e20 - return abs_lhs_bigger ? detail::decimal64_components{detail::shrink_significand(lhs_sig, lhs_exp), lhs_exp, false} : - detail::decimal64_components{detail::shrink_significand(rhs_sig, rhs_exp), rhs_exp, true}; - } - - // The two numbers can be subtracted together without special handling - - auto& sig_bigger {abs_lhs_bigger ? signed_sig_lhs : signed_sig_rhs}; - auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp}; - auto& sig_smaller {abs_lhs_bigger ? signed_sig_rhs : signed_sig_lhs}; - auto& smaller_sign {abs_lhs_bigger ? rhs_sign : lhs_sign}; - - if (delta_exp == 1) - { - sig_bigger *= 10; - --delta_exp; - --exp_bigger; - } - else if (delta_exp >= 2) - { - sig_bigger *= 100; - delta_exp -= 2; - exp_bigger -= 2; - } - - while (delta_exp > 1) - { - sig_smaller /= 10; - --delta_exp; - } - - if (delta_exp == 1) - { - detail::fenv_round(sig_smaller, smaller_sign); - } - - // Both of the significands are less than 9'999'999'999'999'999, so we can safely - // cast them to signed 64-bit ints to calculate the new significand - std::int64_t new_sig {}; // NOLINT : Value is never used but can't leave uninitialized in constexpr function - - if (rhs_sign && !lhs_sign) - { - new_sig = signed_sig_lhs + signed_sig_rhs; - } - else - { - new_sig = signed_sig_lhs - signed_sig_rhs; - } - - const auto new_exp {abs_lhs_bigger ? lhs_exp : rhs_exp}; - const auto new_sign {new_sig < 0}; - const auto res_sig {detail::make_positive_unsigned(new_sig)}; - - return {res_sig, new_exp, new_sign}; -} - -template -constexpr auto d64_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 -> detail::decimal64_components -{ - #ifdef BOOST_DECIMAL_HAS_INT128 - using unsigned_int128_type = boost::decimal::detail::uint128_t; - #else - using unsigned_int128_type = boost::decimal::detail::uint128; - #endif - - #ifdef BOOST_DECIMAL_DEBUG - std::cerr << "sig lhs: " << sig_lhs - << "\nexp lhs: " << exp_lhs - << "\nsig rhs: " << sig_rhs - << "\nexp rhs: " << exp_rhs; - #endif - - 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 {static_cast(lhs_sig) * static_cast(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) - { - res_sig /= static_cast(detail::pow10(static_cast(sig_dig - std::numeric_limits::digits10))); - res_exp += sig_dig - std::numeric_limits::digits10; - } - - const auto res_sig_64 {static_cast(res_sig)}; - - #ifdef BOOST_DECIMAL_DEBUG - std::cerr << "\nres sig: " << res_sig_64 - << "\nres exp: " << res_exp << std::endl; - #endif - - // Always return positive zero - if (res_sig_64 == 0) - { - sign = false; - } - - return {res_sig_64, res_exp, sign}; -} - -constexpr auto d64_generic_div_impl(detail::decimal64_components lhs, detail::decimal64_components rhs, - detail::decimal64_components& q) noexcept -> void -{ - #ifdef BOOST_DECIMAL_HAS_INT128 - using unsigned_int128_type = boost::decimal::detail::uint128_t; - #else - using unsigned_int128_type = boost::decimal::detail::uint128; - #endif - - bool sign {lhs.sign != rhs.sign}; - - // If rhs is greater than we need to offset the significands to get the correct values - // e.g. 4/8 is 0 but 40/8 yields 5 in integer maths - const auto big_sig_lhs {static_cast(lhs.sig) * detail::powers_of_10[detail::precision_v]}; - lhs.exp -= detail::precision_v; - - auto res_sig {big_sig_lhs / static_cast(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) - { - res_sig /= static_cast(detail::pow10(static_cast(sig_dig - std::numeric_limits::digits10))); - res_exp += sig_dig - std::numeric_limits::digits10; - } - - const auto res_sig_64 {static_cast(res_sig)}; - - if (res_sig_64 == 0) - { - sign = false; - } - - // Let the constructor handle shrinking it back down and rounding correctly - q = detail::decimal64_components{res_sig_64, res_exp, sign}; -} - constexpr auto d64_div_impl(decimal64 lhs, decimal64 rhs, decimal64& q, decimal64& r) noexcept -> void { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal64 zero {0, 0}; constexpr decimal64 nan {boost::decimal::from_bits(boost::decimal::detail::d64_snan_mask)}; @@ -1389,6 +1144,9 @@ constexpr auto d64_div_impl(decimal64 lhs, decimal64 rhs, decimal64& q, decimal6 default: static_cast(rhs); } + #else + static_cast(r); + #endif auto sig_lhs {lhs.full_significand()}; auto exp_lhs {lhs.biased_exponent()}; @@ -1409,7 +1167,7 @@ constexpr auto d64_div_impl(decimal64 lhs, decimal64 rhs, decimal64& q, decimal6 detail::decimal64_components rhs_components {sig_rhs, exp_rhs, rhs.isneg()}; detail::decimal64_components q_components {}; - d64_generic_div_impl(lhs_components, rhs_components, q_components); + detail::d64_generic_div_impl(lhs_components, rhs_components, q_components); q = decimal64(q_components.sig, q_components.exp, q_components.sign); } @@ -1425,6 +1183,7 @@ constexpr auto d64_mod_impl(decimal64 lhs, decimal64 rhs, const decimal64& q, de constexpr auto operator+(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal64 zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -1432,6 +1191,7 @@ constexpr auto operator+(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { return res; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && rhs.isneg()) @@ -1458,7 +1218,7 @@ constexpr auto operator+(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 auto rhs_exp {rhs.biased_exponent()}; detail::normalize(rhs_sig, rhs_exp); - const auto result {d64_add_impl(lhs_sig, lhs_exp, lhs.isneg(), + const auto result {detail::d64_add_impl(lhs_sig, lhs_exp, lhs.isneg(), rhs_sig, rhs_exp, rhs.isneg())}; return {result.sig, result.exp, result.sign}; @@ -1468,10 +1228,12 @@ template constexpr auto operator+(decimal64 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif bool lhs_bigger {lhs > rhs}; if (lhs.isneg() && (rhs < 0)) @@ -1509,13 +1271,13 @@ constexpr auto operator+(decimal64 lhs, Integer rhs) noexcept if (!lhs_components.sign && rhs_components.sign) { - result = d64_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, + result = detail::d64_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, rhs_components.sig, rhs_components.exp, rhs_components.sign, abs_lhs_bigger); } else { - result = d64_add_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, + result = detail::d64_add_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, rhs_components.sig, rhs_components.exp, rhs_components.sign); } @@ -1532,6 +1294,7 @@ constexpr auto operator+(Integer lhs, decimal64 rhs) noexcept // NOLINTNEXTLINE : If subtraction is actually addition than use operator+ and vice versa constexpr auto operator-(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal64 zero {0, 0}; const auto res {detail::check_non_finite(lhs, rhs)}; @@ -1539,6 +1302,7 @@ constexpr auto operator-(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { return res; } + #endif if (!lhs.isneg() && rhs.isneg()) { @@ -1555,7 +1319,7 @@ constexpr auto operator-(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 auto exp_rhs {rhs.biased_exponent()}; detail::normalize(sig_rhs, exp_rhs); - const auto result {d64_sub_impl(sig_lhs, exp_lhs, lhs.isneg(), + const auto result {detail::d64_sub_impl(sig_lhs, exp_lhs, lhs.isneg(), sig_rhs, exp_rhs, rhs.isneg(), abs_lhs_bigger)}; @@ -1566,10 +1330,12 @@ template constexpr auto operator-(decimal64 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(lhs) || isnan(lhs)) { return lhs; } + #endif if (!lhs.isneg() && (rhs < 0)) { @@ -1589,7 +1355,7 @@ constexpr auto operator-(decimal64 lhs, Integer rhs) noexcept auto unsigned_sig_rhs = detail::shrink_significand(detail::make_positive_unsigned(sig_rhs), exp_rhs); auto rhs_components {detail::decimal64_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; - const auto result {d64_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, + const auto result {detail::d64_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, rhs_components.sig, rhs_components.exp, rhs_components.sign, abs_lhs_bigger)}; @@ -1600,10 +1366,12 @@ template constexpr auto operator-(Integer lhs, decimal64 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isinf(rhs) || isnan(rhs)) { return rhs; } + #endif if (lhs >= 0 && rhs.isneg()) { @@ -1623,7 +1391,7 @@ constexpr auto operator-(Integer lhs, decimal64 rhs) noexcept detail::normalize(sig_rhs, exp_rhs); auto rhs_components {detail::decimal64_components{sig_rhs, exp_rhs, rhs.isneg()}}; - const auto result {d64_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, + const auto result {detail::d64_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, rhs_components.sig, rhs_components.exp, rhs_components.sign, abs_lhs_bigger)}; @@ -1632,6 +1400,7 @@ constexpr auto operator-(Integer lhs, decimal64 rhs) noexcept constexpr auto operator*(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal64 zero {0, 0}; const auto non_finite {detail::check_non_finite(lhs, rhs)}; @@ -1639,6 +1408,7 @@ constexpr auto operator*(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { return non_finite; } + #endif auto lhs_sig {lhs.full_significand()}; auto lhs_exp {lhs.biased_exponent()}; @@ -1648,7 +1418,7 @@ constexpr auto operator*(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 auto rhs_exp {rhs.biased_exponent()}; detail::normalize(rhs_sig, rhs_exp); - const auto result {d64_mul_impl(lhs_sig, lhs_exp, lhs.isneg(), + const auto result {detail::d64_mul_impl(lhs_sig, lhs_exp, lhs.isneg(), rhs_sig, rhs_exp, rhs.isneg())}; return {result.sig, result.exp, result.sign}; @@ -1658,10 +1428,12 @@ template constexpr auto operator*(decimal64 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isinf(lhs)) { return lhs; } + #endif auto lhs_sig {lhs.full_significand()}; auto lhs_exp {lhs.biased_exponent()}; @@ -1674,7 +1446,7 @@ constexpr auto operator*(decimal64 lhs, Integer rhs) noexcept auto unsigned_sig_rhs {detail::shrink_significand(detail::make_positive_unsigned(rhs_sig), rhs_exp)}; auto rhs_components {detail::decimal64_components{unsigned_sig_rhs, rhs_exp, (rhs < 0)}}; - const auto result {d64_mul_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, + const auto result {detail::d64_mul_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, rhs_components.sig, rhs_components.exp, rhs_components.sign)}; return {result.sig, result.exp, result.sign}; @@ -1700,6 +1472,7 @@ template constexpr auto operator/(decimal64 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal64 zero {0, 0}; constexpr decimal64 nan {boost::decimal::from_bits(boost::decimal::detail::d64_snan_mask)}; @@ -1725,6 +1498,7 @@ constexpr auto operator/(decimal64 lhs, Integer rhs) noexcept { return sign ? -inf : inf; } + #endif auto lhs_sig {lhs.full_significand()}; auto lhs_exp {lhs.biased_exponent()}; @@ -1737,7 +1511,7 @@ constexpr auto operator/(decimal64 lhs, Integer rhs) noexcept detail::decimal64_components rhs_components {detail::shrink_significand(rhs_sig, rhs_exp), rhs_exp, rhs < 0}; detail::decimal64_components q_components {}; - d64_generic_div_impl(lhs_components, rhs_components, q_components); + detail::d64_generic_div_impl(lhs_components, rhs_components, q_components); return decimal64(q_components.sig, q_components.exp, q_components.sign); } @@ -1746,6 +1520,7 @@ template constexpr auto operator/(Integer lhs, decimal64 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64) { + #ifndef BOOST_DECIMAL_FAST_MATH // Check pre-conditions constexpr decimal64 zero {0, 0}; constexpr decimal64 inf {boost::decimal::from_bits(boost::decimal::detail::d64_inf_mask)}; @@ -1769,6 +1544,7 @@ constexpr auto operator/(Integer lhs, decimal64 rhs) noexcept default: static_cast(lhs); } + #endif auto rhs_sig {rhs.full_significand()}; auto rhs_exp {rhs.biased_exponent()}; @@ -1778,7 +1554,7 @@ constexpr auto operator/(Integer lhs, decimal64 rhs) noexcept detail::decimal64_components rhs_components {rhs_sig, rhs_exp, rhs.isneg()}; detail::decimal64_components q_components {}; - d64_generic_div_impl(lhs_components, rhs_components, q_components); + detail::d64_generic_div_impl(lhs_components, rhs_components, q_components); return decimal64(q_components.sig, q_components.exp, q_components.sign); } @@ -1913,11 +1689,13 @@ 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()); @@ -1958,6 +1736,7 @@ constexpr auto operator!=(Integer lhs, decimal64 rhs) noexcept constexpr auto operator<(decimal64 lhs, decimal64 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs) || (!lhs.isneg() && rhs.isneg())) { @@ -1969,15 +1748,18 @@ constexpr auto operator<(decimal64 lhs, decimal64 rhs) noexcept -> bool } else if (isfinite(lhs) && isinf(rhs)) { - if (!rhs.isneg()) - { - return true; - } - else - { - return false; - } + return !rhs.isneg(); + } + #else + if (!lhs.isneg() && rhs.isneg()) + { + return false; + } + else if (lhs.isneg() && !rhs.isneg()) + { + return true; } + #endif return less_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); @@ -1994,20 +1776,24 @@ template constexpr auto operator<(Integer lhs, decimal64 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !less_impl(rhs, lhs) && lhs != rhs; } constexpr auto operator<=(decimal64 lhs, decimal64 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(rhs < lhs); } @@ -2016,10 +1802,12 @@ template constexpr auto operator<=(decimal64 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs)) { return false; } + #endif return !(rhs < lhs); } @@ -2028,10 +1816,12 @@ template constexpr auto operator<=(Integer lhs, decimal64 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !(rhs < lhs); } @@ -2057,10 +1847,12 @@ constexpr auto operator>(Integer lhs, decimal64 rhs) noexcept constexpr auto operator>=(decimal64 lhs, decimal64 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs) || isnan(rhs)) { return false; } + #endif return !(lhs < rhs); } @@ -2069,10 +1861,12 @@ template constexpr auto operator>=(decimal64 lhs, Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(lhs)) { return false; } + #endif return !(lhs < rhs); } @@ -2081,10 +1875,12 @@ template constexpr auto operator>=(Integer lhs, decimal64 rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) { + #ifndef BOOST_DECIMAL_FAST_MATH if (isnan(rhs)) { return false; } + #endif return !(lhs < rhs); } @@ -2258,6 +2054,7 @@ constexpr auto operator~(decimal64 lhs) noexcept -> decimal64 // The samequantum functions raise no exception. constexpr auto samequantumd64(decimal64 lhs, decimal64 rhs) noexcept -> bool { + #ifndef BOOST_DECIMAL_FAST_MATH const auto lhs_fp {fpclassify(lhs)}; const auto rhs_fp {fpclassify(rhs)}; @@ -2269,6 +2066,7 @@ constexpr auto samequantumd64(decimal64 lhs, decimal64 rhs) noexcept -> bool { return false; } + #endif return lhs.unbiased_exponent() == rhs.unbiased_exponent(); } @@ -2278,10 +2076,12 @@ constexpr auto samequantumd64(decimal64 lhs, decimal64 rhs) noexcept -> bool // Otherwise, a domain error occurs and INT_MIN is returned. constexpr auto quantexpd64(decimal64 x) noexcept -> int { + #ifndef BOOST_DECIMAL_FAST_MATH if (!isfinite(x)) { return INT_MIN; } + #endif return static_cast(x.unbiased_exponent()); } @@ -2299,6 +2099,7 @@ constexpr auto quantexpd64(decimal64 x) noexcept -> int // The quantize functions do not signal underflow. constexpr auto quantized64(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { + #ifndef BOOST_DECIMAL_FAST_MATH // Return the correct type of nan if (isnan(lhs)) { @@ -2318,18 +2119,21 @@ constexpr auto quantized64(decimal64 lhs, decimal64 rhs) noexcept -> decimal64 { return lhs; } + #endif return {lhs.full_significand(), rhs.biased_exponent(), lhs.isneg()}; } constexpr auto scalblnd64(decimal64 num, long exp) noexcept -> decimal64 { + #ifndef BOOST_DECIMAL_FAST_MATH constexpr decimal64 zero {0, 0}; if (num == zero || exp == 0 || isinf(num) || isnan(num)) { return num; } + #endif num.edit_exponent(num.biased_exponent() + exp); diff --git a/include/boost/decimal/decimal64_fast.hpp b/include/boost/decimal/decimal64_fast.hpp new file mode 100644 index 000000000..0744c66d7 --- /dev/null +++ b/include/boost/decimal/decimal64_fast.hpp @@ -0,0 +1,1510 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DECIMAL64_FAST_HPP +#define BOOST_DECIMAL_DECIMAL64_FAST_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace boost { +namespace decimal { + +namespace detail { + +BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d64_fast_inf = std::numeric_limits::max(); +BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d64_fast_qnan = std::numeric_limits::max() - 1; +BOOST_DECIMAL_CONSTEXPR_VARIABLE auto d64_fast_snan = std::numeric_limits::max() - 2; + +struct decimal64_fast_components +{ + using sig_type = std::uint_fast64_t; + + std::uint_fast64_t sig; + std::int32_t exp; + bool sign; +}; + +} // namespace detail + +class decimal64_fast final +{ +public: + using significand_type = std::uint_fast64_t; + using exponent_type = std::uint_fast16_t; + using biased_exponent_type = std::int32_t; + +private: + // In regular decimal64 we have to decode the significand end exponent + // Here we will store them directly to avoid the overhead of decoding + + std::uint_fast64_t significand_ {}; + std::uint_fast16_t exponent_ {}; + bool sign_ {}; + + constexpr auto isneg() const noexcept -> bool + { + return sign_; + } + + constexpr auto full_significand() const noexcept -> significand_type + { + return significand_; + } + + constexpr auto unbiased_exponent() const noexcept -> exponent_type + { + return exponent_; + } + + constexpr auto biased_exponent() const noexcept -> biased_exponent_type + { + return static_cast(exponent_) - detail::bias_v; + } + + // Equality template between any integer type and decimal32 + template + friend constexpr auto mixed_equality_impl(Decimal lhs, Integer rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_integral_v), bool>; + + template + friend constexpr auto mixed_decimal_equality_impl(Decimal1 lhs, Decimal2 rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && + detail::is_decimal_floating_point_v), bool>; + + // Template to compare operator< for any integer type and decimal32 + template + friend constexpr auto less_impl(Decimal lhs, Integer rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && detail::is_integral_v), bool>; + + template + friend constexpr auto mixed_decimal_less_impl(Decimal1 lhs, Decimal2 rhs) noexcept + -> std::enable_if_t<(detail::is_decimal_floating_point_v && + detail::is_decimal_floating_point_v), bool>; + + template + friend constexpr auto to_integral(Decimal val) noexcept + BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_integral_v, TargetType, TargetType); + + template + friend BOOST_DECIMAL_CXX20_CONSTEXPR auto to_float(Decimal val) noexcept + BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, Decimal, detail::is_floating_point_v, TargetType, TargetType); + + template + friend constexpr auto to_decimal(Decimal val) noexcept -> TargetType; + + friend constexpr auto d64_fast_div_impl(decimal64_fast lhs, decimal64_fast rhs, decimal64_fast& q, decimal64_fast& r) noexcept -> void; + + template + friend constexpr auto ilogb(T d) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_decimal_floating_point_v, T, int); + + template + friend constexpr auto logb(T num) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T); + +public: + constexpr decimal64_fast() noexcept = default; + + #ifdef BOOST_DECIMAL_HAS_CONCEPTS + template + #else + template && detail::is_integral_v, bool> = true> + #endif + constexpr decimal64_fast(T1 coeff, T2 exp, bool sign = false) noexcept; + + #ifdef BOOST_DECIMAL_HAS_CONCEPTS + template + #else + template , bool> = true> + #endif + constexpr decimal64_fast(Integer val) noexcept; + + #ifdef BOOST_DECIMAL_HAS_CONCEPTS + template + #else + template , bool> = true> + #endif + explicit BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_fast(Float val) noexcept; + + friend constexpr auto direct_init_d64(decimal64_fast::significand_type significand, decimal64_fast::exponent_type exponent, bool sign) noexcept -> decimal64_fast; + + // Classification functions + friend constexpr auto signbit(decimal64_fast val) noexcept -> bool; + friend constexpr auto isinf(decimal64_fast val) noexcept -> bool; + friend constexpr auto isnan(decimal64_fast val) noexcept -> bool; + friend constexpr auto issignaling(decimal64_fast val) noexcept -> bool; + friend constexpr auto isnormal(decimal64_fast val) noexcept -> bool; + + // Comparison operator + friend constexpr auto operator==(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool; + friend constexpr auto operator!=(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool; + friend constexpr auto operator<(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool; + friend constexpr auto operator<=(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool; + friend constexpr auto operator>(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool; + friend constexpr auto operator>=(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool; + + // Mixed type comparison operators + template + friend constexpr auto operator==(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator==(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator!=(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator!=(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<=(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator<=(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>=(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + template + friend constexpr auto operator>=(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool); + + // C++20 Spaceship operator + #ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + friend constexpr auto operator<=>(decimal64_fast lhs, decimal64_fast rhs) noexcept -> std::partial_ordering; + + template + friend constexpr auto operator<=>(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering); + + template + friend constexpr auto operator<=>(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering); + #endif + + // Conversions + explicit constexpr operator bool() const noexcept; + explicit constexpr operator int() const noexcept; + explicit constexpr operator unsigned() const noexcept; + explicit constexpr operator long() const noexcept; + explicit constexpr operator unsigned long() const noexcept; + explicit constexpr operator long long() const noexcept; + explicit constexpr operator unsigned long long() const noexcept; + explicit constexpr operator std::int8_t() const noexcept; + explicit constexpr operator std::uint8_t() const noexcept; + explicit constexpr operator std::int16_t() const noexcept; + explicit constexpr operator std::uint16_t() const noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + explicit constexpr operator detail::int128_t() const noexcept; + explicit constexpr operator detail::uint128_t() const noexcept; + #endif + + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator float() const noexcept; + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator double() const noexcept; + explicit BOOST_DECIMAL_CXX20_CONSTEXPR operator long double() const noexcept; + + #ifdef BOOST_DECIMAL_HAS_FLOAT16 + explicit constexpr operator std::float16_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT32 + explicit constexpr operator std::float32_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT64 + explicit constexpr operator std::float64_t() const noexcept; + #endif + #ifdef BOOST_DECIMAL_HAS_BRAINFLOAT16 + explicit constexpr operator std::bfloat16_t() const noexcept; + #endif + + template , bool> = true> + explicit constexpr operator Decimal() const noexcept; + + // Unary Operators + friend constexpr auto operator+(decimal64_fast val) noexcept -> decimal64_fast; + friend constexpr auto operator-(decimal64_fast val) noexcept -> decimal64_fast; + + // Basic arithmetic operators + friend constexpr auto operator+(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast; + friend constexpr auto operator-(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast; + friend constexpr auto operator*(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast; + friend constexpr auto operator/(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast; + friend constexpr auto operator%(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast; + + // Mixed type arithmetic operators + template + friend constexpr auto operator+(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + template + friend constexpr auto operator+(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + template + friend constexpr auto operator-(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + template + friend constexpr auto operator-(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + template + friend constexpr auto operator*(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + template + friend constexpr auto operator*(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + template + friend constexpr auto operator/(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + template + friend constexpr auto operator/(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast); + + // Compound Operators + constexpr auto operator+=(decimal64_fast rhs) noexcept -> decimal64_fast&; + constexpr auto operator-=(decimal64_fast rhs) noexcept -> decimal64_fast&; + constexpr auto operator*=(decimal64_fast rhs) noexcept -> decimal64_fast&; + constexpr auto operator/=(decimal64_fast rhs) noexcept -> decimal64_fast&; + + // Mixed type compound operators + template + constexpr auto operator+=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&); + + template + constexpr auto operator-=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&); + + template + constexpr auto operator*=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&); + + template + constexpr auto operator/=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&); + + // Increment and decrement + constexpr auto operator++() noexcept -> decimal64_fast&; + constexpr auto operator++(int) noexcept -> decimal64_fast&; + constexpr auto operator--() noexcept -> decimal64_fast&; + constexpr auto operator--(int) noexcept -> decimal64_fast&; + + // Cmath friend functions + template + friend constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type; + + friend constexpr auto copysignd64f(decimal64_fast mag, decimal64_fast sgn) noexcept -> decimal64_fast; + friend constexpr auto fmad64f(decimal64_fast x, decimal64_fast y, decimal64_fast z) noexcept -> decimal64_fast; + friend constexpr auto scalbnd64f(decimal64_fast num, int exp) noexcept -> decimal64_fast; + friend constexpr auto scalblnd64f(decimal64_fast num, long exp) noexcept -> decimal64_fast; +}; + +#ifdef BOOST_DECIMAL_HAS_CONCEPTS +template +#else +template && detail::is_integral_v, bool>> +#endif +constexpr decimal64_fast::decimal64_fast(T1 coeff, T2 exp, bool sign) noexcept +{ + // Older compilers have issues with conversions from __uint128, so we skip all that and use our uint128 + #if defined(BOOST_DECIMAL_HAS_INT128) && (!defined(__GNUC__) || (defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 10)) && (!defined(__clang__) || (defined(__clang__) && __clang_major__ < 13)) + using Unsigned_Integer_1 = detail::make_unsigned_t; + using Unsigned_Integer = std::conditional_t::value, detail::uint128, Unsigned_Integer_1>; + #else + using Unsigned_Integer = detail::make_unsigned_t; + #endif + + using Basis_Unsigned_Integer = std::conditional_t::digits10 < std::numeric_limits::digits10, significand_type, Unsigned_Integer>; + + const bool isneg {coeff < static_cast(0) || sign}; + sign_ = isneg; + auto unsigned_coeff {static_cast(detail::make_positive_unsigned(coeff))}; + + // Normalize the value, so we don't have to worrya bout it with operations + detail::normalize(unsigned_coeff, exp, sign); + + significand_ = static_cast(unsigned_coeff); + + // Normalize the handling of zeros + if (significand_ == UINT64_C(0)) + { + exp = 0; + } + + const auto biased_exp {static_cast(exp + detail::bias_v)}; + + if (biased_exp > detail::max_biased_exp_v) + { + significand_ = detail::d64_fast_inf; + } + else + { + exponent_ = static_cast(biased_exp); + } +} + +#ifdef BOOST_DECIMAL_HAS_CONCEPTS +template +#else +template , bool>> +#endif +constexpr decimal64_fast::decimal64_fast(Integer val) noexcept +{ + using ConversionType = std::conditional_t::value, std::int32_t, Integer>; + *this = decimal64_fast{static_cast(val), 0, false}; +} + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#ifdef BOOST_DECIMAL_HAS_CONCEPTS +template +#else +template , bool>> +#endif +BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_fast::decimal64_fast(Float val) noexcept +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (val != val) + { + significand_ = detail::d64_fast_qnan; + } + else if (val == std::numeric_limits::infinity() || val == -std::numeric_limits::infinity()) + { + significand_ = detail::d64_fast_inf; + } + else + #endif + { + const auto components {detail::ryu::floating_point_to_fd128(val)}; + *this = decimal64_fast {components.mantissa, components.exponent, components.sign}; + } +} + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +constexpr auto direct_init_d64(decimal64_fast::significand_type significand, decimal64_fast::exponent_type exponent, bool sign) noexcept -> decimal64_fast +{ + decimal64_fast val {}; + val.significand_ = significand; + val.exponent_ = exponent; + val.sign_ = sign; + + return val; +} + +constexpr auto signbit(decimal64_fast val) noexcept -> bool +{ + return val.sign_; +} + +constexpr auto isinf(decimal64_fast val) noexcept -> bool +{ + return val.significand_ == detail::d64_fast_inf; +} + +constexpr auto isnan(decimal64_fast val) noexcept -> bool +{ + return val.significand_ == detail::d64_fast_qnan || + val.significand_ == detail::d64_fast_snan; +} + +constexpr auto issignaling(decimal64_fast val) noexcept -> bool +{ + return val.significand_ == detail::d64_fast_snan; +} + +constexpr auto isnormal(decimal64_fast val) noexcept -> bool +{ + if (val.exponent_ <= static_cast(detail::precision_v - 1)) + { + return false; + } + + return (val.significand_ != 0) && isfinite(val); +} + +constexpr auto operator==(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + return lhs.sign_ == rhs.sign_ && + lhs.exponent_ == rhs.exponent_ && + lhs.significand_ == rhs.significand_; +} + +template +constexpr auto operator==(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return mixed_equality_impl(lhs, rhs); +} + +template +constexpr auto operator==(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return mixed_equality_impl(rhs, lhs); +} + +constexpr auto operator!=(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool +{ + return !(lhs == rhs); +} + +template +constexpr auto operator!=(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return !(lhs == rhs); +} + +template +constexpr auto operator!=(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return !(lhs == rhs); +} + +constexpr auto operator<(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + if (!lhs.isneg() && rhs.isneg()) + { + return false; + } + + if (lhs.isneg() && !rhs.isneg()) + { + return true; + } + + #ifndef BOOST_DECIMAL_FAST_MATH + if (isfinite(lhs) && isinf(rhs)) + { + return !signbit(rhs); + } + + if (isinf(lhs) && isfinite(rhs)) + { + return signbit(rhs); + } + #endif + + if (lhs.significand_ == 0 || rhs.significand_ == 0) + { + return lhs.significand_ == 0 ? !rhs.sign_ : lhs.sign_; + } + + if (lhs.exponent_ != rhs.exponent_) + { + return lhs.sign_ ? lhs.exponent_ > rhs.exponent_ : lhs.exponent_ < rhs.exponent_; + } + + return lhs.sign_ ? lhs.significand_ > rhs.significand_ : lhs.significand_ < rhs.significand_; +} + +template +constexpr auto operator<(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return less_impl(lhs, rhs); +} + +template +constexpr auto operator<(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(rhs)) + { + return false; + } + #endif + + return !less_impl(rhs, lhs) && lhs != rhs; +} + +constexpr auto operator<=(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + return !(rhs < lhs); +} + +template +constexpr auto operator<=(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs)) + { + return false; + } + #endif + + return !(rhs < lhs); +} + +template +constexpr auto operator<=(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(rhs)) + { + return false; + } + #endif + + return !(rhs < lhs); +} + +constexpr auto operator>(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool +{ + return rhs < lhs; +} + +template +constexpr auto operator>(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return rhs < lhs; +} + +template +constexpr auto operator>(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + return rhs < lhs; +} + +constexpr auto operator>=(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isnan(rhs)) + { + return false; + } + #endif + + return !(lhs < rhs); +} + +template +constexpr auto operator>=(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs)) + { + return false; + } + #endif + + return !(lhs < rhs); +} + +template +constexpr auto operator>=(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, bool) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(rhs)) + { + return false; + } + #endif + + return !(lhs < rhs); +} + +#ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + +constexpr auto operator<=>(decimal64_fast lhs, decimal64_fast rhs) noexcept -> std::partial_ordering +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +template +constexpr auto operator<=>(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering) +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +template +constexpr auto operator<=>(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, std::partial_ordering) +{ + if (lhs < rhs) + { + return std::partial_ordering::less; + } + else if (lhs > rhs) + { + return std::partial_ordering::greater; + } + else if (lhs == rhs) + { + return std::partial_ordering::equivalent; + } + + return std::partial_ordering::unordered; +} + +#endif // BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + +constexpr auto operator+(decimal64_fast val) noexcept -> decimal64_fast +{ + return val; +} + +constexpr auto operator-(decimal64_fast val) noexcept -> decimal64_fast +{ + val.sign_ = !val.sign_; + return val; +} + +constexpr decimal64_fast::operator bool() const noexcept +{ + constexpr decimal64_fast zero {0, 0}; + return *this != zero; +} + +constexpr decimal64_fast::operator int() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator unsigned() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator long() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator unsigned long() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator long long() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator unsigned long long() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator std::int8_t() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator std::uint8_t() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator std::int16_t() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator std::uint16_t() const noexcept +{ + return to_integral(*this); +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr decimal64_fast::operator detail::int128_t() const noexcept +{ + return to_integral(*this); +} + +constexpr decimal64_fast::operator detail::uint128_t() const noexcept +{ + return to_integral(*this); +} + +#endif // BOOST_DECIMAL_HAS_INT128 + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_fast::operator float() const noexcept +{ + return to_float(*this); +} + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_fast::operator double() const noexcept +{ + return to_float(*this); +} + +BOOST_DECIMAL_CXX20_CONSTEXPR decimal64_fast::operator long double() const noexcept +{ + // TODO(mborland): Don't have an exact way of converting to various long doubles + return static_cast(to_float(*this)); +} + +#ifdef BOOST_DECIMAL_HAS_FLOAT16 +constexpr decimal64_fast::operator std::float16_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_FLOAT32 +constexpr decimal64_fast::operator std::float32_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_FLOAT64 +constexpr decimal64_fast::operator std::float64_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif +#ifdef BOOST_DECIMAL_HAS_BRAINFLOAT16 +constexpr decimal64_fast::operator std::bfloat16_t() const noexcept +{ + return static_cast(to_float(*this)); +} +#endif + +template , bool>> +constexpr decimal64_fast::operator Decimal() const noexcept +{ + return to_decimal(*this); +} + +constexpr auto operator+(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal64_fast zero {0, 0}; + + const auto res {detail::check_non_finite(lhs, rhs)}; + if (res != zero) + { + return res; + } + #endif + + bool lhs_bigger {lhs > rhs}; + if (lhs.isneg() && rhs.isneg()) + { + lhs_bigger = !lhs_bigger; + } + + // Ensure that lhs is always the larger for ease of impl + if (!lhs_bigger) + { + detail::swap(lhs, rhs); + } + + if (!lhs.isneg() && rhs.isneg()) + { + return lhs - abs(rhs); + } + + const auto result {detail::d64_add_impl( + lhs.significand_, lhs.biased_exponent(), lhs.sign_, + rhs.significand_, rhs.biased_exponent(), rhs.sign_ + )}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator+(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isinf(lhs)) + { + return lhs; + } + #endif + + bool lhs_bigger {lhs > rhs}; + if (lhs.isneg() && (rhs < 0)) + { + lhs_bigger = !lhs_bigger; + } + bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)}; + + auto lhs_components {detail::decimal64_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}}; + + auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; + std::int32_t exp_rhs {0}; + detail::normalize(sig_rhs, exp_rhs); + auto unsigned_sig_rhs = detail::shrink_significand(detail::make_positive_unsigned(sig_rhs), exp_rhs); + auto rhs_components {detail::decimal64_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; + + if (!lhs_bigger) + { + detail::swap(lhs_components, rhs_components); + abs_lhs_bigger = !abs_lhs_bigger; + } + + detail::decimal64_fast_components result {}; + + #ifdef BOOST_DECIMAL_DEBUG_ADD + std::cerr << "Lhs sig: " << lhs_components.sig + << "\nLhs exp: " << lhs_components.exp + << "\nRhs sig: " << rhs_components.sig + << "\nRhs exp: " << rhs_components.exp << std::endl; + #endif + + if (!lhs_components.sign && rhs_components.sign) + { + result = detail::d64_sub_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger); + } + else + { + result = detail::d64_add_impl(lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign); + } + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator+(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + return rhs + lhs; +} + +constexpr auto operator-(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal64_fast zero {0, 0}; + + const auto res {detail::check_non_finite(lhs, rhs)}; + if (res != zero) + { + return res; + } + #endif + + if (!lhs.isneg() && rhs.isneg()) + { + return lhs + (-rhs); + } + + const bool abs_lhs_bigger {abs(lhs) > abs(rhs)}; + + const auto result {detail::d64_sub_impl( + lhs.significand_, lhs.biased_exponent(), lhs.sign_, + rhs.significand_, rhs.biased_exponent(), rhs.sign_, + abs_lhs_bigger + )}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator-(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isinf(lhs) || isnan(lhs)) + { + return lhs; + } + #endif + + if (!lhs.isneg() && (rhs < 0)) + { + return lhs + detail::make_positive_unsigned(rhs); + } + + const bool abs_lhs_bigger {abs(lhs) > detail::make_positive_unsigned(rhs)}; + + auto lhs_components {detail::decimal64_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}}; + + auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; + std::int32_t exp_rhs {0}; + detail::normalize(sig_rhs, exp_rhs); + auto unsigned_sig_rhs = detail::shrink_significand(detail::make_positive_unsigned(sig_rhs), exp_rhs); + auto rhs_components {detail::decimal64_fast_components{unsigned_sig_rhs, exp_rhs, (rhs < 0)}}; + + const auto result {detail::d64_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger)}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator-(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isinf(rhs) || isnan(rhs)) + { + return rhs; + } + #endif + + if (lhs >= 0 && rhs.isneg()) + { + return lhs + (-rhs); + } + + const bool abs_lhs_bigger {detail::make_positive_unsigned(lhs) > abs(rhs)}; + + auto sig_lhs {static_cast(detail::make_positive_unsigned(lhs))}; + std::int32_t exp_lhs {0}; + detail::normalize(sig_lhs, exp_lhs); + auto unsigned_sig_lhs = detail::shrink_significand(detail::make_positive_unsigned(sig_lhs), exp_lhs); + auto lhs_components {detail::decimal64_fast_components{unsigned_sig_lhs, exp_lhs, (lhs < 0)}}; + + auto rhs_components {detail::decimal64_fast_components{rhs.significand_, rhs.biased_exponent(), rhs.isneg()}}; + + const auto result {detail::d64_sub_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign, + abs_lhs_bigger)}; + + return {result.sig, result.exp, result.sign}; +} + +constexpr auto operator*(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal64_fast zero {0, 0}; + + const auto non_finite {detail::check_non_finite(lhs, rhs)}; + if (non_finite != zero) + { + return non_finite; + } + #endif + + #if defined(__clang_major__) && __clang_major__ < 13 + + const auto result {detail::d64_mul_impl( + lhs.significand_, lhs.biased_exponent(), lhs.isneg(), + rhs.significand_, rhs.biased_exponent(), rhs.isneg() + )}; + + return {result.sig, result.exp, result.sign}; + + #else + + #ifdef BOOST_DECIMAL_HAS_INT128 + using unsigned_int128_type = boost::decimal::detail::uint128_t; + #else + using unsigned_int128_type = boost::decimal::detail::uint128; + #endif + + auto res_sig {static_cast(lhs.significand_) * static_cast(rhs.significand_)}; + + auto res_exp {lhs.biased_exponent() + rhs.biased_exponent()}; + bool sign {lhs.sign_ != rhs.sign_ && res_sig != 0}; + + return {res_sig, res_exp, sign}; + + #endif // Clang major check +} + +template +constexpr auto operator*(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs) || isinf(lhs)) + { + return lhs; + } + #endif + + auto lhs_components {detail::decimal64_fast_components{lhs.significand_, lhs.biased_exponent(), lhs.isneg()}}; + + auto rhs_sig {static_cast(detail::make_positive_unsigned(rhs))}; + std::int32_t rhs_exp {0}; + detail::normalize(rhs_sig, rhs_exp); + auto unsigned_sig_rhs {detail::shrink_significand(detail::make_positive_unsigned(rhs_sig), rhs_exp)}; + auto rhs_components {detail::decimal64_fast_components{unsigned_sig_rhs, rhs_exp, (rhs < 0)}}; + + const auto result {detail::d64_mul_impl( + lhs_components.sig, lhs_components.exp, lhs_components.sign, + rhs_components.sig, rhs_components.exp, rhs_components.sign + )}; + + return {result.sig, result.exp, result.sign}; +} + +template +constexpr auto operator*(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + return rhs * lhs; +} + +constexpr auto d64_fast_div_impl(decimal64_fast lhs, decimal64_fast rhs, decimal64_fast& q, decimal64_fast& r) noexcept -> void +{ + const bool sign {lhs.isneg() != rhs.isneg()}; + + #ifndef BOOST_DECIMAL_FAST_MATH + // Check pre-conditions + constexpr decimal64_fast zero {0, 0}; + constexpr decimal64_fast nan {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_snan, 0, false)}; + constexpr decimal64_fast inf {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_inf, 0, false)}; + + const auto lhs_fp {fpclassify(lhs)}; + const auto rhs_fp {fpclassify(rhs)}; + + if (lhs_fp == FP_NAN || rhs_fp == FP_NAN) + { + q = nan; + r = nan; + return; + } + + switch (lhs_fp) + { + case FP_INFINITE: + q = sign ? -inf : inf; + r = zero; + return; + case FP_ZERO: + q = sign ? -zero : zero; + r = sign ? -zero : zero; + return; + default: + static_cast(lhs); + } + + switch (rhs_fp) + { + case FP_ZERO: + q = inf; + r = zero; + return; + case FP_INFINITE: + q = sign ? -zero : zero; + r = lhs; + return; + default: + static_cast(rhs); + } + #else + static_cast(r); + #endif + + #ifdef BOOST_DECIMAL_DEBUG + std::cerr << "sig lhs: " << sig_lhs + << "\nexp lhs: " << exp_lhs + << "\nsig rhs: " << sig_rhs + << "\nexp rhs: " << exp_rhs << std::endl; + #endif + + #ifdef BOOST_DECIMAL_HAS_INT128 + using unsigned_int128_type = boost::decimal::detail::uint128_t; + #else + using unsigned_int128_type = boost::decimal::detail::uint128; + #endif + + // If rhs is greater than we need to offset the significands to get the correct values + // e.g. 4/8 is 0 but 40/8 yields 5 in integer maths + constexpr auto tens_needed {detail::pow10(static_cast(detail::precision_v))}; + const auto big_sig_lhs {static_cast(lhs.significand_) * tens_needed}; + + const auto res_sig {big_sig_lhs / static_cast(rhs.significand_)}; + const auto res_exp {(lhs.biased_exponent() - detail::precision_v) - rhs.biased_exponent()}; + + q = decimal64_fast{res_sig, res_exp, sign}; +} + +constexpr auto d64_fast_mod_impl(decimal64_fast lhs, decimal64_fast rhs, const decimal64_fast& q, decimal64_fast& r) noexcept -> void +{ + constexpr decimal64_fast zero {0, 0}; + + // https://en.cppreference.com/w/cpp/numeric/math/fmod + auto q_trunc {q > zero ? floor(q) : ceil(q)}; + r = lhs - (decimal64_fast(q_trunc) * rhs); +} + +constexpr auto operator/(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast +{ + decimal64_fast q {}; + decimal64_fast r {}; + + d64_fast_div_impl(lhs, rhs, q, r); + + return q; +} + +template +constexpr auto operator/(decimal64_fast lhs, Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + // Check pre-conditions + constexpr decimal64_fast zero {0, 0}; + constexpr decimal64_fast nan {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_snan, 0, false)}; + constexpr decimal64_fast inf {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_inf, 0, false)}; + + const bool sign {lhs.isneg() != (rhs < 0)}; + + const auto lhs_fp {fpclassify(lhs)}; + + switch (lhs_fp) + { + case FP_NAN: + return nan; + case FP_INFINITE: + return inf; + case FP_ZERO: + return sign ? -zero : zero; + default: + static_cast(lhs); + } + + if (rhs == 0) + { + return sign ? -inf : inf; + } + #endif + + auto lhs_sig {lhs.full_significand()}; + auto lhs_exp {lhs.biased_exponent()}; + detail::normalize(lhs_sig, lhs_exp); + + detail::decimal64_fast_components lhs_components {lhs_sig, lhs_exp, lhs.isneg()}; + + auto rhs_sig {static_cast(detail::make_positive_unsigned(rhs))}; + std::int32_t rhs_exp {}; + detail::decimal64_fast_components rhs_components {detail::shrink_significand(rhs_sig, rhs_exp), rhs_exp, rhs < 0}; + detail::decimal64_fast_components q_components {}; + + detail::d64_generic_div_impl(lhs_components, rhs_components, q_components); + + return {q_components.sig, q_components.exp, q_components.sign}; +} + +template +constexpr auto operator/(Integer lhs, decimal64_fast rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + // Check pre-conditions + constexpr decimal64_fast zero {0, 0}; + constexpr decimal64_fast nan {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_snan, 0, false)}; + constexpr decimal64_fast inf {boost::decimal::direct_init_d64(boost::decimal::detail::d64_fast_inf, 0, false)}; + + const bool sign {(lhs < 0) != rhs.isneg()}; + + const auto rhs_fp {fpclassify(rhs)}; + + if (rhs_fp == FP_NAN) + { + return nan; + } + + switch (rhs_fp) + { + case FP_INFINITE: + return sign ? -zero : zero; + case FP_ZERO: + return sign ? -inf : inf; + default: + static_cast(lhs); + } + #endif + + auto rhs_sig {rhs.full_significand()}; + auto rhs_exp {rhs.biased_exponent()}; + detail::normalize(rhs_sig, rhs_exp); + + detail::decimal64_fast_components lhs_components {detail::make_positive_unsigned(lhs), 0, lhs < 0}; + detail::decimal64_fast_components rhs_components {rhs_sig, rhs_exp, rhs.isneg()}; + detail::decimal64_fast_components q_components {}; + + detail::d64_generic_div_impl(lhs_components, rhs_components, q_components); + + return {q_components.sig, q_components.exp, q_components.sign}; +} + +constexpr auto operator%(decimal64_fast lhs, decimal64_fast rhs) noexcept -> decimal64_fast +{ + decimal64_fast q {}; + decimal64_fast r {}; + d64_fast_div_impl(lhs, rhs, q, r); + d64_fast_mod_impl(lhs, rhs, q, r); + + return r; +} + +constexpr auto decimal64_fast::operator+=(decimal64_fast rhs) noexcept -> decimal64_fast & +{ + *this = *this + rhs; + return *this; +} + +constexpr auto decimal64_fast::operator-=(decimal64_fast rhs) noexcept -> decimal64_fast & +{ + *this = *this - rhs; + return *this; +} + +constexpr auto decimal64_fast::operator*=(decimal64_fast rhs) noexcept -> decimal64_fast & +{ + *this = *this * rhs; + return *this; +} + +constexpr auto decimal64_fast::operator/=(decimal64_fast rhs) noexcept -> decimal64_fast & +{ + *this = *this / rhs; + return *this; +} + +template +constexpr auto decimal64_fast::operator+=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&) +{ + *this = *this + rhs; + return *this; +} + +template +constexpr auto decimal64_fast::operator-=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&) +{ + *this = *this - rhs; + return *this; +} + +template +constexpr auto decimal64_fast::operator*=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&) +{ + *this = *this * rhs; + return *this; +} + +template +constexpr auto decimal64_fast::operator/=(Integer rhs) noexcept + BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal64_fast&) +{ + *this = *this / rhs; + return *this; +} + +constexpr auto decimal64_fast::operator++() noexcept -> decimal64_fast& +{ + constexpr decimal64_fast one {1, 0}; + *this = *this + one; + return *this; +} + +constexpr auto decimal64_fast::operator++(int) noexcept -> decimal64_fast& +{ + return ++(*this); +} + +constexpr auto decimal64_fast::operator--() noexcept -> decimal64_fast& +{ + constexpr decimal64_fast one {1, 0}; + *this = *this - one; + return *this; +} + +constexpr auto decimal64_fast::operator--(int) noexcept -> decimal64_fast& +{ + return --(*this); +} + +constexpr auto scalblnd64f(decimal64_fast num, long exp) noexcept -> decimal64_fast +{ + #ifndef BOOST_DECIMAL_FAST_MATH + constexpr decimal64_fast zero {0, 0}; + + if (num == zero || exp == 0 || isinf(num) || isnan(num)) + { + return num; + } + #endif + + num = decimal64_fast(num.significand_, num.biased_exponent() + exp, num.sign_); + + return num; +} + +constexpr auto scalbnd64f(decimal64_fast num, int expval) noexcept -> decimal64_fast +{ + return scalblnd64f(num, static_cast(expval)); +} + +constexpr auto copysignd64f(decimal64_fast mag, decimal64_fast sgn) noexcept -> decimal64_fast +{ + mag.sign_ = sgn.sign_; + return mag; +} + +} // namespace decimal +} // namespace boost + +namespace std { + +template <> +#ifdef _MSC_VER +class numeric_limits +#else +struct numeric_limits +#endif +{ +#ifdef _MSC_VER + 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; + + // 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; + #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; + + // 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( + 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( + 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( + 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}; } +}; + +} // namespace std + +#endif //BOOST_DECIMAL_DECIMAL64_FAST_HPP diff --git a/include/boost/decimal/detail/add_impl.hpp b/include/boost/decimal/detail/add_impl.hpp index d27baa23a..72afb8b4f 100644 --- a/include/boost/decimal/detail/add_impl.hpp +++ b/include/boost/decimal/detail/add_impl.hpp @@ -18,8 +18,8 @@ namespace decimal { namespace detail { template -constexpr auto add_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 +BOOST_DECIMAL_FORCE_INLINE constexpr auto add_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 { const bool sign {lhs_sign}; @@ -91,6 +91,171 @@ constexpr auto add_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, return {res_sig, new_exp, sign}; } +template +constexpr auto d64_add_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 +{ + const bool sign {lhs_sign}; + + auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; + + if (delta_exp > detail::precision_v + 1) + { + // If the difference in exponents is more than the digits of accuracy + // we return the larger of the two + // + // e.g. 1e20 + 1e-20 = 1e20 + + return {lhs_sig, lhs_exp, lhs_sign}; + } + else if (delta_exp == detail::precision_v + 1) + { + // Only need to see if we need to add one to the + // significand of the bigger value + // + // e.g. 1.234567e5 + 9.876543e-2 = 1.234568e5 + + if (rhs_sig >= UINT64_C(5'000'000'000'000'000)) + { + ++lhs_sig; + return {lhs_sig, lhs_exp, lhs_sign}; + } + else + { + return {lhs_sig, lhs_exp, lhs_sign}; + } + } + + // The two numbers can be added together without special handling + // + // If we can add to the lhs sig rather than dividing we can save some precision + // 64-bit sign int can have 19 digits, and our normalized significand has 16 + if (delta_exp <= 3) + { + lhs_sig *= pow10(static_cast(delta_exp)); + lhs_exp -= delta_exp; + delta_exp = 0; + } + else + { + lhs_sig *= 1000; + delta_exp -= 3; + lhs_exp -= 3; + + if (delta_exp > 1) + { + rhs_sig /= pow10(static_cast(delta_exp - 1)); + delta_exp = 1; + } + + if (delta_exp == 1) + { + detail::fenv_round(rhs_sig, rhs_sign); + } + } + + // Both of the significands are well under 64-bits, so we can fit them into int64_t without issue + const auto new_sig {static_cast(lhs_sig) + static_cast(rhs_sig)}; + const auto new_exp {lhs_exp}; + + #ifdef BOOST_DECIMAL_DEBUG_ADD + std::cerr << "Res Sig: " << new_sig + << "\nRes Exp: " << new_exp + << "\nRes Neg: " << sign << std::endl; + #endif + + return {new_sig, new_exp, sign}; +} + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // If constexpr macro only works for C++17 and above +#endif + +template +constexpr auto d128_add_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 +{ + const bool sign {lhs_sign}; + + auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; + + if (delta_exp > detail::precision_v + 1) + { + // If the difference in exponents is more than the digits of accuracy + // we return the larger of the two + // + // e.g. 1e20 + 1e-20 = 1e20 + + return {lhs_sig, lhs_exp, lhs_sign}; + } + else if (delta_exp == detail::precision_v + 1) + { + // Only need to see if we need to add one to the + // significand of the bigger value + // + // e.g. 1.234567e5 + 9.876543e-2 = 1.234568e5 + + BOOST_DECIMAL_IF_CONSTEXPR (std::numeric_limits::digits10 > std::numeric_limits::digits10) + { + if (rhs_sig >= detail::uint128 {UINT64_C(0xF684DF56C3E0), UINT64_C(0x1BC6C73200000000)}) + { + ++lhs_sig; + } + + return {lhs_sig, lhs_exp, lhs_sign}; + } + else + { + return {lhs_sig, lhs_exp, lhs_sign}; + } + } + + // The two numbers can be added together without special handling + // + // If we can add to the lhs sig rather than dividing we can save some precision + // 64-bit sign int can have 19 digits, and our normalized significand has 16 + + if (delta_exp <= 3) + { + lhs_sig *= detail::pow10(static_cast(delta_exp)); + lhs_exp -= delta_exp; + } + else + { + lhs_sig *= 1000; + delta_exp -= 3; + lhs_exp -= 3; + + if (delta_exp > 1) + { + rhs_sig /= pow10(static_cast(delta_exp - 1)); + delta_exp = 1; + } + + if (delta_exp == 1) + { + detail::fenv_round(rhs_sig, rhs_sign); + } + } + + const auto new_sig {static_cast(lhs_sig) + + static_cast(rhs_sig)}; + const auto new_exp {lhs_exp}; + + #ifdef BOOST_DECIMAL_DEBUG_ADD_128 + std::cerr << "Res Sig: " << static_cast(new_sig) + << "\nRes Exp: " << new_exp + << "\nRes Neg: " << sign << std::endl; + #endif + + return {new_sig, new_exp, sign}; +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + } // namespace detail } // namespace decimal } // namespace boost diff --git a/include/boost/decimal/detail/attributes.hpp b/include/boost/decimal/detail/attributes.hpp index 1ede3edd1..9d490131f 100644 --- a/include/boost/decimal/detail/attributes.hpp +++ b/include/boost/decimal/detail/attributes.hpp @@ -26,36 +26,54 @@ BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto storage_wid template <> BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto storage_width_v = 128; +template <> +BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto storage_width_v = 128; + 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; + 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; + 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; + 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; + 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; + template , bool> = true> BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto etiny_v = -bias_v; @@ -65,12 +83,18 @@ BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto combination template <> BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto combination_field_width_v = 17; +template <> +BOOST_DECIMAL_CONSTEXPR_VARIABLE_SPECIALIZATION auto combination_field_width_v = 17; + 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; + 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; @@ -78,6 +102,10 @@ 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(0b1111111111'1111111111'1111111111'1111111111'111111), UINT64_MAX}; + // 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; @@ -85,6 +113,9 @@ BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto max_string_ 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 storage_width {storage_width_v}; BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto precision {precision_v}; BOOST_DECIMAL_ATTRIBUTE_UNUSED BOOST_DECIMAL_CONSTEXPR_VARIABLE auto bias {bias_v}; diff --git a/include/boost/decimal/detail/cmath/beta.hpp b/include/boost/decimal/detail/cmath/beta.hpp new file mode 100644 index 000000000..9794542c8 --- /dev/null +++ b/include/boost/decimal/detail/cmath/beta.hpp @@ -0,0 +1,64 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_CMATH_BETA_HPP +#define BOOST_DECIMAL_DETAIL_CMATH_BETA_HPP + +#include // NOLINT(llvm-include-order) +#include +#include +#include +#include +#include + +namespace boost { +namespace decimal { + +namespace detail { + +template +constexpr auto beta_impl(T x, T y) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +{ + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(x) || isnan(y)) + { + return std::numeric_limits::quiet_NaN(); + } + #endif + + // The beta function is defined as tgamma(x) * tgamma(y) / tgamma(x + y) + // If we use lgamma instead and then take the exp at the end we avoid + // the easy case of numerical overflow + const auto temp {lgamma(x) + lgamma(y) - lgamma(x + y)}; + return exp(temp); +} + +} // namespace detail + +BOOST_DECIMAL_EXPORT template +constexpr auto beta(T y, T x) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +{ + #if BOOST_DECIMAL_DEC_EVAL_METHOD == 0 + + using evaluation_type = T; + + #elif BOOST_DECIMAL_DEC_EVAL_METHOD == 1 + + using evaluation_type = detail::promote_args_t; + + #else // BOOST_DECIMAL_DEC_EVAL_METHOD == 2 + + using evaluation_type = detail::promote_args_t; + + #endif + + return static_cast(detail::beta_impl(static_cast(y), static_cast(x))); +} + +} // namespace decimal +} // namespace boost + +#endif //BOOST_DECIMAL_DETAIL_CMATH_BETA_HPP diff --git a/include/boost/decimal/detail/cmath/cos.hpp b/include/boost/decimal/detail/cmath/cos.hpp index 2454ffe31..38b72d0f1 100644 --- a/include/boost/decimal/detail/cmath/cos.hpp +++ b/include/boost/decimal/detail/cmath/cos.hpp @@ -39,19 +39,15 @@ constexpr auto cos_impl(T x) noexcept { result = x; } + else if (signbit(x)) + { + result = cos(-x); + } else { - x = abs(x); - - if (x < std::numeric_limits::epsilon()) - { - constexpr T one {1, 0}; - - result = one; - } - else + if (x > std::numeric_limits::epsilon()) { - // Perform argument reduction and subsequent computation of the result. + // Perform argument reduction and subsequent scaling of the result. // Given x = k * (pi/2) + r, compute n = (k % 4). @@ -62,33 +58,52 @@ constexpr auto cos_impl(T x) noexcept // | 2 | -sin(r) | -cos(r) | sin(r)/cos(r) | // | 3 | -cos(r) | sin(r) | -cos(r)/sin(r) | - #if (defined(_MSC_VER) && (_MSC_VER < 1920)) - const auto my_pi_half = numbers::pi_v / 2; - #else - constexpr auto my_pi_half = numbers::pi_v / 2; - #endif + constexpr T my_pi_half { numbers::pi_v / 2 }; + + const auto k = static_cast(x / my_pi_half); + const auto n = static_cast(k % static_cast(UINT8_C(4))); - int k {}; - auto r { remquo(x, my_pi_half, &k) }; + auto r = x - (my_pi_half * k); - const auto n = static_cast(k % 4); + constexpr T half { 5, -1 }; + + const bool do_scaling { x > half }; + + if(do_scaling) + { + // Reduce the argument with factors of three. + r /= static_cast(UINT8_C(3)); + } switch(n) { - case 3U: - result = detail::sin_series_expansion(r); - break; - case 2U: - result = -detail::cos_series_expansion(r); - break; - case 1U: - result = -detail::sin_series_expansion(r); - break; - case 0U: - default: - result = detail::cos_series_expansion(r); - break; + case static_cast(UINT8_C(1)): + case static_cast(UINT8_C(3)): + result = detail::sin_series_expansion(r); + break; + case static_cast(UINT8_C(0)): + case static_cast(UINT8_C(2)): + default: + result = detail::cos_series_expansion(r); + break; + } + + if(do_scaling) + { + result *= (((result * result) * static_cast(UINT8_C(4))) - static_cast(UINT8_C(3))); } + + if(signbit(result)) { result = -result; } + + const auto b_neg = ((n == static_cast(UINT8_C(1))) || (n == static_cast(UINT8_C(2)))); + + if(b_neg) { result = -result; } + } + else + { + constexpr T one { 1 }; + + result = one; } } diff --git a/include/boost/decimal/detail/cmath/exp.hpp b/include/boost/decimal/detail/cmath/exp.hpp index ae0c830a6..e72b2c3af 100644 --- a/include/boost/decimal/detail/cmath/exp.hpp +++ b/include/boost/decimal/detail/cmath/exp.hpp @@ -43,20 +43,13 @@ constexpr auto exp_impl(T x) noexcept { if (fpc == FP_INFINITE) { - if (signbit(x)) - { - result = zero; - } - else - { - result = x; - } + result = (signbit(x) ? zero : std::numeric_limits::infinity()); } else if (fpc == FP_NAN) { result = x; } - } + } // LCOV_EXCL_LINE else { if (signbit(x)) diff --git a/include/boost/decimal/detail/cmath/fma.hpp b/include/boost/decimal/detail/cmath/fma.hpp index 2baf5e6b0..f7ec9bccf 100644 --- a/include/boost/decimal/detail/cmath/fma.hpp +++ b/include/boost/decimal/detail/cmath/fma.hpp @@ -70,13 +70,13 @@ constexpr auto fmad32(decimal32 x, decimal32 y, decimal32 z) noexcept -> decimal if (!promoted_mul_result.sign && z_components.sign) { - result = d64_sub_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + result = detail::d64_sub_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, z_components.sig, z_components.exp, z_components.sign, abs_lhs_bigger); } else { - result = d64_add_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + result = detail::d64_add_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, z_components.sig, z_components.exp, z_components.sign); } @@ -102,7 +102,7 @@ constexpr auto fmad64(decimal64 x, decimal64 y, decimal64 z) noexcept -> decimal auto exp_rhs {y.biased_exponent()}; detail::normalize(sig_rhs, exp_rhs); - auto mul_result {d64_mul_impl(sig_lhs, exp_lhs, x.isneg(), sig_rhs, exp_rhs, y.isneg())}; + auto mul_result {detail::d64_mul_impl(sig_lhs, exp_lhs, x.isneg(), sig_rhs, exp_rhs, y.isneg())}; const decimal64 dec_result {mul_result.sig, mul_result.exp, mul_result.sign}; const auto res_add {detail::check_non_finite(dec_result, z)}; @@ -139,14 +139,16 @@ constexpr auto fmad64(decimal64 x, decimal64 y, decimal64 z) noexcept -> decimal if (!promoted_mul_result.sign && z_components.sign) { - result = d128_sub_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, - z_components.sig, z_components.exp, z_components.sign, - abs_lhs_bigger); + result = detail::d128_sub_impl( + promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + z_components.sig, z_components.exp, z_components.sign, + abs_lhs_bigger); } else { - result = d128_add_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, - z_components.sig, z_components.exp, z_components.sign); + result = detail::d128_add_impl( + promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + z_components.sig, z_components.exp, z_components.sign); } return {result.sig, result.exp, result.sign}; @@ -157,7 +159,148 @@ constexpr auto fmad128(decimal128 x, decimal128 y, decimal128 z) noexcept -> dec return x * y + z; } +// TODO(mborland): promote to decimal64_fast instead of regular decimal64 once it is available constexpr auto fmad32f(decimal32_fast x, decimal32_fast y, decimal32_fast z) noexcept -> decimal32_fast +{ + // First calculate x * y without rounding + constexpr decimal32_fast zero {0, 0}; + + const auto res {detail::check_non_finite(x, y)}; + if (res != zero) + { + return res; + } + + auto sig_lhs {x.full_significand()}; + auto exp_lhs {x.biased_exponent()}; + detail::normalize(sig_lhs, exp_lhs); + + auto sig_rhs {y.full_significand()}; + auto exp_rhs {y.biased_exponent()}; + detail::normalize(sig_rhs, exp_rhs); + + auto mul_result {detail::mul_impl(sig_lhs, exp_lhs, x.isneg(), sig_rhs, exp_rhs, y.isneg())}; + const decimal32_fast dec_result {mul_result.sig, mul_result.exp, mul_result.sign}; + + const auto res_add {detail::check_non_finite(dec_result, z)}; + if (res_add != zero) + { + return res_add; + } + + bool lhs_bigger {dec_result > z}; + if (dec_result.isneg() && z.isneg()) + { + lhs_bigger = !lhs_bigger; + } + bool abs_lhs_bigger {abs(dec_result) > abs(z)}; + + // To avoid the rounding step we promote the constituent pieces to the next higher type + detail::decimal64_components promoted_mul_result {static_cast(mul_result.sig), + mul_result.exp, mul_result.sign}; + + detail::normalize(promoted_mul_result.sig, promoted_mul_result.exp); + + auto sig_z {static_cast(z.full_significand())}; + auto exp_z {z.biased_exponent()}; + detail::normalize(sig_z, exp_z); + detail::decimal64_components z_components {sig_z, exp_z, z.isneg()}; + + if (!lhs_bigger) + { + detail::swap(promoted_mul_result, z_components); + abs_lhs_bigger = !abs_lhs_bigger; + } + + detail::decimal64_components result {}; + + if (!promoted_mul_result.sign && z_components.sign) + { + result = detail::d64_sub_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + z_components.sig, z_components.exp, z_components.sign, + abs_lhs_bigger); + } + else + { + result = detail::d64_add_impl(promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + z_components.sig, z_components.exp, z_components.sign); + } + + return {result.sig, result.exp, result.sign}; +} + +constexpr auto fmad64f(decimal64_fast x, decimal64_fast y, decimal64_fast z) noexcept -> decimal64_fast +{ + // First calculate x * y without rounding + constexpr decimal64_fast zero {0, 0}; + + const auto res {detail::check_non_finite(x, y)}; + if (res != zero) + { + return res; + } + + auto sig_lhs {x.full_significand()}; + auto exp_lhs {x.biased_exponent()}; + detail::normalize(sig_lhs, exp_lhs); + + auto sig_rhs {y.full_significand()}; + auto exp_rhs {y.biased_exponent()}; + detail::normalize(sig_rhs, exp_rhs); + + auto mul_result {detail::d64_mul_impl(sig_lhs, exp_lhs, x.isneg(), sig_rhs, exp_rhs, y.isneg())}; + const decimal64_fast dec_result {mul_result.sig, mul_result.exp, mul_result.sign}; + + const auto res_add {detail::check_non_finite(dec_result, z)}; + if (res_add != zero) + { + return res_add; + } + + bool lhs_bigger {dec_result > z}; + if (dec_result.isneg() && z.isneg()) + { + lhs_bigger = !lhs_bigger; + } + bool abs_lhs_bigger {abs(dec_result) > abs(z)}; + + // To avoid the rounding step we promote the constituent pieces to the next higher type + detail::decimal128_components promoted_mul_result {static_cast(mul_result.sig), + mul_result.exp, mul_result.sign}; + + detail::normalize(promoted_mul_result.sig, promoted_mul_result.exp); + + auto sig_z {static_cast(z.full_significand())}; + auto exp_z {z.biased_exponent()}; + detail::normalize(sig_z, exp_z); + detail::decimal128_components z_components {sig_z, exp_z, z.isneg()}; + + if (!lhs_bigger) + { + detail::swap(promoted_mul_result, z_components); + abs_lhs_bigger = !abs_lhs_bigger; + } + + detail::decimal128_components result {}; + + if (!promoted_mul_result.sign && z_components.sign) + { + result = detail::d128_sub_impl( + promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + z_components.sig, z_components.exp, z_components.sign, + abs_lhs_bigger); + } + else + { + result = detail::d128_add_impl( + promoted_mul_result.sig, promoted_mul_result.exp, promoted_mul_result.sign, + z_components.sig, z_components.exp, z_components.sign); + } + + return {result.sig, result.exp, result.sign}; +} + +constexpr auto fmad128f(decimal128_fast x, decimal128_fast y, decimal128_fast z) noexcept -> decimal128_fast { return x * y + z; } @@ -182,6 +325,16 @@ BOOST_DECIMAL_EXPORT constexpr auto fma(decimal32_fast x, decimal32_fast y, deci return fmad32f(x, y, z); } +BOOST_DECIMAL_EXPORT constexpr auto fma(decimal64_fast x, decimal64_fast y, decimal64_fast z) noexcept -> decimal64_fast +{ + return fmad64f(x, y, z); +} + +BOOST_DECIMAL_EXPORT constexpr auto fma(decimal128_fast x, decimal128_fast y, decimal128_fast z) noexcept -> decimal128_fast +{ + return fmad128f(x, y, z); +} + } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/frexp10.hpp b/include/boost/decimal/detail/cmath/frexp10.hpp index 393494a59..85f7f4468 100644 --- a/include/boost/decimal/detail/cmath/frexp10.hpp +++ b/include/boost/decimal/detail/cmath/frexp10.hpp @@ -21,8 +21,9 @@ namespace boost { namespace decimal { // Returns the normalized significand and exponent to be cohort agnostic -// Returns num in the range [1'000'000, 9'999'999] -// +// Returns num in the range +// [1e06, 1e06 - 1] for decimal32 +// [1e15, 1e15 - 1] for decimal64 // If the conversion can not be performed returns UINT32_MAX and exp = 0 BOOST_DECIMAL_EXPORT template constexpr auto frexp10(T num, int* expptr) noexcept -> typename T::significand_type diff --git a/include/boost/decimal/detail/cmath/impl/asin_impl.hpp b/include/boost/decimal/detail/cmath/impl/asin_impl.hpp index 2a402e8d3..6101f06a4 100644 --- a/include/boost/decimal/detail/cmath/impl/asin_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/asin_impl.hpp @@ -28,9 +28,20 @@ namespace asin_detail { template struct asin_table_imp { +private: + using d32_coeffs_t = std::array; + using d64_coeffs_t = std::array; + using d128_coeffs_t = std::array; + + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + +public: + // 10th degree remez polynomial calculated from 0, 0.5 // Estimated max error: 7.3651618860008751e-11 - static constexpr std::array d32_coeffs = {{ + static constexpr d32_coeffs_t d32_coeffs = {{ decimal32 {UINT64_C(263887099755925), -15}, decimal32 {UINT64_C(43491393212832818), -17, true}, decimal32 {UINT64_C(38559884786102105), -17}, @@ -44,9 +55,23 @@ struct asin_table_imp decimal32 {UINT64_C(73651618860008751), -27} }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = {{ + decimal32_fast {UINT64_C(263887099755925), -15}, + decimal32_fast {UINT64_C(43491393212832818), -17, true}, + decimal32_fast {UINT64_C(38559884786102105), -17}, + decimal32_fast {UINT64_C(13977130653211101), -17, true}, + decimal32_fast {UINT64_C(54573213517731915), -18}, + decimal32_fast {UINT64_C(64851743877986187), -18}, + decimal32_fast {UINT64_C(11606701725692841), -19}, + decimal32_fast {UINT64_C(16658989049586517), -17}, + decimal32_fast {UINT64_C(25906093603686159), -22}, + decimal32_fast {UINT64_C(99999996600828589), -17}, + decimal32_fast {UINT64_C(73651618860008751), -27} + }}; + // 20th degree remez polynomial calculated from 0, 0.5 // Estimated max error: 6.0872797932519911178133457751215133e-19 - static constexpr std::array d64_coeffs = {{ + static constexpr d64_coeffs_t d64_coeffs = {{ decimal64 {UINT64_C(2201841632531125594), -18}, decimal64 {UINT64_C(9319383818485265142), -18, true}, decimal64 {UINT64_C(1876826158920611297), -17}, @@ -70,9 +95,33 @@ struct asin_table_imp decimal64 {UINT64_C(6087279793251991118), -37} }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = {{ + decimal64_fast {UINT64_C(2201841632531125594), -18}, + decimal64_fast {UINT64_C(9319383818485265142), -18, true}, + decimal64_fast {UINT64_C(1876826158920611297), -17}, + decimal64_fast {UINT64_C(2351630530022519158), -17, true}, + decimal64_fast {UINT64_C(2046603318375014621), -17}, + decimal64_fast {UINT64_C(1304427904865204196), -17, true}, + decimal64_fast {UINT64_C(6308794339076719731), -18}, + decimal64_fast {UINT64_C(2333806156857836980), -18, true}, + decimal64_fast {UINT64_C(6826985955727270693), -19}, + decimal64_fast {UINT64_C(1326415745606167277), -19, true}, + decimal64_fast {UINT64_C(2747750823768175476), -20}, + decimal64_fast {UINT64_C(2660509753516203115), -20}, + decimal64_fast {UINT64_C(3977122944636320545), -22}, + decimal64_fast {UINT64_C(4461135938842722307), -20}, + decimal64_fast {UINT64_C(1826730778134521645), -24}, + decimal64_fast {UINT64_C(7499992533825458566), -20}, + decimal64_fast {UINT64_C(2034140780525051207), -27}, + decimal64_fast {UINT64_C(1666666666327808185), -19}, + decimal64_fast {UINT64_C(2987315928933390856), -31}, + decimal64_fast {UINT64_C(9999999999999989542), -19}, + decimal64_fast {UINT64_C(6087279793251991118), -37} + }}; + // 40th degree remez polynomial calculated from 0, 0.5 // Estimated max error: 1.084502473818005718919720519483941e-34 - static constexpr std::array d128_coeffs = {{ + static constexpr d128_coeffs_t d128_coeffs = {{ decimal128 {uint128{UINT64_C(236367828732266), UINT64_C(4865873281479238114)}, -31}, decimal128 {uint128{UINT64_C(218966359248756), UINT64_C(1393338271545593644)}, -30, true}, decimal128 {uint128{UINT64_C(98104038983693), UINT64_C(4819646069944316372)}, -29}, @@ -115,18 +164,71 @@ struct asin_table_imp decimal128 {uint128{UINT64_C(542101086242752), UINT64_C(4003012203950105568)}, -34}, decimal128 {uint128{UINT64_C(58790996908969), UINT64_C(5250765973560640036)}, -67} }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = {{ + decimal128_fast {uint128{UINT64_C(236367828732266), UINT64_C(4865873281479238114)}, -31}, + decimal128_fast {uint128{UINT64_C(218966359248756), UINT64_C(1393338271545593644)}, -30, true}, + decimal128_fast {uint128{UINT64_C(98104038983693), UINT64_C(4819646069944316372)}, -29}, + decimal128_fast {uint128{UINT64_C(282853615727310), UINT64_C(10104044375051504970)}, -29, true}, + decimal128_fast {uint128{UINT64_C(58930987436658), UINT64_C(3829646337759276014)}, -28}, + decimal128_fast {uint128{UINT64_C(94467942291578), UINT64_C(14212526794757587650)}, -28, true}, + decimal128_fast {uint128{UINT64_C(121156109355190), UINT64_C(6171523396929956760)}, -28}, + decimal128_fast {uint128{UINT64_C(127640043209581), UINT64_C(8369619306382995314)}, -28, true}, + decimal128_fast {uint128{UINT64_C(112556984011870), UINT64_C(14401172681696800280)}, -28}, + decimal128_fast {uint128{UINT64_C(84240716950351), UINT64_C(10152945328926072964)}, -28, true}, + decimal128_fast {uint128{UINT64_C(540724366020485), UINT64_C(8813105586620168570)}, -29}, + decimal128_fast {uint128{UINT64_C(300054630162323), UINT64_C(4862687399308912842)}, -29, true}, + decimal128_fast {uint128{UINT64_C(144827005285082), UINT64_C(4790810090757542758)}, -29}, + decimal128_fast {uint128{UINT64_C(61085784025333), UINT64_C(3908625641731373429)}, -29, true}, + decimal128_fast {uint128{UINT64_C(225929173229512), UINT64_C(18404095637827467688)}, -30}, + decimal128_fast {uint128{UINT64_C(73452862511516), UINT64_C(2655967943189644664)}, -30, true}, + decimal128_fast {uint128{UINT64_C(210254502661653), UINT64_C(14174199201997297032)}, -31}, + decimal128_fast {uint128{UINT64_C(530269670900176), UINT64_C(3023877239296322874)}, -32, true}, + decimal128_fast {uint128{UINT64_C(117870705400334), UINT64_C(8785618254907029456)}, -32}, + decimal128_fast {uint128{UINT64_C(230285265351731), UINT64_C(8107756519153341434)}, -33, true}, + decimal128_fast {uint128{UINT64_C(397318429350031), UINT64_C(567549410172969484)}, -34}, + decimal128_fast {uint128{UINT64_C(54772616787306), UINT64_C(4168475956004989379)}, -34, true}, + decimal128_fast {uint128{UINT64_C(79509164538790), UINT64_C(17928590725399689320)}, -35}, + decimal128_fast {uint128{UINT64_C(534376054761824), UINT64_C(1987644731805023176)}, -36}, + decimal128_fast {uint128{UINT64_C(92204817966183), UINT64_C(17576450582561384882)}, -37}, + decimal128_fast {uint128{UINT64_C(75623542590285), UINT64_C(990523592779300020)}, -35}, + decimal128_fast {uint128{UINT64_C(59680570668825), UINT64_C(14870623164911255928)}, -39}, + decimal128_fast {uint128{UINT64_C(94069144841714), UINT64_C(11353995396932754836)}, -35}, + decimal128_fast {uint128{UINT64_C(204081757431333), UINT64_C(1300964680833664202)}, -42}, + decimal128_fast {uint128{UINT64_C(121279716530202), UINT64_C(3054546075061258708)}, -35}, + decimal128_fast {uint128{UINT64_C(340541736068294), UINT64_C(674620373211314186)}, -45}, + decimal128_fast {uint128{UINT64_C(164700850853976), UINT64_C(1203142186405381614)}, -35}, + decimal128_fast {uint128{UINT64_C(246590930469756), UINT64_C(6088477928552847004)}, -48}, + decimal128_fast {uint128{UINT64_C(242009413501228), UINT64_C(3841246034215456962)}, -35}, + decimal128_fast {uint128{UINT64_C(64561634810301), UINT64_C(5259904364587721972)}, -51}, + decimal128_fast {uint128{UINT64_C(406575814682064), UINT64_C(3001055340328133406)}, -35}, + decimal128_fast {uint128{UINT64_C(447242814330412), UINT64_C(4234427805033793948)}, -56}, + decimal128_fast {uint128{UINT64_C(90350181040458), UINT64_C(12964998079628443792)}, -34}, + decimal128_fast {uint128{UINT64_C(430604756670586), UINT64_C(9888097447655546704)}, -61}, + decimal128_fast {uint128{UINT64_C(542101086242752), UINT64_C(4003012203950105568)}, -34}, + decimal128_fast {uint128{UINT64_C(58790996908969), UINT64_C(5250765973560640036)}, -67} + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) template -constexpr std::array asin_table_imp::d32_coeffs; +constexpr typename asin_table_imp::d32_coeffs_t asin_table_imp::d32_coeffs; + +template +constexpr typename asin_table_imp::d64_coeffs_t asin_table_imp::d64_coeffs; template -constexpr std::array asin_table_imp::d64_coeffs; +constexpr typename asin_table_imp::d128_coeffs_t asin_table_imp::d128_coeffs; template -constexpr std::array asin_table_imp::d128_coeffs; +constexpr typename asin_table_imp::d32_fast_coeffs_t asin_table_imp::d32_fast_coeffs; + +template +constexpr typename asin_table_imp::d64_fast_coeffs_t asin_table_imp::d64_fast_coeffs; + +template +constexpr typename asin_table_imp::d128_fast_coeffs_t asin_table_imp::d128_fast_coeffs; #endif @@ -143,18 +245,36 @@ constexpr auto asin_series(decimal32 x) noexcept return remez_series_result(x, asin_detail::asin_table::d32_coeffs); } +template <> +constexpr auto asin_series(decimal32_fast x) noexcept +{ + return remez_series_result(x, asin_detail::asin_table::d32_fast_coeffs); +} + template <> constexpr auto asin_series(decimal64 x) noexcept { return remez_series_result(x, asin_detail::asin_table::d64_coeffs); } +template <> +constexpr auto asin_series(decimal64_fast x) noexcept +{ + return remez_series_result(x, asin_detail::asin_table::d64_fast_coeffs); +} + template <> constexpr auto asin_series(decimal128 x) noexcept { return remez_series_result(x, asin_detail::asin_table::d128_coeffs); } +template <> +constexpr auto asin_series(decimal128_fast x) noexcept +{ + return remez_series_result(x, asin_detail::asin_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/assoc_legendre_lookup.hpp b/include/boost/decimal/detail/cmath/impl/assoc_legendre_lookup.hpp index dad9cb876..56327220a 100644 --- a/include/boost/decimal/detail/cmath/impl/assoc_legendre_lookup.hpp +++ b/include/boost/decimal/detail/cmath/impl/assoc_legendre_lookup.hpp @@ -129,6 +129,109 @@ struct assoc_legendre_lookup { decimal32{UINT32_C(2725392),72}, }}; + static constexpr std::array d32_fast_values = + {{ + decimal32_fast{UINT32_C(1000000),-6}, + decimal32_fast{UINT32_C(1000000),-6}, + decimal32_fast{UINT32_C(2000000),-6}, + decimal32_fast{UINT32_C(3000000),-6}, + decimal32_fast{UINT32_C(8000000),-6}, + decimal32_fast{UINT32_C(1500000),-5}, + decimal32_fast{UINT32_C(4800000),-5}, + decimal32_fast{UINT32_C(1050000),-4}, + decimal32_fast{UINT32_C(3840000),-4}, + decimal32_fast{UINT32_C(9450000),-4}, + decimal32_fast{UINT32_C(3840000),-3}, + decimal32_fast{UINT32_C(1039500),-2}, + decimal32_fast{UINT32_C(4608000),-2}, + decimal32_fast{UINT32_C(1351350),-1}, + decimal32_fast{UINT32_C(6451200),-1}, + decimal32_fast{UINT32_C(2027025),0}, + decimal32_fast{UINT32_C(1032192),1}, + decimal32_fast{UINT32_C(3445943),1}, + decimal32_fast{UINT32_C(1857946),2}, + decimal32_fast{UINT32_C(6547291),2}, + decimal32_fast{UINT32_C(3715891),3}, + decimal32_fast{UINT32_C(1374931),4}, + decimal32_fast{UINT32_C(8174961),4}, + decimal32_fast{UINT32_C(3162341),5}, + decimal32_fast{UINT32_C(1961991),6}, + decimal32_fast{UINT32_C(7905854),6}, + decimal32_fast{UINT32_C(5101175),7}, + decimal32_fast{UINT32_C(2134580),8}, + decimal32_fast{UINT32_C(1428329),9}, + decimal32_fast{UINT32_C(6190283),9}, + decimal32_fast{UINT32_C(4284987),10}, + decimal32_fast{UINT32_C(1918988),11}, + decimal32_fast{UINT32_C(1371196),12}, + decimal32_fast{UINT32_C(6332660),12}, + decimal32_fast{UINT32_C(4662066),13}, + decimal32_fast{UINT32_C(2216431),14}, + decimal32_fast{UINT32_C(1678344),15}, + decimal32_fast{UINT32_C(8200795),15}, + decimal32_fast{UINT32_C(6377707),16}, + decimal32_fast{UINT32_C(3198310),17}, + decimal32_fast{UINT32_C(2551083),18}, + decimal32_fast{UINT32_C(1311307),19}, + decimal32_fast{UINT32_C(1071455),20}, + decimal32_fast{UINT32_C(5638620),20}, + decimal32_fast{UINT32_C(4714401),21}, + decimal32_fast{UINT32_C(2537379),22}, + decimal32_fast{UINT32_C(2168624),23}, + decimal32_fast{UINT32_C(1192568),24}, + decimal32_fast{UINT32_C(1040940),25}, + decimal32_fast{UINT32_C(5843584),25}, + decimal32_fast{UINT32_C(5204698),26}, + decimal32_fast{UINT32_C(2980228),27}, + decimal32_fast{UINT32_C(2706443),28}, + decimal32_fast{UINT32_C(1579521),29}, + decimal32_fast{UINT32_C(1461479),30}, + decimal32_fast{UINT32_C(8687364),30}, + decimal32_fast{UINT32_C(8184284),31}, + decimal32_fast{UINT32_C(4951798),32}, + decimal32_fast{UINT32_C(4746885),33}, + decimal32_fast{UINT32_C(2921561),34}, + decimal32_fast{UINT32_C(2848131),35}, + decimal32_fast{UINT32_C(1782152),36}, + decimal32_fast{UINT32_C(1765841),37}, + decimal32_fast{UINT32_C(1122756),38}, + decimal32_fast{UINT32_C(1130138),39}, + decimal32_fast{UINT32_C(7297912),39}, + decimal32_fast{UINT32_C(7458913),40}, + decimal32_fast{UINT32_C(4889601),41}, + decimal32_fast{UINT32_C(5072061),42}, + decimal32_fast{UINT32_C(3373825),43}, + decimal32_fast{UINT32_C(3550443),44}, + decimal32_fast{UINT32_C(2395416),45}, + decimal32_fast{UINT32_C(2556319),46}, + decimal32_fast{UINT32_C(1748653),47}, + decimal32_fast{UINT32_C(1891676),48}, + decimal32_fast{UINT32_C(1311490),49}, + decimal32_fast{UINT32_C(1437674),50}, + decimal32_fast{UINT32_C(1009847),51}, + decimal32_fast{UINT32_C(1121385),52}, + decimal32_fast{UINT32_C(7977794),52}, + decimal32_fast{UINT32_C(8971083),53}, + decimal32_fast{UINT32_C(6462013),54}, + decimal32_fast{UINT32_C(7356288),55}, + decimal32_fast{UINT32_C(5363471),56}, + decimal32_fast{UINT32_C(6179282),57}, + decimal32_fast{UINT32_C(4558950),58}, + decimal32_fast{UINT32_C(5314183),59}, + decimal32_fast{UINT32_C(3966287),60}, + decimal32_fast{UINT32_C(4676481),61}, + decimal32_fast{UINT32_C(3529995),62}, + decimal32_fast{UINT32_C(4208833),63}, + decimal32_fast{UINT32_C(3212296),64}, + decimal32_fast{UINT32_C(3872126),65}, + decimal32_fast{UINT32_C(2987435),66}, + decimal32_fast{UINT32_C(3639799),67}, + decimal32_fast{UINT32_C(2838063),68}, + decimal32_fast{UINT32_C(3494207),69}, + decimal32_fast{UINT32_C(2752921),70}, + decimal32_fast{UINT32_C(3424322),71}, + decimal32_fast{UINT32_C(2725392),72}, + }}; static constexpr std::array d64_values = {{ @@ -234,6 +337,110 @@ struct assoc_legendre_lookup { decimal64{UINT64_C(2725392139750730),63}, }}; + static constexpr std::array d64_fast_values = + {{ + decimal64_fast{UINT64_C(1000000000000000),-15}, + decimal64_fast{UINT64_C(1000000000000000),-15}, + decimal64_fast{UINT64_C(2000000000000000),-15}, + decimal64_fast{UINT64_C(3000000000000000),-15}, + decimal64_fast{UINT64_C(8000000000000000),-15}, + decimal64_fast{UINT64_C(1500000000000000),-14}, + decimal64_fast{UINT64_C(4800000000000000),-14}, + decimal64_fast{UINT64_C(1050000000000000),-13}, + decimal64_fast{UINT64_C(3840000000000000),-13}, + decimal64_fast{UINT64_C(9450000000000000),-13}, + decimal64_fast{UINT64_C(3840000000000000),-12}, + decimal64_fast{UINT64_C(1039500000000000),-11}, + decimal64_fast{UINT64_C(4608000000000000),-11}, + decimal64_fast{UINT64_C(1351350000000000),-10}, + decimal64_fast{UINT64_C(6451200000000000),-10}, + decimal64_fast{UINT64_C(2027025000000000),-9}, + decimal64_fast{UINT64_C(1032192000000000),-8}, + decimal64_fast{UINT64_C(3445942500000000),-8}, + decimal64_fast{UINT64_C(1857945600000000),-7}, + decimal64_fast{UINT64_C(6547290750000000),-7}, + decimal64_fast{UINT64_C(3715891200000000),-6}, + decimal64_fast{UINT64_C(1374931057500000),-5}, + decimal64_fast{UINT64_C(8174960640000000),-5}, + decimal64_fast{UINT64_C(3162341432250000),-4}, + decimal64_fast{UINT64_C(1961990553600000),-3}, + decimal64_fast{UINT64_C(7905853580625000),-3}, + decimal64_fast{UINT64_C(5101175439360000),-2}, + decimal64_fast{UINT64_C(2134580466768750),-1}, + decimal64_fast{UINT64_C(1428329123020800),0}, + decimal64_fast{UINT64_C(6190283353629375),0}, + decimal64_fast{UINT64_C(4284987369062400),1}, + decimal64_fast{UINT64_C(1918987839625106),2}, + decimal64_fast{UINT64_C(1371195958099968),3}, + decimal64_fast{UINT64_C(6332659870762850),3}, + decimal64_fast{UINT64_C(4662066257539891),4}, + decimal64_fast{UINT64_C(2216430954766998),5}, + decimal64_fast{UINT64_C(1678343852714361),6}, + decimal64_fast{UINT64_C(8200794532637892),6}, + decimal64_fast{UINT64_C(6377706640314571),7}, + decimal64_fast{UINT64_C(3198309867728778),8}, + decimal64_fast{UINT64_C(2551082656125829),9}, + decimal64_fast{UINT64_C(1311307045768799),10}, + decimal64_fast{UINT64_C(1071454715572848),11}, + decimal64_fast{UINT64_C(5638620296805835),11}, + decimal64_fast{UINT64_C(4714400748520531),12}, + decimal64_fast{UINT64_C(2537379133562626),13}, + decimal64_fast{UINT64_C(2168624344319444),14}, + decimal64_fast{UINT64_C(1192568192774434),15}, + decimal64_fast{UINT64_C(1040939685273333),16}, + decimal64_fast{UINT64_C(5843584144594727),16}, + decimal64_fast{UINT64_C(5204698426366666),17}, + decimal64_fast{UINT64_C(2980227913743311),18}, + decimal64_fast{UINT64_C(2706443181710667),19}, + decimal64_fast{UINT64_C(1579520794283955),20}, + decimal64_fast{UINT64_C(1461479318123760),21}, + decimal64_fast{UINT64_C(8687364368561751),21}, + decimal64_fast{UINT64_C(8184284181493056),22}, + decimal64_fast{UINT64_C(4951797690080198),23}, + decimal64_fast{UINT64_C(4746884825265972),24}, + decimal64_fast{UINT64_C(2921560637147317),25}, + decimal64_fast{UINT64_C(2848130895159583),26}, + decimal64_fast{UINT64_C(1782151988659863),27}, + decimal64_fast{UINT64_C(1765841154998942),28}, + decimal64_fast{UINT64_C(1122755752855714),29}, + decimal64_fast{UINT64_C(1130138339199323),30}, + decimal64_fast{UINT64_C(7297912393562140),30}, + decimal64_fast{UINT64_C(7458913038715529),31}, + decimal64_fast{UINT64_C(4889601303686634),32}, + decimal64_fast{UINT64_C(5072060866326560),33}, + decimal64_fast{UINT64_C(3373824899543778),34}, + decimal64_fast{UINT64_C(3550442606428592),35}, + decimal64_fast{UINT64_C(2395415678676082),36}, + decimal64_fast{UINT64_C(2556318676628587),37}, + decimal64_fast{UINT64_C(1748653445433540),38}, + decimal64_fast{UINT64_C(1891675820705154),39}, + decimal64_fast{UINT64_C(1311490084075155),40}, + decimal64_fast{UINT64_C(1437673623735917),41}, + decimal64_fast{UINT64_C(1009847364737869),42}, + decimal64_fast{UINT64_C(1121385426514015),43}, + decimal64_fast{UINT64_C(7977794181429167),43}, + decimal64_fast{UINT64_C(8971083412112120),44}, + decimal64_fast{UINT64_C(6462013286957625),45}, + decimal64_fast{UINT64_C(7356288397931940),46}, + decimal64_fast{UINT64_C(5363471028174829),47}, + decimal64_fast{UINT64_C(6179282254262830),48}, + decimal64_fast{UINT64_C(4558950373948605),49}, + decimal64_fast{UINT64_C(5314182738666033),50}, + decimal64_fast{UINT64_C(3966286825335287),51}, + decimal64_fast{UINT64_C(4676480810026109),52}, + decimal64_fast{UINT64_C(3529995274548405),53}, + decimal64_fast{UINT64_C(4208832729023498),54}, + decimal64_fast{UINT64_C(3212295699839048),55}, + decimal64_fast{UINT64_C(3872126110701619),56}, + decimal64_fast{UINT64_C(2987435000850315),57}, + decimal64_fast{UINT64_C(3639798544059521),58}, + decimal64_fast{UINT64_C(2838063250807799),59}, + decimal64_fast{UINT64_C(3494206602297140),60}, + decimal64_fast{UINT64_C(2752921353283565),61}, + decimal64_fast{UINT64_C(3424322470251197),62}, + decimal64_fast{UINT64_C(2725392139750730),63}, + }}; + static constexpr std::array d128_values = {{ decimal128{detail::uint128{UINT64_C(54210108624275),UINT64_C(4089650035136921600)},-33}, @@ -337,6 +544,110 @@ struct assoc_legendre_lookup { decimal128{detail::uint128{UINT64_C(185632893076863),UINT64_C(16002051557646139392)},44}, decimal128{detail::uint128{UINT64_C(147743803939632),UINT64_C(16999359510947954688)},45}, }}; + + static constexpr std::array d128_fast_values = + {{ + decimal128_fast{detail::uint128{UINT64_C(54210108624275),UINT64_C(4089650035136921600)},-33}, + decimal128_fast{detail::uint128{UINT64_C(54210108624275),UINT64_C(4089650035136921600)},-33}, + decimal128_fast{detail::uint128{UINT64_C(108420217248550),UINT64_C(8179300070273843200)},-33}, + decimal128_fast{detail::uint128{UINT64_C(162630325872825),UINT64_C(12268950105410764800)},-33}, + decimal128_fast{detail::uint128{UINT64_C(433680868994201),UINT64_C(14270456207385821184)},-33}, + decimal128_fast{detail::uint128{UINT64_C(81315162936412),UINT64_C(15357847089560158208)},-32}, + decimal128_fast{detail::uint128{UINT64_C(260208521396521),UINT64_C(1183576094947672064)},-32}, + decimal128_fast{detail::uint128{UINT64_C(56920614055488),UINT64_C(18129190592175931392)},-31}, + decimal128_fast{detail::uint128{UINT64_C(208166817117216),UINT64_C(15704256134925778944)},-31}, + decimal128_fast{detail::uint128{UINT64_C(512285526499400),UINT64_C(15588762739906969600)},-31}, + decimal128_fast{detail::uint128{UINT64_C(208166817117216),UINT64_C(15704256134925778944)},-30}, + decimal128_fast{detail::uint128{UINT64_C(56351407914934),UINT64_C(1714763901389766656)},-29}, + decimal128_fast{detail::uint128{UINT64_C(249800180540660),UINT64_C(4087712102943293440)},-29}, + decimal128_fast{detail::uint128{UINT64_C(73256830289414),UINT64_C(5918541886548606976)},-28}, + decimal128_fast{detail::uint128{UINT64_C(349720252756924),UINT64_C(5722796944120610816)},-28}, + decimal128_fast{detail::uint128{UINT64_C(109885245434121),UINT64_C(8877812829822910464)},-27}, + decimal128_fast{detail::uint128{UINT64_C(55955240441107),UINT64_C(16410912532975321088)},-26}, + decimal128_fast{detail::uint128{UINT64_C(186804917238006),UINT64_C(9558258588586082304)},-26}, + decimal128_fast{detail::uint128{UINT64_C(100719432793994),UINT64_C(3714200856162205696)},-25}, + decimal128_fast{detail::uint128{UINT64_C(354929342752212),UINT64_C(7092644874087825408)},-25}, + decimal128_fast{detail::uint128{UINT64_C(201438865587988),UINT64_C(7428401712324411392)},-24}, + decimal128_fast{detail::uint128{UINT64_C(74535161977964),UINT64_C(11081762341887410176)},-23}, + decimal128_fast{detail::uint128{UINT64_C(443165504293574),UINT64_C(8963786137629884416)},-23}, + decimal128_fast{detail::uint128{UINT64_C(171430872549318),UINT64_C(10730658127373402112)},-22}, + decimal128_fast{detail::uint128{UINT64_C(106359721030457),UINT64_C(16170834169050431488)},-21}, + decimal128_fast{detail::uint128{UINT64_C(428577181373296),UINT64_C(8379901244723953664)},-21}, + decimal128_fast{detail::uint128{UINT64_C(276535274679190),UINT64_C(8840029506853928960)},-20}, + decimal128_fast{detail::uint128{UINT64_C(115715838970790),UINT64_C(786833810178703360)},-19}, + decimal128_fast{detail::uint128{UINT64_C(77429876910173),UINT64_C(6164557076661010432)},-18}, + decimal128_fast{detail::uint128{UINT64_C(335575933015291),UINT64_C(2281818049518239744)},-18}, + decimal128_fast{detail::uint128{UINT64_C(232289630730520),UINT64_C(46927156273479680)},-17}, + decimal128_fast{detail::uint128{UINT64_C(104028539234740),UINT64_C(4531179850829660160)},-16}, + decimal128_fast{detail::uint128{UINT64_C(74332681833766),UINT64_C(7393714319491334144)},-15}, + decimal128_fast{detail::uint128{UINT64_C(343294179474642),UINT64_C(14492893507737878528)},-15}, + decimal128_fast{detail::uint128{UINT64_C(252731118234805),UINT64_C(13870582242044805120)},-14}, + decimal128_fast{detail::uint128{UINT64_C(120152962816124),UINT64_C(18085233579304943616)},-13}, + decimal128_fast{detail::uint128{UINT64_C(90983202564530),UINT64_C(1344060792394219520)},-12}, + decimal128_fast{detail::uint128{UINT64_C(444565962419662),UINT64_C(8765783207557726208)},-12}, + decimal128_fast{detail::uint128{UINT64_C(345736169745214),UINT64_C(5067431011098034176)},-11}, + decimal128_fast{detail::uint128{UINT64_C(173380725343668),UINT64_C(6359069384215232512)},-10}, + decimal128_fast{detail::uint128{UINT64_C(138294467898085),UINT64_C(13195018848664944640)},-9}, + decimal128_fast{detail::uint128{UINT64_C(71086097390904),UINT64_C(418609158683099136)},-8}, + decimal128_fast{detail::uint128{UINT64_C(58083676517196),UINT64_C(37884694326411264)},-7}, + decimal128_fast{detail::uint128{UINT64_C(305670218780887),UINT64_C(5649368197079236608)},-7}, + decimal128_fast{detail::uint128{UINT64_C(255568176675662),UINT64_C(7345390284520030208)},-6}, + decimal128_fast{detail::uint128{UINT64_C(137551598451399),UINT64_C(5159227299742089216)},-5}, + decimal128_fast{detail::uint128{UINT64_C(117561361270804),UINT64_C(13111186449208180736)},-4}, + decimal128_fast{detail::uint128{UINT64_C(64649251272157),UINT64_C(12369611189944844288)},-3}, + decimal128_fast{detail::uint128{UINT64_C(56429453409986),UINT64_C(4705629969723162624)},-2}, + decimal128_fast{detail::uint128{UINT64_C(316781331233572),UINT64_C(10224885831713947648)},-2}, + decimal128_fast{detail::uint128{UINT64_C(282147267049931),UINT64_C(5081405774906261504)},-1}, + decimal128_fast{detail::uint128{UINT64_C(161558478929122),UINT64_C(279603433535438848)},0}, + decimal128_fast{detail::uint128{UINT64_C(146716578865964),UINT64_C(5035940291796402176)},1}, + decimal128_fast{detail::uint128{UINT64_C(85625993832434),UINT64_C(12193040908422086656)},2}, + decimal128_fast{detail::uint128{UINT64_C(79226952587620),UINT64_C(12939584438847406080)},3}, + decimal128_fast{detail::uint128{UINT64_C(470942966078390),UINT64_C(11871492775192821760)},3}, + decimal128_fast{detail::uint128{UINT64_C(443670934490675),UINT64_C(18241440636416819200)},4}, + decimal128_fast{detail::uint128{UINT64_C(268437490664682),UINT64_C(12230774103972773888)},5}, + decimal128_fast{detail::uint128{UINT64_C(257329142004592),UINT64_C(876663532266979328)},6}, + decimal128_fast{detail::uint128{UINT64_C(158378119492162),UINT64_C(14405919469353566208)},7}, + decimal128_fast{detail::uint128{UINT64_C(154397485202755),UINT64_C(4415346934102097920)},8}, + decimal128_fast{detail::uint128{UINT64_C(96610652890219),UINT64_C(5497196943037956096)},9}, + decimal128_fast{detail::uint128{UINT64_C(95726440825708),UINT64_C(4374189506514255872)},10}, + decimal128_fast{detail::uint128{UINT64_C(60864711320838),UINT64_C(2767831751902625792)},11}, + decimal128_fast{detail::uint128{UINT64_C(61264922128453),UINT64_C(5053090573014269952)},12}, + decimal128_fast{detail::uint128{UINT64_C(395620623585447),UINT64_C(18290906387367067648)},12}, + decimal128_fast{detail::uint128{UINT64_C(404348486047791),UINT64_C(11054304893442719744)},13}, + decimal128_fast{detail::uint128{UINT64_C(265065817802250),UINT64_C(3047067801944064000)},14}, + decimal128_fast{detail::uint128{UINT64_C(274956970512498),UINT64_C(5583318038695903232)},15}, + decimal128_fast{detail::uint128{UINT64_C(182895414283552),UINT64_C(11365848820196179968)},16}, + decimal128_fast{detail::uint128{UINT64_C(192469879358748),UINT64_C(14976369071312863232)},17}, + decimal128_fast{detail::uint128{UINT64_C(129855744141322),UINT64_C(6569013136442523648)},18}, + decimal128_fast{detail::uint128{UINT64_C(138578313138299),UINT64_C(2926418338913058816)},19}, + decimal128_fast{detail::uint128{UINT64_C(94794693223165),UINT64_C(5842184234025615360)},20}, + decimal128_fast{detail::uint128{UINT64_C(102547951722341),UINT64_C(6951703029960146944)},21}, + decimal128_fast{detail::uint128{UINT64_C(71096019917373),UINT64_C(18166696230801375232)},22}, + decimal128_fast{detail::uint128{UINT64_C(77936443308979),UINT64_C(8194773354563239936)},23}, + decimal128_fast{detail::uint128{UINT64_C(54743935336377),UINT64_C(17966172353196064768)},24}, + decimal128_fast{detail::uint128{UINT64_C(60790425781003),UINT64_C(17768904542259249152)},25}, + decimal128_fast{detail::uint128{UINT64_C(432477089157385),UINT64_C(17869576296394915840)},25}, + decimal128_fast{detail::uint128{UINT64_C(486323406248031),UINT64_C(11424027822107131904)},26}, + decimal128_fast{detail::uint128{UINT64_C(350306442217482),UINT64_C(11437345189023449088)},27}, + decimal128_fast{detail::uint128{UINT64_C(398785193123386),UINT64_C(268591251376308224)},28}, + decimal128_fast{detail::uint128{UINT64_C(290754347040510),UINT64_C(10849801151312035840)},29}, + decimal128_fast{detail::uint128{UINT64_C(334979562223644),UINT64_C(4552835228846391296)},30}, + decimal128_fast{detail::uint128{UINT64_C(247141194984434),UINT64_C(348958941760454656)},31}, + decimal128_fast{detail::uint128{UINT64_C(288082423512334),UINT64_C(593959245014368256)},32}, + decimal128_fast{detail::uint128{UINT64_C(215012839636457),UINT64_C(11152705842083135488)},33}, + decimal128_fast{detail::uint128{UINT64_C(253512532690853),UINT64_C(17753688683425431552)},34}, + decimal128_fast{detail::uint128{UINT64_C(191361427276447),UINT64_C(4960287299552411648)},35}, + decimal128_fast{detail::uint128{UINT64_C(228161279421768),UINT64_C(10074296592970022912)},36}, + decimal128_fast{detail::uint128{UINT64_C(174138898821566),UINT64_C(18167854379349049344)},37}, + decimal128_fast{detail::uint128{UINT64_C(209908377068027),UINT64_C(1491785473100218368)},38}, + decimal128_fast{detail::uint128{UINT64_C(161949175904057),UINT64_C(5819123247094693888)},39}, + decimal128_fast{detail::uint128{UINT64_C(197313874443945),UINT64_C(8022041092723834880)},40}, + decimal128_fast{detail::uint128{UINT64_C(153851717108854),UINT64_C(8045178695796391936)},41}, + decimal128_fast{detail::uint128{UINT64_C(189421319466187),UINT64_C(11630508263756791808)},42}, + decimal128_fast{detail::uint128{UINT64_C(149236165595588),UINT64_C(14983586082932129792)},43}, + decimal128_fast{detail::uint128{UINT64_C(185632893076863),UINT64_C(16002051557646139392)},44}, + decimal128_fast{detail::uint128{UINT64_C(147743803939632),UINT64_C(16999359510947954688)},45}, + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -350,6 +661,15 @@ constexpr std::array assoc_legendre_lookup::d64_values; template constexpr std::array assoc_legendre_lookup::d128_values; +template +constexpr std::array assoc_legendre_lookup::d32_fast_values; + +template +constexpr std::array assoc_legendre_lookup::d64_fast_values; + +template +constexpr std::array assoc_legendre_lookup::d128_fast_values; + #endif using assoc_legendre_lookup_table = assoc_legendre_lookup; @@ -365,18 +685,36 @@ constexpr auto assoc_legendre_p0_lookup(unsigned n) -> decimal32 return assoc_legendre_detail::assoc_legendre_lookup_table::d32_values[static_cast(n)]; } +template <> +constexpr auto assoc_legendre_p0_lookup(unsigned n) -> decimal32_fast +{ + return assoc_legendre_detail::assoc_legendre_lookup_table::d32_fast_values[static_cast(n)]; +} + template <> constexpr auto assoc_legendre_p0_lookup(unsigned n) -> decimal64 { return assoc_legendre_detail::assoc_legendre_lookup_table::d64_values[static_cast(n)]; } +template <> +constexpr auto assoc_legendre_p0_lookup(unsigned n) -> decimal64_fast +{ + return assoc_legendre_detail::assoc_legendre_lookup_table::d64_fast_values[static_cast(n)]; +} + template <> constexpr auto assoc_legendre_p0_lookup(unsigned n) -> decimal128 { return assoc_legendre_detail::assoc_legendre_lookup_table::d128_values[static_cast(n)]; } +template <> +constexpr auto assoc_legendre_p0_lookup(unsigned n) -> decimal128_fast +{ + return assoc_legendre_detail::assoc_legendre_lookup_table::d128_fast_values[static_cast(n)]; +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/atan_impl.hpp b/include/boost/decimal/detail/cmath/impl/atan_impl.hpp index 21f3e53e5..9f8528984 100644 --- a/include/boost/decimal/detail/cmath/impl/atan_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/atan_impl.hpp @@ -32,6 +32,13 @@ struct atan_table_imp ::boost::decimal::decimal32 { UINT64_C(9827937232473290679), -19 }, // atan_three_halves }}; + static constexpr std::array<::boost::decimal::decimal32_fast, 3> d32_fast_atan_values = + {{ + ::boost::decimal::decimal32_fast { UINT64_C(4636476090008061162), -19 }, // atan_half + ::boost::decimal::decimal32_fast { UINT64_C(7853981633974483096), -19 }, // atan_one + ::boost::decimal::decimal32_fast { UINT64_C(9827937232473290679), -19 }, // atan_three_halves + }}; + static constexpr std::array<::boost::decimal::decimal64, 3> d64_atan_values = {{ ::boost::decimal::decimal64 { UINT64_C(4636476090008061162), -19 }, // atan_half @@ -39,6 +46,13 @@ struct atan_table_imp ::boost::decimal::decimal64 { UINT64_C(9827937232473290679), -19 }, // atan_three_halves }}; + static constexpr std::array<::boost::decimal::decimal64_fast, 3> d64_fast_atan_values = + {{ + ::boost::decimal::decimal64_fast { UINT64_C(4636476090008061162), -19 }, // atan_half + ::boost::decimal::decimal64_fast { UINT64_C(7853981633974483096), -19 }, // atan_one + ::boost::decimal::decimal64_fast { UINT64_C(9827937232473290679), -19 }, // atan_three_halves + }}; + static constexpr std::array<::boost::decimal::decimal128, 3> d128_atan_values = {{ ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(251343872473191), UINT64_C(15780610568723885484) }, -34 }, // atan_half @@ -46,6 +60,13 @@ struct atan_table_imp ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(532773544924935), UINT64_C(16408933314882201700) }, -34 }, // atan_three_halves }}; + static constexpr std::array<::boost::decimal::decimal128_fast, 3> d128_fast_atan_values = + {{ + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(251343872473191), UINT64_C(15780610568723885484) }, -34 }, // atan_half + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(425765197510819), UINT64_C(5970600460659265246) }, -34 }, // atan_one + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(532773544924935), UINT64_C(16408933314882201700) }, -34 }, // atan_three_halves + }}; + // 10th degree remez polynomial calculated from 0, 0.4375 // Estimated max error: 2.3032664387910605e-12 static constexpr std::array<::boost::decimal::decimal32, 11> d32_coeffs = @@ -63,6 +84,21 @@ struct atan_table_imp ::boost::decimal::decimal32 { UINT64_C(23032664387910606), -29 }, }}; + static constexpr std::array<::boost::decimal::decimal32_fast, 11> d32_fast_coeffs = + {{ + ::boost::decimal::decimal32_fast { UINT64_C(61037779951304161), -18, true }, + ::boost::decimal::decimal32_fast { UINT64_C(10723099589331457), -17 }, + ::boost::decimal::decimal32_fast { UINT64_C(22515613909953665), -18 }, + ::boost::decimal::decimal32_fast { UINT64_C(15540713402718176), -17, true }, + ::boost::decimal::decimal32_fast { UINT64_C(35999727706986597), -19 }, + ::boost::decimal::decimal32_fast { UINT64_C(19938867353282852), -17 }, + ::boost::decimal::decimal32_fast { UINT64_C(62252075283915644), -22 }, + ::boost::decimal::decimal32_fast { UINT64_C(33333695504913247), -17, true }, + ::boost::decimal::decimal32_fast { UINT64_C(10680927642397763), -24 }, + ::boost::decimal::decimal32_fast { UINT64_C(99999999877886492), -17 }, + ::boost::decimal::decimal32_fast { UINT64_C(23032664387910606), -29 }, + }}; + static constexpr std::array<::boost::decimal::decimal64, 11> d64_coeffs = {{ ::boost::decimal::decimal64 { UINT64_C(61037779951304161), -18, true }, @@ -77,16 +113,36 @@ struct atan_table_imp ::boost::decimal::decimal64 { UINT64_C(99999999877886492), -17 }, ::boost::decimal::decimal64 { UINT64_C(23032664387910606), -29 }, }}; + + static constexpr std::array<::boost::decimal::decimal64_fast, 11> d64_fast_coeffs = + {{ + ::boost::decimal::decimal64_fast { UINT64_C(61037779951304161), -18, true }, + ::boost::decimal::decimal64_fast { UINT64_C(10723099589331457), -17 }, + ::boost::decimal::decimal64_fast { UINT64_C(22515613909953665), -18 }, + ::boost::decimal::decimal64_fast { UINT64_C(15540713402718176), -17, true }, + ::boost::decimal::decimal64_fast { UINT64_C(35999727706986597), -19 }, + ::boost::decimal::decimal64_fast { UINT64_C(19938867353282852), -17 }, + ::boost::decimal::decimal64_fast { UINT64_C(62252075283915644), -22 }, + ::boost::decimal::decimal64_fast { UINT64_C(33333695504913247), -17, true }, + ::boost::decimal::decimal64_fast { UINT64_C(10680927642397763), -24 }, + ::boost::decimal::decimal64_fast { UINT64_C(99999999877886492), -17 }, + ::boost::decimal::decimal64_fast { UINT64_C(23032664387910606), -29 }, + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) template constexpr std::array atan_table_imp::d32_coeffs; +template constexpr std::array atan_table_imp::d32_fast_coeffs; template constexpr std::array atan_table_imp::d64_coeffs; +template constexpr std::array atan_table_imp::d64_fast_coeffs; template constexpr std::array atan_table_imp::d32_atan_values; +template constexpr std::array atan_table_imp::d32_fast_atan_values; template constexpr std::array atan_table_imp::d64_atan_values; +template constexpr std::array atan_table_imp::d64_fast_atan_values; template constexpr std::array atan_table_imp::d128_atan_values; +template constexpr std::array atan_table_imp::d128_fast_atan_values; #endif @@ -103,6 +159,9 @@ constexpr auto atan_values(std::size_t idx) noexcept -> T; template <> constexpr auto atan_series (decimal32 x) noexcept { return remez_series_result(x, atan_detail::atan_table::d32_coeffs); } template <> constexpr auto atan_series (decimal64 x) noexcept { return remez_series_result(x, atan_detail::atan_table::d64_coeffs); } +template <> constexpr auto atan_series (decimal32_fast x) noexcept { return remez_series_result(x, atan_detail::atan_table::d32_fast_coeffs); } +template <> constexpr auto atan_series (decimal64_fast x) noexcept { return remez_series_result(x, atan_detail::atan_table::d64_fast_coeffs); } + template <> constexpr auto atan_series(decimal128 x) noexcept { @@ -146,10 +205,58 @@ constexpr auto atan_series(decimal128 x) noexcept return (x * top) / bot; } +template <> +constexpr auto atan_series(decimal128_fast x) noexcept +{ + // PadeApproximant[ArcTan[x]/x, {x, 0, {18, 18}}] + // FullSimplify[%] + // HornerForm[Numerator[Out[2]]] + // HornerForm[Denominator[Out[2]]] + + const decimal128_fast x2 { x * x }; + + const decimal128_fast + top + { + decimal128_fast { UINT64_C(21427381364263875) } + + x2 * (decimal128_fast { UINT64_C(91886788553059500) } + + x2 * (decimal128_fast { UINT64_C(163675410390191700) } + + x2 * (decimal128_fast { UINT64_C(156671838074852100) } + + x2 * (decimal128_fast { UINT64_C(87054123957610810) } + + x2 * (decimal128_fast { UINT64_C(28283323008669300) } + + x2 * (decimal128_fast { UINT64_C(5134145876036100) } + + x2 * (decimal128_fast { UINT64_C(463911017673180) } + + x2 * (decimal128_fast { UINT64_C(16016872057515) } + + x2 * decimal128_fast { UINT64_C(90194313216) })))))))) + }; + + const decimal128_fast + bot + { + decimal128_fast { UINT64_C(21427381364263875) } + + x2 * (decimal128_fast { UINT64_C(99029249007814125) } + + x2 * (decimal128_fast { UINT64_C(192399683786610300) } + + x2 * (decimal128_fast { UINT64_C(204060270682768500) } + + x2 * (decimal128_fast { UINT64_C(128360492848838250) } + + x2 * (decimal128_fast { UINT64_C(48688462804731750) } + + x2 * (decimal128_fast { UINT64_C(10819658401051500) } + + x2 * (decimal128_fast { UINT64_C(1298359008126180) } + + x2 * (decimal128_fast { UINT64_C(70562989572075) } + + x2 * decimal128_fast { UINT64_C(1120047453525) })))))))) + }; + + return (x * top) / bot; +} + + template <> constexpr auto atan_values (std::size_t idx) noexcept -> decimal32 { return atan_detail::atan_table::d32_atan_values [idx]; } template <> constexpr auto atan_values (std::size_t idx) noexcept -> decimal64 { return atan_detail::atan_table::d64_atan_values [idx]; } template <> constexpr auto atan_values(std::size_t idx) noexcept -> decimal128 { return atan_detail::atan_table::d128_atan_values[idx]; } +template <> constexpr auto atan_values (std::size_t idx) noexcept -> decimal32_fast { return atan_detail::atan_table::d32_fast_atan_values [idx]; } +template <> constexpr auto atan_values (std::size_t idx) noexcept -> decimal64_fast { return atan_detail::atan_table::d64_fast_atan_values [idx]; } +template <> constexpr auto atan_values(std::size_t idx) noexcept -> decimal128_fast { return atan_detail::atan_table::d128_fast_atan_values[idx]; } + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/cos_impl.hpp b/include/boost/decimal/detail/cmath/impl/cos_impl.hpp index 8fb951ca4..0350130f2 100644 --- a/include/boost/decimal/detail/cmath/impl/cos_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/cos_impl.hpp @@ -40,6 +40,19 @@ struct cos_table_imp decimal32 {UINT64_C(99999999999908662), -17} }}; + static constexpr std::array d32_fast_coeffs = + {{ + decimal32_fast {UINT64_C(22805960529562646), -21}, + decimal32_fast {UINT64_C(39171880037888081), -22}, + decimal32_fast {UINT64_C(1392392773950284), -18, true}, + decimal32_fast {UINT64_C(17339629614857501), -22}, + decimal32_fast {UINT64_C(41666173896377827), -18}, + decimal32_fast {UINT64_C(77764646000512304), -24}, + decimal32_fast {UINT64_C(50000000610949535), -17, true}, + decimal32_fast {UINT64_C(18421494272283811), -26}, + decimal32_fast {UINT64_C(99999999999908662), -17} + }}; + // 12th Degree Remez Polynomial from 0 to pi / 4 // Estimated max error: 7.911867233315355155595617164843665e-20 static constexpr std::array d64_coeffs = @@ -59,32 +72,22 @@ struct cos_table_imp decimal64 {UINT64_C(9999999999999999999), -19} }}; - // 20th Degree Remez Polynomial from 0 to pi / 4 - // Estimated max error: 2.1310510195548626186539810165524781e-35 - static constexpr std::array d128_coeffs = + static constexpr std::array d64_fast_coeffs = {{ - decimal128 {uint128{UINT64_C(205464805604747), UINT64_C(1103437048276783858)}, -52}, - decimal128 {uint128{UINT64_C(88395173126016), UINT64_C(16033763930510860544)}, -52}, - decimal128 {uint128{UINT64_C(84906215169376), UINT64_C(15713701775139901874)}, -49, true}, - decimal128 {uint128{UINT64_C(406502864446184), UINT64_C(12823522983377384156)}, -52}, - decimal128 {uint128{UINT64_C(259090944919015), UINT64_C(2392820714740683920)}, -47}, - decimal128 {uint128{UINT64_C(467445711741371), UINT64_C(4983292921389624904)}, -52}, - decimal128 {uint128{UINT64_C(62183039812775), UINT64_C(15652271656899615679)}, -44, true}, - decimal128 {uint128{UINT64_C(184508989294410), UINT64_C(1375137197219348330)}, -52}, - decimal128 {uint128{UINT64_C(113173126395461), UINT64_C(16339984202390313234)}, -42}, - decimal128 {uint128{UINT64_C(274884181093086), UINT64_C(12747689940557963034)}, -53}, - decimal128 {uint128{UINT64_C(149388526852617), UINT64_C(12302422570283469338)}, -40, true}, - decimal128 {uint128{UINT64_C(154088679459876), UINT64_C(3924311363127714460)}, -54}, - decimal128 {uint128{UINT64_C(134449674167349), UINT64_C(4753674936935436426)}, -33}, - decimal128 {uint128{UINT64_C(299007263162206), UINT64_C(7798573768093066264)}, -56}, - decimal128 {uint128{UINT64_C(75291817533715), UINT64_C(10804169962871218270)}, -36, true}, - decimal128 {uint128{UINT64_C(166117873118141), UINT64_C(15619656560639581524)}, -58}, - decimal128 {uint128{UINT64_C(225875452601146), UINT64_C(13965751132838711524)}, -35}, - decimal128 {uint128{UINT64_C(177440011694387), UINT64_C(4853507633156477618)}, -61}, - decimal128 {uint128{UINT64_C(271050543121376), UINT64_C(2001506101975100694)}, -34, true}, - decimal128 {uint128{UINT64_C(129186594797812), UINT64_C(16941950919815074018)}, -65}, - decimal128 {uint128{UINT64_C(54210108624275), UINT64_C(4089650035136921600)}, -33} - }}; + decimal64_fast {UINT64_C(1922641020040661424), -27}, + decimal64_fast {UINT64_C(4960385936049718134), -28}, + decimal64_fast {UINT64_C(2763064713566851512), -25, true}, + decimal64_fast {UINT64_C(6633276621376137827), -28}, + decimal64_fast {UINT64_C(2480119161297283187), -23}, + decimal64_fast {UINT64_C(1600210781837650114), -28}, + decimal64_fast {UINT64_C(1388888932852646133), -21, true}, + decimal64_fast {UINT64_C(8054772849254568869), -30}, + decimal64_fast {UINT64_C(4166666666572238908), -20}, + decimal64_fast {UINT64_C(6574164404618517322), -32}, + decimal64_fast {UINT64_C(5000000000000023748), -19, true}, + decimal64_fast {UINT64_C(3367952043014273196), -35}, + decimal64_fast {UINT64_C(9999999999999999999), -19} + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -96,7 +99,10 @@ template constexpr std::array cos_table_imp::d64_coeffs; template -constexpr std::array cos_table_imp::d128_coeffs; +constexpr std::array cos_table_imp::d32_fast_coeffs; + +template +constexpr std::array cos_table_imp::d64_fast_coeffs; #endif @@ -113,16 +119,88 @@ constexpr auto cos_series_expansion(decimal32 x) noexcept return remez_series_result(x, cos_detail::cos_table::d32_coeffs); } +template <> +constexpr auto cos_series_expansion(decimal32_fast x) noexcept +{ + return remez_series_result(x, cos_detail::cos_table::d32_fast_coeffs); +} + template <> constexpr auto cos_series_expansion(decimal64 x) noexcept { return remez_series_result(x, cos_detail::cos_table::d64_coeffs); } +template <> +constexpr auto cos_series_expansion(decimal64_fast x) noexcept +{ + return remez_series_result(x, cos_detail::cos_table::d64_fast_coeffs); +} + template <> constexpr auto cos_series_expansion(decimal128 x) noexcept { - return remez_series_result(x, cos_detail::cos_table::d128_coeffs); + // PadeApproximant[Cos[x], {x, 0, {14, 14}}] + // FullSimplify[%] + // HornerForm[Numerator[Out[2]]] + // HornerForm[Denominator[Out[2]]] + + constexpr decimal128 c0 { boost::decimal::detail::uint128 { UINT64_C(307807346375396), UINT64_C(9191352932158695424) }, 3 }; + constexpr decimal128 c1 { boost::decimal::detail::uint128 { UINT64_C(149996550055690), UINT64_C(222763958071016960) }, 3, true }; + constexpr decimal128 c2 { boost::decimal::detail::uint128 { UINT64_C(108967212479807), UINT64_C(3937477076487471608) }, 2 }; + constexpr decimal128 c3 { boost::decimal::detail::uint128 { UINT64_C(277096228519262), UINT64_C(6277888927557284608) }, 0, true }; + constexpr decimal128 c4 { boost::decimal::detail::uint128 { UINT64_C(319580269604048), UINT64_C(10708241405247058432) }, -2 }; + constexpr decimal128 c5 { boost::decimal::detail::uint128 { UINT64_C(183739194803716), UINT64_C(9003931728965394944) }, -4, true }; + constexpr decimal128 c6 { boost::decimal::detail::uint128 { UINT64_C(518817586019902), UINT64_C(14598542072727738368) }, -7 }; + constexpr decimal128 c7 { boost::decimal::detail::uint128 { UINT64_C(58205916937364), UINT64_C(13388002334603019776) }, -9, true }; + + constexpr decimal128 d1 { boost::decimal::detail::uint128 { UINT64_C(390712313200823), UINT64_C(13016137105513388032) }, 1 }; + constexpr decimal128 d2 { boost::decimal::detail::uint128 { UINT64_C(249767150099857), UINT64_C(14534865724066009088) }, -1 }; + constexpr decimal128 d3 { boost::decimal::detail::uint128 { UINT64_C(105535117882474), UINT64_C(16245151810017622016) }, -3 }; + constexpr decimal128 d4 { boost::decimal::detail::uint128 { UINT64_C(322928599993793), UINT64_C(8055050913586880512) }, -6 }; + constexpr decimal128 d5 { boost::decimal::detail::uint128 { UINT64_C(72777849685460), UINT64_C(10172723920765296640) }, -8 }; + constexpr decimal128 d6 { boost::decimal::detail::uint128 { UINT64_C(114133059907344), UINT64_C(3036923607254532096) }, -11 }; + constexpr decimal128 d7 { boost::decimal::detail::uint128 { UINT64_C(98470690251347), UINT64_C(1521187190289973248) }, -14 }; + + const decimal128 x2 { x * x }; + + const decimal128 top { c0 + x2 * (c1 + x2 * (c2 + x2 * (c3 + x2 * (c4 + x2 * (c5 + x2 * (c6 + x2 * c7)))))) }; + const decimal128 bot { c0 + x2 * (d1 + x2 * (d2 + x2 * (d3 + x2 * (d4 + x2 * (d5 + x2 * (d6 + x2 * d7)))))) }; + + return decimal128 { top / bot }; +} + +template <> +constexpr auto cos_series_expansion(decimal128_fast x) noexcept +{ + // PadeApproximant[Cos[x], {x, 0, {14, 14}}] + // FullSimplify[%] + // HornerForm[Numerator[Out[2]]] + // HornerForm[Denominator[Out[2]]] + + constexpr decimal128_fast c0 { boost::decimal::detail::uint128 { UINT64_C(307807346375396), UINT64_C(9191352932158695424) }, 3 }; + constexpr decimal128_fast c1 { boost::decimal::detail::uint128 { UINT64_C(149996550055690), UINT64_C(222763958071016960) }, 3, true }; + constexpr decimal128_fast c2 { boost::decimal::detail::uint128 { UINT64_C(108967212479807), UINT64_C(3937477076487471608) }, 2 }; + constexpr decimal128_fast c3 { boost::decimal::detail::uint128 { UINT64_C(277096228519262), UINT64_C(6277888927557284608) }, 0, true }; + constexpr decimal128_fast c4 { boost::decimal::detail::uint128 { UINT64_C(319580269604048), UINT64_C(10708241405247058432) }, -2 }; + constexpr decimal128_fast c5 { boost::decimal::detail::uint128 { UINT64_C(183739194803716), UINT64_C(9003931728965394944) }, -4, true }; + constexpr decimal128_fast c6 { boost::decimal::detail::uint128 { UINT64_C(518817586019902), UINT64_C(14598542072727738368) }, -7 }; + constexpr decimal128_fast c7 { boost::decimal::detail::uint128 { UINT64_C(58205916937364), UINT64_C(13388002334603019776) }, -9, true }; + + constexpr decimal128_fast d1 { boost::decimal::detail::uint128 { UINT64_C(390712313200823), UINT64_C(13016137105513388032) }, 1 }; + constexpr decimal128_fast d2 { boost::decimal::detail::uint128 { UINT64_C(249767150099857), UINT64_C(14534865724066009088) }, -1 }; + constexpr decimal128_fast d3 { boost::decimal::detail::uint128 { UINT64_C(105535117882474), UINT64_C(16245151810017622016) }, -3 }; + constexpr decimal128_fast d4 { boost::decimal::detail::uint128 { UINT64_C(322928599993793), UINT64_C(8055050913586880512) }, -6 }; + constexpr decimal128_fast d5 { boost::decimal::detail::uint128 { UINT64_C(72777849685460), UINT64_C(10172723920765296640) }, -8 }; + constexpr decimal128_fast d6 { boost::decimal::detail::uint128 { UINT64_C(114133059907344), UINT64_C(3036923607254532096) }, -11 }; + constexpr decimal128_fast d7 { boost::decimal::detail::uint128 { UINT64_C(98470690251347), UINT64_C(1521187190289973248) }, -14 }; + + const decimal128_fast x2 { x * x }; + + const decimal128_fast top { c0 + x2 * (c1 + x2 * (c2 + x2 * (c3 + x2 * (c4 + x2 * (c5 + x2 * (c6 + x2 * c7)))))) }; + const decimal128_fast bot { c0 + x2 * (d1 + x2 * (d2 + x2 * (d3 + x2 * (d4 + x2 * (d5 + x2 * (d6 + x2 * d7)))))) }; + + return decimal128_fast { top / bot }; } } // namespace detail diff --git a/include/boost/decimal/detail/cmath/impl/cosh_impl.hpp b/include/boost/decimal/detail/cmath/impl/cosh_impl.hpp index 0812a9076..94d5262e2 100644 --- a/include/boost/decimal/detail/cmath/impl/cosh_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/cosh_impl.hpp @@ -29,6 +29,10 @@ struct cosh_table_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + public: static constexpr d32_coeffs_t d32_coeffs = {{ @@ -42,6 +46,18 @@ struct cosh_table_imp ::boost::decimal::decimal32 { UINT64_C(2087675698786809898), - 19 - 8 }, // * x^12 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // Series[Cosh[x], {x, 0, 12}] + // (1), // * 1 + ::boost::decimal::decimal32_fast { 5, -1 }, // * x^2 + ::boost::decimal::decimal32_fast { UINT64_C(4166666666666666667), - 19 - 1 }, // * x^6 + ::boost::decimal::decimal32_fast { UINT64_C(1388888888888888889), - 19 - 2 }, // * x^8 + ::boost::decimal::decimal32_fast { UINT64_C(2480158730158730159), - 19 - 4 }, // * x^10 + ::boost::decimal::decimal32_fast { UINT64_C(2755731922398589065), - 19 - 6 }, // * x^12 + ::boost::decimal::decimal32_fast { UINT64_C(2087675698786809898), - 19 - 8 }, // * x^12 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // Series[Cosh[x], {x, 0, 18}] @@ -57,6 +73,21 @@ struct cosh_table_imp ::boost::decimal::decimal64 { UINT64_C(1561920696858622646), - 19 - 15 } // * x^18 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // Series[Cosh[x], {x, 0, 18}] + // (1), // * 1 + ::boost::decimal::decimal64_fast { 5, -1 }, // * x^2 + ::boost::decimal::decimal64_fast { UINT64_C(4166666666666666667), - 19 - 1 }, // * x^4 + ::boost::decimal::decimal64_fast { UINT64_C(1388888888888888889), - 19 - 2 }, // * x^6 + ::boost::decimal::decimal64_fast { UINT64_C(2480158730158730159), - 19 - 4 }, // * x^8 + ::boost::decimal::decimal64_fast { UINT64_C(2755731922398589065), - 19 - 6 }, // * x^10 + ::boost::decimal::decimal64_fast { UINT64_C(2087675698786809898), - 19 - 8 }, // * x^12 + ::boost::decimal::decimal64_fast { UINT64_C(1147074559772972471), - 19 - 10 }, // * x^14 + ::boost::decimal::decimal64_fast { UINT64_C(4779477332387385297), - 19 - 13 }, // * x^16 + ::boost::decimal::decimal64_fast { UINT64_C(1561920696858622646), - 19 - 15 } // * x^18 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // Series[Cosh[x], {x, 0, 34}] @@ -79,6 +110,29 @@ struct cosh_table_imp ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(206019595635366), UINT64_C(17625897212400736954) }, -69 }, // * x^32 ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(183618177928134), UINT64_C(9987905770721758456) }, -72 }, // * x^34 }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // Series[Cosh[x], {x, 0, 34}] + // (1), // * 1 + ::boost::decimal::decimal128_fast { 5, -1 }, // * x^2 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(225875452601146), UINT64_C(13965751134118914724) }, -35 }, // * x^4 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(75291817533715), UINT64_C(10804165069276155440) }, -36 }, // * x^6 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(134449674167349), UINT64_C(4799281565792772746) }, -38 }, // * x^8 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(149388526852610), UINT64_C(5332535073103080820) }, -40 }, // * x^10 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(113173126403492), UINT64_C(11865690723015477068) }, -42 }, // * x^12 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(62183036485435), UINT64_C(9560282387433155251) }, -44 }, // * x^14 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(259095985355981), UINT64_C(6015479145837302244) }, -47 }, // * x^16 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(84671890639209), UINT64_C(10767230553416093986) }, -49 }, // * x^18 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(222820764840025), UINT64_C(4062785569898205740) }, -52 }, // * x^20 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(482296027792262), UINT64_C(7037075391028107068) }, -55 }, // * x^22 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(87372468802946), UINT64_C(1542176615384940434) }, -57 }, // * x^24 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(134419182773763), UINT64_C(3791559721646796942) }, -60 }, // * x^26 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(177803151817147), UINT64_C(1794430560736952558) }, -63 }, // * x^28 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(204371438870284), UINT64_C(366311534299067156) }, -66 }, // * x^30 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(206019595635366), UINT64_C(17625897212400736954) }, -69 }, // * x^32 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(183618177928134), UINT64_C(9987905770721758456) }, -72 }, // * x^34 + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -92,6 +146,15 @@ constexpr typename cosh_table_imp::d64_coeffs_t cosh_table_imp::d64_coeffs template constexpr typename cosh_table_imp::d128_coeffs_t cosh_table_imp::d128_coeffs; +template +constexpr typename cosh_table_imp::d32_fast_coeffs_t cosh_table_imp::d32_fast_coeffs; + +template +constexpr typename cosh_table_imp::d64_fast_coeffs_t cosh_table_imp::d64_fast_coeffs; + +template +constexpr typename cosh_table_imp::d128_fast_coeffs_t cosh_table_imp::d128_fast_coeffs; + #endif } //namespace cosh_detail @@ -119,6 +182,24 @@ constexpr auto cosh_series_expansion(decimal128 z2) noexcept return taylor_series_result(z2, cosh_table::d128_coeffs); } +template <> +constexpr auto cosh_series_expansion(decimal32_fast z2) noexcept +{ + return taylor_series_result(z2, cosh_table::d32_fast_coeffs); +} + +template <> +constexpr auto cosh_series_expansion(decimal64_fast z2) noexcept +{ + return taylor_series_result(z2, cosh_table::d64_fast_coeffs); +} + +template <> +constexpr auto cosh_series_expansion(decimal128_fast z2) noexcept +{ + return taylor_series_result(z2, cosh_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/expm1_impl.hpp b/include/boost/decimal/detail/cmath/impl/expm1_impl.hpp index 9a172db6f..8326c867a 100644 --- a/include/boost/decimal/detail/cmath/impl/expm1_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/expm1_impl.hpp @@ -29,6 +29,10 @@ struct expm1_table_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + public: static constexpr d32_coeffs_t d32_coeffs = {{ @@ -49,6 +53,25 @@ struct expm1_table_imp ::boost::decimal::decimal32 { UINT64_C(2780855729673643225), - 19 - 6 }, // * x^10 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // Specifically derive a polynomial expansion for Exp[x] - 1 for this work. + // Table[{x, Exp[x] - 1}, {x, -Log[2], Log[2], 1/60}] + // N[%, 48] + // Fit[%, {x, x^2, x^3, x^4, x^5, x^6, x^7, x^8, x^9, x^10}, x] + + ::boost::decimal::decimal32_fast { UINT64_C(1000000000005449334), - 19 + 1 }, // * x + ::boost::decimal::decimal32_fast { UINT64_C(5000000000003881336), - 19 - 0 }, // * x^2 + ::boost::decimal::decimal32_fast { UINT64_C(1666666664242981149), - 19 - 0 }, // * x^3 + ::boost::decimal::decimal32_fast { UINT64_C(4166666665026072773), - 19 - 1 }, // * x^4 + ::boost::decimal::decimal32_fast { UINT64_C(8333336317448167991), - 19 - 2 }, // * x^5 + ::boost::decimal::decimal32_fast { UINT64_C(1388889096793935619), - 19 - 2 }, // * x^6 + ::boost::decimal::decimal32_fast { UINT64_C(1983978347911205530), - 19 - 3 }, // * x^7 + ::boost::decimal::decimal32_fast { UINT64_C(2480049494648544583), - 19 - 4 }, // * x^8 + ::boost::decimal::decimal32_fast { UINT64_C(2787876201220259352), - 19 - 5 }, // * x^9 + ::boost::decimal::decimal32_fast { UINT64_C(2780855729673643225), - 19 - 6 }, // * x^10 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // Specifically derive a polynomial expansion for Exp[x] - 1 for this work. @@ -72,6 +95,29 @@ struct expm1_table_imp ::boost::decimal::decimal64 { UINT64_C(1154399218598221557), - 19 - 10 } // * x^14 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // Specifically derive a polynomial expansion for Exp[x] - 1 for this work. + // Table[{x, Exp[x] - 1}, {x, -Log[2], Log[2], 1/60}] + // N[%, 48] + // Fit[%, {x, x^2, x^3, x^4, x^5, x^6, x^7, x^8, x^9, x^10, x^11, x^12, x^13, x^14}, x] + + ::boost::decimal::decimal64_fast { UINT64_C(1000000000000000003), - 19 + 1 }, // * x + ::boost::decimal::decimal64_fast { UINT64_C(4999999999999999998), - 19 - 0 }, // * x^2 + ::boost::decimal::decimal64_fast { UINT64_C(1666666666666664035), - 19 - 0 }, // * x^3 + ::boost::decimal::decimal64_fast { UINT64_C(4166666666666666934), - 19 - 1 }, // * x^4 + ::boost::decimal::decimal64_fast { UINT64_C(8333333333339521841), - 19 - 2 }, // * x^5 + ::boost::decimal::decimal64_fast { UINT64_C(1388888888888953513), - 19 - 2 }, // * x^6 + ::boost::decimal::decimal64_fast { UINT64_C(1984126983488689186), - 19 - 3 }, // * x^7 + ::boost::decimal::decimal64_fast { UINT64_C(2480158730001499149), - 19 - 4 }, // * x^8 + ::boost::decimal::decimal64_fast { UINT64_C(2755732258782898252), - 19 - 5 }, // * x^9 + ::boost::decimal::decimal64_fast { UINT64_C(2755732043147979013), - 19 - 6 }, // * x^10 + ::boost::decimal::decimal64_fast { UINT64_C(2505116286861719378), - 19 - 7 }, // * x^11 + ::boost::decimal::decimal64_fast { UINT64_C(2087632598463662328), - 19 - 8 }, // * x^12 + ::boost::decimal::decimal64_fast { UINT64_C(1619385892296180390), - 19 - 9 }, // * x^13 + ::boost::decimal::decimal64_fast { UINT64_C(1154399218598221557), - 19 - 10 } // * x^14 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // Specifically derive a polynomial expansion for Exp[x] - 1 for this work. @@ -111,6 +157,46 @@ struct expm1_table_imp ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(66162682638108), UINT64_C(6755035083974089930) }, -67 }, // * x^31 ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(206436477688751), UINT64_C(15666750779045089894) }, -69 }, // * x^32 }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // Specifically derive a polynomial expansion for Exp[x] - 1 for this work. + // Table[{x, Exp[x] - 1}, {x, -Log[2], Log[2], 1/60}] + // N[%, 48] + // Fit[%, {x, x^2, x^3, x^4, x^5, x^6, x^7, x^8, x^9, x^10, x^11, x^12, x^13, x^14, x^15, x^16, x^17, x^18, x^19, x^20, x^21, x^22, x^23, x^24, x^25, x^26, x^27, x^28, x^29, x^30, x^31, x^32 }, x] + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(54210108624275), UINT64_C(4089650035136921600) }, -33 }, // * x + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(271050543121376), UINT64_C(2001506101975056384) }, -34 }, // * x^2 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(90350181040458), UINT64_C(12964998083131386532) }, -34 }, // * x^3 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(225875452601146), UINT64_C(13965751134118914724) }, -35 }, // * x^4 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(451750905202293), UINT64_C(9484758194528277842) }, -36 }, // * x^5 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(75291817533715), UINT64_C(10804165069276155440) }, -36 }, // * x^6 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(107559739333879), UINT64_C(7528774067376128516) }, -37 }, // * x^7 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(134449674167349), UINT64_C(4799281565792772746) }, -38 }, // * x^8 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(149388526852610), UINT64_C(5332535073103080820) }, -39 }, // * x^9 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(149388526852610), UINT64_C(5332535073103080820) }, -40 }, // * x^10 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(135807751684191), UINT64_C(3170782423392841514) }, -41 }, // * x^11 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(113173126403492), UINT64_C(11865690723015477068) }, -42 }, // * x^12 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(87056251079609), UINT64_C(13384395342406416636) }, -43 }, // * x^13 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(62183036485435), UINT64_C(9560282387433156335) }, -44 }, // * x^14 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(414553576569570), UINT64_C(2246069003862680020) }, -46 }, // * x^15 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(259095985355981), UINT64_C(6015479145828949264) }, -47 }, // * x^16 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(152409403150577), UINT64_C(4623619732418095578) }, -48 }, // * x^17 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(84671890639209), UINT64_C(10767230558026320466) }, -49 }, // * x^18 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(445641529680050), UINT64_C(8125595620937745600) }, -51 }, // * x^19 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(222820764840025), UINT64_C(4062767274683195140) }, -52 }, // * x^20 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(106105126114297), UINT64_C(13344759429965740488) }, -53 }, // * x^21 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(482296027792262), UINT64_C(7088674266265745598) }, -55 }, // * x^22 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(209693925127072), UINT64_C(336105452763225878) }, -56 }, // * x^23 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(87372468802945), UINT64_C(10013088901203012320) }, -57 }, // * x^24 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(349489875208886), UINT64_C(9445768661182748344) }, -59 }, // * x^25 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(134419182774415), UINT64_C(9680981560342232810) }, -60 }, // * x^26 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(497848829278818), UINT64_C(16288994997110182382) }, -62 }, // * x^27 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(177803151475355), UINT64_C(16680206430774781810) }, -63 }, // * x^28 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(61311025561137), UINT64_C(7837795588749518446) }, -64 }, // * x^29 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(204371229207757), UINT64_C(18366861741830034248) }, -66 }, // * x^30 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(66162682638108), UINT64_C(6755035083974089930) }, -67 }, // * x^31 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(206436477688751), UINT64_C(15666750779045089894) }, -69 }, // * x^32 + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -124,6 +210,12 @@ constexpr typename expm1_table_imp::d64_coeffs_t expm1_table_imp::d64_coef template constexpr typename expm1_table_imp::d128_coeffs_t expm1_table_imp::d128_coeffs; +template +constexpr typename expm1_table_imp::d32_fast_coeffs_t expm1_table_imp::d32_fast_coeffs; + +template +constexpr typename expm1_table_imp::d64_fast_coeffs_t expm1_table_imp::d64_fast_coeffs; + #endif } //namespace expm1_detail @@ -139,18 +231,36 @@ constexpr auto expm1_series_expansion(decimal32 x) noexcept return taylor_series_result(x, expm1_table::d32_coeffs); } +template <> +constexpr auto expm1_series_expansion(decimal32_fast x) noexcept +{ + return taylor_series_result(x, expm1_table::d32_fast_coeffs); +} + template <> constexpr auto expm1_series_expansion(decimal64 x) noexcept { return taylor_series_result(x, expm1_table::d64_coeffs); } +template <> +constexpr auto expm1_series_expansion(decimal64_fast x) noexcept +{ + return taylor_series_result(x, expm1_table::d64_fast_coeffs); +} + template <> constexpr auto expm1_series_expansion(decimal128 x) noexcept { return taylor_series_result(x, expm1_table::d128_coeffs); } +template <> +constexpr auto expm1_series_expansion(decimal128_fast x) noexcept +{ + return taylor_series_result(x, expm1_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/lgamma_impl.hpp b/include/boost/decimal/detail/cmath/impl/lgamma_impl.hpp index 764327d9e..29687fb60 100644 --- a/include/boost/decimal/detail/cmath/impl/lgamma_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/lgamma_impl.hpp @@ -29,6 +29,10 @@ struct lgamma_taylor_series_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + static constexpr d32_coeffs_t d32_coeffs = {{ // Use a Taylor series expansion of the logarithm of the gamma function. @@ -54,6 +58,31 @@ struct lgamma_taylor_series_imp + decimal32 { UINT64_C(5555576762740361110), - 19 - 1 }, // x^18 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // Use a Taylor series expansion of the logarithm of the gamma function. + // N[Series[Log[Gamma[x]], {x, 0, 18}], 19] + // log(1/x) + // -EulerGamma // * x + + decimal32_fast { UINT64_C(8224670334241132182), - 19 - 0 }, // x^2 + - decimal32_fast { UINT64_C(4006856343865314285), - 19 - 0 }, // x^3 + + decimal32_fast { UINT64_C(2705808084277845479), - 19 - 0 }, // x^4 + - decimal32_fast { UINT64_C(2073855510286739853), - 19 - 0 }, // x^5 + + decimal32_fast { UINT64_C(1695571769974081900), - 19 - 0 }, // x^6 + - decimal32_fast { UINT64_C(1440498967688461181), - 19 - 0 }, // x^7 + + decimal32_fast { UINT64_C(1255096695247430424), - 19 - 0 }, // x^8 + - decimal32_fast { UINT64_C(1113342658695646905), - 19 - 0 }, // x^9 + + decimal32_fast { UINT64_C(1000994575127818085), - 19 - 0 }, // x^10 + - decimal32_fast { UINT64_C(9095401714582904223), - 19 - 1 }, // x^11 + + decimal32_fast { UINT64_C(8335384054610900402), - 19 - 1 }, // x^12 + - decimal32_fast { UINT64_C(7693251641135219147), - 19 - 1 }, // x^13 + + decimal32_fast { UINT64_C(7143294629536133606), - 19 - 1 }, // x^14 + - decimal32_fast { UINT64_C(6666870588242046803), - 19 - 1 }, // x^15 + + decimal32_fast { UINT64_C(6250095514121304074), - 19 - 1 }, // x^16 + - decimal32_fast { UINT64_C(5882397865868458234), - 19 - 1 }, // x^17 + + decimal32_fast { UINT64_C(5555576762740361110), - 19 - 1 }, // x^18 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // Use a Taylor series expansion of the logarithm of the gamma function. @@ -89,6 +118,41 @@ struct lgamma_taylor_series_imp + decimal64 { UINT64_C(3571428584733335803), - 19 - 1 }, // x^28 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // Use a Taylor series expansion of the logarithm of the gamma function. + // N[Series[Log[Gamma[x]], {x, 0, 28}], 19] + // log(1/x) + // -EulerGamma // * x + + decimal64_fast { UINT64_C(8224670334241132182), - 19 - 0 }, // x^2 + - decimal64_fast { UINT64_C(4006856343865314285), - 19 - 0 }, // x^3 + + decimal64_fast { UINT64_C(2705808084277845479), - 19 - 0 }, // x^4 + - decimal64_fast { UINT64_C(2073855510286739853), - 19 - 0 }, // x^5 + + decimal64_fast { UINT64_C(1695571769974081900), - 19 - 0 }, // x^6 + - decimal64_fast { UINT64_C(1440498967688461181), - 19 - 0 }, // x^7 + + decimal64_fast { UINT64_C(1255096695247430424), - 19 - 0 }, // x^8 + - decimal64_fast { UINT64_C(1113342658695646905), - 19 - 0 }, // x^9 + + decimal64_fast { UINT64_C(1000994575127818085), - 19 - 0 }, // x^10 + - decimal64_fast { UINT64_C(9095401714582904223), - 19 - 1 }, // x^11 + + decimal64_fast { UINT64_C(8335384054610900402), - 19 - 1 }, // x^12 + - decimal64_fast { UINT64_C(7693251641135219147), - 19 - 1 }, // x^13 + + decimal64_fast { UINT64_C(7143294629536133606), - 19 - 1 }, // x^14 + - decimal64_fast { UINT64_C(6666870588242046803), - 19 - 1 }, // x^15 + + decimal64_fast { UINT64_C(6250095514121304074), - 19 - 1 }, // x^16 + - decimal64_fast { UINT64_C(5882397865868458234), - 19 - 1 }, // x^17 + + decimal64_fast { UINT64_C(5555576762740361110), - 19 - 1 }, // x^18 + - decimal64_fast { UINT64_C(5263167937961666073), - 19 - 1 }, // x^19 + + decimal64_fast { UINT64_C(5000004769810169364), - 19 - 1 }, // x^20 + - decimal64_fast { UINT64_C(4761907033014222799), - 19 - 1 }, // x^21 + + decimal64_fast { UINT64_C(4545455629320466944), - 19 - 1 }, // x^22 + - decimal64_fast { UINT64_C(4347826605304025936), - 19 - 1 }, // x^23 + + decimal64_fast { UINT64_C(4166666915034121047), - 19 - 1 }, // x^24 + - decimal64_fast { UINT64_C(4000000119214014059), - 19 - 1 }, // x^25 + + decimal64_fast { UINT64_C(3846153903467518571), - 19 - 1 }, // x^26 + - decimal64_fast { UINT64_C(3703703731298932555), - 19 - 1 }, // x^27 + + decimal64_fast { UINT64_C(3571428584733335803), - 19 - 1 }, // x^28 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // Use a Taylor series expansion of the logarithm of the gamma function. @@ -140,6 +204,58 @@ struct lgamma_taylor_series_imp + decimal128 { detail::uint128 { UINT64_C(123204792327905), UINT64_C(4326111080777755730) }, -35 }, // x^44 - decimal128 { detail::uint128 { UINT64_C(120466908053948), UINT64_C(6659042857988127932) }, -35 }, // x^45 }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // Use a Taylor series expansion of the logarithm of the gamma function. + // N[Series[Log[Gamma[x]], {x, 0, 46}], 36] + // log(1/x) + // -EulerGamma // * x + + decimal128_fast { detail::uint128 { UINT64_C(445860272218065), UINT64_C(14203420802908087080) }, -34 }, // x^2 + - decimal128_fast { detail::uint128 { UINT64_C(217212117642804), UINT64_C(17657476868182733566) }, -34 }, // x^3 + + decimal128_fast { detail::uint128 { UINT64_C(146682150165144), UINT64_C(868910464649280216) }, -34 }, // x^4 + - decimal128_fast { detail::uint128 { UINT64_C(112423932483695), UINT64_C(16359302115292012940) }, -34 }, // x^5 + + decimal128_fast { detail::uint128 { UINT64_C(91917129830549), UINT64_C(10691428771700534346) }, -34 }, // x^6 + - decimal128_fast { detail::uint128 { UINT64_C(78089605511547), UINT64_C(14820105259104989758) }, -34 }, // x^7 + + decimal128_fast { detail::uint128 { UINT64_C(68038928183332), UINT64_C(1064388729383271304) }, -34 }, // x^8 + - decimal128_fast { detail::uint128 { UINT64_C(60354426463930), UINT64_C(7248291600601936245) }, -34 }, // x^9 + + decimal128_fast { detail::uint128 { UINT64_C(54264024649989), UINT64_C(4473690885429568095) }, -34 }, // x^10 + - decimal128_fast { detail::uint128 { UINT64_C(493062714928958), UINT64_C(6174527806367053599) }, -35 }, // x^11 + + decimal128_fast { detail::uint128 { UINT64_C(451862075025508), UINT64_C(9914131103541110236) }, -35 }, // x^12 + - decimal128_fast { detail::uint128 { UINT64_C(417052007139823), UINT64_C(15614141522487214162) }, -35 }, // x^13 + + decimal128_fast { detail::uint128 { UINT64_C(387238777802355), UINT64_C(11643663834403323860) }, -35 }, // x^14 + - decimal128_fast { detail::uint128 { UINT64_C(361411778772587), UINT64_C(34493970254387038) }, -35 }, // x^15 + + decimal128_fast { detail::uint128 { UINT64_C(338818356732611), UINT64_C(3351583636956848354) }, -35 }, // x^16 + - decimal128_fast { detail::uint128 { UINT64_C(318885427279933), UINT64_C(15984063291116028642) }, -35 }, // x^17 + + decimal128_fast { detail::uint128 { UINT64_C(301168419778654), UINT64_C(4924861572478909706) }, -35 }, // x^18 + - decimal128_fast { detail::uint128 { UINT64_C(285316905624704), UINT64_C(10127525246402845676) }, -35 }, // x^19 + + decimal128_fast { detail::uint128 { UINT64_C(271050801693303), UINT64_C(9350577868080165772) }, -35 }, // x^20 + - decimal128_fast { detail::uint128 { UINT64_C(258143497518401), UINT64_C(2808067049374616864) }, -35 }, // x^21 + + decimal128_fast { detail::uint128 { UINT64_C(246409643412285), UINT64_C(14764422888076943740) }, -35 }, // x^22 + - decimal128_fast { detail::uint128 { UINT64_C(235696152553045), UINT64_C(678353819689262840) }, -35 }, // x^23 + + decimal128_fast { detail::uint128 { UINT64_C(225875466065173), UINT64_C(8075483572721697962) }, -35 }, // x^24 + - decimal128_fast { detail::uint128 { UINT64_C(216840440959705), UINT64_C(9932733544470621540) }, -35 }, // x^25 + + decimal128_fast { detail::uint128 { UINT64_C(208500420892654), UINT64_C(6216313969171226926) }, -35 }, // x^26 + - decimal128_fast { detail::uint128 { UINT64_C(200778181585848), UINT64_C(10737065351264354832) }, -35 }, // x^27 + + decimal128_fast { detail::uint128 { UINT64_C(193607531522235), UINT64_C(12111991390268743970) }, -35 }, // x^28 + - decimal128_fast { detail::uint128 { UINT64_C(186931409397414), UINT64_C(9391433164642506256) }, -35 }, // x^29 + + decimal128_fast { detail::uint128 { UINT64_C(180700362249208), UINT64_C(11251075665678452422) }, -35 }, // x^30 + - decimal128_fast { detail::uint128 { UINT64_C(174871318224254), UINT64_C(7048097254153902066) }, -35 }, // x^31 + + decimal128_fast { detail::uint128 { UINT64_C(169406589490303), UINT64_C(3772465895856270802) }, -35 }, // x^32 + - decimal128_fast { detail::uint128 { UINT64_C(164273056456321), UINT64_C(10547878619739699804) }, -35 }, // x^33 + + decimal128_fast { detail::uint128 { UINT64_C(159441495963031), UINT64_C(6975690319727375544) }, -35 }, // x^34 + - decimal128_fast { detail::uint128 { UINT64_C(154886024645294), UINT64_C(2350345999520845736) }, -35 }, // x^35 + + decimal128_fast { detail::uint128 { UINT64_C(150583635069622), UINT64_C(8350573461615823988) }, -35 }, // x^36 + - decimal128_fast { detail::uint128 { UINT64_C(146513807093701), UINT64_C(14073039848059635994) }, -35 }, // x^37 + + decimal128_fast { detail::uint128 { UINT64_C(142658180590716), UINT64_C(17328619387130735144) }, -35 }, // x^38 + - decimal128_fast { detail::uint128 { UINT64_C(139000278524035), UINT64_C(8482044796809236580) }, -35 }, // x^39 + + decimal128_fast { detail::uint128 { UINT64_C(135525271560811), UINT64_C(5788192050685000054) }, -35 }, // x^40 + - decimal128_fast { detail::uint128 { UINT64_C(132219777132438), UINT64_C(13209900018467184292) }, -35 }, // x^41 + + decimal128_fast { detail::uint128 { UINT64_C(129071687200684), UINT64_C(11755517601068760186) }, -35 }, // x^42 + - decimal128_fast { detail::uint128 { UINT64_C(126070020056468), UINT64_C(6206529728400242912) }, -35 }, // x^43 + + decimal128_fast { detail::uint128 { UINT64_C(123204792327905), UINT64_C(4326111080777755730) }, -35 }, // x^44 + - decimal128_fast { detail::uint128 { UINT64_C(120466908053948), UINT64_C(6659042857988127932) }, -35 }, // x^45 + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -153,6 +269,12 @@ constexpr typename lgamma_taylor_series_imp::d64_coeffs_t lgamma_taylor_serie template constexpr typename lgamma_taylor_series_imp::d128_coeffs_t lgamma_taylor_series_imp::d128_coeffs; +template +constexpr typename lgamma_taylor_series_imp::d32_fast_coeffs_t lgamma_taylor_series_imp::d32_fast_coeffs; + +template +constexpr typename lgamma_taylor_series_imp::d64_fast_coeffs_t lgamma_taylor_series_imp::d64_fast_coeffs; + #endif } //namespace lgamma_detail @@ -168,18 +290,36 @@ constexpr auto lgamma_taylor_series_expansion(decimal32 x) noexcept return taylor_series_result(x, lgamma_taylor_series_table::d32_coeffs); } +template <> +constexpr auto lgamma_taylor_series_expansion(decimal32_fast x) noexcept +{ + return taylor_series_result(x, lgamma_taylor_series_table::d32_fast_coeffs); +} + template <> constexpr auto lgamma_taylor_series_expansion(decimal64 x) noexcept { return taylor_series_result(x, lgamma_taylor_series_table::d64_coeffs); } +template <> +constexpr auto lgamma_taylor_series_expansion(decimal64_fast x) noexcept +{ + return taylor_series_result(x, lgamma_taylor_series_table::d64_fast_coeffs); +} + template <> constexpr auto lgamma_taylor_series_expansion(decimal128 x) noexcept { return taylor_series_result(x, lgamma_taylor_series_table::d128_coeffs); } +template <> +constexpr auto lgamma_taylor_series_expansion(decimal128_fast x) noexcept +{ + return taylor_series_result(x, lgamma_taylor_series_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/log1p_impl.hpp b/include/boost/decimal/detail/cmath/impl/log1p_impl.hpp index e2e864ed0..c0843ead6 100644 --- a/include/boost/decimal/detail/cmath/impl/log1p_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/log1p_impl.hpp @@ -6,6 +6,7 @@ #ifndef BOOST_DECIMAL_DETAIL_CMATH_IMPL_LOG1P_IMPL_HPP #define BOOST_DECIMAL_DETAIL_CMATH_IMPL_LOG1P_IMPL_HPP +#include #include #include @@ -29,6 +30,10 @@ struct log1p_table_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + public: static constexpr d32_coeffs_t d32_coeffs = {{ @@ -48,6 +53,24 @@ struct log1p_table_imp boost::decimal::decimal32 { UINT64_C(7692307692307692308), -19 - 1 }, // * z^13 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // Series[Log[1 + x], {x, 0, 13}] + // (1), // * z + -boost::decimal::decimal32_fast { 5, -1 }, // * z^2 + boost::decimal::decimal32_fast { UINT64_C(3333333333333333333), -19 }, // * z^3 + -boost::decimal::decimal32_fast { 25, -2 }, // * z^4 + boost::decimal::decimal32_fast { 2, -1 }, // * z^5 + -boost::decimal::decimal32_fast { UINT64_C(1666666666666666667), -19 }, // * z^6 + boost::decimal::decimal32_fast { UINT64_C(1428571428571428571), -19 }, // * z^7 + -boost::decimal::decimal32_fast { 125, -3 }, // * z^8 + boost::decimal::decimal32_fast { UINT64_C(1111111111111111111), -19 }, // * z^9 + -boost::decimal::decimal32_fast { 1, -1 }, // * z^10 + boost::decimal::decimal32_fast { UINT64_C(9090909090909090909), -19 - 1 }, // * z^11 + -boost::decimal::decimal32_fast { UINT64_C(8333333333333333333), -19 - 1 }, // * z^12 + boost::decimal::decimal32_fast { UINT64_C(7692307692307692308), -19 - 1 }, // * z^13 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // Series[Log[1 + x], {x, 0, 21}] @@ -74,6 +97,32 @@ struct log1p_table_imp boost::decimal::decimal64 { UINT64_C(4761904761904761905), -19 - 1 }, // * z^21 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // Series[Log[1 + x], {x, 0, 21}] + // (1), // * z + -boost::decimal::decimal64_fast { 5, -1 }, // * z^2 + boost::decimal::decimal64_fast { UINT64_C(3333333333333333333), -19 }, // * z^3 + -boost::decimal::decimal64_fast { 25, -2 }, // * z^4 + boost::decimal::decimal64_fast { 2, -1 }, // * z^5 + -boost::decimal::decimal64_fast { UINT64_C(1666666666666666667), -19 }, // * z^6 + boost::decimal::decimal64_fast { UINT64_C(1428571428571428571), -19 }, // * z^7 + -boost::decimal::decimal64_fast { 125, -3 }, // * z^8 + boost::decimal::decimal64_fast { UINT64_C(1111111111111111111), -19 }, // * z^9 + -boost::decimal::decimal64_fast { 1, -1 }, // * z^10 + boost::decimal::decimal64_fast { UINT64_C(9090909090909090909), -19 - 1 }, // * z^11 + -boost::decimal::decimal64_fast { UINT64_C(8333333333333333333), -19 - 1 }, // * z^12 + boost::decimal::decimal64_fast { UINT64_C(7692307692307692308), -19 - 1 }, // * z^13 + -boost::decimal::decimal64_fast { UINT64_C(7142857142857142857), -19 - 1 }, // * z^14 + boost::decimal::decimal64_fast { UINT64_C(6666666666666666667), -19 - 1 }, // * z^15 + -boost::decimal::decimal64_fast { UINT64_C(6250000000000000000), -19 - 1 }, // * z^16 + boost::decimal::decimal64_fast { UINT64_C(5882352941176470588), -19 - 1 }, // * z^17 + -boost::decimal::decimal64_fast { UINT64_C(5555555555555555556), -19 - 1 }, // * z^18 + boost::decimal::decimal64_fast { UINT64_C(5263157894736842105), -19 - 1 }, // * z^19 + -boost::decimal::decimal64_fast { 5, -2 }, // * z^20 + boost::decimal::decimal64_fast { UINT64_C(4761904761904761905), -19 - 1 }, // * z^21 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // Series[Log[(1 + (z/2))/(1 - (z/2))], {z, 0, 43}] @@ -115,6 +164,48 @@ struct log1p_table_imp -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(150583635067431), UINT64_C(3161586064842759274) }, -35 }, // * z^36 ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(146513807092635), UINT64_C(13545911456276754540) }, -35 }, // * z^37 }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // Series[Log[(1 + (z/2))/(1 - (z/2))], {z, 0, 43}] + // (1), // * z + -::boost::decimal::decimal128_fast { 5, -1 }, // * z^2 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(180700362080917), UINT64_C(7483252092553221458) }, -34 }, // * z^3 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(135525271560688), UINT64_C(1000753050987528192) }, -34 }, // * z^4 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(108420217248550), UINT64_C(8179300070273843200) }, -34 }, // * z^5 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(90350181040458), UINT64_C(12964998083131386532) }, -34 }, // * z^6 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(77443012320393), UINT64_C(3207108039665666332) }, -34 }, // * z^7 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(67762635780344), UINT64_C(500376525493764096) }, -34 }, // * z^8 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(60233454026972), UINT64_C(8643332055420924359) }, -34 }, // * z^9 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(54210108624275), UINT64_C(4089650035136921600) }, -34 }, // * z^10 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(492819169311592), UINT64_C(17054915875379776418) }, -35 }, // * z^11 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(451750905202293), UINT64_C(9484758194528277842) }, -35 }, // * z^12 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(417000835571347), UINT64_C(15850062977145160938) }, -35 }, // * z^13 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(387215061601965), UINT64_C(16035540198328331700) }, -35 }, // * z^14 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(361400724161834), UINT64_C(14966504185106442916) }, -35 }, // * z^15 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(338813178901720), UINT64_C(2501882627468820480) }, -35 }, // * z^16 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(318882991907501), UINT64_C(5610020838860575434) }, -35 }, // * z^17 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(301167270134862), UINT64_C(6323172129685518558) }, -35 }, // * z^18 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(285316361180395), UINT64_C(16670067533954968520) }, -35 }, // * z^19 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(271050543121376), UINT64_C(2001506101975056384) }, -35 }, // * z^20 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(258143374401310), UINT64_C(10690360132218887800) }, -35 }, // * z^21 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(246409584655796), UINT64_C(8527457937689888204) }, -35 }, // * z^22 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(235696124453370), UINT64_C(9760763598982462770) }, -35 }, // * z^23 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(225875452601146), UINT64_C(13965751134118914724) }, -35 }, // * z^24 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(216840434497100), UINT64_C(16358600140547686400) }, -35 }, // * z^25 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(208500417785673), UINT64_C(17148403525427356272) }, -35 }, // * z^26 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(200778180089908), UINT64_C(4215448086457012372) }, -35 }, // * z^27 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(193607530800982), UINT64_C(17241142136018941658) }, -35 }, // * z^28 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(186931409049224), UINT64_C(16646619993397598836) }, -35 }, // * z^29 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(180700362080917), UINT64_C(7483252092553221458) }, -35 }, // * z^30 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(174871318142823), UINT64_C(5456688082434451252) }, -35 }, // * z^31 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(169406589450860), UINT64_C(1250941313734410240) }, -35 }, // * z^32 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(164273056437197), UINT64_C(11833886649696442678) }, -35 }, // * z^33 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(159441495953750), UINT64_C(12028382456285063520) }, -35 }, // * z^34 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(154886024640786), UINT64_C(6414216079331332674) }, -35 }, // * z^35 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(150583635067431), UINT64_C(3161586064842759274) }, -35 }, // * z^36 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(146513807092635), UINT64_C(13545911456276754540) }, -35 }, // * z^37 + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -128,6 +219,13 @@ constexpr typename log1p_table_imp::d64_coeffs_t log1p_table_imp::d64_coef template constexpr typename log1p_table_imp::d128_coeffs_t log1p_table_imp::d128_coeffs; +template +constexpr typename log1p_table_imp::d32_fast_coeffs_t log1p_table_imp::d32_fast_coeffs; + +template +constexpr typename log1p_table_imp::d64_fast_coeffs_t log1p_table_imp::d64_fast_coeffs; + + #endif } //namespace log1p_detail @@ -143,18 +241,36 @@ constexpr auto log1p_series_expansion(decimal32 z2) noexcept return taylor_series_result(z2, log1p_table::d32_coeffs); } +template <> +constexpr auto log1p_series_expansion(decimal32_fast z2) noexcept +{ + return taylor_series_result(z2, log1p_table::d32_fast_coeffs); +} + template <> constexpr auto log1p_series_expansion(decimal64 z2) noexcept { return taylor_series_result(z2, log1p_table::d64_coeffs); } +template <> +constexpr auto log1p_series_expansion(decimal64_fast z2) noexcept +{ + return taylor_series_result(z2, log1p_table::d64_fast_coeffs); +} + template <> constexpr auto log1p_series_expansion(decimal128 z2) noexcept { return taylor_series_result(z2, log1p_table::d128_coeffs); } +template <> +constexpr auto log1p_series_expansion(decimal128_fast z2) noexcept +{ + return taylor_series_result(z2, log1p_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/log_impl.hpp b/include/boost/decimal/detail/cmath/impl/log_impl.hpp index ed0a14e3e..f67fc6eb0 100644 --- a/include/boost/decimal/detail/cmath/impl/log_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/log_impl.hpp @@ -29,6 +29,10 @@ struct log_table_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + public: static constexpr d32_coeffs_t d32_coeffs = {{ @@ -44,6 +48,20 @@ struct log_table_imp ::boost::decimal::decimal32 { UINT64_C(8975758272058823529), - 19 - 6 }, // * z^17 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // Series[Log[(1 + (z/2))/(1 - (z/2))], {z, 0, 17}] + // (1), // * z + ::boost::decimal::decimal32_fast { UINT64_C(8333333333333333333), - 19 - 1 }, // * z^3 + ::boost::decimal::decimal32_fast { UINT64_C(1250000000000000000), - 19 - 1 }, // * z^5 + ::boost::decimal::decimal32_fast { UINT64_C(2232142857142857143), - 19 - 2 }, // * z^7 + ::boost::decimal::decimal32_fast { UINT64_C(4340277777777777778), - 19 - 3 }, // * z^9 + ::boost::decimal::decimal32_fast { UINT64_C(8877840909090909091), - 19 - 4 }, // * z^11 + ::boost::decimal::decimal32_fast { UINT64_C(1878004807692307692), - 19 - 4 }, // * z^13 + ::boost::decimal::decimal32_fast { UINT64_C(4069010416666666667), - 19 - 5 }, // * z^15 + ::boost::decimal::decimal32_fast { UINT64_C(8975758272058823529), - 19 - 6 }, // * z^17 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // Series[Log[(1 + (z/2))/(1 - (z/2))], {z, 0, 23}] @@ -61,6 +79,23 @@ struct log_table_imp ::boost::decimal::decimal64 { UINT64_C(1036602517832880435), - 19 - 7 }, // * z^23 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // Series[Log[(1 + (z/2))/(1 - (z/2))], {z, 0, 23}] + // (1), // * z + ::boost::decimal::decimal64_fast { UINT64_C(8333333333333333333), - 19 - 1 }, // * z^3 + ::boost::decimal::decimal64_fast { UINT64_C(1250000000000000000), - 19 - 1 }, // * z^5 + ::boost::decimal::decimal64_fast { UINT64_C(2232142857142857143), - 19 - 2 }, // * z^7 + ::boost::decimal::decimal64_fast { UINT64_C(4340277777777777778), - 19 - 3 }, // * z^9 + ::boost::decimal::decimal64_fast { UINT64_C(8877840909090909091), - 19 - 4 }, // * z^11 + ::boost::decimal::decimal64_fast { UINT64_C(1878004807692307692), - 19 - 4 }, // * z^13 + ::boost::decimal::decimal64_fast { UINT64_C(4069010416666666667), - 19 - 5 }, // * z^15 + ::boost::decimal::decimal64_fast { UINT64_C(8975758272058823529), - 19 - 6 }, // * z^17 + ::boost::decimal::decimal64_fast { UINT64_C(2007735402960526316), - 19 - 6 }, // * z^19 + ::boost::decimal::decimal64_fast { UINT64_C(4541306268601190476), - 19 - 7 }, // * z^21 + ::boost::decimal::decimal64_fast { UINT64_C(1036602517832880435), - 19 - 7 }, // * z^23 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // Series[Log[(1 + (z/2))/(1 - (z/2))], {z, 0, 43}] @@ -87,6 +122,33 @@ struct log_table_imp ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(120253186771495), UINT64_C(12950434681540257980) }, -47 }, // * z^41 ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(286650038234379), UINT64_C(5345076336561816786) }, -48 }, // * z^43 }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // Series[Log[(1 + (z/2))/(1 - (z/2))], {z, 0, 43}] + // (1), // * z + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(451750905202293), UINT64_C(9484758194528277842) }, -35 }, // * z^3 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(67762635780344), UINT64_C(500376525493764096) }, -35 }, // * z^5 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(121004706750614), UINT64_C(6164027816584450626) }, -36 }, // * z^7 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(235286929792861), UINT64_C(3787056721709964394) }, -37 }, // * z^9 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(481268720030852), UINT64_C(8584740752302634068) }, -38 }, // * z^11 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(101806844621911), UINT64_C(1816002851448634124) }, -38 }, // * z^13 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(220581496680807), UINT64_C(7009130190423632548) }, -39 }, // * z^15 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(486576830913545), UINT64_C(12748560115094843630) }, -40 }, // * z^17 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(108839554283293), UINT64_C(2123490654414259032) }, -40 }, // * z^19 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(246184706116972), UINT64_C(9634423737622849438) }, -41 }, // * z^21 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(56194335091917), UINT64_C(11823550152479764302) }, -41 }, // * z^23 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(129246970711410), UINT64_C(10592095684364861440) }, -42 }, // * z^25 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(299182802572709), UINT64_C(12220910627630811506) }, -43 }, // * z^27 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(69637376460889), UINT64_C(5865971761607874066) }, -43 }, // * z^29 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(162861606239176), UINT64_C(11636108014793143194) }, -44 }, // * z^31 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(382478014652611), UINT64_C(14470401740943906374) }, -45 }, // * z^33 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(90155532025258), UINT64_C(9076666090147568782) }, -45 }, // * z^35 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(213205650059732), UINT64_C(16978042870933143358) }, -46 }, // * z^37 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(505680067449366), UINT64_C(9996854995997550184) }, -47 }, // * z^39 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(120253186771495), UINT64_C(12950434681540257980) }, -47 }, // * z^41 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(286650038234379), UINT64_C(5345076336561816786) }, -48 }, // * z^43 + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -100,6 +162,15 @@ constexpr typename log_table_imp::d64_coeffs_t log_table_imp::d64_coeffs; template constexpr typename log_table_imp::d128_coeffs_t log_table_imp::d128_coeffs; +template +constexpr typename log_table_imp::d32_fast_coeffs_t log_table_imp::d32_fast_coeffs; + +template +constexpr typename log_table_imp::d64_fast_coeffs_t log_table_imp::d64_fast_coeffs; + +template +constexpr typename log_table_imp::d128_fast_coeffs_t log_table_imp::d128_fast_coeffs; + #endif } //namespace log_detail @@ -115,18 +186,36 @@ constexpr auto log_series_expansion(decimal32 z2) noexcept return taylor_series_result(z2, log_table::d32_coeffs); } +template <> +constexpr auto log_series_expansion(decimal32_fast z2) noexcept +{ + return taylor_series_result(z2, log_table::d32_fast_coeffs); +} + template <> constexpr auto log_series_expansion(decimal64 z2) noexcept { return taylor_series_result(z2, log_table::d64_coeffs); } +template <> +constexpr auto log_series_expansion(decimal64_fast z2) noexcept +{ + return taylor_series_result(z2, log_table::d64_fast_coeffs); +} + template <> constexpr auto log_series_expansion(decimal128 z2) noexcept { return taylor_series_result(z2, log_table::d128_coeffs); } +template <> +constexpr auto log_series_expansion(decimal128_fast z2) noexcept +{ + return taylor_series_result(z2, log_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/riemann_zeta_impl.hpp b/include/boost/decimal/detail/cmath/impl/riemann_zeta_impl.hpp new file mode 100644 index 000000000..56b750f85 --- /dev/null +++ b/include/boost/decimal/detail/cmath/impl/riemann_zeta_impl.hpp @@ -0,0 +1,368 @@ +// Copyright 2024 Matt Borland +// Copyright 2024 Christopher Kormanyos +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_CMATH_IMPL_RIEMANN_ZETA_IMPL_HPP +#define BOOST_DECIMAL_DETAIL_CMATH_IMPL_RIEMANN_ZETA_IMPL_HPP + +#include +#include + +#ifndef BOOST_DECIMAL_BUILD_MODULE +#include +#include +#include +#endif + +namespace boost { +namespace decimal { +namespace detail { + +namespace riemann_zeta_detail { + +template +struct prime_table_imp +{ + using decimal_type = T; + + using prime_table_t = std::array; + + static constexpr prime_table_t primes = + {{ + // Table[Prime[n], {n, 1, 36, 1}] + + decimal_type { 2 }, decimal_type { 3 }, decimal_type { 5 }, decimal_type { 7 }, + decimal_type { 11 }, decimal_type { 13 }, decimal_type { 17 }, decimal_type { 19 }, + decimal_type { 23 }, decimal_type { 29 }, decimal_type { 31 }, decimal_type { 37 }, + decimal_type { 41 }, decimal_type { 43 }, decimal_type { 47 }, decimal_type { 53 }, + decimal_type { 59 }, decimal_type { 61 }, decimal_type { 67 }, decimal_type { 71 }, + decimal_type { 73 }, decimal_type { 79 }, decimal_type { 83 }, decimal_type { 89 }, + decimal_type { 97 }, decimal_type { 101 }, decimal_type { 103 }, decimal_type { 107 }, + decimal_type { 109 }, decimal_type { 113 }, decimal_type { 127 }, decimal_type { 131 }, + decimal_type { 137 }, decimal_type { 139 }, decimal_type { 149 }, decimal_type { 151 } + }}; +}; + +template +struct riemann_zeta_table_imp +{ +private: + using d32_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + +public: + static constexpr d32_coeffs_t d32_coeffs = + {{ + // N[Series[Zeta[x], {x, 1, 6}], 19] + + +::boost::decimal::decimal32 { UINT64_C(5772156649015328606), - 19 - 0 }, // EulerGamma + +::boost::decimal::decimal32 { UINT64_C(7281584548367672486), - 19 - 1 }, // * (x - 1) + -::boost::decimal::decimal32 { UINT64_C(4845181596436159242), - 19 - 2 }, // * (x - 1)^2 + -::boost::decimal::decimal32 { UINT64_C(3423057367172243110), - 19 - 3 }, // * (x - 1)^3 + +::boost::decimal::decimal32 { UINT64_C(9689041939447083573), - 19 - 4 }, // * (x - 1)^4 + -::boost::decimal::decimal32 { UINT64_C(6611031810842189181), - 19 - 5 }, // * (x - 1)^5 + -::boost::decimal::decimal32 { UINT64_C(3316240908752772359), - 19 - 6 }, // * (x - 1)^6 + }}; + + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // N[Series[Zeta[x], {x, 1, 6}], 19] + + +::boost::decimal::decimal32_fast { UINT64_C(5772156649015328606), - 19 - 0 }, // EulerGamma + +::boost::decimal::decimal32_fast { UINT64_C(7281584548367672486), - 19 - 1 }, // * (x - 1) + -::boost::decimal::decimal32_fast { UINT64_C(4845181596436159242), - 19 - 2 }, // * (x - 1)^2 + -::boost::decimal::decimal32_fast { UINT64_C(3423057367172243110), - 19 - 3 }, // * (x - 1)^3 + +::boost::decimal::decimal32_fast { UINT64_C(9689041939447083573), - 19 - 4 }, // * (x - 1)^4 + -::boost::decimal::decimal32_fast { UINT64_C(6611031810842189181), - 19 - 5 }, // * (x - 1)^5 + -::boost::decimal::decimal32_fast { UINT64_C(3316240908752772359), - 19 - 6 }, // * (x - 1)^6 + }}; + + static constexpr d64_coeffs_t d64_coeffs = + {{ + // N[Series[Zeta[x], {x, 1, 9}], 19] + + +::boost::decimal::decimal64 { UINT64_C(5772156649015328606), - 19 - 0 }, // EulerGamma + +::boost::decimal::decimal64 { UINT64_C(7281584548367672486), - 19 - 1 }, // * (x - 1) + -::boost::decimal::decimal64 { UINT64_C(4845181596436159242), - 19 - 2 }, // * (x - 1)^2 + -::boost::decimal::decimal64 { UINT64_C(3423057367172243110), - 19 - 3 }, // * (x - 1)^3 + +::boost::decimal::decimal64 { UINT64_C(9689041939447083573), - 19 - 4 }, // * (x - 1)^4 + -::boost::decimal::decimal64 { UINT64_C(6611031810842189181), - 19 - 5 }, // * (x - 1)^5 + -::boost::decimal::decimal64 { UINT64_C(3316240908752772359), - 19 - 6 }, // * (x - 1)^6 + +::boost::decimal::decimal64 { UINT64_C(1046209458447918742), - 19 - 6 }, // * (x - 1)^7 + -::boost::decimal::decimal64 { UINT64_C(8733218100273797361), - 19 - 8 }, // * (x - 1)^8 + +::boost::decimal::decimal64 { UINT64_C(9478277782762358956), - 19 - 10 }, // * (x - 1)^9 + }}; + + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // N[Series[Zeta[x], {x, 1, 9}], 19] + + +::boost::decimal::decimal64_fast { UINT64_C(5772156649015328606), - 19 - 0 }, // EulerGamma + +::boost::decimal::decimal64_fast { UINT64_C(7281584548367672486), - 19 - 1 }, // * (x - 1) + -::boost::decimal::decimal64_fast { UINT64_C(4845181596436159242), - 19 - 2 }, // * (x - 1)^2 + -::boost::decimal::decimal64_fast { UINT64_C(3423057367172243110), - 19 - 3 }, // * (x - 1)^3 + +::boost::decimal::decimal64_fast { UINT64_C(9689041939447083573), - 19 - 4 }, // * (x - 1)^4 + -::boost::decimal::decimal64_fast { UINT64_C(6611031810842189181), - 19 - 5 }, // * (x - 1)^5 + -::boost::decimal::decimal64_fast { UINT64_C(3316240908752772359), - 19 - 6 }, // * (x - 1)^6 + +::boost::decimal::decimal64_fast { UINT64_C(1046209458447918742), - 19 - 6 }, // * (x - 1)^7 + -::boost::decimal::decimal64_fast { UINT64_C(8733218100273797361), - 19 - 8 }, // * (x - 1)^8 + +::boost::decimal::decimal64_fast { UINT64_C(9478277782762358956), - 19 - 10 }, // * (x - 1)^9 + }}; + + static constexpr d128_coeffs_t d128_coeffs = + {{ + // N[Series[Zeta[x], {x, 1, 14}], 36] + + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(312909238939453), UINT64_C(7916302232898517972) }, -34 }, + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(394735489323855), UINT64_C(10282954930524890450) }, -35 }, + -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(262657820647143), UINT64_C(7801536535536173172) }, -36 }, + -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(185564311701532), UINT64_C(15687007158497646588) }, -37 }, + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(525244016002584), UINT64_C(12277750447068982866) }, -38 }, + -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(358384752584293), UINT64_C(18370286456371002882) }, -39 }, + -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(179773779887752), UINT64_C(17772011513518515048) }, -40 }, + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(56715128386205), UINT64_C(15292499466693711883) }, -40 }, + -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(473428701855329), UINT64_C(926484760170384186) }, -42 }, + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(513818468174601), UINT64_C(18105240268308765734) }, -44 }, + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(306743667337648), UINT64_C(15567754919026551912) }, -44 }, + -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(366931412745108), UINT64_C(2220247416524400302) }, -45 }, + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(189307984255553), UINT64_C(8448217616480074192) }, -46 }, + +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(239089604329878), UINT64_C(14831803080673374292) }, -48 }, + -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(130092671757244), UINT64_C(16458215134170057406) }, -48 }, + }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // N[Series[Zeta[x], {x, 1, 14}], 36] + + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(312909238939453), UINT64_C(7916302232898517972) }, -34 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(394735489323855), UINT64_C(10282954930524890450) }, -35 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(262657820647143), UINT64_C(7801536535536173172) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(185564311701532), UINT64_C(15687007158497646588) }, -37 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(525244016002584), UINT64_C(12277750447068982866) }, -38 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(358384752584293), UINT64_C(18370286456371002882) }, -39 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(179773779887752), UINT64_C(17772011513518515048) }, -40 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(56715128386205), UINT64_C(15292499466693711883) }, -40 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(473428701855329), UINT64_C(926484760170384186) }, -42 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(513818468174601), UINT64_C(18105240268308765734) }, -44 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(306743667337648), UINT64_C(15567754919026551912) }, -44 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(366931412745108), UINT64_C(2220247416524400302) }, -45 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(189307984255553), UINT64_C(8448217616480074192) }, -46 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(239089604329878), UINT64_C(14831803080673374292) }, -48 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(130092671757244), UINT64_C(16458215134170057406) }, -48 }, + }}; +}; + +#if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) + +template +constexpr typename riemann_zeta_table_imp::d32_coeffs_t riemann_zeta_table_imp::d32_coeffs; + +template +constexpr typename riemann_zeta_table_imp::d32_fast_coeffs_t riemann_zeta_table_imp::d32_fast_coeffs; + +template +constexpr typename riemann_zeta_table_imp::d64_coeffs_t riemann_zeta_table_imp::d64_coeffs; + +template +constexpr typename riemann_zeta_table_imp::d128_coeffs_t riemann_zeta_table_imp::d128_coeffs; + +template +constexpr typename riemann_zeta_table_imp::d128_fast_coeffs_t riemann_zeta_table_imp::d128_fast_coeffs; + +template +constexpr typename prime_table_imp::prime_table_t prime_table_imp::primes; + +#endif + +} //namespace lgamma_detail + +using riemann_zeta_table = riemann_zeta_detail::riemann_zeta_table_imp; + +template +constexpr auto riemann_zeta_series_or_pade_expansion(T x) noexcept; + +template <> +constexpr auto riemann_zeta_series_or_pade_expansion(decimal32 x) noexcept +{ + constexpr decimal32 one { 1 }; + + const decimal32 dx { x - one }; + + if (fabs(dx) < decimal32 { 5, - 2 }) + { + return one / dx + taylor_series_result(dx, riemann_zeta_table::d32_coeffs); + } + else + { + const decimal32 top = + (decimal32 { UINT64_C(7025346442393055904), -19 + 1 } + + x * (decimal32 { UINT64_C(6331631438687936980), -19 + 1 } + + x * decimal32 { UINT64_C(1671529107642800378), -19 + 1 })); + + const decimal32 bot = + (decimal32 { UINT64_C(1402850698872379326), -19 + 2, true } + + x * (decimal32 { UINT64_C(1302850698872379326), -19 + 2 } + + x * decimal32 { 1 })); + + return top / bot; + } +} + +template <> +constexpr auto riemann_zeta_series_or_pade_expansion(decimal32_fast x) noexcept +{ + constexpr decimal32_fast one { 1 }; + + const decimal32_fast dx { x - one }; + + if (fabs(dx) < decimal32_fast { 5, -2 }) + { + return one / dx + taylor_series_result(dx, riemann_zeta_table::d32_fast_coeffs); + } + else + { + const decimal32_fast top = + (decimal32_fast { UINT64_C(7025346442393055904), -19 + 1 } + + x * (decimal32_fast { UINT64_C(6331631438687936980), -19 + 1 } + + x * decimal32_fast { UINT64_C(1671529107642800378), -19 + 1 })); + + const decimal32_fast bot = + (decimal32_fast { UINT64_C(1402850698872379326), -19 + 2, true } + + x * (decimal32_fast { UINT64_C(1302850698872379326), -19 + 2 } + + x * decimal32_fast { 1 })); + + return top / bot; + } +} + +template <> +constexpr auto riemann_zeta_series_or_pade_expansion(decimal64 x) noexcept +{ + constexpr decimal64 one { 1 }; + + const decimal64 dx { x - one }; + + if (fabs(dx) < decimal64 { 5, -2 }) + { + return one / dx + taylor_series_result(dx, riemann_zeta_table::d64_coeffs); + } + else + { + constexpr decimal64 c0 { UINT64_C(4124764818173475125), - 19 + 5 }; + constexpr decimal64 c1 { UINT64_C(4582078064035558510), - 19 + 5 }; + constexpr decimal64 c2 { UINT64_C(1806662427082674333), - 19 + 5 }; + constexpr decimal64 c3 { UINT64_C(3281232347201801441), - 19 + 4 }; + constexpr decimal64 c4 { UINT64_C(3092253262304078300), - 19 + 3 }; + constexpr decimal64 c5 { UINT64_C(1985384224421766402), - 19 + 2 }; + constexpr decimal64 c6 { UINT64_C(1016070109033501213), - 19 + 1 }; + + constexpr decimal64 d0 { UINT64_C(8249529636338921254), - 19 + 5, true }; + constexpr decimal64 d1 { UINT64_C(5997465199121809585), - 19 + 5 }; + constexpr decimal64 d2 { UINT64_C(1915568444415559307), - 19 + 5 }; + constexpr decimal64 d3 { UINT64_C(3021354370625514285), - 19 + 4 }; + constexpr decimal64 d4 { UINT64_C(3227310996533313801), - 19 + 3 }; + constexpr decimal64 d5 { UINT64_C(1987445773667795184), - 19 + 2 }; + + const decimal64 top { c0 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * (c5+ x * c6))))) }; + const decimal64 bot { d0 + x * (d1 + x * (d2 + x * (d3 + x * (d4 + x * (d5 + x))))) }; + + return top / bot; + } +} + +template <> +constexpr auto riemann_zeta_series_or_pade_expansion(decimal64_fast x) noexcept +{ + constexpr decimal64_fast one { 1 }; + + const decimal64_fast dx { x - one }; + + if (fabs(dx) < decimal64_fast { 5, -2 }) + { + return one / dx + taylor_series_result(dx, riemann_zeta_table::d64_fast_coeffs); + } + else + { + constexpr decimal64_fast c0 { UINT64_C(4124764818173475125), - 19 + 5 }; + constexpr decimal64_fast c1 { UINT64_C(4582078064035558510), - 19 + 5 }; + constexpr decimal64_fast c2 { UINT64_C(1806662427082674333), - 19 + 5 }; + constexpr decimal64_fast c3 { UINT64_C(3281232347201801441), - 19 + 4 }; + constexpr decimal64_fast c4 { UINT64_C(3092253262304078300), - 19 + 3 }; + constexpr decimal64_fast c5 { UINT64_C(1985384224421766402), - 19 + 2 }; + constexpr decimal64_fast c6 { UINT64_C(1016070109033501213), - 19 + 1 }; + + constexpr decimal64_fast d0 { UINT64_C(8249529636338921254), - 19 + 5, true }; + constexpr decimal64_fast d1 { UINT64_C(5997465199121809585), - 19 + 5 }; + constexpr decimal64_fast d2 { UINT64_C(1915568444415559307), - 19 + 5 }; + constexpr decimal64_fast d3 { UINT64_C(3021354370625514285), - 19 + 4 }; + constexpr decimal64_fast d4 { UINT64_C(3227310996533313801), - 19 + 3 }; + constexpr decimal64_fast d5 { UINT64_C(1987445773667795184), - 19 + 2 }; + + const decimal64_fast top { c0 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * (c5+ x * c6))))) }; + const decimal64_fast bot { d0 + x * (d1 + x * (d2 + x * (d3 + x * (d4 + x * (d5 + x))))) }; + + return top / bot; + } +} + +template <> +constexpr auto riemann_zeta_series_or_pade_expansion(decimal128 x) noexcept +{ + constexpr decimal128 one { 1 }; + + const decimal128 dx { x - one }; + + return one / dx + taylor_series_result(dx, riemann_zeta_table::d128_coeffs); +} + +template <> +constexpr auto riemann_zeta_series_or_pade_expansion(decimal128_fast x) noexcept +{ + constexpr decimal128_fast one { 1 }; + + const decimal128_fast dx { x - one }; + + return one / dx + taylor_series_result(dx, riemann_zeta_table::d128_fast_coeffs); +} + +template +using prime_table_t = typename riemann_zeta_detail::prime_table_imp::prime_table_t; + +template +using prime_table = riemann_zeta_detail::prime_table_imp; + +template +constexpr auto riemann_zeta_decimal_order(T x) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +{ + int n { }; + + const T fr10 = frexp10(x, &n); + + constexpr int order_bias + { + std::numeric_limits::digits10 < 10 ? 6 + : std::numeric_limits::digits10 < 20 ? 15 + : 33 + }; + + return n + order_bias; +} + +template +constexpr auto riemann_zeta_factorial(int nf) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +{ + return { (nf <= 1) ? T { 1 } : riemann_zeta_factorial(nf - 1) * nf }; +} + +} //namespace detail +} //namespace decimal +} //namespace boost + +#endif //BOOST_DECIMAL_DETAIL_CMATH_IMPL_RIEMANN_ZETA_IMPL_HPP diff --git a/include/boost/decimal/detail/cmath/impl/sin_impl.hpp b/include/boost/decimal/detail/cmath/impl/sin_impl.hpp index c7261a259..cc246b26e 100644 --- a/include/boost/decimal/detail/cmath/impl/sin_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/sin_impl.hpp @@ -37,6 +37,16 @@ struct sin_table_imp { decimal32 {UINT64_C(60055992690454536), -24} }}; + static constexpr std::array d32_fast_coeffs = + {{ + decimal32_fast {UINT64_C(76426704684128569), -19}, + decimal32_fast {UINT64_C(8163484279370784), -19}, + decimal32_fast {UINT64_C(16704305092800237), -17, true}, + decimal32_fast {UINT64_C(74622903795259856), -21}, + decimal32_fast {UINT64_C(9999946918542727), -16}, + decimal32_fast {UINT64_C(60055992690454536), -24} + }}; + // 11th Degree Remez Polynomial // Estimated max error: 5.2301715421592162270336342660001217e-18 static constexpr std::array d64_coeffs = @@ -55,32 +65,21 @@ struct sin_table_imp { decimal64 {UINT64_C(5230171542159216227), -36, true} }}; - // 20th Degree Remez Polynomial - // Estimated max error: 5.1424960359035132189835410157248994e-35 - static constexpr std::array d128_coeffs = + static constexpr std::array d64_fast_coeffs = {{ - decimal128 {uint128{UINT64_C(85106305874239), UINT64_C(16929064868128953896)}, -52}, - decimal128 {uint128{UINT64_C(477768502693008), UINT64_C(6230918648367889942)}, -51, true}, - decimal128 {uint128{UINT64_C(75154315253822), UINT64_C(13833706134005544038)}, -51}, - decimal128 {uint128{UINT64_C(152287788904364), UINT64_C(1676311666321267536)}, -48}, - decimal128 {uint128{UINT64_C(144214752508825), UINT64_C(2528999524738537100)}, -51}, - decimal128 {uint128{UINT64_C(414554872884779), UINT64_C(15931857976032858760)}, -46, true}, - decimal128 {uint128{UINT64_C(90156974414685), UINT64_C(14279793832049340120)}, -51}, - decimal128 {uint128{UINT64_C(87056250588597), UINT64_C(16057379721599586648)}, -43}, - decimal128 {uint128{UINT64_C(210637815468175), UINT64_C(7636003443272702110)}, -52}, - decimal128 {uint128{UINT64_C(135807751684903), UINT64_C(10512681453991690152)}, -41, true}, - decimal128 {uint128{UINT64_C(189273977706970), UINT64_C(1683985612936918840)}, -53}, - decimal128 {uint128{UINT64_C(149388526852609), UINT64_C(16550971142245619806)}, -39}, - decimal128 {uint128{UINT64_C(62386708229102), UINT64_C(17615400106141663882)}, -54}, - decimal128 {uint128{UINT64_C(107559739333879), UINT64_C(7530156268905159646)}, -37, true}, - decimal128 {uint128{UINT64_C(66059193820724), UINT64_C(9642511815583692046)}, -56}, - decimal128 {uint128{UINT64_C(451750905202293), UINT64_C(9484757435910730332)}, -36}, - decimal128 {uint128{UINT64_C(170869449273575), UINT64_C(3295407555488151196)}, -59}, - decimal128 {uint128{UINT64_C(90350181040458), UINT64_C(12964998083139403502)}, -34, true}, - decimal128 {uint128{UINT64_C(58541029533765), UINT64_C(17525845691359836026)}, -62}, - decimal128 {uint128{UINT64_C(542101086242752), UINT64_C(4003012203950106990)}, -34}, - decimal128 {uint128{UINT64_C(278775268706234), UINT64_C(3358921116451750765)}, -68} - }}; + decimal64_fast {UINT64_C(2306518628003855678), -26, true}, + decimal64_fast {UINT64_C(5453073257634027470), -27, true}, + decimal64_fast {UINT64_C(2762996699568163845), -24}, + decimal64_fast {UINT64_C(5023027013521532307), -27, true}, + decimal64_fast {UINT64_C(1984096861383546182), -22, true}, + decimal64_fast {UINT64_C(1026912296061211491), -27, true}, + decimal64_fast {UINT64_C(8333333562151404340), -21}, + decimal64_fast {UINT64_C(3217043986646625014), -29, true}, + decimal64_fast {UINT64_C(1666666666640042905), -19, true}, + decimal64_fast {UINT64_C(1135995742940218051), -31, true}, + decimal64_fast {UINT64_C(1000000000000001896), -18}, + decimal64_fast {UINT64_C(5230171542159216227), -36, true} + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -92,7 +91,10 @@ template constexpr std::array sin_table_imp::d64_coeffs; template -constexpr std::array sin_table_imp::d128_coeffs; +constexpr std::array sin_table_imp::d32_fast_coeffs; + +template +constexpr std::array sin_table_imp::d64_fast_coeffs; #endif @@ -112,6 +114,15 @@ constexpr auto sin_series_expansion(decimal32 x) noexcept return b_neg ? -result : result; } +template <> +constexpr auto sin_series_expansion(decimal32_fast x) noexcept +{ + const auto b_neg = signbit(x); + x = abs(x); + auto result = remez_series_result(x, sin_detail::sin_table::d32_fast_coeffs); + return b_neg ? -result : result; +} + template <> constexpr auto sin_series_expansion(decimal64 x) noexcept { @@ -122,11 +133,85 @@ constexpr auto sin_series_expansion(decimal64 x) noexcept } template <> -constexpr auto sin_series_expansion(decimal128 x) noexcept +constexpr auto sin_series_expansion(decimal64_fast x) noexcept { const auto b_neg = signbit(x); x = abs(x); - auto result = remez_series_result(x, sin_detail::sin_table::d128_coeffs); + auto result = remez_series_result(x, sin_detail::sin_table::d64_fast_coeffs); + return b_neg ? -result : result; +} + +template <> +constexpr auto sin_series_expansion(decimal128 x) noexcept +{ + const bool b_neg { signbit(x) }; + + x = abs(x); + + // PadeApproximant[Sin[x], {x, 0, {14, 13}}] + // FullSimplify[%] + // HornerForm[Numerator[Out[2]]] + // HornerForm[Denominator[Out[2]]] + + constexpr decimal128 c0 { boost::decimal::detail::uint128 { UINT64_C(72470724512963), UINT64_C(12010094287581601792) }, -1 }; + constexpr decimal128 c1 { boost::decimal::detail::uint128 { UINT64_C(111100426260665), UINT64_C(12001293056709775360) }, -2, true }; + constexpr decimal128 c2 { boost::decimal::detail::uint128 { UINT64_C(448976101608303), UINT64_C(8651619847551332352) }, -4 }; + constexpr decimal128 c3 { boost::decimal::detail::uint128 { UINT64_C(73569920121966), UINT64_C(7922026052315602944) }, -5, true }; + constexpr decimal128 c4 { boost::decimal::detail::uint128 { UINT64_C(56791565109495), UINT64_C(18025512837605806080) }, -7 }; + constexpr decimal128 c5 { boost::decimal::detail::uint128 { UINT64_C(208944907042123), UINT64_C(1905626912845279232) }, -10, true }; + constexpr decimal128 c6 { boost::decimal::detail::uint128 { UINT64_C(301324799882787), UINT64_C(8861120840873566208) }, -13 }; + + constexpr decimal128 d1 { boost::decimal::detail::uint128 { UINT64_C(96841145942737), UINT64_C(12517245955660587008) }, -3 }; + constexpr decimal128 d2 { boost::decimal::detail::uint128 { UINT64_C(64553072381691), UINT64_C(13718792646062137344) }, -5 }; + constexpr decimal128 d3 { boost::decimal::detail::uint128 { UINT64_C(279090388104865), UINT64_C(5072548100861788160) }, -8 }; + constexpr decimal128 d4 { boost::decimal::detail::uint128 { UINT64_C(84086452204639), UINT64_C(9046779044634853376) }, -10 }; + constexpr decimal128 d5 { boost::decimal::detail::uint128 { UINT64_C(171178955723736), UINT64_C(18053324302671642624) }, -13 }; + constexpr decimal128 d6 { boost::decimal::detail::uint128 { UINT64_C(189091057352841), UINT64_C(2258222749986258944) }, -16 }; + + const decimal128 x2 { x * x }; + + const decimal128 top { x * (c0 + x2 * (c1 + x2 * (c2 + x2 * (c3 + x2 * (c4 + x2 * (c5 + x2 * c6)))))) }; + const decimal128 bot { c0 + x2 * (d1 + x2 * (d2 + x2 * (d3 + x2 * (d4 + x2 * (d5 + x2 * d6))))) }; + + const decimal128 result { top / bot }; + + return b_neg ? -result : result; +} + +template <> +constexpr auto sin_series_expansion(decimal128_fast x) noexcept +{ + const bool b_neg { signbit(x) }; + + x = abs(x); + + // PadeApproximant[Sin[x], {x, 0, {14, 13}}] + // FullSimplify[%] + // HornerForm[Numerator[Out[2]]] + // HornerForm[Denominator[Out[2]]] + + constexpr decimal128_fast c0 { boost::decimal::detail::uint128 { UINT64_C(72470724512963), UINT64_C(12010094287581601792) }, -1 }; + constexpr decimal128_fast c1 { boost::decimal::detail::uint128 { UINT64_C(111100426260665), UINT64_C(12001293056709775360) }, -2, true }; + constexpr decimal128_fast c2 { boost::decimal::detail::uint128 { UINT64_C(448976101608303), UINT64_C(8651619847551332352) }, -4 }; + constexpr decimal128_fast c3 { boost::decimal::detail::uint128 { UINT64_C(73569920121966), UINT64_C(7922026052315602944) }, -5, true }; + constexpr decimal128_fast c4 { boost::decimal::detail::uint128 { UINT64_C(56791565109495), UINT64_C(18025512837605806080) }, -7 }; + constexpr decimal128_fast c5 { boost::decimal::detail::uint128 { UINT64_C(208944907042123), UINT64_C(1905626912845279232) }, -10, true }; + constexpr decimal128_fast c6 { boost::decimal::detail::uint128 { UINT64_C(301324799882787), UINT64_C(8861120840873566208) }, -13 }; + + constexpr decimal128_fast d1 { boost::decimal::detail::uint128 { UINT64_C(96841145942737), UINT64_C(12517245955660587008) }, -3 }; + constexpr decimal128_fast d2 { boost::decimal::detail::uint128 { UINT64_C(64553072381691), UINT64_C(13718792646062137344) }, -5 }; + constexpr decimal128_fast d3 { boost::decimal::detail::uint128 { UINT64_C(279090388104865), UINT64_C(5072548100861788160) }, -8 }; + constexpr decimal128_fast d4 { boost::decimal::detail::uint128 { UINT64_C(84086452204639), UINT64_C(9046779044634853376) }, -10 }; + constexpr decimal128_fast d5 { boost::decimal::detail::uint128 { UINT64_C(171178955723736), UINT64_C(18053324302671642624) }, -13 }; + constexpr decimal128_fast d6 { boost::decimal::detail::uint128 { UINT64_C(189091057352841), UINT64_C(2258222749986258944) }, -16 }; + + const decimal128_fast x2 { x * x }; + + const decimal128_fast top { x * (c0 + x2 * (c1 + x2 * (c2 + x2 * (c3 + x2 * (c4 + x2 * (c5 + x2 * c6)))))) }; + const decimal128_fast bot { c0 + x2 * (d1 + x2 * (d2 + x2 * (d3 + x2 * (d4 + x2 * (d5 + x2 * d6))))) }; + + const decimal128_fast result { top / bot }; + return b_neg ? -result : result; } diff --git a/include/boost/decimal/detail/cmath/impl/sinh_impl.hpp b/include/boost/decimal/detail/cmath/impl/sinh_impl.hpp index 69d390ba7..72d09d62c 100644 --- a/include/boost/decimal/detail/cmath/impl/sinh_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/sinh_impl.hpp @@ -29,6 +29,10 @@ struct sinh_table_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + public: static constexpr d32_coeffs_t d32_coeffs = {{ @@ -42,6 +46,18 @@ struct sinh_table_imp ::boost::decimal::decimal32 { UINT64_C(1605904383682161460), - 19 - 9 }, // * x^13 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // Series[Sinh[x], {x, 0, 13}] + // (1), // * x + ::boost::decimal::decimal32_fast { UINT64_C(1666666666666666667), - 19 - 0 }, // * x^3 + ::boost::decimal::decimal32_fast { UINT64_C(8333333333333333333), - 19 - 2 }, // * x^5 + ::boost::decimal::decimal32_fast { UINT64_C(1984126984126984127), - 19 - 3 }, // * x^7 + ::boost::decimal::decimal32_fast { UINT64_C(2755731922398589065), - 19 - 5 }, // * x^9 + ::boost::decimal::decimal32_fast { UINT64_C(2505210838544171878), - 19 - 7 }, // * x^11 + ::boost::decimal::decimal32_fast { UINT64_C(1605904383682161460), - 19 - 9 }, // * x^13 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // Series[Sinh[x], {x, 0, 19}] @@ -57,6 +73,21 @@ struct sinh_table_imp ::boost::decimal::decimal64 { UINT64_C(8220635246624329717), - 19 - 17 } // * x^19 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // Series[Sinh[x], {x, 0, 19}] + // (1), // * x + ::boost::decimal::decimal64_fast { UINT64_C(1666666666666666667), - 19 - 0 }, // * x^3 + ::boost::decimal::decimal64_fast { UINT64_C(8333333333333333333), - 19 - 2 }, // * x^5 + ::boost::decimal::decimal64_fast { UINT64_C(1984126984126984127), - 19 - 3 }, // * x^7 + ::boost::decimal::decimal64_fast { UINT64_C(2755731922398589065), - 19 - 5 }, // * x^9 + ::boost::decimal::decimal64_fast { UINT64_C(2505210838544171878), - 19 - 7 }, // * x^11 + ::boost::decimal::decimal64_fast { UINT64_C(1605904383682161460), - 19 - 9 }, // * x^13 + ::boost::decimal::decimal64_fast { UINT64_C(7647163731819816476), - 19 - 12 }, // * x^15 + ::boost::decimal::decimal64_fast { UINT64_C(2811457254345520763), - 19 - 14 }, // * x^17 + ::boost::decimal::decimal64_fast { UINT64_C(8220635246624329717), - 19 - 17 } // * x^19 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // Series[Sinh[x], {x, 0, 34}] @@ -79,6 +110,29 @@ struct sinh_table_imp ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(62430180495565), UINT64_C(13726064643322746782) }, -70 }, // * x^33 ::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(524623365508955), UINT64_C(15360627863698201590) }, -74 }, // * x^35 }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // Series[Sinh[x], {x, 0, 34}] + // (1), // * x + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(90350181040458), UINT64_C(12964998083131386532) }, -34 }, // * x^3 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(451750905202293), UINT64_C(9484758194528277842) }, -36 }, // * x^5 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(107559739333879), UINT64_C(7528774067376128516) }, -37 }, // * x^7 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(149388526852610), UINT64_C(5332535073103080820) }, -39 }, // * x^9 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(135807751684191), UINT64_C(3170782423392841514) }, -41 }, // * x^11 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(87056251079609), UINT64_C(13384395342406417346) }, -43 }, // * x^13 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(414553576569570), UINT64_C(2246069003855862950) }, -46 }, // * x^15 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(152409403150577), UINT64_C(4623619737181327888) }, -48 }, // * x^17 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(445641529680050), UINT64_C(8125571139796411480) }, -51 }, // * x^19 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(106105126114297), UINT64_C(13354072793200296588) }, -53 }, // * x^21 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(209693925127070), UINT64_C(11079921506407677690) }, -56 }, // * x^23 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(349489875211784), UINT64_C(6168706461539761736) }, -59 }, // * x^25 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(497848825088011), UINT64_C(16092452014289198134) }, -62 }, // * x^27 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(61311431661085), UINT64_C(3799242275031630472) }, -64 }, // * x^29 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(65926270603317), UINT64_C(7853896396813382020) }, -67 }, // * x^31 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(62430180495565), UINT64_C(13726064643322746782) }, -70 }, // * x^33 + ::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(524623365508955), UINT64_C(15360627863698201590) }, -74 }, // * x^35 + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -92,6 +146,15 @@ constexpr typename sinh_table_imp::d64_coeffs_t sinh_table_imp::d64_coeffs template constexpr typename sinh_table_imp::d128_coeffs_t sinh_table_imp::d128_coeffs; +template +constexpr typename sinh_table_imp::d32_fast_coeffs_t sinh_table_imp::d32_fast_coeffs; + +template +constexpr typename sinh_table_imp::d64_fast_coeffs_t sinh_table_imp::d64_fast_coeffs; + +template +constexpr typename sinh_table_imp::d128_fast_coeffs_t sinh_table_imp::d128_fast_coeffs; + #endif } //namespace sinh_detail @@ -107,18 +170,36 @@ constexpr auto sinh_series_expansion(decimal32 z2) noexcept return taylor_series_result(z2, sinh_table::d32_coeffs); } +template <> +constexpr auto sinh_series_expansion(decimal32_fast z2) noexcept +{ + return taylor_series_result(z2, sinh_table::d32_fast_coeffs); +} + template <> constexpr auto sinh_series_expansion(decimal64 z2) noexcept { return taylor_series_result(z2, sinh_table::d64_coeffs); } +template <> +constexpr auto sinh_series_expansion(decimal64_fast z2) noexcept +{ + return taylor_series_result(z2, sinh_table::d64_fast_coeffs); +} + template <> constexpr auto sinh_series_expansion(decimal128 z2) noexcept { return taylor_series_result(z2, sinh_table::d128_coeffs); } +template <> +constexpr auto sinh_series_expansion(decimal128_fast z2) noexcept +{ + return taylor_series_result(z2, sinh_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/tanh_impl.hpp b/include/boost/decimal/detail/cmath/impl/tanh_impl.hpp index 66a022652..6003e372b 100644 --- a/include/boost/decimal/detail/cmath/impl/tanh_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/tanh_impl.hpp @@ -29,6 +29,10 @@ struct tanh_table_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + public: static constexpr d32_coeffs_t d32_coeffs = {{ @@ -43,6 +47,19 @@ struct tanh_table_imp -::boost::decimal::decimal32 { UINT64_C(1455834387051318268), - 19 - 2 }, // * x^15 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // Series[Tanh[x], {x, 0, 15}] + // (1), // * x + -::boost::decimal::decimal32_fast { UINT64_C(3333333333333333333), - 19 - 0 }, // * x^3 + +::boost::decimal::decimal32_fast { UINT64_C(1333333333333333333), - 19 - 0 }, // * x^5 + -::boost::decimal::decimal32_fast { UINT64_C(5396825396825396825), - 19 - 1 }, // * x^7 + +::boost::decimal::decimal32_fast { UINT64_C(2186948853615520282), - 19 - 1 }, // * x^9 + -::boost::decimal::decimal32_fast { UINT64_C(8863235529902196569), - 19 - 2 }, // * x^11 + +::boost::decimal::decimal32_fast { UINT64_C(3592128036572481017), - 19 - 2 }, // * x^13 + -::boost::decimal::decimal32_fast { UINT64_C(1455834387051318268), - 19 - 2 }, // * x^15 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // Series[Tanh[x], {x, 0, 23}] @@ -60,6 +77,23 @@ struct tanh_table_imp -::boost::decimal::decimal64 { UINT64_C(3927832388331683405), - 19 - 4 } // * x^23 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // Series[Tanh[x], {x, 0, 23}] + // (1), // * x + -::boost::decimal::decimal64_fast { UINT64_C(3333333333333333333), - 19 - 0 }, // * x^3 + +::boost::decimal::decimal64_fast { UINT64_C(1333333333333333333), - 19 - 0 }, // * x^5 + -::boost::decimal::decimal64_fast { UINT64_C(5396825396825396825), - 19 - 1 }, // * x^7 + +::boost::decimal::decimal64_fast { UINT64_C(2186948853615520282), - 19 - 1 }, // * x^9 + -::boost::decimal::decimal64_fast { UINT64_C(8863235529902196569), - 19 - 2 }, // * x^11 + +::boost::decimal::decimal64_fast { UINT64_C(3592128036572481017), - 19 - 2 }, // * x^13 + -::boost::decimal::decimal64_fast { UINT64_C(1455834387051318268), - 19 - 2 }, // * x^15 + +::boost::decimal::decimal64_fast { UINT64_C(5900274409455859814), - 19 - 3 }, // * x^17 + -::boost::decimal::decimal64_fast { UINT64_C(2391291142435524815), - 19 - 3 } // * x^19 + +::boost::decimal::decimal64_fast { UINT64_C(9691537956929450326), - 19 - 4 } // * x^21 + -::boost::decimal::decimal64_fast { UINT64_C(3927832388331683405), - 19 - 4 } // * x^23 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // Series[Tanh[x], {x, 0, 45}] @@ -87,6 +121,34 @@ struct tanh_table_imp -::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(254586683669781), UINT64_C(685051056553551574) }, -42 }, // * x^43 +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(103180096515998), UINT64_C(10260917890244709612) }, -42 }, // * x^45 }}; + + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // Series[Tanh[x], {x, 0, 45}] + // (1), // * x + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(180700362080917), UINT64_C(7483252092553221458) }, -34 }, // * x^3 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(72280144832366), UINT64_C(17750696095988929874) }, -34 }, // * x^5 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(292562490988151), UINT64_C(18264656174417923374) }, -35 }, // * x^7 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(118554734910231), UINT64_C(9692136079832632224) }, -35 }, // * x^9 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(480476960838533), UINT64_C(11637084576724682862) }, -36 }, // * x^11 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(194729651054898), UINT64_C(12399216788389290642) }, -36 }, // * x^13 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(78920940261007), UINT64_C(1837289537202064798) }, -36 }, // * x^15 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(319854516649633), UINT64_C(9164231904936043282) }, -37 }, // * x^17 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(129632152583696), UINT64_C(18287072186516798384) }, -37 }, // * x^19 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(525379325381431), UINT64_C(15812750027817946304) }, -38 }, // * x^21 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(212928220429406), UINT64_C(17197028753568092234) }, -38 }, // * x^23 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(86296557298784), UINT64_C(15970280607199622056) }, -38 }, // * x^25 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(349746773190745), UINT64_C(16747878178724308630) }, -39 }, // * x^27 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(141747028139092), UINT64_C(18150782588055037068) }, -39 }, // * x^29 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(57447906675346), UINT64_C(7219349788452465642) }, -39 }, // * x^31 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(232827596084826), UINT64_C(637032669719018454) }, -40 }, // * x^33 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(94361470479658), UINT64_C(14765552217602302942) }, -40 }, // * x^35 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(382432635169221), UINT64_C(13797141926350469414) }, -41 }, // * x^37 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(154994109035215), UINT64_C(9538263982529984200) }, -41 }, // * x^39 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(62816746340150), UINT64_C(7041711327684726779) }, -41 }, // * x^41 + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(254586683669781), UINT64_C(685051056553551574) }, -42 }, // * x^43 + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(103180096515998), UINT64_C(10260917890244709612) }, -42 }, // * x^45 + }}; }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -100,6 +162,12 @@ constexpr typename tanh_table_imp::d64_coeffs_t tanh_table_imp::d64_coeffs template constexpr typename tanh_table_imp::d128_coeffs_t tanh_table_imp::d128_coeffs; +template +constexpr typename tanh_table_imp::d32_fast_coeffs_t tanh_table_imp::d32_fast_coeffs; + +template +constexpr typename tanh_table_imp::d64_fast_coeffs_t tanh_table_imp::d64_fast_coeffs; + #endif } //namespace tanh_detail @@ -115,18 +183,36 @@ constexpr auto tanh_series_expansion(decimal32 z2) noexcept return taylor_series_result(z2, tanh_table::d32_coeffs); } +template <> +constexpr auto tanh_series_expansion(decimal32_fast z2) noexcept +{ + return taylor_series_result(z2, tanh_table::d32_fast_coeffs); +} + template <> constexpr auto tanh_series_expansion(decimal64 z2) noexcept { return taylor_series_result(z2, tanh_table::d64_coeffs); } +template <> +constexpr auto tanh_series_expansion(decimal64_fast z2) noexcept +{ + return taylor_series_result(z2, tanh_table::d64_fast_coeffs); +} + template <> constexpr auto tanh_series_expansion(decimal128 z2) noexcept { return taylor_series_result(z2, tanh_table::d128_coeffs); } +template <> +constexpr auto tanh_series_expansion(decimal128_fast z2) noexcept +{ + return taylor_series_result(z2, tanh_table::d128_fast_coeffs); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/impl/tgamma_impl.hpp b/include/boost/decimal/detail/cmath/impl/tgamma_impl.hpp index 7fc61d3cd..52ad606ad 100644 --- a/include/boost/decimal/detail/cmath/impl/tgamma_impl.hpp +++ b/include/boost/decimal/detail/cmath/impl/tgamma_impl.hpp @@ -28,10 +28,18 @@ struct tgamma_table_imp using d64_coeffs_t = std::array; using d128_coeffs_t = std::array; + using d32_fast_coeffs_t = std::array; + using d64_fast_coeffs_t = std::array; + using d128_fast_coeffs_t = std::array; + using d32_coeffs_asymp_t = std::array; using d64_coeffs_asymp_t = std::array; using d128_coeffs_asymp_t = std::array; + using d32_fast_coeffs_asymp_t = std::array; + using d64_fast_coeffs_asymp_t = std::array; + using d128_fast_coeffs_asymp_t = std::array; + static constexpr d32_coeffs_t d32_coeffs = {{ // N[Series[1/Gamma[z], {z, 0, 16}], 19] @@ -52,6 +60,26 @@ struct tgamma_table_imp +::boost::decimal::decimal32 { UINT64_C(6'116'095'104'481'415'818), - 19 - 8 }, // * z^16 }}; + static constexpr d32_fast_coeffs_t d32_fast_coeffs = + {{ + // N[Series[1/Gamma[z], {z, 0, 16}], 19] + +::boost::decimal::decimal32_fast { UINT64_C(5'772'156'649'015'328'606), - 19 - 0 }, // * z^2 + -::boost::decimal::decimal32_fast { UINT64_C(6'558'780'715'202'538'811), - 19 - 0 }, // * z^3 + -::boost::decimal::decimal32_fast { UINT64_C(4'200'263'503'409'523'553), - 19 - 1 }, // * z^4 + +::boost::decimal::decimal32_fast { UINT64_C(1'665'386'113'822'914'895), - 19 - 0 }, // * z^5 + -::boost::decimal::decimal32_fast { UINT64_C(4'219'773'455'554'433'675), - 19 - 1 }, // * z^6 + -::boost::decimal::decimal32_fast { UINT64_C(9'621'971'527'876'973'562), - 19 - 2 }, // * z^7 + +::boost::decimal::decimal32_fast { UINT64_C(7'218'943'246'663'099'542), - 19 - 2 }, // * z^8 + -::boost::decimal::decimal32_fast { UINT64_C(1'165'167'591'859'065'112), - 19 - 2 }, // * z^9 + -::boost::decimal::decimal32_fast { UINT64_C(2'152'416'741'149'509'728), - 19 - 3 }, // * z^10 + +::boost::decimal::decimal32_fast { UINT64_C(1'280'502'823'881'161'862), - 19 - 3 }, // * z^11 + -::boost::decimal::decimal32_fast { UINT64_C(2'013'485'478'078'823'866), - 19 - 4 }, // * z^12 + -::boost::decimal::decimal32_fast { UINT64_C(1'250'493'482'142'670'657), - 19 - 5 }, // * z^13 + +::boost::decimal::decimal32_fast { UINT64_C(1'133'027'231'981'695'882), - 19 - 5 }, // * z^14 + -::boost::decimal::decimal32_fast { UINT64_C(2'056'338'416'977'607'103), - 19 - 6 }, // * z^15 + +::boost::decimal::decimal32_fast { UINT64_C(6'116'095'104'481'415'818), - 19 - 8 }, // * z^16 + }}; + static constexpr d32_coeffs_asymp_t d32_coeffs_asymp = {{ // N[Series[Gamma[x] Sqrt[x], {x, Infinity, 7}], 19] @@ -67,6 +95,21 @@ struct tgamma_table_imp +::boost::decimal::decimal32 { UINT64_C(2104311229753206373), - 19 - 2 }, // / x^9 }}; + static constexpr d32_fast_coeffs_asymp_t d32_fast_coeffs_asymp = + {{ + // N[Series[Gamma[x] Sqrt[x], {x, Infinity, 7}], 19] + +::boost::decimal::decimal32_fast { UINT64_C(2506628274631000502), - 19 + 1 }, + +::boost::decimal::decimal32_fast { UINT64_C(2088856895525833752), - 19 - 0 }, // / x + +::boost::decimal::decimal32_fast { UINT64_C(8703570398024307300), - 19 - 2 }, // / x^2 + -::boost::decimal::decimal32_fast { UINT64_C(6721090474029881748), - 19 - 2 }, // / x^3 + -::boost::decimal::decimal32_fast { UINT64_C(5752012381101712348), - 19 - 3 }, // / x^4 + +::boost::decimal::decimal32_fast { UINT64_C(1965294881583203064), - 19 - 2 }, // / x^5 + +::boost::decimal::decimal32_fast { UINT64_C(1747825212045591212), - 19 - 3 }, // / x^6 + -::boost::decimal::decimal32_fast { UINT64_C(1484341135158276145), - 19 - 2 }, // / x^7 + -::boost::decimal::decimal32_fast { UINT64_C(1296375732112554321), - 19 - 3 }, // / x^8 + +::boost::decimal::decimal32_fast { UINT64_C(2104311229753206373), - 19 - 2 }, // / x^9 + }}; + static constexpr d64_coeffs_t d64_coeffs = {{ // N[Series[1/Gamma[z], {z, 0, 27}], 19] @@ -98,6 +141,37 @@ struct tgamma_table_imp +::boost::decimal::decimal64 { UINT64_C(1'186'692'254'751'600'333), - 19 - 17 }, // * z^27 }}; + static constexpr d64_fast_coeffs_t d64_fast_coeffs = + {{ + // N[Series[1/Gamma[z], {z, 0, 27}], 19] + +::boost::decimal::decimal64_fast { UINT64_C(5'772'156'649'015'328'606), - 19 - 0 }, // * z^2 + -::boost::decimal::decimal64_fast { UINT64_C(6'558'780'715'202'538'811), - 19 - 0 }, // * z^3 + -::boost::decimal::decimal64_fast { UINT64_C(4'200'263'503'409'523'553), - 19 - 1 }, // * z^4 + +::boost::decimal::decimal64_fast { UINT64_C(1'665'386'113'822'914'895), - 19 - 0 }, // * z^5 + -::boost::decimal::decimal64_fast { UINT64_C(4'219'773'455'554'433'675), - 19 - 1 }, // * z^6 + -::boost::decimal::decimal64_fast { UINT64_C(9'621'971'527'876'973'562), - 19 - 2 }, // * z^7 + +::boost::decimal::decimal64_fast { UINT64_C(7'218'943'246'663'099'542), - 19 - 2 }, // * z^8 + -::boost::decimal::decimal64_fast { UINT64_C(1'165'167'591'859'065'112), - 19 - 2 }, // * z^9 + -::boost::decimal::decimal64_fast { UINT64_C(2'152'416'741'149'509'728), - 19 - 3 }, // * z^10 + +::boost::decimal::decimal64_fast { UINT64_C(1'280'502'823'881'161'862), - 19 - 3 }, // * z^11 + -::boost::decimal::decimal64_fast { UINT64_C(2'013'485'478'078'823'866), - 19 - 4 }, // * z^12 + -::boost::decimal::decimal64_fast { UINT64_C(1'250'493'482'142'670'657), - 19 - 5 }, // * z^13 + +::boost::decimal::decimal64_fast { UINT64_C(1'133'027'231'981'695'882), - 19 - 5 }, // * z^14 + -::boost::decimal::decimal64_fast { UINT64_C(2'056'338'416'977'607'103), - 19 - 6 }, // * z^15 + +::boost::decimal::decimal64_fast { UINT64_C(6'116'095'104'481'415'818), - 19 - 8 }, // * z^16 + +::boost::decimal::decimal64_fast { UINT64_C(5'002'007'644'469'222'930), - 19 - 8 }, // * z^17 + -::boost::decimal::decimal64_fast { UINT64_C(1'181'274'570'487'020'145), - 19 - 8 }, // * z^18 + +::boost::decimal::decimal64_fast { UINT64_C(1'043'426'711'691'100'510), - 19 - 9 }, // * z^19 + +::boost::decimal::decimal64_fast { UINT64_C(7'782'263'439'905'071'254), - 19 - 11 }, // * z^20 + -::boost::decimal::decimal64_fast { UINT64_C(3'696'805'618'642'205'708), - 19 - 11 }, // * z^21 + +::boost::decimal::decimal64_fast { UINT64_C(5'100'370'287'454'475'979), - 19 - 12 }, // * z^22 + -::boost::decimal::decimal64_fast { UINT64_C(2'058'326'053'566'506'783), - 19 - 13 }, // * z^23 + -::boost::decimal::decimal64_fast { UINT64_C(5'348'122'539'423'017'982), - 19 - 14 }, // * z^24 + +::boost::decimal::decimal64_fast { UINT64_C(1'226'778'628'238'260'790), - 19 - 14 }, // * z^25 + -::boost::decimal::decimal64_fast { UINT64_C(1'181'259'301'697'458'770), - 19 - 15 }, // * z^26 + +::boost::decimal::decimal64_fast { UINT64_C(1'186'692'254'751'600'333), - 19 - 17 }, // * z^27 + }}; + static constexpr d64_coeffs_asymp_t d64_coeffs_asymp = {{ // N[Series[Gamma[x] Sqrt[x], {x, Infinity, 14}], 19] @@ -118,6 +192,26 @@ struct tgamma_table_imp +::boost::decimal::decimal64 { UINT64_C(1353992280159094113), - 19 - 2 }, // / x^14 }}; + static constexpr d64_fast_coeffs_asymp_t d64_fast_coeffs_asymp = + {{ + // N[Series[Gamma[x] Sqrt[x], {x, Infinity, 14}], 19] + +::boost::decimal::decimal64_fast { UINT64_C(2506628274631000502), - 19 + 1 }, + +::boost::decimal::decimal64_fast { UINT64_C(2088856895525833752), - 19 - 0 }, // / x + +::boost::decimal::decimal64_fast { UINT64_C(8703570398024307300), - 19 - 2 }, // / x^2 + -::boost::decimal::decimal64_fast { UINT64_C(6721090474029881748), - 19 - 2 }, // / x^3 + -::boost::decimal::decimal64_fast { UINT64_C(5752012381101712348), - 19 - 3 }, // / x^4 + +::boost::decimal::decimal64_fast { UINT64_C(1965294881583203064), - 19 - 2 }, // / x^5 + +::boost::decimal::decimal64_fast { UINT64_C(1747825212045591212), - 19 - 3 }, // / x^6 + -::boost::decimal::decimal64_fast { UINT64_C(1484341135158276145), - 19 - 2 }, // / x^7 + -::boost::decimal::decimal64_fast { UINT64_C(1296375732112554321), - 19 - 3 }, // / x^8 + +::boost::decimal::decimal64_fast { UINT64_C(2104311229753206373), - 19 - 2 }, // / x^9 + +::boost::decimal::decimal64_fast { UINT64_C(1805999456555504364), - 19 - 3 }, // / x^10 + -::boost::decimal::decimal64_fast { UINT64_C(4798785670546346063), - 19 - 2 }, // / x^11 + -::boost::decimal::decimal64_fast { UINT64_C(4073678593815251825), - 19 - 3 }, // / x^12 + +::boost::decimal::decimal64_fast { UINT64_C(1605085033194459600), - 19 - 1 }, // / x^13 + +::boost::decimal::decimal64_fast { UINT64_C(1353992280159094113), - 19 - 2 }, // / x^14 + }}; + static constexpr d128_coeffs_t d128_coeffs = {{ // N[Series[1/Gamma[z], {z, 0, 46}], 36] @@ -203,6 +297,91 @@ struct tgamma_table_imp +::boost::decimal::decimal128 { boost::decimal::detail::uint128 { UINT64_C(93943319594850), UINT64_C(9012698938647704180) }, -27 }, }}; + static constexpr d128_fast_coeffs_t d128_fast_coeffs = + {{ + // N[Series[1/Gamma[z], {z, 0, 46}], 36] + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(312909238939453), UINT64_C(7916302232898517972) }, -34 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(355552215013931), UINT64_C(2875353717947891404) }, -34 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(227696740770409), UINT64_C(1287992959696612036) }, -35 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(90280762131699), UINT64_C(14660682722320745466) }, -34 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(228754377395439), UINT64_C(1086189775515439306) }, -35 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(521608121705894), UINT64_C(2882773517907923486) }, -36 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(391339697554084), UINT64_C(12203646426790846826) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(63163861720165), UINT64_C(1793625582468481749) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(116682745342423), UINT64_C(7466931387917530902) }, -37 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(69416197176288), UINT64_C(17486507952476000235) }, -37 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(109151266480053), UINT64_C(14157573701904186532) }, -38 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(67789387500902), UINT64_C(6337242598258275460) }, -39 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(61421529319989), UINT64_C(11330812743044278521) }, -39 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(111474328952626), UINT64_C(4349913604764276954) }, -40 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(331554179970335), UINT64_C(8536598537651543980) }, -42 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(271159377746131), UINT64_C(11232450780359262294) }, -42 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(64037022781195), UINT64_C(7729482665838775386) }, -42 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(56564275382244), UINT64_C(15921046388084405946) }, -43 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(421877346419979), UINT64_C(12114109382397224706) }, -45 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(200404234149424), UINT64_C(17191629897693416576) }, -45 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(276491627306932), UINT64_C(18075235341994261118) }, -46 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(111582078948016), UINT64_C(1315679057212061374) }, -47 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(289922303798056), UINT64_C(8236273575746269444) }, -48 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(66503802694735), UINT64_C(8619931044472680662) }, -48 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(64036195058454), UINT64_C(13570784405336680634) }, -49 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(64330716033670), UINT64_C(6228121739584017954) }, -51 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(76565308743615), UINT64_C(9665163337994634860) }, -51 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(124615253252825), UINT64_C(5713012462345318490) }, -52 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(92938152937825), UINT64_C(2160517649493992050) }, -53 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(72497982578925), UINT64_C(10055707640313829460) }, -55 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(111360223980902), UINT64_C(528747408384118098) }, -55 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(148320486134320), UINT64_C(12662323637555269860) }, -56 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(93911231108772), UINT64_C(8663955293807189228) }, -57 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(127969413738636), UINT64_C(17978922200959991754) }, -59 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(101100927852914), UINT64_C(16158702556622869636) }, -59 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(120243204727301), UINT64_C(13141135468649758444) }, -60 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(70352901832557), UINT64_C(2975454173305568482) }, -61 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(64005738370342), UINT64_C(18063645830042937300) }, -63 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(60963839731470), UINT64_C(14965217315129705920) }, -63 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(69230926066837), UINT64_C(16656915204960392533) }, -64 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(400691370795862), UINT64_C(16972369904241895558) }, -66 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(61514934723438), UINT64_C(5918930041313493498) }, -68 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(251487992814431), UINT64_C(6680121266003781724) }, -68 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(289879709778175), UINT64_C(4432551928123929090) }, -69 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(173905807485311), UINT64_C(17752316546962770214) }, -70 }, + }}; + + static constexpr d128_fast_coeffs_asymp_t d128_fast_coeffs_asymp = + {{ + // N[Series[Gamma[x] Sqrt[x], {x, Infinity, 29}], 36] + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(135884591048426), UINT64_C(2199768757482254624) }, -33 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(113237159207021), UINT64_C(14130970013708246594) }, -34 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(471821496695924), UINT64_C(464352157037447386) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(364351044670741), UINT64_C(6097570099755222654) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(311817215987699), UINT64_C(14568946901511994136) }, -37 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(106538849009357), UINT64_C(10090636838411945598) }, -36 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(94749794601238), UINT64_C(6866971493329372072) }, -37 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(80466294172410), UINT64_C(2547924282344488810) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(70276669255695), UINT64_C(16334355597894868319) }, -37 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(114074940344203), UINT64_C(9044723431924593842) }, -36 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(97903426715255), UINT64_C(16799883086492113070) }, -37 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(260142692464932), UINT64_C(15263500517507471568) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(220834559071109), UINT64_C(9975868582270637886) }, -37 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(87011834000670), UINT64_C(1012280154922930780) }, -35 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(73400068583854), UINT64_C(10697903424322046536) }, -36 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(401238402683293), UINT64_C(16385890397153029532) }, -35 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(337230714209057), UINT64_C(16967592325356259778) }, -36 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(243967353836524), UINT64_C(9499344852909361366) }, -34 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(204589376322286), UINT64_C(11872292347365127784) }, -35 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(189124322379112), UINT64_C(14090568112327257998) }, -33 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(158368431339348), UINT64_C(2168574764773383622) }, -34 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(182057977444481), UINT64_C(3733389993208297254) }, -32 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(152300056275284), UINT64_C(17612360680536377126) }, -33 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(213068958411016), UINT64_C(2976936113300142334) }, -31 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(178115660634938), UINT64_C(17553310597074079872) }, -32 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(297934512372623), UINT64_C(8697957613905306402) }, -30 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(248927979034058), UINT64_C(1995178765856766712) }, -31 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(490558583154992), UINT64_C(14703601007071441008) }, -29 }, + -::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(409702547605069), UINT64_C(12211101634789053596) }, -30 }, + +::boost::decimal::decimal128_fast { boost::decimal::detail::uint128 { UINT64_C(93943319594850), UINT64_C(9012698938647704180) }, -27 }, + }}; + }; #if !(defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L) && (!defined(_MSC_VER) || _MSC_VER != 1900) @@ -211,10 +390,18 @@ template constexpr typename tgamma_table_imp::d32_coeffs_t tgamma_t template constexpr typename tgamma_table_imp::d64_coeffs_t tgamma_table_imp::d64_coeffs; template constexpr typename tgamma_table_imp::d128_coeffs_t tgamma_table_imp::d128_coeffs; +template constexpr typename tgamma_table_imp::d32_fast_coeffs_t tgamma_table_imp::d32_fast_coeffs; +template constexpr typename tgamma_table_imp::d64_fast_coeffs_t tgamma_table_imp::d64_fast_coeffs; +template constexpr typename tgamma_table_imp::d128_fast_coeffs_t tgamma_table_imp::d128_fast_coeffs; + template constexpr typename tgamma_table_imp::d32_coeffs_asymp_t tgamma_table_imp::d32_coeffs_asymp; template constexpr typename tgamma_table_imp::d64_coeffs_asymp_t tgamma_table_imp::d64_coeffs_asymp; template constexpr typename tgamma_table_imp::d128_coeffs_asymp_t tgamma_table_imp::d128_coeffs_asymp; +template constexpr typename tgamma_table_imp::d32_fast_coeffs_asymp_t tgamma_table_imp::d32_fast_coeffs_asymp; +template constexpr typename tgamma_table_imp::d64_fast_coeffs_asymp_t tgamma_table_imp::d64_fast_coeffs_asymp; +template constexpr typename tgamma_table_imp::d128_fast_coeffs_asymp_t tgamma_table_imp::d128_fast_coeffs_asymp; + #endif } //namespace tgamma_detail @@ -230,18 +417,36 @@ constexpr auto tgamma_series_expansion(decimal32 z) noexcept return taylor_series_result(z, tgamma_table::d32_coeffs); } +template <> +constexpr auto tgamma_series_expansion(decimal32_fast z) noexcept +{ + return taylor_series_result(z, tgamma_table::d32_fast_coeffs); +} + template <> constexpr auto tgamma_series_expansion(decimal64 z) noexcept { return taylor_series_result(z, tgamma_table::d64_coeffs); } +template <> +constexpr auto tgamma_series_expansion(decimal64_fast z) noexcept +{ + return taylor_series_result(z, tgamma_table::d64_fast_coeffs); +} + template <> constexpr auto tgamma_series_expansion(decimal128 z) noexcept { return taylor_series_result(z, tgamma_table::d128_coeffs); } +template <> +constexpr auto tgamma_series_expansion(decimal128_fast z) noexcept +{ + return taylor_series_result(z, tgamma_table::d128_fast_coeffs); +} + template constexpr auto tgamma_series_expansion_asymp(T z) noexcept; @@ -251,18 +456,36 @@ constexpr auto tgamma_series_expansion_asymp(decimal32 z) noexcept return taylor_series_result(z, tgamma_table::d32_coeffs_asymp); } +template <> +constexpr auto tgamma_series_expansion_asymp(decimal32_fast z) noexcept +{ + return taylor_series_result(z, tgamma_table::d32_fast_coeffs_asymp); +} + template <> constexpr auto tgamma_series_expansion_asymp(decimal64 z) noexcept { return taylor_series_result(z, tgamma_table::d64_coeffs_asymp); } +template <> +constexpr auto tgamma_series_expansion_asymp(decimal64_fast z) noexcept +{ + return taylor_series_result(z, tgamma_table::d64_fast_coeffs_asymp); +} + template <> constexpr auto tgamma_series_expansion_asymp(decimal128 z) noexcept { return taylor_series_result(z, tgamma_table::d128_coeffs_asymp); } +template <> +constexpr auto tgamma_series_expansion_asymp(decimal128_fast z) noexcept +{ + return taylor_series_result(z, tgamma_table::d128_fast_coeffs_asymp); +} + } //namespace detail } //namespace decimal } //namespace boost diff --git a/include/boost/decimal/detail/cmath/lgamma.hpp b/include/boost/decimal/detail/cmath/lgamma.hpp index fec8154e4..51348aef9 100644 --- a/include/boost/decimal/detail/cmath/lgamma.hpp +++ b/include/boost/decimal/detail/cmath/lgamma.hpp @@ -76,12 +76,12 @@ constexpr auto lgamma_impl(T x) noexcept { constexpr int asymp_cutoff { - std::numeric_limits::digits10 < 10 ? T { 2, 1 } - : std::numeric_limits::digits10 < 20 ? T { 5, 1 } - : T { 1, 2 } + std::numeric_limits::digits10 < 10 ? T { 2, 1 } // 20 + : std::numeric_limits::digits10 < 20 ? T { 5, 1 } // 50 + : T { 15, 1 } // 150 }; - if (x < T { 1, -1 }) + if (x < T { 2, -1 }) { // Perform the Taylor series expansion. diff --git a/include/boost/decimal/detail/cmath/remquo.hpp b/include/boost/decimal/detail/cmath/remquo.hpp index e60492981..13fed3da8 100644 --- a/include/boost/decimal/detail/cmath/remquo.hpp +++ b/include/boost/decimal/detail/cmath/remquo.hpp @@ -24,7 +24,8 @@ template constexpr auto remquo(T x, T y, int* quo) noexcept BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) { - using unsigned_significand_type = std::conditional_t::value, detail::uint128, std::uint64_t>; + using unsigned_significand_type = std::conditional_t::value || std::is_same::value, + detail::uint128, std::uint64_t>; constexpr T zero {0, 0}; constexpr T half {5, -1}; diff --git a/include/boost/decimal/detail/cmath/riemann_zeta.hpp b/include/boost/decimal/detail/cmath/riemann_zeta.hpp new file mode 100644 index 000000000..8ad26d5ce --- /dev/null +++ b/include/boost/decimal/detail/cmath/riemann_zeta.hpp @@ -0,0 +1,247 @@ +// Copyright 2024 Matt Borland +// Copyright 2024 Christopher Kormanyos +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_CMATH_RIEMANN_ZETA_HPP +#define BOOST_DECIMAL_DETAIL_CMATH_RIEMANN_ZETA_HPP + +#include // NOLINT(llvm-include-order) +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BOOST_DECIMAL_BUILD_MODULE +#include +#include +#endif + +namespace boost { +namespace decimal { + +namespace detail { + +template +constexpr auto riemann_zeta_impl(T x) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +{ + const auto fpc = fpclassify(x); + + constexpr T one { 1, 0 }; + + const bool is_neg { signbit(x) }; + + T result { }; + + if (fpc == FP_ZERO) + { + // The value of riemann_zeta(0) is 1/2. + + result = T { 5, -1, true }; + } + else if (fpc != FP_NORMAL) + { + result = ((fpc == FP_INFINITE) ? (is_neg ? -std::numeric_limits::infinity() : one) : x); + } + else + { + if (is_neg) + { + // Handle Riemann-zeta reflection. + + const T dmx { one - x }; + const T two_pi_term { pow(numbers::pi_v * 2, x) / numbers::pi_v }; + const T chi { (two_pi_term * sin((numbers::pi_v * x) / 2)) * tgamma(dmx) }; + + result = chi * riemann_zeta(dmx); + } + else + { + constexpr int asymp_cutoff + { + std::numeric_limits::digits10 < 10 ? T { 2, 1 } // 20 + : std::numeric_limits::digits10 < 20 ? T { 5, 1 } // 50 + : T { 15, 1 } // 150 + }; + + if(x > asymp_cutoff) + { + // For large argument the power series is irrelevant. + // In such cases, simply return 1. + + result = one; + } + else if((x > T { 9, -1 }) && (x < T { 11, -1 })) + { + // Arguments near +1 receive special treatment. + + if((x > one) || (x < one)) + { + // Use a Taylor series (or Pade approximation) near the discontinuity at x=1. + + result = detail::riemann_zeta_series_or_pade_expansion(x); + } + else + { + // The argument is exaclty one. The result is complex-infinity. + + result = std::numeric_limits::quiet_NaN(); + } + } + else + { + // Test the conditions for the expansion of the product of primes. Set up a + // test for the product of primes. The expansion in the product of primes can + // be used if the number of prime-power terms remains reasonably small. + // This test for being reasonably "small" is checked in relation + // to the precision of the type and the size of primes available in the table. + + using prime_table_type = detail::prime_table_t; + + constexpr std::size_t n_primes = std::tuple_size::value; + + const T lg10_max_prime { log10(detail::prime_table::primes[n_primes - 1U]) }; + + if((x * lg10_max_prime) > std::numeric_limits::digits10) + { + // Perform the product of primes. + result = one; + + for(std::size_t p = static_cast(UINT8_C(0)); p < n_primes; ++p) + { + const T prime_p_pow_s = pow(detail::prime_table::primes[p], x); + + const T pps_term { prime_p_pow_s / (prime_p_pow_s - one) }; + + if((pps_term - one) < std::numeric_limits::epsilon()) + { + break; + } + + result *= pps_term; + } + } + else + { + // Use the accelerated alternating converging series for Zeta as shown in: + // http://numbers.computation.free.fr/Constants/Miscellaneous/zetaevaluations.html + // taken from P. Borwein, "An Efficient Algorithm for the Riemann Zeta Function", + // January 1995. + + // Compute the coefficients dk in a loop and calculate the zeta function sum + // within the same loop on the fly. + + // Set up the factorials and powers for the calculation of the coefficients dk. + // Note that j = n at this stage in the calculation. Also note that the value of + // dn is equal to the value of d0 at the end of the loop. + + // Use nd = (digits * 1.45) + {|imag(s)| * 1.1} + // Here we have, however, only real valued arguments so this + // is streamlined a tad. Also instead of 1.45, we simply use 1.5. + + constexpr int nd { static_cast(std::numeric_limits::digits10 * 1.5F) }; + + bool neg_term { (nd % 2) == 0 }; + + T n_plus_j_minus_one_fact = riemann_zeta_factorial(2 * nd - 1); + T four_pow_j = pow(T { 4 }, nd); + T n_minus_j_fact = one; + T two_j_fact = n_plus_j_minus_one_fact * (2 * nd); + + T dn = (n_plus_j_minus_one_fact * four_pow_j) / (n_minus_j_fact * two_j_fact); + + T jps = pow(T { nd }, x); + + result = ((!neg_term) ? dn : -dn) / jps; + + for(auto j = nd - 1; j >= 0; --j) + { + const bool j_is_zero { j == 0 }; + + const int two_jp1_two_j { ((2 * j) + 1) * (2 * ((!j_is_zero) ? j : 1)) }; + + n_plus_j_minus_one_fact /= (nd + j); + four_pow_j /= 4; + n_minus_j_fact *= (nd - j); + two_j_fact /= two_jp1_two_j; + + dn += ((n_plus_j_minus_one_fact * four_pow_j) / (n_minus_j_fact * two_j_fact)); + + if(!j_is_zero) + { + // Increment the zeta function sum. + jps = pow(T { j }, x); + + neg_term = (!neg_term); + + result += ((!neg_term) ? dn : -dn) / jps; + } + } + + const T two_pow_one_minus_s { pow(T { 2 }, one - x) }; + + result /= (dn * (one - two_pow_one_minus_s)); + } + } + } + } + + return result; +} + +} //namespace detail + +BOOST_DECIMAL_EXPORT template +constexpr auto riemann_zeta(T x) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +{ + #if BOOST_DECIMAL_DEC_EVAL_METHOD == 0 + + using evaluation_type = T; + + #elif BOOST_DECIMAL_DEC_EVAL_METHOD == 1 + + using evaluation_type = detail::promote_args_t; + + #else // BOOST_DECIMAL_DEC_EVAL_METHOD == 2 + + using evaluation_type = detail::promote_args_t; + + #endif + + return static_cast(detail::riemann_zeta_impl(static_cast(x))); +} + +BOOST_DECIMAL_EXPORT template +constexpr auto riemann_zeta(IntegralType n) noexcept + BOOST_DECIMAL_REQUIRES_TWO_RETURN(detail::is_decimal_floating_point_v, T, detail::is_integral_v, IntegralType, T) +{ + #if BOOST_DECIMAL_DEC_EVAL_METHOD == 0 + + using evaluation_type = T; + + #elif BOOST_DECIMAL_DEC_EVAL_METHOD == 1 + + using evaluation_type = detail::promote_args_t; + + #else // BOOST_DECIMAL_DEC_EVAL_METHOD == 2 + + using evaluation_type = detail::promote_args_t; + + #endif + + // TODO(ckormanyos) Consider making an integral-argument specialization. + // Some exact values are known. Some simplifications for small-n are possible. + + return static_cast(detail::riemann_zeta_impl(static_cast(n))); +} + +} //namespace decimal +} //namespace boost + +#endif // BOOST_DECIMAL_DETAIL_CMATH_RIEMANN_ZETA_HPP diff --git a/include/boost/decimal/detail/cmath/sin.hpp b/include/boost/decimal/detail/cmath/sin.hpp index 50e3fe845..9fc8ed1ca 100644 --- a/include/boost/decimal/detail/cmath/sin.hpp +++ b/include/boost/decimal/detail/cmath/sin.hpp @@ -1,5 +1,5 @@ -// Copyright 2023 Matt Borland -// Copyright 2023 Christopher Kormanyos +// Copyright 2023 - 2024 Matt Borland +// Copyright 2023 - 2024 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -45,43 +45,61 @@ constexpr auto sin_impl(T x) noexcept } else { - // Perform argument reduction and subsequent computation of the result. + result = T { }; - // Given x = k * (pi/2) + r, compute n = (k % 4). + if(x > static_cast(INT8_C(0))) + { + // Perform argument reduction and subsequent scaling of the result. - // | n | sin(x) | cos(x) | sin(x)/cos(x) | - // |----------------------------------------| - // | 0 | sin(r) | cos(r) | sin(r)/cos(r) | - // | 1 | cos(r) | -sin(r) | -cos(r)/sin(r) | - // | 2 | -sin(r) | -cos(r) | sin(r)/cos(r) | - // | 3 | -cos(r) | sin(r) | -cos(r)/sin(r) | + // Given x = k * (pi/2) + r, compute n = (k % 4). - #if (defined(_MSC_VER) && (_MSC_VER < 1920)) - const auto my_pi_half = numbers::pi_v / 2; - #else - constexpr auto my_pi_half = numbers::pi_v / 2; - #endif + // | n | sin(x) | cos(x) | sin(x)/cos(x) | + // |----------------------------------------| + // | 0 | sin(r) | cos(r) | sin(r)/cos(r) | + // | 1 | cos(r) | -sin(r) | -cos(r)/sin(r) | + // | 2 | -sin(r) | -cos(r) | sin(r)/cos(r) | + // | 3 | -cos(r) | sin(r) | -cos(r)/sin(r) | - int k { }; - auto r { remquo(x, my_pi_half, &k) }; + constexpr T my_pi_half { numbers::pi_v / 2 }; - const auto n = static_cast(k % 4); + const auto k = static_cast(x / my_pi_half); + const auto n = static_cast(k % static_cast(UINT8_C(4))); - switch(n) - { - case 3U: - result = -detail::cos_series_expansion(r); - break; - case 2U: - result = -detail::sin_series_expansion(r); - break; - case 1U: + auto r = x - (my_pi_half * k); + + constexpr T half { 5, -1 }; + + const bool do_scaling { x > half }; + + if(do_scaling) + { + // Reduce the argument with factors of three. + r /= static_cast(UINT8_C(3)); + } + + switch(n) + { + case static_cast(UINT8_C(1)): + case static_cast(UINT8_C(3)): result = detail::cos_series_expansion(r); break; - case 0U: - default: + case static_cast(UINT8_C(0)): + case static_cast(UINT8_C(2)): + default: result = detail::sin_series_expansion(r); break; + } + + if(do_scaling) + { + result *= (static_cast(UINT8_C(3)) - ((result * result) * static_cast(UINT8_C(4)))); + } + + if(signbit(result)) { result = -result; } + + const auto b_neg = (n > static_cast(UINT8_C(1))); + + if(b_neg) { result = -result; } } } diff --git a/include/boost/decimal/detail/cmath/tgamma.hpp b/include/boost/decimal/detail/cmath/tgamma.hpp index e3b1f8522..265910783 100644 --- a/include/boost/decimal/detail/cmath/tgamma.hpp +++ b/include/boost/decimal/detail/cmath/tgamma.hpp @@ -33,36 +33,37 @@ constexpr auto tgamma_impl(T x) noexcept const auto is_pure_int = (nx == x); + const bool is_neg = signbit(x); + const auto fpc = fpclassify(x); if (fpc != FP_NORMAL) { if (fpc == FP_ZERO) { - result = (signbit(x) ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); + result = (is_neg ? -std::numeric_limits::infinity() : std::numeric_limits::infinity()); } else if(fpc == FP_INFINITE) { - result = (signbit(x) ? std::numeric_limits::quiet_NaN() : std::numeric_limits::infinity()); + result = (is_neg ? std::numeric_limits::quiet_NaN() : std::numeric_limits::infinity()); } else { result = x; } } - else if ((nx < 0) && is_pure_int && ((nx & 1) != 0)) + else if (is_pure_int && is_neg) { // Pure negative integer argument. result = std::numeric_limits::quiet_NaN(); } else { - if (signbit(x)) + if (is_neg) { // Reflection for negative argument. - const auto ga = tgamma(-x); - result = -numbers::pi_v / ((x * ga) * sin(numbers::pi_v * x)); + result = -numbers::pi_v / ((x * tgamma(-x)) * sin(numbers::pi_v * x)); } else { @@ -81,14 +82,14 @@ constexpr auto tgamma_impl(T x) noexcept { constexpr int asymp_cutoff { - std::numeric_limits::digits10 < 10 ? T { 2, 1 } - : std::numeric_limits::digits10 < 20 ? T { 5, 1 } - : T { 1, 2 } + std::numeric_limits::digits10 < 10 ? T { 2, 1 } // 20 + : std::numeric_limits::digits10 < 20 ? T { 4, 1 } // 40 + : T { 9, 1 } // 90 }; if (x < T { asymp_cutoff }) { - T r { 1 }; + T r { one }; T z { x }; @@ -108,7 +109,7 @@ constexpr auto tgamma_impl(T x) noexcept { // Use large-argument asymptotic expansion. - const T prefix { exp(-x) * pow(x, x - T { 5, -1 }) }; + const T prefix { exp(((x - T { 5, -1 }) * log(x)) - x) }; result = prefix * detail::tgamma_series_expansion_asymp(one / x); } diff --git a/include/boost/decimal/detail/cmath/trunc.hpp b/include/boost/decimal/detail/cmath/trunc.hpp index a2b895b85..42bbc866d 100644 --- a/include/boost/decimal/detail/cmath/trunc.hpp +++ b/include/boost/decimal/detail/cmath/trunc.hpp @@ -9,11 +9,14 @@ #include #include #include +#include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include +#include #endif namespace boost { diff --git a/include/boost/decimal/detail/cmath/trunc_to.hpp b/include/boost/decimal/detail/cmath/trunc_to.hpp new file mode 100644 index 000000000..91aa90779 --- /dev/null +++ b/include/boost/decimal/detail/cmath/trunc_to.hpp @@ -0,0 +1,71 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_CMATH_TRUNC_TO_HPP +#define BOOST_DECIMAL_DETAIL_CMATH_TRUNC_TO_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef BOOST_DECIMAL_BUILD_MODULE +#include +#include +#endif + +namespace boost { +namespace decimal { + +BOOST_DECIMAL_EXPORT template +constexpr auto trunc_to(T val, int precision = 0) noexcept + BOOST_DECIMAL_REQUIRES(detail::is_decimal_floating_point_v, T) +{ + constexpr auto biggest_val {1 / std::numeric_limits::epsilon()}; + + if (precision == 0) + { + return trunc(val); + } + else if (isnan(val) || isinf(val) || abs(val) == 0 || val > biggest_val) + { + return val; + } + + int exp {}; + auto sig {frexp10(val, &exp)}; + const auto isneg {val < 0}; + auto sig_dig {detail::num_digits(sig)}; + + if (sig_dig <= precision) + { + return val; + } + + if (sig_dig > precision + 1) + { + const auto digits_to_remove {sig_dig - (precision + 1)}; + sig /= detail::pow10(static_cast(digits_to_remove)); + exp += digits_to_remove; + sig_dig -= digits_to_remove; + } + + if (sig_dig > precision) + { + exp += detail::fenv_round(sig, isneg); + } + + return {sig, exp, isneg}; +} + + +} // namespace decimal +} // namespace boost + +#endif //BOOST_DECIMAL_DETAIL_CMATH_TRUNC_TO_HPP diff --git a/include/boost/decimal/detail/comparison.hpp b/include/boost/decimal/detail/comparison.hpp index 8e119afc2..2c75c99ef 100644 --- a/include/boost/decimal/detail/comparison.hpp +++ b/include/boost/decimal/detail/comparison.hpp @@ -16,20 +16,24 @@ #include #ifndef BOOST_DECIMAL_BUILD_MODULE +#include #include #endif namespace boost { namespace decimal { -template +template constexpr auto equal_parts_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept -> bool { - using sig_type = typename DecimalType::significand_type; + using comp_type = std::conditional_t<(std::numeric_limits::digits10 > std::numeric_limits::digits10), T1, T2>; - auto new_lhs_sig {detail::shrink_significand(lhs_sig, lhs_exp)}; - auto new_rhs_sig {detail::shrink_significand(rhs_sig, rhs_exp)}; + BOOST_DECIMAL_ASSERT(lhs_sig >= 0); + BOOST_DECIMAL_ASSERT(rhs_sig >= 0); + + auto new_lhs_sig {static_cast(lhs_sig)}; + auto new_rhs_sig {static_cast(rhs_sig)}; detail::normalize(new_lhs_sig, lhs_exp); detail::normalize(new_rhs_sig, rhs_exp); @@ -106,63 +110,41 @@ constexpr auto operator!=(Decimal1 lhs, Decimal2 rhs) noexcept return !(mixed_decimal_equality_impl(lhs, rhs)); } -template +template constexpr auto less_parts_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign) noexcept -> bool { - const bool both_neg {lhs_sign && rhs_sign}; + using comp_type = std::conditional_t<(std::numeric_limits::digits10 > std::numeric_limits::digits10), T1, T2>; - // Normalize the significands and exponents - using sig_type = typename DecimalType::significand_type; + BOOST_DECIMAL_ASSERT(lhs_sig >= 0); + BOOST_DECIMAL_ASSERT(rhs_sig >= 0); - auto new_lhs_sig {detail::shrink_significand(lhs_sig, lhs_exp)}; - auto new_rhs_sig {detail::shrink_significand(rhs_sig, rhs_exp)}; + auto new_lhs_sig {static_cast(lhs_sig)}; + auto new_rhs_sig {static_cast(rhs_sig)}; detail::normalize(new_lhs_sig, lhs_exp); detail::normalize(new_rhs_sig, rhs_exp); - if (new_lhs_sig == 0 && new_rhs_sig != 0) - { - return (!rhs_sign); - } - else if (new_lhs_sig != 0 && new_rhs_sig == 0) - { - return lhs_sign; - } - else if (new_lhs_sig == 0 && new_rhs_sig == 0) - { - return false; - } - else if (both_neg) + if (new_lhs_sig == 0 || new_rhs_sig == 0) { - if (lhs_exp > rhs_exp) - { - return true; - } - else if (lhs_exp < rhs_exp) + if (new_lhs_sig == 0 && new_rhs_sig == 0) { return false; } - else - { - return (new_lhs_sig > new_rhs_sig); - } + return new_lhs_sig == 0 ? !rhs_sign : lhs_sign; } - else + + if (lhs_sign != rhs_sign) { - if ((lhs_exp < rhs_exp) && (new_lhs_sig != static_cast(0))) - { - return true; - } - else if (lhs_exp > rhs_exp) - { - return false; - } - else - { - return (new_lhs_sig < new_rhs_sig); - } + return lhs_sign; } + + if (lhs_exp != rhs_exp) + { + return lhs_sign ? lhs_exp > rhs_exp : lhs_exp < rhs_exp; + } + + return lhs_sign ? new_lhs_sig > new_rhs_sig : new_lhs_sig < new_rhs_sig; } template diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index 1df2a02fc..6e8ae6131 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -284,4 +284,12 @@ typedef unsigned __int128 uint128_t; # define BOOST_DECIMAL_UNREACHABLE std::abort() #endif +#if defined(_MSC_VER) +# define BOOST_DECIMAL_FORCE_INLINE __forceinline +#elif defined(__GNUC__) || defined(__clang__) +# define BOOST_DECIMAL_FORCE_INLINE __attribute__((always_inline)) inline +#else +# define BOOST_DECIMAL_FORCE_INLINE inline +#endif + #endif // BOOST_DECIMAL_DETAIL_CONFIG_HPP diff --git a/include/boost/decimal/detail/div_impl.hpp b/include/boost/decimal/detail/div_impl.hpp index fc0bf0dc9..a9062116d 100644 --- a/include/boost/decimal/detail/div_impl.hpp +++ b/include/boost/decimal/detail/div_impl.hpp @@ -5,6 +5,8 @@ #ifndef BOOST_DECIMAL_DETAIL_DIV_IMPL_HPP #define BOOST_DECIMAL_DETAIL_DIV_IMPL_HPP +#include + #ifndef BOOST_DECIMAL_BUILD_MODULE #include #include @@ -15,7 +17,7 @@ namespace decimal { namespace detail { template -constexpr auto generic_div_impl(const T& lhs, const T& rhs, T& q) noexcept -> void +BOOST_DECIMAL_FORCE_INLINE constexpr auto generic_div_impl(const T& lhs, const T& rhs, T& q) noexcept -> void { bool sign {lhs.sign != rhs.sign}; @@ -50,6 +52,74 @@ constexpr auto generic_div_impl(const T& lhs, const T& rhs, T& q) noexcept -> vo q = T{res_sig_32, res_exp, sign}; } +template +constexpr auto d64_generic_div_impl(const T& lhs, const T& rhs, T& q) noexcept -> void +{ + #ifdef BOOST_DECIMAL_HAS_INT128 + using unsigned_int128_type = boost::decimal::detail::uint128_t; + #else + using unsigned_int128_type = boost::decimal::detail::uint128; + #endif + + bool sign {lhs.sign != rhs.sign}; + + // If rhs is greater than we need to offset the significands to get the correct values + // e.g. 4/8 is 0 but 40/8 yields 5 in integer maths + constexpr auto tens_needed {detail::pow10(static_cast(detail::precision_v))}; + const auto big_sig_lhs {static_cast(lhs.sig) * tens_needed}; + + auto res_sig {big_sig_lhs / static_cast(rhs.sig)}; + auto res_exp {(lhs.exp - detail::precision_v) - rhs.exp}; + + const auto sig_dig {detail::num_digits(res_sig)}; + + if (sig_dig > std::numeric_limits::digits10) + { + res_sig /= static_cast(detail::pow10(static_cast(sig_dig - std::numeric_limits::digits10))); + res_exp += sig_dig - std::numeric_limits::digits10; + } + + const auto res_sig_64 {static_cast(res_sig)}; + + if (res_sig_64 == 0) + { + sign = false; + } + + // Let the constructor handle shrinking it back down and rounding correctly + q = T{res_sig_64, res_exp, sign}; +} + +template +constexpr auto d128_generic_div_impl(T lhs, T rhs, T& q) noexcept -> void +{ + bool sign {lhs.sign != rhs.sign}; + + const auto big_sig_lhs {detail::uint256_t(lhs.sig) * detail::uint256_t(pow10(detail::uint128(detail::precision_v)))}; + lhs.exp -= detail::precision_v; + + auto res_sig {big_sig_lhs / detail::uint256_t(rhs.sig)}; + auto res_exp {lhs.exp - rhs.exp}; + + // TODO(mborland): Since the values are normalized is sig_dig always near 34? + 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; + } + + if (res_sig == 0) + { + sign = false; + } + + // Let the constructor handle shrinking it back down and rounding correctly + q = T {res_sig.low, res_exp, sign}; +} + } // namespace detail } // namespace decimal } // namespace boost diff --git a/include/boost/decimal/detail/emulated128.hpp b/include/boost/decimal/detail/emulated128.hpp index 06fd06910..f3ea65876 100644 --- a/include/boost/decimal/detail/emulated128.hpp +++ b/include/boost/decimal/detail/emulated128.hpp @@ -743,9 +743,14 @@ constexpr auto uint128::operator^=(uint128 v) noexcept -> uint128& constexpr auto uint128::operator+=(std::uint64_t n) noexcept -> uint128& { - auto sum = low + n; - high += (sum < low ? 1 : 0); - low = sum; + const std::uint64_t new_low { low + n }; + + if (new_low < low) + { + ++high; + } + + low = new_low; return *this; } @@ -771,14 +776,9 @@ constexpr auto uint128::operator+=(uint128 v) noexcept -> uint128& constexpr auto uint128::operator++() noexcept -> uint128& { - if (this->low == UINT64_MAX) - { - this->low = 0; - ++this->high; - } - else + if (++low == UINT64_C(0)) { - ++this->low; + ++high; } return *this; @@ -886,26 +886,6 @@ constexpr auto operator*(uint128 lhs, uint128 rhs) noexcept -> uint128 return result; } -// TODO(mborland): Can be replaced by intrinsics at runtime -constexpr auto multiply_64_64(std::uint64_t a, std::uint64_t b) -> uint128 -{ - std::uint64_t a_low = a & UINT32_MAX; - std::uint64_t a_high = a >> 32; - std::uint64_t b_low = b & UINT32_MAX; - std::uint64_t b_high = b >> 32; - - std::uint64_t low_product = a_low * b_low; - std::uint64_t mid_product1 = a_high * b_low; - std::uint64_t mid_product2 = a_low * b_high; - std::uint64_t high_product = a_high * b_high; - - std::uint64_t mid_sum = (low_product >> 32) + (mid_product1 & UINT32_MAX) + mid_product2; - std::uint64_t high = high_product + (mid_product1 >> 32) + (mid_sum >> 32); - std::uint64_t low = (mid_sum << 32) | (low_product & UINT32_MAX); - - return {high, low}; -} - constexpr auto operator*(uint128 lhs, std::uint64_t rhs) noexcept -> uint128 { using local_unsigned_fast_type = ::boost::decimal::math::wide_integer::detail::unsigned_fast_type; diff --git a/include/boost/decimal/detail/emulated256.hpp b/include/boost/decimal/detail/emulated256.hpp index 62c3018a0..1a00b7996 100644 --- a/include/boost/decimal/detail/emulated256.hpp +++ b/include/boost/decimal/detail/emulated256.hpp @@ -522,12 +522,18 @@ constexpr uint256_t operator%(uint256_t lhs, std::uint64_t rhs) noexcept // Get the 256-bit result of multiplication of two 128-bit unsigned integers constexpr uint256_t umul256_impl(std::uint64_t a_high, std::uint64_t a_low, std::uint64_t b_high, std::uint64_t b_low) noexcept { - const auto low_product {static_cast(a_low) * b_low}; - const auto mid_product1 {static_cast(a_low) * b_high}; - const auto mid_product2 {static_cast(a_high) * b_low}; - const auto high_product {static_cast(a_high) * b_high}; + #ifdef BOOST_DECIMAL_HAS_INT128 + using unsigned_int128_type = boost::decimal::detail::uint128_t; + #else + using unsigned_int128_type = boost::decimal::detail::uint128; + #endif + + const auto low_product {static_cast(a_low) * b_low}; + const auto mid_product1 {static_cast(a_low) * b_high}; + const auto mid_product2 {static_cast(a_high) * b_low}; + const auto high_product {static_cast(a_high) * b_high}; - uint128 carry {}; + std::uint64_t carry {}; const auto mid_combined {mid_product1 + mid_product2}; if (mid_combined < mid_product1) diff --git a/include/boost/decimal/detail/fenv_rounding.hpp b/include/boost/decimal/detail/fenv_rounding.hpp index 48e504a9e..55cb7087a 100644 --- a/include/boost/decimal/detail/fenv_rounding.hpp +++ b/include/boost/decimal/detail/fenv_rounding.hpp @@ -20,7 +20,7 @@ namespace detail { template , bool> = true> constexpr auto fenv_round(T& val, bool = false) noexcept -> int { - using significand_type = std::conditional_t::value, detail::uint128, int>; + using significand_type = std::conditional_t::value || std::is_same::value, detail::uint128, int>; const auto trailing_num {val % 10}; int exp_delta {}; @@ -46,7 +46,7 @@ constexpr auto fenv_round(T& val, bool = false) noexcept -> int template , bool> = true> constexpr auto fenv_round(T& val, bool is_neg = false) noexcept -> int // NOLINT(readability-function-cognitive-complexity) { - using significand_type = std::conditional_t::value, detail::uint128, int>; + using significand_type = std::conditional_t::value || std::is_same::value, detail::uint128, int>; if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(coeff)) { @@ -81,46 +81,48 @@ constexpr auto fenv_round(T& val, bool is_neg = false) noexcept -> int // NOLINT ++exp; // Default rounding mode - if (round == rounding_mode::fe_dec_to_nearest_from_zero) + switch (round) { - if (trailing_num >= 5) - { - ++val; - } - } - else if (round == rounding_mode::fe_dec_downward) - { - if (trailing_num >= 5 && is_neg) - { - ++val; - } - } - else if (round == rounding_mode::fe_dec_to_nearest) - { - // Round to even - if (trailing_num == 5) - { - if (val % 2 == 1) + case rounding_mode::fe_dec_to_nearest_from_zero: + if (trailing_num >= 5) { ++val; } - } - // ... or nearest - else if (trailing_num > 5) - { - ++val; - } - } - else if (round == rounding_mode::fe_dec_toward_zero) - { - // Do nothing - } - else // rounding_mode::fe_dec_upward - { - if (!is_neg && trailing_num != 0) - { - ++val; - } + break; + case rounding_mode::fe_dec_downward: + if (trailing_num >= 5 && is_neg) + { + ++val; + } + break; + case rounding_mode::fe_dec_to_nearest: + // Round to even + if (trailing_num == 5) + { + if (val % 2 == 1) + { + ++val; + } + } + // ... or nearest + else if (trailing_num > 5) + { + ++val; + } + break; + case rounding_mode::fe_dec_toward_zero: + // Do nothing + break; + case rounding_mode::fe_dec_upward: + if (!is_neg && trailing_num != 0) + { + ++val; + } + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP } diff --git a/include/boost/decimal/detail/integer_search_trees.hpp b/include/boost/decimal/detail/integer_search_trees.hpp index 668a96843..345c347aa 100644 --- a/include/boost/decimal/detail/integer_search_trees.hpp +++ b/include/boost/decimal/detail/integer_search_trees.hpp @@ -18,7 +18,9 @@ #include #endif -namespace boost { namespace decimal { namespace detail { +namespace boost { +namespace decimal { +namespace detail { // Generic solution template @@ -166,39 +168,22 @@ constexpr auto num_digits(std::uint64_t x) noexcept -> int # pragma warning(disable: 4307) // MSVC 14.1 warns of intergral constant overflow #endif -#if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L - -template -constexpr auto generate_array() noexcept -> std::array -{ - std::array values {}; - - values[0] = 1; - for (std::size_t i {1}; i < N; ++i) - { - values[i] = values[i - 1] * 10; - } - - return values; -} - -constexpr int num_digits(uint128 x) noexcept +constexpr int num_digits(const uint128& x) noexcept { - constexpr auto big_powers_of_10 = generate_array(); - - if (x == 0) + if (x.high == UINT64_C(0)) { - return 1; + return num_digits(x.low); } - std::uint32_t left = 0U; + // We start left at 19 because we already eliminated the high word being 0 + std::uint32_t left = 19U; std::uint32_t right = 38U; while (left < right) { std::uint32_t mid = (left + right + 1U) / 2U; - if (x >= big_powers_of_10[mid]) + if (x >= impl::emulated_128_pow10[mid]) { left = mid; } @@ -211,34 +196,6 @@ constexpr int num_digits(uint128 x) noexcept return static_cast(left + 1); } -#else - -constexpr int num_digits(uint128 x) noexcept -{ - if (x.high == 0) - { - return num_digits(x.low); - } - - constexpr uint128 digits_39 = static_cast(UINT64_C(10000000000000000000)) * - static_cast(UINT64_C(10000000000000000000)); - uint128 current_power_of_10 = digits_39; - - for (int i = 39; i > 0; --i) - { - if (x >= current_power_of_10) - { - return i; - } - - current_power_of_10 /= 10U; - } - - return 1; -} - -#endif // Constexpr array - constexpr int num_digits(const uint256_t& x) noexcept { if (x.high == 0) @@ -246,12 +203,8 @@ constexpr int num_digits(const uint256_t& x) noexcept return num_digits(x.low); } - constexpr uint256_t max_digits = umul256({static_cast(UINT64_C(10000000000000000000)) * - static_cast(UINT64_C(10000000000000000000))}, - {static_cast(UINT64_C(10000000000000000000)) * - static_cast(UINT64_C(10000000000000000000))}); - - uint256_t current_power_of_10 = max_digits; + // 10^77 + auto current_power_of_10 {uint256_t{uint128{UINT64_C(15930919111324522770), UINT64_C(5327493063679123134)}, uint128{UINT64_C(12292710897160462336), UINT64_C(0)}}}; for (int i = 78; i > 0; --i) { @@ -272,25 +225,22 @@ constexpr int num_digits(const uint256_t& x) noexcept #ifdef BOOST_DECIMAL_HAS_INT128 -#if defined(__cpp_lib_array_constexpr) && __cpp_lib_array_constexpr >= 201603L - -constexpr auto num_digits(boost::decimal::detail::uint128_t x) noexcept -> int +constexpr auto num_digits(const uint128_t& x) noexcept -> int { - constexpr auto big_powers_of_10 = generate_array(); - - if (x == 0) + if (static_cast(x >> 64) == UINT64_C(0)) { - return 1; + return num_digits(static_cast(x)); } - std::uint32_t left = 0U; + // We start left at 19 because we already eliminated the high word being 0 + std::uint32_t left = 19U; std::uint32_t right = 38U; while (left < right) { std::uint32_t mid = (left + right + 1U) / 2U; - if (x >= big_powers_of_10[mid]) + if (x >= impl::emulated_128_pow10[mid]) { left = mid; } @@ -303,79 +253,6 @@ constexpr auto num_digits(boost::decimal::detail::uint128_t x) noexcept -> int return static_cast(left + 1); } -#else - -// Assume that if someone is using 128 bit ints they are favoring the top end of the range -// Max value is 340,282,366,920,938,463,463,374,607,431,768,211,455 (39 digits) -constexpr auto num_digits(boost::decimal::detail::uint128_t x) noexcept -> int -{ - // There is no literal for boost::decimal::detail::uint128_t, so we need to calculate them using the max value of the - // std::uint64_t powers of 10 - constexpr boost::decimal::detail::uint128_t digits_39 = static_cast(UINT64_C(10000000000000000000)) * - static_cast(UINT64_C(10000000000000000000)); - - constexpr boost::decimal::detail::uint128_t digits_38 = digits_39 / 10; - constexpr boost::decimal::detail::uint128_t digits_37 = digits_38 / 10; - constexpr boost::decimal::detail::uint128_t digits_36 = digits_37 / 10; - constexpr boost::decimal::detail::uint128_t digits_35 = digits_36 / 10; - constexpr boost::decimal::detail::uint128_t digits_34 = digits_35 / 10; - constexpr boost::decimal::detail::uint128_t digits_33 = digits_34 / 10; - constexpr boost::decimal::detail::uint128_t digits_32 = digits_33 / 10; - constexpr boost::decimal::detail::uint128_t digits_31 = digits_32 / 10; - constexpr boost::decimal::detail::uint128_t digits_30 = digits_31 / 10; - constexpr boost::decimal::detail::uint128_t digits_29 = digits_30 / 10; - constexpr boost::decimal::detail::uint128_t digits_28 = digits_29 / 10; - constexpr boost::decimal::detail::uint128_t digits_27 = digits_28 / 10; - constexpr boost::decimal::detail::uint128_t digits_26 = digits_27 / 10; - constexpr boost::decimal::detail::uint128_t digits_25 = digits_26 / 10; - constexpr boost::decimal::detail::uint128_t digits_24 = digits_25 / 10; - constexpr boost::decimal::detail::uint128_t digits_23 = digits_24 / 10; - constexpr boost::decimal::detail::uint128_t digits_22 = digits_23 / 10; - constexpr boost::decimal::detail::uint128_t digits_21 = digits_22 / 10; - - return (x >= digits_39) ? 39 : - (x >= digits_38) ? 38 : - (x >= digits_37) ? 37 : - (x >= digits_36) ? 36 : - (x >= digits_35) ? 35 : - (x >= digits_34) ? 34 : - (x >= digits_33) ? 33 : - (x >= digits_32) ? 32 : - (x >= digits_31) ? 31 : - (x >= digits_30) ? 30 : - (x >= digits_29) ? 29 : - (x >= digits_28) ? 28 : - (x >= digits_27) ? 27 : - (x >= digits_26) ? 26 : - (x >= digits_25) ? 25 : - (x >= digits_24) ? 24 : - (x >= digits_23) ? 23 : - (x >= digits_22) ? 22 : - (x >= digits_21) ? 21 : - (x >= powers_of_10[19]) ? 20 : - (x >= powers_of_10[18]) ? 19 : - (x >= powers_of_10[17]) ? 18 : - (x >= powers_of_10[16]) ? 17 : - (x >= powers_of_10[15]) ? 16 : - (x >= powers_of_10[14]) ? 15 : - (x >= powers_of_10[13]) ? 14 : - (x >= powers_of_10[12]) ? 13 : - (x >= powers_of_10[11]) ? 12 : - (x >= powers_of_10[10]) ? 11 : - (x >= powers_of_10[9]) ? 10 : - (x >= powers_of_10[8]) ? 9 : - (x >= powers_of_10[7]) ? 8 : - (x >= powers_of_10[6]) ? 7 : - (x >= powers_of_10[5]) ? 6 : - (x >= powers_of_10[4]) ? 5 : - (x >= powers_of_10[3]) ? 4 : - (x >= powers_of_10[2]) ? 3 : - (x >= powers_of_10[1]) ? 2 : - (x >= powers_of_10[0]) ? 1 : 0; -} - -#endif // constexpr arrays - #endif // Has int128 } // namespace detail diff --git a/include/boost/decimal/detail/mul_impl.hpp b/include/boost/decimal/detail/mul_impl.hpp index fddc1eb51..d537ba5c8 100644 --- a/include/boost/decimal/detail/mul_impl.hpp +++ b/include/boost/decimal/detail/mul_impl.hpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -18,8 +20,8 @@ namespace decimal { namespace detail { template -constexpr auto 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 +BOOST_DECIMAL_FORCE_INLINE constexpr auto 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 { #ifdef BOOST_DECIMAL_DEBUG std::cerr << "sig lhs: " << sig_lhs @@ -38,13 +40,13 @@ constexpr auto mul_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, auto res_sig {static_cast(lhs_sig) * static_cast(rhs_sig)}; auto res_exp {lhs_exp + rhs_exp}; - const auto sig_dig {detail::num_digits(res_sig)}; - - if (sig_dig > 9) - { - res_sig /= detail::pow10(static_cast(sig_dig - 9)); - res_exp += sig_dig - 9; - } + // We don't need to use the regular binary search tree detail::num_digits(res_sig) + // because we know that res_sig must be [1'000'000^2, 9'999'999^2] which only differ by one order + // of magnitude in their number of digits + const auto sig_dig {res_sig >= UINT64_C(10000000000000) ? 14 : 13}; + constexpr auto max_dig {std::numeric_limits::digits10}; + res_sig /= detail::pow10(static_cast(sig_dig - max_dig)); + res_exp += sig_dig - max_dig; const auto res_sig_32 {static_cast(res_sig)}; @@ -61,6 +63,83 @@ constexpr auto mul_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, return {res_sig_32, res_exp, sign}; } +template +constexpr auto d64_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 +{ + #ifdef BOOST_DECIMAL_HAS_INT128 + using unsigned_int128_type = boost::decimal::detail::uint128_t; + constexpr auto comp_value {impl::builtin_128_pow10[31]}; + #else + using unsigned_int128_type = boost::decimal::detail::uint128; + constexpr auto comp_value {impl::emulated_128_pow10[31]}; + #endif + + #ifdef BOOST_DECIMAL_DEBUG + std::cerr << "sig lhs: " << sig_lhs + << "\nexp lhs: " << exp_lhs + << "\nsig rhs: " << sig_rhs + << "\nexp rhs: " << exp_rhs; + #endif + + 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 {static_cast(lhs_sig) * static_cast(rhs_sig)}; + auto res_exp {lhs_exp + rhs_exp}; + + const auto sig_dig {res_sig >= comp_value ? 32 : 31}; + constexpr auto max_dig {std::numeric_limits::digits10}; + res_sig /= detail::pow10(static_cast(sig_dig - max_dig)); + res_exp += sig_dig - max_dig; + + const auto res_sig_64 {static_cast(res_sig)}; + + #ifdef BOOST_DECIMAL_DEBUG + std::cerr << "\nres sig: " << res_sig_64 + << "\nres exp: " << res_exp << std::endl; + #endif + + // Always return positive zero + if (res_sig_64 == 0) + { + sign = false; + } + + 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 +{ + 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)}; + + 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; + } + + if (res_sig == 0) + { + sign = false; + } + + BOOST_DECIMAL_ASSERT(res_sig.high == uint128(0,0)); + return {res_sig.low, res_exp, sign}; +} + } // namespace detail } // namespace decimal } // namespace boost diff --git a/include/boost/decimal/detail/normalize.hpp b/include/boost/decimal/detail/normalize.hpp index 284273443..e5fce0bb3 100644 --- a/include/boost/decimal/detail/normalize.hpp +++ b/include/boost/decimal/detail/normalize.hpp @@ -17,7 +17,7 @@ namespace detail { // Converts the significand to full precision to remove the effects of cohorts template -constexpr auto normalize(T1& significand, T2& exp) noexcept -> void +constexpr auto normalize(T1& significand, T2& exp, bool sign = false) noexcept -> void { constexpr auto target_precision {detail::precision_v}; const auto digits {num_digits(significand)}; @@ -32,9 +32,8 @@ constexpr auto normalize(T1& significand, T2& exp) noexcept -> void { const auto excess_digits {digits - (target_precision + 1)}; significand /= pow10(static_cast(excess_digits)); - exp += excess_digits; // Perform final rounding according to the fenv rounding mode - exp += detail::fenv_round(significand, significand < 0); + exp += detail::fenv_round(significand, sign || significand < 0) + excess_digits; } } diff --git a/include/boost/decimal/detail/power_tables.hpp b/include/boost/decimal/detail/power_tables.hpp index b9d7c4ea2..486ed9673 100644 --- a/include/boost/decimal/detail/power_tables.hpp +++ b/include/boost/decimal/detail/power_tables.hpp @@ -16,35 +16,129 @@ namespace boost { namespace decimal { namespace detail { +namespace impl { + BOOST_DECIMAL_CONSTEXPR_VARIABLE std::uint64_t powers_of_10[20] = { - UINT64_C(1), UINT64_C(10), UINT64_C(100), UINT64_C(1000), UINT64_C(10000), UINT64_C(100000), UINT64_C(1000000), - UINT64_C(10000000), UINT64_C(100000000), UINT64_C(1000000000), UINT64_C(10000000000), UINT64_C(100000000000), - UINT64_C(1000000000000), UINT64_C(10000000000000), UINT64_C(100000000000000), UINT64_C(1000000000000000), - UINT64_C(10000000000000000), UINT64_C(100000000000000000), UINT64_C(1000000000000000000), UINT64_C(10000000000000000000) - }; + UINT64_C(1), UINT64_C(10), UINT64_C(100), UINT64_C(1000), UINT64_C(10000), UINT64_C(100000), UINT64_C(1000000), + UINT64_C(10000000), UINT64_C(100000000), UINT64_C(1000000000), UINT64_C(10000000000), UINT64_C(100000000000), + UINT64_C(1000000000000), UINT64_C(10000000000000), UINT64_C(100000000000000), UINT64_C(1000000000000000), + UINT64_C(10000000000000000), UINT64_C(100000000000000000), UINT64_C(1000000000000000000), + UINT64_C(10000000000000000000) +}; + +BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 emulated_128_pow10[] = +{ + uint128 {UINT64_C(0), UINT64_C(1)}, + uint128 {UINT64_C(0), UINT64_C(10)}, + uint128 {UINT64_C(0), UINT64_C(100)}, + uint128 {UINT64_C(0), UINT64_C(1000)}, + uint128 {UINT64_C(0), UINT64_C(10000)}, + uint128 {UINT64_C(0), UINT64_C(100000)}, + uint128 {UINT64_C(0), UINT64_C(1000000)}, + uint128 {UINT64_C(0), UINT64_C(10000000)}, + uint128 {UINT64_C(0), UINT64_C(100000000)}, + uint128 {UINT64_C(0), UINT64_C(1000000000)}, + uint128 {UINT64_C(0), UINT64_C(10000000000)}, + uint128 {UINT64_C(0), UINT64_C(100000000000)}, + uint128 {UINT64_C(0), UINT64_C(1000000000000)}, + uint128 {UINT64_C(0), UINT64_C(10000000000000)}, + uint128 {UINT64_C(0), UINT64_C(100000000000000)}, + uint128 {UINT64_C(0), UINT64_C(1000000000000000)}, + uint128 {UINT64_C(0), UINT64_C(10000000000000000)}, + uint128 {UINT64_C(0), UINT64_C(100000000000000000)}, + uint128 {UINT64_C(0), UINT64_C(1000000000000000000)}, + uint128 {UINT64_C(0), UINT64_C(10000000000000000000)}, + uint128 {UINT64_C(5), UINT64_C(7766279631452241920)}, + uint128 {UINT64_C(54), UINT64_C(3875820019684212736)}, + uint128 {UINT64_C(542), UINT64_C(1864712049423024128)}, + uint128 {UINT64_C(5421), UINT64_C(200376420520689664)}, + uint128 {UINT64_C(54210), UINT64_C(2003764205206896640)}, + uint128 {UINT64_C(542101), UINT64_C(1590897978359414784)}, + uint128 {UINT64_C(5421010), UINT64_C(15908979783594147840)}, + uint128 {UINT64_C(54210108), UINT64_C(11515845246265065472)}, + uint128 {UINT64_C(542101086), UINT64_C(4477988020393345024)}, + uint128 {UINT64_C(5421010862), UINT64_C(7886392056514347008)}, + uint128 {UINT64_C(54210108624), UINT64_C(5076944270305263616)}, + uint128 {UINT64_C(542101086242), UINT64_C(13875954555633532928)}, + uint128 {UINT64_C(5421010862427), UINT64_C(9632337040368467968)}, + uint128 {UINT64_C(54210108624275), UINT64_C(4089650035136921600)}, + uint128 {UINT64_C(542101086242752), UINT64_C(4003012203950112768)}, + uint128 {UINT64_C(5421010862427522), UINT64_C(3136633892082024448)}, + uint128 {UINT64_C(54210108624275221), UINT64_C(12919594847110692864)}, + uint128 {UINT64_C(542101086242752217), UINT64_C(68739955140067328)}, + uint128 {UINT64_C(5421010862427522170), UINT64_C(687399551400673280)}, + uint128 {UINT64_C(17316620476856118468), UINT64_C(6873995514006732800)}, +}; + +static_assert(sizeof(emulated_128_pow10) == sizeof(uint128) * 40, "Should have 10^0 to 10^39"); + +#ifdef BOOST_DECIMAL_HAS_INT128 + +static constexpr uint128_t builtin_128_pow10[] = { + uint128_t(1), + uint128_t(10), + uint128_t(100), + uint128_t(1000), + uint128_t(10000), + uint128_t(100000), + uint128_t(1000000), + uint128_t(10000000), + uint128_t(100000000), + uint128_t(1000000000), + uint128_t(10000000000), + uint128_t(100000000000), + uint128_t(1000000000000), + uint128_t(10000000000000), + uint128_t(100000000000000), + uint128_t(1000000000000000), + uint128_t(10000000000000000), + uint128_t(100000000000000000), + uint128_t(1000000000000000000), + uint128_t(10000000000000000000ULL), + uint128_t(10000000000000000000ULL) * uint128_t(10), + uint128_t(10000000000000000000ULL) * uint128_t(100), + uint128_t(10000000000000000000ULL) * uint128_t(1000), + uint128_t(10000000000000000000ULL) * uint128_t(10000), + uint128_t(10000000000000000000ULL) * uint128_t(100000), + uint128_t(10000000000000000000ULL) * uint128_t(1000000), + uint128_t(10000000000000000000ULL) * uint128_t(10000000), + uint128_t(10000000000000000000ULL) * uint128_t(100000000), + uint128_t(10000000000000000000ULL) * uint128_t(1000000000), + uint128_t(10000000000000000000ULL) * uint128_t(10000000000), + uint128_t(10000000000000000000ULL) * uint128_t(100000000000), + uint128_t(10000000000000000000ULL) * uint128_t(1000000000000), + uint128_t(10000000000000000000ULL) * uint128_t(10000000000000), + uint128_t(10000000000000000000ULL) * uint128_t(100000000000000), + uint128_t(10000000000000000000ULL) * uint128_t(1000000000000000), + uint128_t(10000000000000000000ULL) * uint128_t(10000000000000000), + uint128_t(10000000000000000000ULL) * uint128_t(100000000000000000), + uint128_t(10000000000000000000ULL) * uint128_t(1000000000000000000), + uint128_t(10000000000000000000ULL) * uint128_t(10000000000000000000ULL), + uint128_t(10000000000000000000ULL) * uint128_t(10000000000000000000ULL) * uint128_t(10ULL), +}; + +static_assert(sizeof(builtin_128_pow10) == sizeof(boost::decimal::detail::uint128_t) * 40, "Should have 10^0 to 10^39"); + +#endif + +} // namespace impl template constexpr auto pow10(T n) noexcept -> T { - return static_cast(powers_of_10[static_cast(n)]); + return static_cast(impl::powers_of_10[static_cast(n)]); } +#if defined(__GNUC__) && __GNUC__ == 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Warray-bounds" +#endif + template <> constexpr auto pow10(detail::uint128 n) noexcept -> detail::uint128 { - detail::uint128 res {1}; - if (n <= 19) - { - res = powers_of_10[static_cast(n)]; - } - else - { - res = powers_of_10[static_cast(19)]; - res *= powers_of_10[static_cast(n - 19)]; - } - - return res; + return impl::emulated_128_pow10[static_cast(n.low)]; } #ifdef BOOST_DECIMAL_HAS_INT128 @@ -52,22 +146,15 @@ constexpr auto pow10(detail::uint128 n) noexcept -> detail::uint128 template <> constexpr auto pow10(detail::uint128_t n) noexcept -> detail::uint128_t { - detail::uint128_t res {1}; - if (n <= 19) - { - res = powers_of_10[static_cast(n)]; - } - else - { - res = powers_of_10[static_cast(19)]; - res *= powers_of_10[static_cast(n - 19)]; - } - - return res; + return impl::builtin_128_pow10[static_cast(n)]; } #endif +#if defined(__GNUC__) && __GNUC__ == 7 +# pragma GCC diagnostic pop +#endif + } // namespace detail } // namespace decimal } // namespace boost diff --git a/include/boost/decimal/detail/promotion.hpp b/include/boost/decimal/detail/promotion.hpp index a2cdc45e5..9e0063b2b 100644 --- a/include/boost/decimal/detail/promotion.hpp +++ b/include/boost/decimal/detail/promotion.hpp @@ -19,6 +19,56 @@ namespace detail { namespace impl { +// Assign explicit decimal values because the decimal32_fast size could be greater than that +// of decimal64 even though the precision is worse + +template +struct decimal_val +{ + static constexpr int value = 0; +}; + +template <> +struct decimal_val +{ + static constexpr int value = 32; +}; + +// Assign a higher value to the fast type for consistency of promotion +// Side effect is the same calculation will be faster with the same precision +template <> +struct decimal_val +{ + static constexpr int value = 33; +}; + +template <> +struct decimal_val +{ + static constexpr int value = 64; +}; + +template <> +struct decimal_val +{ + static constexpr int value = 65; +}; + +template <> +struct decimal_val +{ + static constexpr int value = 128; +}; + +template <> +struct decimal_val +{ + static constexpr int value = 129; +}; + +template +constexpr int decimal_val_v = decimal_val::value; + // Promotes a single argument to double if it is an integer type template struct promote_arg @@ -37,7 +87,7 @@ template struct promote_2_args { using type = std::conditional_t<(is_decimal_floating_point_v && is_decimal_floating_point_v), - std::conditional_t<(sizeof(T1) > sizeof(T2)), T1, T2>, + std::conditional_t<(decimal_val_v > decimal_val_v), T1, T2>, std::conditional_t, T1, std::conditional_t, T2, std::conditional_t<(sizeof(promote_arg_t) > sizeof(promote_arg_t)), diff --git a/include/boost/decimal/detail/shrink_significand.hpp b/include/boost/decimal/detail/shrink_significand.hpp index 3aeb9a039..6f624fa6a 100644 --- a/include/boost/decimal/detail/shrink_significand.hpp +++ b/include/boost/decimal/detail/shrink_significand.hpp @@ -30,7 +30,7 @@ constexpr auto shrink_significand(Integer sig, std::int32_t& exp) noexcept -> Ta if (sig_dig > max_digits) { - unsigned_sig /= static_cast(powers_of_10[static_cast(sig_dig - max_digits)]); + unsigned_sig /= pow10(static_cast(sig_dig - max_digits)); exp += sig_dig - max_digits; } diff --git a/include/boost/decimal/detail/sub_impl.hpp b/include/boost/decimal/detail/sub_impl.hpp index b1e9903d3..6b2dd87dd 100644 --- a/include/boost/decimal/detail/sub_impl.hpp +++ b/include/boost/decimal/detail/sub_impl.hpp @@ -18,9 +18,9 @@ namespace decimal { namespace detail { template -constexpr auto sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, - T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, - bool abs_lhs_bigger) noexcept -> ReturnType +BOOST_DECIMAL_FORCE_INLINE constexpr auto sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, + T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, + bool abs_lhs_bigger) noexcept -> ReturnType { auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; auto signed_sig_lhs {detail::make_signed_value(lhs_sig, lhs_sign)}; @@ -80,6 +80,147 @@ constexpr auto sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, return {res_sig, new_exp, new_sign}; } +template +constexpr auto d64_sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, + T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, + bool abs_lhs_bigger) noexcept -> ReturnType +{ + auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; + auto signed_sig_lhs {detail::make_signed_value(lhs_sig, lhs_sign)}; + auto signed_sig_rhs {detail::make_signed_value(rhs_sig, rhs_sign)}; + + if (delta_exp > detail::precision_v + 1) + { + // If the difference in exponents is more than the digits of accuracy + // we return the larger of the two + // + // e.g. 1e20 - 1e-20 = 1e20 + return abs_lhs_bigger ? ReturnType{detail::shrink_significand(lhs_sig, lhs_exp), lhs_exp, false} : + ReturnType{detail::shrink_significand(rhs_sig, rhs_exp), rhs_exp, true}; + } + + // The two numbers can be subtracted together without special handling + + auto& sig_bigger {abs_lhs_bigger ? signed_sig_lhs : signed_sig_rhs}; + auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp}; + auto& sig_smaller {abs_lhs_bigger ? signed_sig_rhs : signed_sig_lhs}; + auto& smaller_sign {abs_lhs_bigger ? rhs_sign : lhs_sign}; + + if (delta_exp == 1) + { + sig_bigger *= 10; + --delta_exp; + --exp_bigger; + } + else if (delta_exp >= 2) + { + sig_bigger *= 100; + delta_exp -= 2; + exp_bigger -= 2; + } + + while (delta_exp > 1) + { + sig_smaller /= 10; + --delta_exp; + } + + if (delta_exp == 1) + { + detail::fenv_round(sig_smaller, smaller_sign); + } + + // Both of the significands are less than 9'999'999'999'999'999, so we can safely + // cast them to signed 64-bit ints to calculate the new significand + std::int64_t new_sig {}; // NOLINT : Value is never used but can't leave uninitialized in constexpr function + + if (rhs_sign && !lhs_sign) + { + new_sig = signed_sig_lhs + signed_sig_rhs; + } + else + { + new_sig = signed_sig_lhs - signed_sig_rhs; + } + + const auto new_exp {abs_lhs_bigger ? lhs_exp : rhs_exp}; + const auto new_sign {new_sig < 0}; + const auto res_sig {detail::make_positive_unsigned(new_sig)}; + + return {res_sig, new_exp, new_sign}; +} + +template +constexpr auto d128_sub_impl(T1 lhs_sig, std::int32_t lhs_exp, bool lhs_sign, + T2 rhs_sig, std::int32_t rhs_exp, bool rhs_sign, + bool abs_lhs_bigger) noexcept -> ReturnType +{ + auto delta_exp {lhs_exp > rhs_exp ? lhs_exp - rhs_exp : rhs_exp - lhs_exp}; + + if (delta_exp > detail::precision_v + 1) + { + // If the difference in exponents is more than the digits of accuracy + // we return the larger of the two + // + // e.g. 1e20 - 1e-20 = 1e20 + return abs_lhs_bigger ? ReturnType{detail::shrink_significand(lhs_sig, lhs_exp), lhs_exp, false} : + ReturnType{detail::shrink_significand(rhs_sig, rhs_exp), rhs_exp, true}; + } + + // The two numbers can be subtracted together without special handling + + auto& sig_bigger {abs_lhs_bigger ? lhs_sig : rhs_sig}; + auto& exp_bigger {abs_lhs_bigger ? lhs_exp : rhs_exp}; + auto& sig_smaller {abs_lhs_bigger ? rhs_sig : lhs_sig}; + auto& smaller_sign {abs_lhs_bigger ? rhs_sign : lhs_sign}; + + if (delta_exp == 1) + { + sig_bigger *= 10; + --delta_exp; + --exp_bigger; + } + else if (delta_exp >= 2) + { + sig_bigger *= 100; + delta_exp -= 2; + exp_bigger -= 2; + } + + while (delta_exp > 1) + { + sig_smaller /= detail::pow10(static_cast(delta_exp - 1)); + delta_exp = 1; + } + + if (delta_exp == 1) + { + detail::fenv_round(sig_smaller, smaller_sign); + } + + auto signed_sig_lhs {detail::make_signed_value(lhs_sig, lhs_sign)}; + auto signed_sig_rhs {detail::make_signed_value(rhs_sig, rhs_sign)}; + + // Both of the significands are less than 9'999'999'999'999'999, so we can safely + // cast them to signed 64-bit ints to calculate the new significand + detail::int128 new_sig {}; // NOLINT : Value is never used but can't leave uninitialized in constexpr function + + if (rhs_sign && !lhs_sign) + { + new_sig = signed_sig_lhs + signed_sig_rhs; + } + else + { + new_sig = signed_sig_lhs - signed_sig_rhs; + } + + const auto new_exp {abs_lhs_bigger ? lhs_exp : rhs_exp}; + const auto new_sign {new_sig < 0}; + const auto res_sig {detail::make_positive_unsigned(new_sig)}; + + return {res_sig, new_exp, new_sign}; +} + } } } diff --git a/include/boost/decimal/detail/to_integral.hpp b/include/boost/decimal/detail/to_integral.hpp index 772570208..51e060127 100644 --- a/include/boost/decimal/detail/to_integral.hpp +++ b/include/boost/decimal/detail/to_integral.hpp @@ -63,7 +63,7 @@ constexpr auto to_integral(Decimal val) noexcept } else if (expval < 0) { - result /= detail::pow10(detail::make_positive_unsigned(expval)); + result /= detail::pow10(abs_exp_val); } BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) @@ -106,7 +106,7 @@ constexpr auto to_integral_128(Decimal val) noexcept } else if (expval < 0) { - sig /= detail::pow10(detail::make_positive_unsigned(expval)); + sig /= detail::pow10(abs_exp_val); } auto result {static_cast(sig)}; diff --git a/include/boost/decimal/detail/type_traits.hpp b/include/boost/decimal/detail/type_traits.hpp index 2faba809a..5b7b841ec 100644 --- a/include/boost/decimal/detail/type_traits.hpp +++ b/include/boost/decimal/detail/type_traits.hpp @@ -144,6 +144,12 @@ struct is_decimal_floating_point { static constexpr bool value = tru template <> struct is_decimal_floating_point { static constexpr bool value = true; }; +template <> +struct is_decimal_floating_point { static constexpr bool value = true; }; + +template <> +struct is_decimal_floating_point { static constexpr bool value = true; }; + template constexpr bool is_decimal_floating_point::value; diff --git a/include/boost/decimal/fwd.hpp b/include/boost/decimal/fwd.hpp index f08861a4b..67583524b 100644 --- a/include/boost/decimal/fwd.hpp +++ b/include/boost/decimal/fwd.hpp @@ -16,7 +16,9 @@ namespace decimal { class decimal32; class decimal32_fast; class decimal64; +class decimal64_fast; class decimal128; +class decimal128_fast; } // namespace decimal } // namespace boost @@ -44,6 +46,13 @@ class numeric_limits; struct numeric_limits; #endif +template <> +#ifdef _MSC_VER +class numeric_limits; +#else +struct numeric_limits; +#endif + template <> #ifdef _MSC_VER class numeric_limits; @@ -51,6 +60,13 @@ class numeric_limits; struct numeric_limits; #endif +template <> +#ifdef _MSC_VER +class numeric_limits; +#else +struct numeric_limits; +#endif + } // Namespace std #endif // BOOST_DECIMAL_BUILD_MODULE diff --git a/include/boost/decimal/hash.hpp b/include/boost/decimal/hash.hpp index 299e21621..6fd66d7f2 100644 --- a/include/boost/decimal/hash.hpp +++ b/include/boost/decimal/hash.hpp @@ -79,6 +79,44 @@ struct hash } }; -} +BOOST_DECIMAL_EXPORT template <> +struct hash +{ + // Since the underlying type is a std::uint64_t we will rely on its hash function from the STL + // First we convert to a decimal64 so they will have the same hash value + auto operator()(const boost::decimal::decimal64_fast& v) const noexcept -> std::size_t + { + boost::decimal::decimal64 v_64 {v}; + std::uint64_t bits; + std::memcpy(&bits, &v_64, sizeof(std::uint64_t)); + + return std::hash{}(bits); + } +}; + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + +BOOST_DECIMAL_EXPORT template <> +struct hash +{ + // Take the xor of the two words and hash that + auto operator()(const boost::decimal::decimal128_fast& v) const noexcept -> std::size_t + { + boost::decimal::decimal128 v_128 {v}; + boost::decimal::detail::uint128 bits; + std::memcpy(&bits, &v_128, sizeof(boost::decimal::detail::uint128)); + + return std::hash{}(bits.high ^ bits.low); + } +}; + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic pop +#endif + +} // namespace std #endif //BOOST_DECIMAL_HASH_HPP diff --git a/include/boost/decimal/numbers.hpp b/include/boost/decimal/numbers.hpp index cf183702c..d99b12fbe 100644 --- a/include/boost/decimal/numbers.hpp +++ b/include/boost/decimal/numbers.hpp @@ -24,6 +24,10 @@ 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 <> +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 log2e_v = Dec{UINT64_C(1442695040888963407), -18}; @@ -31,6 +35,10 @@ 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 <> +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 log10e_v = Dec{UINT64_C(4342944819032518277), -19}; @@ -38,12 +46,19 @@ 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 <> +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 log10_2_v = Dec{UINT64_C(3010299956639811952), -19}; 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}; @@ -52,6 +67,10 @@ 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}; @@ -63,6 +82,10 @@ 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}; @@ -70,6 +93,10 @@ 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}; @@ -77,6 +104,10 @@ 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}; @@ -84,6 +115,10 @@ 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}; @@ -91,6 +126,10 @@ 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}; @@ -98,6 +137,10 @@ 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}; @@ -105,6 +148,10 @@ 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}; @@ -112,6 +159,10 @@ 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}; @@ -119,6 +170,10 @@ 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}; @@ -126,6 +181,10 @@ 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}; @@ -133,6 +192,10 @@ 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}; @@ -140,6 +203,10 @@ 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}; @@ -147,6 +214,10 @@ 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}; @@ -154,6 +225,10 @@ 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}; + BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto e {e_v}; BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto log10_2 {log10_2_v}; BOOST_DECIMAL_EXPORT BOOST_DECIMAL_CONSTEXPR_VARIABLE auto log10e {log10e_v}; diff --git a/include/boost/decimal/type_traits.hpp b/include/boost/decimal/type_traits.hpp index 8e7d1629e..9540c0945 100644 --- a/include/boost/decimal/type_traits.hpp +++ b/include/boost/decimal/type_traits.hpp @@ -41,26 +41,36 @@ BOOST_DECIMAL_EXPORT template <> struct is_arithmetic BOOST_DECIMAL_EXPORT template <> struct is_arithmetic : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_arithmetic : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_arithmetic : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_arithmetic : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_arithmetic : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_fundamental : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_fundamental : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_fundamental : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_fundamental : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_fundamental : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_fundamental : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_scalar : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_scalar : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_scalar : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_scalar : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_scalar : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_scalar : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_class : public false_type {}; BOOST_DECIMAL_EXPORT template <> struct is_class : public false_type {}; BOOST_DECIMAL_EXPORT template <> struct is_class : public false_type {}; BOOST_DECIMAL_EXPORT template <> struct is_class : public false_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_class : public false_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_class : public false_type {}; BOOST_DECIMAL_EXPORT template <> struct is_pod : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_pod : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_pod : public true_type {}; BOOST_DECIMAL_EXPORT template <> struct is_pod : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_pod : public true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_pod : public true_type {}; } // namespace boost @@ -92,6 +102,8 @@ BOOST_DECIMAL_EXPORT template <> struct is_decimal_floating_point struct is_decimal_floating_point : public decimal::detail::local_true_type{}; BOOST_DECIMAL_EXPORT template <> struct is_decimal_floating_point : public decimal::detail::local_true_type{}; BOOST_DECIMAL_EXPORT template <> struct is_decimal_floating_point : public decimal::detail::local_true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_decimal_floating_point : public decimal::detail::local_true_type {}; +BOOST_DECIMAL_EXPORT template <> struct is_decimal_floating_point : public decimal::detail::local_true_type {}; #if defined(__cpp_inline_variables) && __cpp_inline_variables >= 201606L BOOST_DECIMAL_EXPORT template diff --git a/test/Jamfile b/test/Jamfile index b066569a6..ac403747d 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -42,6 +42,7 @@ project : requirements ; run-fail benchmarks.cpp ; +run compare_dec128_and_fast.cpp ; compile-fail concepts_test.cpp ; run github_issue_426.cpp ; run github_issue_448.cpp ; @@ -55,12 +56,17 @@ run random_decimal32_fast_comp.cpp ; run random_decimal32_fast_math.cpp ; run random_decimal32_math.cpp ; run random_decimal64_comp.cpp ; +run random_decimal64_fast_comp.cpp ; +run random_decimal64_fast_math.cpp ; run random_decimal64_math.cpp ; run random_decimal128_comp.cpp ; +run random_decimal128_fast_comp.cpp ; +run random_decimal128_fast_math.cpp ; run random_decimal128_math.cpp ; run random_mixed_decimal_comp.cpp ; run random_mixed_decimal_math.cpp ; run roundtrip_decimal32.cpp ; +run roundtrip_decimal32_fast.cpp ; run roundtrip_decimal64.cpp ; run roundtrip_decimal128.cpp ; run test_acos.cpp ; @@ -73,6 +79,8 @@ run test_atan.cpp ; run test_atan2.cpp ; run test_atanh.cpp ; compile-fail test_bad_evaluation_method.cpp ; +run test_beta.cpp ; +run test_bid_conversions.cpp ; run test_big_uints.cpp ; run test_boost_math_univariate_stats.cpp ; run test_cbrt.cpp ; @@ -81,8 +89,11 @@ run test_constants.cpp ; run test_cosh.cpp ; run test_decimal32.cpp ; run test_decimal32_fast_basis.cpp ; +run test_decimal32_fast_stream.cpp ; run test_decimal32_stream.cpp ; run test_decimal64_basis.cpp ; +run test_decimal64_fast_basis.cpp ; +run test_decimal64_fast_stream.cpp ; run test_decimal64_stream.cpp ; run test_decimal128_basis.cpp ; run test_decimal_quantum.cpp ; @@ -94,7 +105,9 @@ run test_erf.cpp ; run test_exp.cpp ; compile-fail test_explicit_floats.cpp ; run test_expm1.cpp ; +run test_fast_math.cpp ; run test_fenv.cpp ; +run test_fixed_width_trunc.cpp ; run test_float_conversion.cpp ; run-fail test_fprintf.cpp ; run test_frexp_ldexp.cpp ; @@ -126,3 +139,4 @@ run test_tgamma.cpp ; run test_to_chars.cpp ; run test_to_string.cpp ; run test_type_traits.cpp ; +run test_zeta.cpp ; diff --git a/test/benchmarks.cpp b/test/benchmarks.cpp index 1476b0df4..c2250271d 100644 --- a/test/benchmarks.cpp +++ b/test/benchmarks.cpp @@ -101,7 +101,7 @@ BOOST_DECIMAL_NO_INLINE void test_comparisons(const std::vector& data_vec, co const auto t2 = std::chrono::steady_clock::now(); - std::cout << "comparisons<" << std::left << std::setw(10) << label << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << "comparisons<" << std::left << std::setw(11) << label << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } template @@ -122,7 +122,7 @@ BOOST_DECIMAL_NO_INLINE void test_two_element_operation(const std::vector& da const auto t2 = std::chrono::steady_clock::now(); - std::cout << operation << "<" << std::left << std::setw(10) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << operation << "<" << std::left << std::setw(11) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } template @@ -141,7 +141,7 @@ BOOST_DECIMAL_NO_INLINE void test_one_element_operation(const std::vector& da const auto t2 = std::chrono::steady_clock::now(); - std::cout << operation << "<" << std::left << std::setw(10) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << operation << "<" << std::left << std::setw(11) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } template @@ -191,7 +191,7 @@ 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(10) << 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> @@ -220,7 +220,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(10) << type << ">, " << label << ", " << precision << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << "boost::charconv::to_chars<" << std::left << std::setw(11) << type << ">, " << label << ", " << precision << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } @@ -307,7 +307,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(10) << type << ">, " << label << ": " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << "boost::charconv::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 +329,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(10) << 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 @@ -351,6 +351,8 @@ int main() const auto dec128_vector = generate_random_vector(); const auto dec32_fast_vector = generate_random_vector(); + const auto dec64_fast_vector = generate_random_vector(); + const auto dec128_fast_vector = generate_random_vector(); std::cout << "===== Comparisons =====\n"; @@ -360,6 +362,8 @@ int main() test_comparisons(dec64_vector, "decimal64"); test_comparisons(dec128_vector, "decimal128"); test_comparisons(dec32_fast_vector, "dec32_fast"); + test_comparisons(dec64_fast_vector, "dec64_fast"); + test_comparisons(dec128_fast_vector, "dec128_fast"); std::cout << "\n===== Addition =====\n"; @@ -369,6 +373,8 @@ int main() test_two_element_operation(dec64_vector, std::plus<>(), "Addition", "decimal64"); test_two_element_operation(dec128_vector, std::plus<>(), "Addition", "decimal128"); test_two_element_operation(dec32_fast_vector, std::plus<>(), "Addition", "dec32_fast"); + test_two_element_operation(dec64_fast_vector, std::plus<>(), "Addition", "dec64_fast"); + test_two_element_operation(dec128_fast_vector, std::plus<>(), "Addition", "dec128_fast"); std::cout << "\n===== Subtraction =====\n"; @@ -378,6 +384,8 @@ int main() test_two_element_operation(dec64_vector, std::minus<>(), "Subtraction", "decimal64"); test_two_element_operation(dec128_vector, std::minus<>(), "Subtraction", "decimal128"); test_two_element_operation(dec32_fast_vector, std::minus<>(), "Subtraction", "dec32_fast"); + test_two_element_operation(dec64_fast_vector, std::minus<>(), "Subtraction", "dec64_fast"); + test_two_element_operation(dec128_fast_vector, std::minus<>(), "Subtraction", "dec128_fast"); std::cout << "\n===== Multiplication =====\n"; @@ -387,6 +395,8 @@ int main() test_two_element_operation(dec64_vector, std::multiplies<>(), "Multiplication", "decimal64"); test_two_element_operation(dec128_vector, std::multiplies<>(), "Multiplication", "decimal128"); test_two_element_operation(dec32_fast_vector, std::multiplies<>(), "Multiplication", "dec32_fast"); + test_two_element_operation(dec64_fast_vector, std::multiplies<>(), "Multiplication", "dec64_fast"); + test_two_element_operation(dec128_fast_vector, std::multiplies<>(), "Multiplication", "dec128_fast"); std::cout << "\n===== Division =====\n"; @@ -396,6 +406,8 @@ int main() test_two_element_operation(dec64_vector, std::divides<>(), "Division", "decimal64"); test_two_element_operation(dec128_vector, std::divides<>(), "Division", "decimal128"); test_two_element_operation(dec32_fast_vector, std::divides<>(), "Division", "dec32_fast"); + test_two_element_operation(dec64_fast_vector, std::divides<>(), "Division", "dec64_fast"); + test_two_element_operation(dec64_fast_vector, std::divides<>(), "Division", "dec128_fast"); /* std::cout << "\n===== sqrt =====\n"; diff --git a/test/compare_dec128_and_fast.cpp b/test/compare_dec128_and_fast.cpp new file mode 100644 index 000000000..83bb971e4 --- /dev/null +++ b/test/compare_dec128_and_fast.cpp @@ -0,0 +1,296 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(128U); // Number of trials +#else +static constexpr auto N = static_cast(8U); // Number of trials +#endif + +static std::mt19937_64 rng(42); + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4146) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + +void test_add() +{ + std::uniform_real_distribution big_vals(std::numeric_limits::lowest(), std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {big_vals(rng)}; + const auto val2 {big_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 + dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 + dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } + + std::uniform_real_distribution small_vals(0.0, 1.0); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {small_vals(rng)}; + const auto val2 {small_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 + dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 + dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } +} + +void test_sub() +{ + std::uniform_real_distribution big_vals(std::numeric_limits::lowest(), std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {big_vals(rng)}; + const auto val2 {big_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 - dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 + dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } + + std::uniform_real_distribution small_vals(0.0, 1.0); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {small_vals(rng)}; + const auto val2 {small_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 - dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 - dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } +} + +void test_mul() +{ + std::uniform_real_distribution big_vals(std::numeric_limits::lowest(), std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {big_vals(rng)}; + const auto val2 {big_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 * dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 * dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } + + std::uniform_real_distribution small_vals(0.0, 1.0); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {small_vals(rng)}; + const auto val2 {small_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 * dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 * dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } +} + +void test_div() +{ + std::uniform_real_distribution big_vals(std::numeric_limits::lowest(), std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {big_vals(rng)}; + const auto val2 {big_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 / dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 / dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } + + std::uniform_real_distribution small_vals(0.0, 1.0); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {small_vals(rng)}; + const auto val2 {small_vals(rng)}; + + const decimal128 dec128_1 {val1}; + const decimal128 dec128_2 {val2}; + const decimal128 dec128_res {dec128_1 / dec128_2}; + + const decimal128_fast dec128_fast_1 {val1}; + const decimal128_fast dec128_fast_2 {val2}; + const decimal128_fast dec128_fast_res {dec128_fast_1 / dec128_fast_2}; + + if (!BOOST_TEST_EQ(static_cast(dec128_res), static_cast(dec128_fast_res))) + { + std::cerr << std::setprecision(35) + << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 1: " << dec128_1 + << "\nDec 2: " << dec128_2 + << "\nDec Res: " << dec128_res + << "\nDecfast 1: " << dec128_fast_1 + << "\nDecfast 2: " << dec128_fast_2 + << "\nDecfast res: " << dec128_fast_res << std::endl; + } + } +} + +int main() +{ + #if !(defined(__i386) || defined(_M_IX86)) && !(defined(_MSVC_LANG)) + test_add(); + test_sub(); + test_mul(); + test_div(); + #endif + + return boost::report_errors(); +} diff --git a/test/mini_to_chars.hpp b/test/mini_to_chars.hpp index f8bc8d615..23db85674 100644 --- a/test/mini_to_chars.hpp +++ b/test/mini_to_chars.hpp @@ -14,6 +14,8 @@ #ifdef BOOST_DECIMAL_HAS_INT128 +// LCOV_EXCL_START + #include static char* mini_to_chars( char (&buffer)[ 64 ], boost::decimal::detail::uint128_t v ) @@ -58,6 +60,8 @@ std::ostream& operator<<( std::ostream& os, boost::decimal::detail::int128_t v ) return os; } +// LCOV_EXCL_STOP + #endif // #ifdef BOOST_HAS_INT128 #endif //BOOST_DECIMAL_MINI_TO_CHARS_HPP diff --git a/test/random_decimal128_fast_comp.cpp b/test/random_decimal128_fast_comp.cpp new file mode 100644 index 000000000..84711e2b1 --- /dev/null +++ b/test/random_decimal128_fast_comp.cpp @@ -0,0 +1,597 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +// NOLINTNEXTLINE : Seed with a constant for repeatability +static std::mt19937_64 rng(42); // NOSONAR : Global rng is not const + +template +void random_LT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 < dec2, val1 < val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge cases + BOOST_TEST(decimal128_fast(dist(rng)) < std::numeric_limits::infinity()); + BOOST_TEST(!(decimal128_fast(dist(rng)) < -std::numeric_limits::infinity())); + BOOST_TEST(!(decimal128_fast(dist(rng)) < std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() < std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_LT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 < dec2, val1 < val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Reverse order of the operands + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(decimal128_fast(val1))}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 < dec2, val1 < val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge Cases + BOOST_TEST_EQ(decimal128_fast(1) < T(1), false); + BOOST_TEST_EQ(decimal128_fast(10) < T(10), false); + BOOST_TEST_EQ(T(1) < decimal128_fast(1), false); + BOOST_TEST_EQ(T(10) < decimal128_fast(10), false); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_INFINITY < T(1), false); + BOOST_TEST_EQ(-BOOST_DECIMAL_DEC_INFINITY < T(1), true); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_NAN < T(1), false); +} + +template +void random_LE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 <= dec2, val1 <= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(decimal128_fast(dist(rng)) <= std::numeric_limits::infinity()); + BOOST_TEST(!(decimal128_fast(dist(rng)) <= -std::numeric_limits::infinity())); + BOOST_TEST(!(decimal128_fast(dist(rng)) <= std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() <= std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_LE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 <= dec2, val1 <= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(dist(rng) <= std::numeric_limits::infinity()); + BOOST_TEST(!(dist(rng) <= -std::numeric_limits::infinity())); + BOOST_TEST(!(dist(rng) <= std::numeric_limits::quiet_NaN())); +} + +template +void random_GT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 > dec2, val1 > val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + const auto inf = std::numeric_limits::infinity(); + BOOST_TEST(!(decimal128_fast(dist(rng)) > inf)); + BOOST_TEST((decimal128_fast(dist(rng)) > -inf)); + BOOST_TEST(!(decimal128_fast(dist(rng)) > std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() > std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_GT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 > dec2, val1 > val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(dist(rng) > std::numeric_limits::infinity())); + BOOST_TEST((dist(rng) > -std::numeric_limits::infinity())); + BOOST_TEST(!(dist(rng) > std::numeric_limits::quiet_NaN())); +} + +template +void random_GE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 >= dec2, val1 >= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(decimal128_fast(dist(rng)) >= std::numeric_limits::infinity())); + + // MSVC 14.2 + #if !defined(_MSC_VER) + BOOST_TEST((decimal128_fast(dist(rng)) >= -std::numeric_limits::infinity())); + #endif + + BOOST_TEST(!(decimal128_fast(dist(rng)) >= std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() >= std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_GE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 >= dec2, val1 >= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(dist(rng) >= std::numeric_limits::infinity())); + BOOST_TEST((dist(rng) >= -std::numeric_limits::infinity())); + BOOST_TEST(!(dist(rng) >= std::numeric_limits::quiet_NaN())); +} + +template +void random_EQ(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(std::numeric_limits::quiet_NaN() == std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_EQ(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(decimal128_fast(val1))}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge Cases + BOOST_TEST_EQ(decimal128_fast(1), T(1)); + BOOST_TEST_EQ(decimal128_fast(10), T(10)); + BOOST_TEST_EQ(decimal128_fast(100), T(100)); + BOOST_TEST_EQ(decimal128_fast(1000), T(1000)); + BOOST_TEST_EQ(decimal128_fast(10000), T(10000)); + BOOST_TEST_EQ(decimal128_fast(100000), T(100000)); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_NAN == T(1), false); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_INFINITY == T(1), false); +} + +template +void random_NE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 != dec2, val1 != val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST((std::numeric_limits::quiet_NaN() != std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_NE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 != dec2, val1 != val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } +} + +#ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR +template +void random_SPACESHIP(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + if (!BOOST_TEST((dec1 <=> dec2) == (val1 <=> val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST((decimal128_fast(dist(rng)) <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); + BOOST_TEST((std::numeric_limits::quiet_NaN() <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); +} + +template +void random_mixed_SPACESHIP(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + if (!BOOST_TEST((dec1 <=> dec2) == (val1 <=> val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + if (!BOOST_TEST((dist(rng) <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered)) + { + // LCOV_EXCL_START + const auto eval {dist(rng) <=> std::numeric_limits::quiet_NaN()}; + if (eval == std::partial_ordering::less) + std::cerr << "Less" << std::endl; + else if (eval == std::partial_ordering::greater) + std::cerr << "Greater" << std::endl; + else if (eval == std::partial_ordering::equivalent) + std::cerr << "Equivalent" << std::endl; + else + std::cerr << "Unordered" << std::endl; + // LCOV_EXCL_STOP + } + + BOOST_TEST((std::numeric_limits::quiet_NaN() <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); +} +#endif + +int main() +{ + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + + #ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + #endif + + return boost::report_errors(); +} diff --git a/test/random_decimal128_fast_math.cpp b/test/random_decimal128_fast_math.cpp new file mode 100644 index 000000000..2f46ec6e2 --- /dev/null +++ b/test/random_decimal128_fast_math.cpp @@ -0,0 +1,979 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +static std::mt19937_64 rng(42); + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4146) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + + +template +void random_addition(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + const decimal128_fast res = dec1 + dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() + decimal128_fast{0,0})); + BOOST_TEST(isinf(decimal128_fast{0,0} + std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() + decimal128_fast{0,0})); + BOOST_TEST(isnan(decimal128_fast{0,0} + std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_addition(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T trunc_val_2 {static_cast(decimal128_fast(val2))}; + + const decimal128_fast res = dec1 + trunc_val_2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << trunc_val_2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() + dist(rng))); + BOOST_TEST(isinf(dist(rng) + std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() + dist(rng))); + BOOST_TEST(isnan(dist(rng) + std::numeric_limits::quiet_NaN())); +} + +template +void random_subtraction(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + const decimal128_fast res = dec1 - dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() - decimal128_fast{0,0})); + BOOST_TEST(isinf(decimal128_fast{0,0} - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - decimal128_fast{0,0})); + BOOST_TEST(isnan(decimal128_fast{0,0} - std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_subtraction(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T trunc_val_2 {static_cast(decimal128_fast(val2))}; + + const decimal128_fast res = dec1 - trunc_val_2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << trunc_val_2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T trunc_val_1 {static_cast(decimal128_fast(val1))}; + const decimal128_fast dec2 {val2}; + + const decimal128_fast res = trunc_val_1 - dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << trunc_val_1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() - dist(rng))); + BOOST_TEST(isinf(dist(rng) - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - dist(rng))); + BOOST_TEST(isnan(dist(rng) - std::numeric_limits::quiet_NaN())); +} + +template +void spot_check_sub(T lhs, T rhs) +{ + const decimal128_fast dec1 {lhs}; + const decimal128_fast dec2 {rhs}; + const decimal128_fast res {dec1 - dec2}; + const auto res_int {static_cast(res)}; + + if (!BOOST_TEST_EQ(res_int, lhs - rhs)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << lhs + << "\nDec 1: " << dec1 + << "\nVal 2: " << rhs + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << lhs - rhs << std::endl; + // LCOV_EXCL_STOP + } +} + +template +void random_multiplication(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + const decimal128_fast res {dec1 * dec2}; + const decimal128_fast res_int {val1 * val2}; + + if (val1 * val2 == 0) + { + continue; + } + + if (!BOOST_TEST_EQ(res, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << decimal128_fast{val1 * val2} << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() * decimal128_fast(dist(rng)))); + BOOST_TEST(isinf(decimal128_fast(dist(rng)) * std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() * decimal128_fast(dist(rng)))); + BOOST_TEST(isnan(decimal128_fast(dist(rng)) * std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_multiplication(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + const decimal128_fast res {dec1 * dec2}; + const decimal128_fast res_int {val1 * val2}; + + // Integers don't have a concept of negative 0 + if (val1 * val2 == 0) + { + continue; + } + + if (!BOOST_TEST_EQ(res, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 * val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() * dist(rng))); + BOOST_TEST(isinf(dist(rng) * std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() * dist(rng))); + BOOST_TEST(isnan(dist(rng) * std::numeric_limits::quiet_NaN())); +} + +template +void random_division(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const decimal128_fast dec2 {val2}; + + const decimal128_fast res {dec1 / dec2}; + const decimal128_fast res_int {static_cast(val1) / static_cast(val2)}; + + if (isinf(res) && isinf(res_int)) + { + } + else if (!BOOST_TEST_EQ(static_cast(res), static_cast(res_int))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() / decimal128_fast(dist(rng)))); + BOOST_TEST(!isinf(decimal128_fast(dist(rng)) / std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() / decimal128_fast(dist(rng)))); + BOOST_TEST(isnan(decimal128_fast(dist(rng)) / std::numeric_limits::quiet_NaN())); + BOOST_TEST(isinf(decimal128_fast(dist(rng)) / decimal128_fast(0))); +} + +template +void random_mixed_division(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal128_fast dec1 {val1}; + const T dec2 {static_cast(decimal128_fast(val2))}; + + const decimal128_fast res {dec1 / dec2}; + const decimal128_fast res_int {static_cast(val1) / static_cast(val2)}; + + if (isinf(res) && isinf(res_int)) + { + } + else if (!BOOST_TEST_EQ(static_cast(res), static_cast(res_int))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(decimal128_fast(val1))}; + const decimal128_fast dec2 {val2}; + + const decimal128_fast res {dec1 / dec2}; + const decimal128_fast res_int {static_cast(val1) / static_cast(val2)}; + + if (isinf(res) && isinf(res_int)) + { + } + else if (!BOOST_TEST(abs(res - res_int) < decimal128_fast(1, -1))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) + << "\nDist: " << abs(res - res_int) << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge cases + const decimal128_fast val1 {dist(rng)}; + const decimal128_fast zero {0, 0}; + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() / dist(rng))); + BOOST_TEST(isinf(std::numeric_limits::infinity() / dist(rng))); + BOOST_TEST(isnan(dist(rng) / std::numeric_limits::quiet_NaN())); + BOOST_TEST_EQ(abs(dist(rng) / std::numeric_limits::infinity()), zero); + BOOST_TEST(isinf(decimal128_fast(dist(rng)) / 0)); + BOOST_TEST(isinf(val1 / zero)); +} +/* +void random_and() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 & dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 & val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_and() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 & val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 & val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {val1 & dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 & val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_START + } + } +} + +void random_or() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 | dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 | val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_or() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 | val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 | val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {val1 | dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 | val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_xor() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 ^ dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 ^ val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_xor() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 ^ val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 ^ val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {val1 ^ dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 ^ val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_left_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 << dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 << val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_left_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 << val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 << val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {val1 << dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 << val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_right_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 >> dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 >> val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_right_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal128_fast res {dec1 >> val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 >> val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal128_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal128_fast res {val1 >> dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 >> val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} +*/ + +int main() +{ + // Values that won't exceed the range of the significand + // Only positive values + random_addition(0, 5'000'000); + random_addition(0LL, 4'000'000'000'000LL); + random_mixed_addition(0, 5'000'000); + random_mixed_addition(0LL, 4'000'000'000'000LL); + + // Only two negative values + random_addition(-5'000'000, 0); + random_addition(-4'000'000'000'000LL, 0LL); + random_mixed_addition(-5'000'000, 0); + random_mixed_addition(-4'000'000'000'000LL, 0LL); + + // Mixed values + random_addition(-5'000'000, 5'000'000); + random_addition(-5'000'000'000'000LL, 5'000'000'000'000LL); + random_mixed_addition(-5'000'000, 5'000'000); + random_mixed_addition(-5'000'000'000'000LL, 5'000'000'000'000LL); + + // Subtraction + random_subtraction(0, 5'000'000); + random_subtraction(0LL, 4'000'000'000'000LL); + random_mixed_subtraction(0, 5'000'000); + random_mixed_subtraction(0LL, 4'000'000'000'000LL); + + // Only two negative values + random_subtraction(-5'000'000, 0); + random_subtraction(-4'000'000'000'000LL, 0LL); + random_mixed_subtraction(-5'000'000, 0); + random_mixed_subtraction(-4'000'000'000'000LL, 0LL); + + // Mixed values + random_subtraction(-5'000'000, 5'000'000); + random_subtraction(-4'000'000'000'000LL, 4'000'000'000'000LL); + random_mixed_subtraction(-5'000'000, 5'000'000); + random_mixed_subtraction(-4'000'000'000'000LL, 4'000'000'000'000LL); + + // Multiplication + const auto sqrt_int_max = static_cast(std::sqrt(static_cast((std::numeric_limits::max)()))); + + // Positive + random_multiplication(0, 5'000); + random_multiplication(0LL, 5'000LL); + random_multiplication(0, sqrt_int_max); + random_mixed_multiplication(0, 5'000); + random_mixed_multiplication(0LL, 5'000LL); + random_mixed_multiplication(0, sqrt_int_max); + + // Negative + random_multiplication(-5'000, 0); + random_multiplication(-5'000LL, 0LL); + random_multiplication(-sqrt_int_max, 0); + random_mixed_multiplication(-5'000, 0); + random_mixed_multiplication(-5'000LL, 0LL); + random_mixed_multiplication(-sqrt_int_max, 0); + + // Mixed + random_multiplication(-5'000, 5'000); + random_multiplication(-5'000LL, 5'000LL); + random_multiplication(-sqrt_int_max, sqrt_int_max); + random_mixed_multiplication(-5'000, 5'000); + random_mixed_multiplication(-5'000LL, 5'000LL); + random_mixed_multiplication(-sqrt_int_max, sqrt_int_max); + + // Division + + // Positive + random_division(0, 5'000); + random_division(0LL, 5'000LL); + random_division(0, sqrt_int_max); + random_mixed_division(0, 5'000); + random_mixed_division(0LL, 5'000LL); + random_mixed_division(0, sqrt_int_max); + + // Negative + random_division(-5'000, 0); + random_division(-5'000LL, 0LL); + random_division(-sqrt_int_max, 0); + random_mixed_division(-5'000, 0); + random_mixed_division(-5'000LL, 0LL); + random_mixed_division(-sqrt_int_max, 0); + + // Mixed + random_division(-5'000, 5'000); + random_division(-5'000LL, 5'000LL); + random_division(-sqrt_int_max, sqrt_int_max); + random_mixed_division(-5'000, 5'000); + random_mixed_division(-5'000LL, 5'000LL); + random_mixed_division(-sqrt_int_max, sqrt_int_max); + + // Spot checked values + spot_check_sub(945501, 80); + spot_check_sub(562, 998980); + spot_check_sub(-954783, 746); + spot_check_sub(513479119LL, 972535711690LL); + /* + // Bitwise operators + random_and(); + random_mixed_and(); + random_or(); + random_mixed_or(); + random_xor(); + random_mixed_xor(); + random_left_shift(); + random_mixed_left_shift(); + random_right_shift(); + random_mixed_right_shift(); + */ + + return boost::report_errors(); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic pop +#endif + diff --git a/test/random_decimal64_fast_comp.cpp b/test/random_decimal64_fast_comp.cpp new file mode 100644 index 000000000..ea24b9fa3 --- /dev/null +++ b/test/random_decimal64_fast_comp.cpp @@ -0,0 +1,597 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +// NOLINTNEXTLINE : Seed with a constant for repeatability +static std::mt19937_64 rng(42); // NOSONAR : Global rng is not const + +template +void random_LT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 < dec2, val1 < val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge cases + BOOST_TEST(decimal64_fast(dist(rng)) < std::numeric_limits::infinity()); + BOOST_TEST(!(decimal64_fast(dist(rng)) < -std::numeric_limits::infinity())); + BOOST_TEST(!(decimal64_fast(dist(rng)) < std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() < std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_LT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 < dec2, val1 < val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Reverse order of the operands + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(decimal64_fast(val1))}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 < dec2, val1 < val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge Cases + BOOST_TEST_EQ(decimal64_fast(1) < T(1), false); + BOOST_TEST_EQ(decimal64_fast(10) < T(10), false); + BOOST_TEST_EQ(T(1) < decimal64_fast(1), false); + BOOST_TEST_EQ(T(10) < decimal64_fast(10), false); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_INFINITY < T(1), false); + BOOST_TEST_EQ(-BOOST_DECIMAL_DEC_INFINITY < T(1), true); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_NAN < T(1), false); +} + +template +void random_LE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 <= dec2, val1 <= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(decimal64_fast(dist(rng)) <= std::numeric_limits::infinity()); + BOOST_TEST(!(decimal64_fast(dist(rng)) <= -std::numeric_limits::infinity())); + BOOST_TEST(!(decimal64_fast(dist(rng)) <= std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() <= std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_LE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 <= dec2, val1 <= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(dist(rng) <= std::numeric_limits::infinity()); + BOOST_TEST(!(dist(rng) <= -std::numeric_limits::infinity())); + BOOST_TEST(!(dist(rng) <= std::numeric_limits::quiet_NaN())); +} + +template +void random_GT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 > dec2, val1 > val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + const auto inf = std::numeric_limits::infinity(); + BOOST_TEST(!(decimal64_fast(dist(rng)) > inf)); + BOOST_TEST((decimal64_fast(dist(rng)) > -inf)); + BOOST_TEST(!(decimal64_fast(dist(rng)) > std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() > std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_GT(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 > dec2, val1 > val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(dist(rng) > std::numeric_limits::infinity())); + BOOST_TEST((dist(rng) > -std::numeric_limits::infinity())); + BOOST_TEST(!(dist(rng) > std::numeric_limits::quiet_NaN())); +} + +template +void random_GE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 >= dec2, val1 >= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(decimal64_fast(dist(rng)) >= std::numeric_limits::infinity())); + + // MSVC 14.2 + #if !defined(_MSC_VER) + BOOST_TEST((decimal64_fast(dist(rng)) >= -std::numeric_limits::infinity())); + #endif + + BOOST_TEST(!(decimal64_fast(dist(rng)) >= std::numeric_limits::quiet_NaN())); + BOOST_TEST(!(std::numeric_limits::quiet_NaN() >= std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_GE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 >= dec2, val1 >= val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(dist(rng) >= std::numeric_limits::infinity())); + BOOST_TEST((dist(rng) >= -std::numeric_limits::infinity())); + BOOST_TEST(!(dist(rng) >= std::numeric_limits::quiet_NaN())); +} + +template +void random_EQ(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(!(std::numeric_limits::quiet_NaN() == std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_EQ(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(decimal64_fast(val1))}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 == dec2, val1 == val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge Cases + BOOST_TEST_EQ(decimal64_fast(1), T(1)); + BOOST_TEST_EQ(decimal64_fast(10), T(10)); + BOOST_TEST_EQ(decimal64_fast(100), T(100)); + BOOST_TEST_EQ(decimal64_fast(1000), T(1000)); + BOOST_TEST_EQ(decimal64_fast(10000), T(10000)); + BOOST_TEST_EQ(decimal64_fast(100000), T(100000)); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_NAN == T(1), false); + BOOST_TEST_EQ(BOOST_DECIMAL_DEC_INFINITY == T(1), false); +} + +template +void random_NE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST_EQ(dec1 != dec2, val1 != val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST((std::numeric_limits::quiet_NaN() != std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_NE(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + if (!BOOST_TEST_EQ(dec1 != dec2, val1 != val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } +} + +#ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR +template +void random_SPACESHIP(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + if (!BOOST_TEST((dec1 <=> dec2) == (val1 <=> val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST((decimal64_fast(dist(rng)) <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); + BOOST_TEST((std::numeric_limits::quiet_NaN() <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); +} + +template +void random_mixed_SPACESHIP(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + if (!BOOST_TEST((dec1 <=> dec2) == (val1 <=> val2))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 << std::endl; + // LCOV_EXCL_STOP + } + } + + if (!BOOST_TEST((dist(rng) <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered)) + { + // LCOV_EXCL_START + const auto eval {dist(rng) <=> std::numeric_limits::quiet_NaN()}; + if (eval == std::partial_ordering::less) + std::cerr << "Less" << std::endl; + else if (eval == std::partial_ordering::greater) + std::cerr << "Greater" << std::endl; + else if (eval == std::partial_ordering::equivalent) + std::cerr << "Equivalent" << std::endl; + else + std::cerr << "Unordered" << std::endl; + // LCOV_EXCL_STOP + } + + BOOST_TEST((std::numeric_limits::quiet_NaN() <=> std::numeric_limits::quiet_NaN()) == std::partial_ordering::unordered); +} +#endif + +int main() +{ + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_LT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_LE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_LE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_GT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GT(std::numeric_limits::min(), std::numeric_limits::max()); + + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_GE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_GE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_EQ(std::numeric_limits::min(), std::numeric_limits::max()); + + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_NE(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_NE(std::numeric_limits::min(), std::numeric_limits::max()); + + #ifdef BOOST_DECIMAL_HAS_SPACESHIP_OPERATOR + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); + #endif + + return boost::report_errors(); +} diff --git a/test/random_decimal64_fast_math.cpp b/test/random_decimal64_fast_math.cpp new file mode 100644 index 000000000..b8f5e6e32 --- /dev/null +++ b/test/random_decimal64_fast_math.cpp @@ -0,0 +1,969 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +static std::mt19937_64 rng(42); + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4146) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + + +template +void random_addition(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + const decimal64_fast res = dec1 + dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() + decimal64_fast{0,0})); + BOOST_TEST(isinf(decimal64_fast{0,0} + std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() + decimal64_fast{0,0})); + BOOST_TEST(isnan(decimal64_fast{0,0} + std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_addition(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T trunc_val_2 {static_cast(decimal64_fast(val2))}; + + const decimal64_fast res = dec1 + trunc_val_2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << trunc_val_2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() + dist(rng))); + BOOST_TEST(isinf(dist(rng) + std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() + dist(rng))); + BOOST_TEST(isnan(dist(rng) + std::numeric_limits::quiet_NaN())); +} + +template +void random_subtraction(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + const decimal64_fast res = dec1 - dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() - decimal64_fast{0,0})); + BOOST_TEST(isinf(decimal64_fast{0,0} - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - decimal64_fast{0,0})); + BOOST_TEST(isnan(decimal64_fast{0,0} - std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_subtraction(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T trunc_val_2 {static_cast(decimal64_fast(val2))}; + + const decimal64_fast res = dec1 - trunc_val_2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << trunc_val_2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T trunc_val_1 {static_cast(decimal64_fast(val1))}; + const decimal64_fast dec2 {val2}; + + const decimal64_fast res = trunc_val_1 - dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << trunc_val_1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 - val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() - dist(rng))); + BOOST_TEST(isinf(dist(rng) - std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() - dist(rng))); + BOOST_TEST(isnan(dist(rng) - std::numeric_limits::quiet_NaN())); +} + +template +void spot_check_sub(T lhs, T rhs) +{ + const decimal64_fast dec1 {lhs}; + const decimal64_fast dec2 {rhs}; + const decimal64_fast res {dec1 - dec2}; + const auto res_int {static_cast(res)}; + + if (!BOOST_TEST_EQ(res_int, lhs - rhs)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << lhs + << "\nDec 1: " << dec1 + << "\nVal 2: " << rhs + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << lhs - rhs << std::endl; + // LCOV_EXCL_STOP + } +} + + +template +void random_multiplication(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + const decimal64_fast res {dec1 * dec2}; + const decimal64_fast res_int {val1 * val2}; + + if (!BOOST_TEST_EQ(res, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 * val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() * decimal64_fast(dist(rng)))); + BOOST_TEST(isinf(decimal64_fast(dist(rng)) * std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() * decimal64_fast(dist(rng)))); + BOOST_TEST(isnan(decimal64_fast(dist(rng)) * std::numeric_limits::quiet_NaN())); +} + +template +void random_mixed_multiplication(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + const decimal64_fast res {dec1 * dec2}; + const decimal64_fast res_int {val1 * val2}; + + if (!BOOST_TEST_EQ(res, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 * val2 << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() * dist(rng))); + BOOST_TEST(isinf(dist(rng) * std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() * dist(rng))); + BOOST_TEST(isnan(dist(rng) * std::numeric_limits::quiet_NaN())); +} + +template +void random_division(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const decimal64_fast dec2 {val2}; + + const decimal64_fast res {dec1 / dec2}; + const decimal64_fast res_int {static_cast(val1) / static_cast(val2)}; + + if (isinf(res) && isinf(res_int)) + { + } + else if (!BOOST_TEST_EQ(static_cast(res), static_cast(res_int))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isinf(std::numeric_limits::infinity() / decimal64_fast(dist(rng)))); + BOOST_TEST(!isinf(decimal64_fast(dist(rng)) / std::numeric_limits::infinity())); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() / decimal64_fast(dist(rng)))); + BOOST_TEST(isnan(decimal64_fast(dist(rng)) / std::numeric_limits::quiet_NaN())); + BOOST_TEST(isinf(decimal64_fast(dist(rng)) / decimal64_fast(0))); +} + +template +void random_mixed_division(T lower, T upper) +{ + std::uniform_int_distribution dist(lower, upper); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const decimal64_fast dec1 {val1}; + const T dec2 {static_cast(decimal64_fast(val2))}; + + const decimal64_fast res {dec1 / dec2}; + const decimal64_fast res_int {static_cast(val1) / static_cast(val2)}; + + if (isinf(res) && isinf(res_int)) + { + } + else if (!BOOST_TEST_EQ(static_cast(res), static_cast(res_int))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {static_cast(decimal64_fast(val1))}; + const decimal64_fast dec2 {val2}; + + const decimal64_fast res {dec1 / dec2}; + const decimal64_fast res_int {static_cast(val1) / static_cast(val2)}; + + if (isinf(res) && isinf(res_int)) + { + } + else if (!BOOST_TEST(abs(res - res_int) < decimal64_fast(1, -1))) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << static_cast(val1) / static_cast(val2) << std::endl; + // LCOV_EXCL_STOP + } + } + + // Edge cases + const decimal64_fast val1 {dist(rng)}; + const decimal64_fast zero {0, 0}; + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN() / dist(rng))); + BOOST_TEST(isinf(std::numeric_limits::infinity() / dist(rng))); + BOOST_TEST(isnan(dist(rng) / std::numeric_limits::quiet_NaN())); + BOOST_TEST_EQ(abs(dist(rng) / std::numeric_limits::infinity()), zero); + BOOST_TEST(isinf(decimal64_fast(dist(rng)) / 0)); + BOOST_TEST(isinf(val1 / zero)); +} +/* +void random_and() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 & dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 & val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_and() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 & val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 & val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {val1 & dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 & val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_START + } + } +} + +void random_or() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 | dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 | val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_or() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 | val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 | val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {val1 | dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 | val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_xor() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 ^ dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 ^ val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_xor() +{ + std::uniform_int_distribution dist(0, 9'999'999'999'999'999); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 ^ val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 ^ val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {val1 ^ dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 ^ val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_left_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 << dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 << val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_left_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 << val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 << val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {val1 << dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 << val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_right_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 >> dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 >> val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +void random_mixed_right_shift() +{ + std::uniform_int_distribution dist(0, 10); + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec1 {}; + std::memcpy(&dec1, &val1, sizeof(std::uint64_t)); + + const decimal64_fast res {dec1 >> val2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 >> val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } + + for (std::size_t i {}; i < N; ++i) + { + const auto val1 {dist(rng)}; + const auto val2 {dist(rng)}; + + decimal64_fast dec2 {}; + std::memcpy(&dec2, &val2, sizeof(std::uint64_t)); + + const decimal64_fast res {val1 >> dec2}; + std::uint64_t dec_int {}; + std::memcpy(&dec_int, &res, sizeof(std::uint64_t)); + const auto res_int {val1 >> val2}; + + if (!BOOST_TEST_EQ(dec_int, res_int)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << res_int << std::endl; + // LCOV_EXCL_STOP + } + } +} +*/ + +int main() +{ + // Values that won't exceed the range of the significand + // Only positive values + random_addition(0, 5'000'000); + random_addition(0LL, 4'000'000'000'000LL); + random_mixed_addition(0, 5'000'000); + random_mixed_addition(0LL, 4'000'000'000'000LL); + + // Only two negative values + random_addition(-5'000'000, 0); + random_addition(-4'000'000'000'000LL, 0LL); + random_mixed_addition(-5'000'000, 0); + random_mixed_addition(-4'000'000'000'000LL, 0LL); + + // Mixed values + random_addition(-5'000'000, 5'000'000); + random_addition(-5'000'000'000'000LL, 5'000'000'000'000LL); + random_mixed_addition(-5'000'000, 5'000'000); + random_mixed_addition(-5'000'000'000'000LL, 5'000'000'000'000LL); + + // Subtraction + random_subtraction(0, 5'000'000); + random_subtraction(0LL, 4'000'000'000'000LL); + random_mixed_subtraction(0, 5'000'000); + random_mixed_subtraction(0LL, 4'000'000'000'000LL); + + // Only two negative values + random_subtraction(-5'000'000, 0); + random_subtraction(-4'000'000'000'000LL, 0LL); + random_mixed_subtraction(-5'000'000, 0); + random_mixed_subtraction(-4'000'000'000'000LL, 0LL); + + // Mixed values + random_subtraction(-5'000'000, 5'000'000); + random_subtraction(-4'000'000'000'000LL, 4'000'000'000'000LL); + random_mixed_subtraction(-5'000'000, 5'000'000); + random_mixed_subtraction(-4'000'000'000'000LL, 4'000'000'000'000LL); + + // Multiplication + const auto sqrt_int_max = static_cast(std::sqrt(static_cast((std::numeric_limits::max)()))); + + // Positive + random_multiplication(0, 5'000); + random_multiplication(0LL, 5'000LL); + random_multiplication(0, sqrt_int_max); + random_mixed_multiplication(0, 5'000); + random_mixed_multiplication(0LL, 5'000LL); + random_mixed_multiplication(0, sqrt_int_max); + + // Negative + random_multiplication(-5'000, 0); + random_multiplication(-5'000LL, 0LL); + random_multiplication(-sqrt_int_max, 0); + random_mixed_multiplication(-5'000, 0); + random_mixed_multiplication(-5'000LL, 0LL); + random_mixed_multiplication(-sqrt_int_max, 0); + + // Mixed + random_multiplication(-5'000, 5'000); + random_multiplication(-5'000LL, 5'000LL); + random_multiplication(-sqrt_int_max, sqrt_int_max); + random_mixed_multiplication(-5'000, 5'000); + random_mixed_multiplication(-5'000LL, 5'000LL); + random_mixed_multiplication(-sqrt_int_max, sqrt_int_max); + + // Division + + // Positive + random_division(0, 5'000); + random_division(0LL, 5'000LL); + random_division(0, sqrt_int_max); + random_mixed_division(0, 5'000); + random_mixed_division(0LL, 5'000LL); + random_mixed_division(0, sqrt_int_max); + + // Negative + random_division(-5'000, 0); + random_division(-5'000LL, 0LL); + random_division(-sqrt_int_max, 0); + random_mixed_division(-5'000, 0); + random_mixed_division(-5'000LL, 0LL); + random_mixed_division(-sqrt_int_max, 0); + + // Mixed + random_division(-5'000, 5'000); + random_division(-5'000LL, 5'000LL); + random_division(-sqrt_int_max, sqrt_int_max); + random_mixed_division(-5'000, 5'000); + random_mixed_division(-5'000LL, 5'000LL); + random_mixed_division(-sqrt_int_max, sqrt_int_max); + + // Spot checked values + spot_check_sub(945501, 80); + spot_check_sub(562, 998980); + spot_check_sub(-954783, 746); + spot_check_sub(513479119LL, 972535711690LL); + + /* + // Bitwise operators + random_and(); + random_mixed_and(); + random_or(); + random_mixed_or(); + random_xor(); + random_mixed_xor(); + random_left_shift(); + random_mixed_left_shift(); + random_right_shift(); + random_mixed_right_shift(); + */ + + return boost::report_errors(); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +#if defined(__GNUC__) && __GNUC__ >= 8 +# pragma GCC diagnostic pop +#endif + diff --git a/test/roundtrip_decimal32.cpp b/test/roundtrip_decimal32.cpp index a32604aa9..1d2789d22 100644 --- a/test/roundtrip_decimal32.cpp +++ b/test/roundtrip_decimal32.cpp @@ -279,11 +279,6 @@ int main() test_roundtrip_conversion_integer(-9'999'999, 9'999'999); test_roundtrip_conversion_integer(0, 9'999'999); - #if defined(BOOST_DECIMAL_HAS_INT128) && !defined(__STRICT_ANSI__) - test_roundtrip_conversion_integer(-9'999'999, 9'999'999); - test_roundtrip_conversion_integer(0, 9'999'999); - #endif - test_conversion_to_float(); test_conversion_to_float(); test_conversion_to_float(); diff --git a/test/roundtrip_decimal32_fast.cpp b/test/roundtrip_decimal32_fast.cpp new file mode 100644 index 000000000..71eb9c243 --- /dev/null +++ b/test/roundtrip_decimal32_fast.cpp @@ -0,0 +1,338 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "mini_to_chars.hpp" + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4146) +#endif + +template +void test_conversion_to_integer() +{ + errno = 0; + constexpr decimal32_fast one(1, 0); + constexpr decimal32_fast zero(0, 0); + constexpr decimal32_fast half(5, -1); + BOOST_TEST_EQ(static_cast(one), static_cast(1)) && BOOST_TEST_EQ(errno, 0); + BOOST_TEST_EQ(static_cast(one + one), static_cast(2)) && BOOST_TEST_EQ(errno, 0); + BOOST_TEST_EQ(static_cast(zero), static_cast(0)) && BOOST_TEST_EQ(errno, 0); + + BOOST_IF_CONSTEXPR (std::is_signed::value) + { + BOOST_TEST_EQ(static_cast(-one), static_cast(-1)) && BOOST_TEST_EQ(errno, 0); + } + else + { + // Bad conversion so we use zero + BOOST_TEST_EQ(static_cast(-one), static_cast(0)) && BOOST_TEST_EQ(errno, ERANGE); + } + + errno = 0; + BOOST_TEST_EQ(static_cast(std::numeric_limits::infinity()), static_cast(0)) && BOOST_TEST_EQ(errno, ERANGE); + + errno = 0; + BOOST_TEST_EQ(static_cast(-std::numeric_limits::infinity()), static_cast(0)) && BOOST_TEST_EQ(errno, ERANGE); + + errno = 0; + BOOST_TEST_EQ(static_cast(std::numeric_limits::quiet_NaN()), static_cast(0)) && BOOST_TEST_EQ(errno, EINVAL); + + errno = 0; + BOOST_TEST_EQ(static_cast(std::numeric_limits::signaling_NaN()), static_cast(0)) && BOOST_TEST_EQ(errno, EINVAL); + + errno = 0; + BOOST_TEST_EQ(static_cast(half), static_cast(0)) && BOOST_TEST_EQ(errno, 0); + + constexpr decimal32_fast one_e_8(1, 8); + BOOST_TEST_EQ(static_cast(one_e_8), static_cast(100'000'000)) && BOOST_TEST_EQ(errno, 0); + + constexpr decimal32_fast one_e_8_2(1'000'000, 2); + BOOST_TEST_EQ(static_cast(one_e_8_2), static_cast(100'000'000)) && BOOST_TEST_EQ(errno, 0); + + // Edge case + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(-100, -20); + errno = 0; + BOOST_TEST_EQ(static_cast(decimal32_fast(dist(rng))), static_cast(0)) && BOOST_TEST_EQ(errno, ERANGE); + + errno = 0; + BOOST_TEST_EQ(static_cast(decimal32_fast(dist(rng))), static_cast(0)) && BOOST_TEST_EQ(errno, ERANGE); + + errno = 0; + BOOST_TEST_EQ(static_cast(decimal32_fast(dist(rng))), static_cast(0)) && BOOST_TEST_EQ(errno, ERANGE); +} + +template +void test_roundtrip_conversion_integer(T min = T(0), T max = T(detail::max_significand)) +{ + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(min, max); + + for (std::size_t i = 0; i < N; ++i) + { + const T val = dist(rng); + const decimal32_fast initial_decimal(val); + const T return_val (initial_decimal); + const decimal32_fast return_decimal(return_val); + + BOOST_TEST_EQ(val, return_val); + BOOST_TEST_EQ(initial_decimal, return_decimal); + } + + // These will have loss of precision for the integer, + // but should roundtrip for the decimal part + std::uniform_int_distribution big_dist((std::numeric_limits::min)(), (std::numeric_limits::max)()); + + for (std::size_t i = 0; i < N; ++i) + { + const T val = dist(rng); + const decimal32_fast initial_decimal(val); + const T return_val (initial_decimal); + const decimal32_fast return_decimal(return_val); + + BOOST_TEST_EQ(initial_decimal, return_decimal); + } +} + +template +void test_conversion_to_float() +{ + errno = 0; + + constexpr decimal32_fast half(5, -1); + BOOST_TEST_EQ(static_cast(half), T(0.5)) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + BOOST_TEST_EQ(static_cast(std::numeric_limits::infinity()), std::numeric_limits::infinity()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + BOOST_TEST_EQ(static_cast(-std::numeric_limits::infinity()), std::numeric_limits::infinity()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + BOOST_TEST(static_cast(std::numeric_limits::quiet_NaN()) != std::numeric_limits::quiet_NaN()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + BOOST_TEST(static_cast(std::numeric_limits::signaling_NaN()) != std::numeric_limits::signaling_NaN()) && BOOST_TEST_EQ(errno, 0); +} + +template +void test_roundtrip_conversion_float() +{ + std::mt19937_64 rng(42); + std::uniform_real_distribution dist(0, (std::numeric_limits::max)()); + + for (std::size_t i = 0; i < N; ++i) + { + const T val {dist(rng)}; + const decimal32_fast initial_decimal(val); + const T return_val {static_cast(initial_decimal)}; + const decimal32_fast return_decimal {return_val}; + + if(!BOOST_TEST_EQ(initial_decimal, return_decimal)) + { + // LCOV_EXCL_START + std::cerr << "Val: " << val + << "\nDec: " << initial_decimal + << "\nReturn Val: " << return_val + << "\nReturn Dec: " << return_decimal << std::endl; + // LCOV_EXCL_STOP + } + } +} + +template +void test_roundtrip_integer_stream() +{ + std::mt19937_64 rng(42); + std::uniform_int_distribution dist((std::numeric_limits::min)(), (std::numeric_limits::max)()); + + for (std::size_t i {}; i < N; ++i) + { + const decimal32_fast first_val {dist(rng)}; + const T first_val_int {static_cast(first_val)}; + std::stringstream ss; + ss << std::setprecision(std::numeric_limits::digits10); + ss << first_val; + decimal32_fast return_val {}; + ss >> return_val; + const T return_val_int {static_cast(return_val)}; + + if (!BOOST_TEST_EQ(first_val, return_val) || !BOOST_TEST_EQ(first_val_int, return_val_int)) + { + // LCOV_EXCL_START + std::cerr << "Val: " << first_val + << "\nInt Val: " << first_val_int + << "\nRet: " << return_val + << "\nInt Ret: " << return_val_int << std::endl; + // LCOV_EXCL_STOP + } + } +} + +template +void test_roundtrip_float_stream() +{ + std::mt19937_64 rng(42); + std::uniform_real_distribution dist((std::numeric_limits::min)(), (std::numeric_limits::max)()); + + for (std::size_t i {}; i < N; ++i) + { + const decimal32_fast first_val {dist(rng)}; + const T first_val_flt {static_cast(first_val)}; + std::stringstream ss; + ss << std::setprecision(std::numeric_limits::digits10); + ss << first_val; + decimal32_fast return_val {}; + ss >> return_val; + const T return_val_flt {static_cast(return_val)}; + + if (!BOOST_TEST_EQ(first_val, return_val) || !BOOST_TEST_EQ(first_val_flt, return_val_flt)) + { + // LCOV_EXCL_START + std::cerr << "Val: " << first_val + << "\nInt Val: " << first_val_flt + << "\nRet: " << return_val + << "\nInt Ret: " << return_val_flt << std::endl; + // LCOV_EXCL_STOP + } + } +} + +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif + +void test_roundtrip_conversion_decimal64() +{ + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(-9'999'999, 9'999'999); + + for (std::size_t i = 0; i < N; ++i) + { + const decimal32_fast val {dist(rng)}; + const decimal64 long_dec(val); + const decimal32_fast return_decimal {long_dec}; + + if(!BOOST_TEST_EQ(val, return_decimal)) + { + // LCOV_EXCL_START + std::cerr << " Val: " << val + << "\n Dec: " << long_dec + << "\nReturn Dec: " << return_decimal << std::endl; + // LCOV_EXCL_STOP + } + } +} + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + +int main() +{ + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + test_conversion_to_integer(); + + test_roundtrip_conversion_integer(-9'999'999, 9'999'999); + test_roundtrip_conversion_integer(0, 9'999'999); + test_roundtrip_conversion_integer(-9'999'999, 9'999'999); + test_roundtrip_conversion_integer(0, 9'999'999); + test_roundtrip_conversion_integer(-9'999'999, 9'999'999); + test_roundtrip_conversion_integer(0, 9'999'999); + + #ifndef _MSC_VER + test_roundtrip_conversion_integer(INT8_MIN, INT8_MAX); + test_roundtrip_conversion_integer(0, UINT8_MAX); + #endif + + test_roundtrip_conversion_integer(INT16_MIN, INT16_MAX); + test_roundtrip_conversion_integer(0, UINT16_MAX); + test_roundtrip_conversion_integer(-9'999'999, 9'999'999); + test_roundtrip_conversion_integer(0, 9'999'999); + test_roundtrip_conversion_integer(-9'999'999, 9'999'999); + test_roundtrip_conversion_integer(0, 9'999'999); + + test_conversion_to_float(); + test_conversion_to_float(); + test_conversion_to_float(); + + test_roundtrip_conversion_float(); + test_roundtrip_conversion_float(); + test_roundtrip_conversion_float(); + + test_roundtrip_integer_stream(); + test_roundtrip_integer_stream(); + test_roundtrip_integer_stream(); + test_roundtrip_integer_stream(); + test_roundtrip_integer_stream(); + test_roundtrip_integer_stream(); + + test_roundtrip_float_stream(); + test_roundtrip_float_stream(); + test_roundtrip_float_stream(); + + #ifdef BOOST_DECIMAL_HAS_FLOAT16 + test_conversion_to_float(); + // test_roundtrip_conversion_float(); + // test_roundtrip_float_stream(); + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT32 + test_conversion_to_float(); + test_roundtrip_conversion_float(); + test_roundtrip_float_stream(); + #endif + #ifdef BOOST_DECIMAL_HAS_FLOAT64 + test_conversion_to_float(); + test_roundtrip_conversion_float(); + test_roundtrip_float_stream(); + #endif + #ifdef BOOST_DECIMAL_HAS_BRAINFLOAT16 + test_conversion_to_float(); + // test_roundtrip_conversion_float(); + // test_roundtrip_float_stream(); + #endif + + test_roundtrip_conversion_decimal64(); + + return boost::report_errors(); +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif diff --git a/test/roundtrip_decimal64.cpp b/test/roundtrip_decimal64.cpp index c150d5d33..09216bdd3 100644 --- a/test/roundtrip_decimal64.cpp +++ b/test/roundtrip_decimal64.cpp @@ -299,11 +299,6 @@ int main() test_roundtrip_conversion_integer(-9'999'999, 9'999'999); test_roundtrip_conversion_integer(0, 9'999'999); - #if defined(BOOST_DECIMAL_HAS_INT128) && !defined(__STRICT_ANSI__) - test_roundtrip_conversion_integer(-9'999'999, 9'999'999); - test_roundtrip_conversion_integer(0, 9'999'999); - #endif - test_conversion_from_float(); test_conversion_from_float(); test_conversion_from_float(); diff --git a/test/test_acos.cpp b/test/test_acos.cpp index 76475cc5d..944e520e5 100644 --- a/test/test_acos.cpp +++ b/test/test_acos.cpp @@ -144,14 +144,16 @@ void test_acos() auto ret_dec {static_cast(acos(d1))}; const auto distance {std::fabs(boost::math::float_distance(ret_val, ret_dec))}; - if (!BOOST_TEST(distance < 100)) + if (!BOOST_TEST(distance < 400)) { // LCOV_EXCL_START - std::cerr << "Val 1: " << val1 + std::cerr << std::setprecision(std::numeric_limits::digits10) + << "Val 1: " << val1 << "\nDec 1: " << d1 << "\nRet val: " << ret_val << "\nRet dec: " << ret_dec - << "\nEps: " << distance << std::endl; + << "\nEps: " << distance + << "\nLoop #: " << n << std::endl; // LCOV_EXCL_STOP } } @@ -168,5 +170,7 @@ int main() test_acos(); test_acos(); + test_acos(); + return boost::report_errors(); } diff --git a/test/test_asin.cpp b/test/test_asin.cpp index 995f7ab41..62c161a72 100644 --- a/test/test_asin.cpp +++ b/test/test_asin.cpp @@ -42,8 +42,8 @@ using namespace boost::decimal; template void test_asin() { - constexpr auto max_iter {std::is_same::value ? 2 : N}; - constexpr auto tol {std::is_same::value ? 25000 : 50}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? 2 : N}; + constexpr auto tol {std::is_same::value || std::is_same::value ? 25000 : 50}; for (std::size_t n {}; n < max_iter; ++n) { @@ -76,6 +76,11 @@ void test_asin() auto ret_val {std::asin(val1)}; auto ret_dec {static_cast(asin(d1))}; + if (std::isinf(ret_dec)) + { + std::cerr << "INF: " << d1 << " iter: n = " << n << std::endl; + } + const auto distance {std::fabs(boost::math::float_distance(ret_val, ret_dec))}; if (!BOOST_TEST(distance < tol)) { @@ -184,5 +189,7 @@ int main() test_asin(); #endif + test_asin(); + return boost::report_errors(); } diff --git a/test/test_assoc_laguerre.cpp b/test/test_assoc_laguerre.cpp index 955a38093..1dada9176 100644 --- a/test/test_assoc_laguerre.cpp +++ b/test/test_assoc_laguerre.cpp @@ -77,6 +77,7 @@ int main() { test(); test(); + test(); #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) test(); diff --git a/test/test_assoc_legendre.cpp b/test/test_assoc_legendre.cpp index dd700c086..b2f2d97c4 100644 --- a/test/test_assoc_legendre.cpp +++ b/test/test_assoc_legendre.cpp @@ -229,5 +229,7 @@ int main() throw; #endif + test(); + return boost::report_errors(); } diff --git a/test/test_atan.cpp b/test/test_atan.cpp index d6a763877..fd9f1ac64 100644 --- a/test/test_atan.cpp +++ b/test/test_atan.cpp @@ -375,6 +375,7 @@ int main() { test_atan(); test_atan(); + test_atan(); spot_test(0.344559F); spot_test(0.181179F); diff --git a/test/test_atan2.cpp b/test/test_atan2.cpp index 7fefeaadb..6dcbe668c 100644 --- a/test/test_atan2.cpp +++ b/test/test_atan2.cpp @@ -125,6 +125,7 @@ int main() { test(); test(); + test(); spot_test(2.36174F, 0.427896F); diff --git a/test/test_beta.cpp b/test/test_beta.cpp new file mode 100644 index 000000000..85d907fa4 --- /dev/null +++ b/test/test_beta.cpp @@ -0,0 +1,92 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +// Propagates up from boost.math +#define _SILENCE_CXX23_DENORM_DEPRECATION_WARNING + +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wold-style-cast" +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wsign-conversion" +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wold-style-cast" +# pragma GCC diagnostic ignored "-Wundef" +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wfloat-conversion" +#endif + +#include +#include +#include + +#include +#include +#include + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(128U); // Number of trials +#else +static constexpr auto N = static_cast(128U >> 4U); // Number of trials +#endif + +static std::mt19937_64 rng(42); + +using namespace boost::decimal; + +template +void test() +{ + std::uniform_real_distribution dist(0.0, 3.0); + + for (std::size_t n {}; n < N; ++n) + { + const auto x {dist(rng)}; + const auto y {dist(rng)}; + + const auto double_ret {boost::math::beta(x, y)}; + + const auto dec_ret {boost::decimal::beta(static_cast(x), static_cast(y))}; + const auto dec_ret_double {static_cast(dec_ret)}; + + if (!BOOST_TEST(abs(1.0 - (double_ret / dec_ret_double)) < 1e-5)) + { + // LCOV_EXCL_START + + std::cerr << std::setprecision(std::numeric_limits::digits10) + << "X: " << x + << "\nY: " << y + << "\nBoost::Math: " << double_ret + << "\nDecimal val: " << dec_ret + << "\nDist: " << abs(double_ret - dec_ret_double) / std::numeric_limits::epsilon() << std::endl; + + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(isnan(beta(T(1), std::numeric_limits::quiet_NaN()))); + BOOST_TEST(isnan(beta(std::numeric_limits::quiet_NaN(), T(1)))); +} + +int main() +{ + test(); + test(); + test(); + test(); + + #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) + test(); + // test(); + #endif + + return boost::report_errors(); +} diff --git a/test/test_bid_conversions.cpp b/test/test_bid_conversions.cpp new file mode 100644 index 000000000..712366047 --- /dev/null +++ b/test/test_bid_conversions.cpp @@ -0,0 +1,39 @@ +// 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 +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 auto bits {to_bid(val)}; + const T return_val {from_bid(bits)}; + BOOST_TEST_EQ(val, return_val); + } +} + +int main() +{ + test(); + test(); + + test(); + test(); + + test(); + test(); + + return boost::report_errors(); +} diff --git a/test/test_big_uints.cpp b/test/test_big_uints.cpp index de967e8d6..99029e4ec 100644 --- a/test/test_big_uints.cpp +++ b/test/test_big_uints.cpp @@ -261,6 +261,73 @@ auto test_big_uints_div() -> void } } +auto test_various_spots() -> void +{ + using local_uint128_type = boost::decimal::detail::uint128; + + std::uniform_int_distribution + lower_dist + ( + UINT64_C(0xFFFFFFFFFFFFFFF8), + (std::numeric_limits::max)() + ); + + using random_engine_type = std::mt19937_64; + + random_engine_type rng(local::time_point()); + + for(auto trials = static_cast(INT8_C(0)); trials < static_cast(INT8_C(0x8)); ++trials) + { + static_cast(trials); + + std::uint64_t lowest_low { (std::numeric_limits::max)() }; + + local_uint128_type low { UINT64_C(0), lower_dist(rng) }; + + for(auto idx = static_cast(INT8_C(0)); idx < static_cast(INT8_C(0x10)); ++idx) + { + static_cast(idx); + + const local_uint128_type low_old { low }; + + ++low; + + const std::uint64_t new_low { static_cast(low) }; + + if(new_low < lowest_low) + { + lowest_low = new_low; + } + + BOOST_TEST(low > low_old); + } + + BOOST_TEST_EQ(lowest_low, UINT64_C(0)); + } + + std::uniform_int_distribution n_dist(1, 4); + + for(auto trials = static_cast(INT8_C(0)); trials < static_cast(INT8_C(0x8)); ++trials) + { + static_cast(trials); + + local_uint128_type low { UINT64_C(0), lower_dist(rng) }; + + for(auto idx = static_cast(INT8_C(0)); idx < static_cast(INT8_C(0x10)); ++idx) + { + static_cast(idx); + + const local_uint128_type low_old { low }; + + const int n_add = n_dist(rng); + + low += static_cast(n_add); + + BOOST_TEST((low > low_old) && ((low - n_add) == low_old)); + } + } +} + auto test_spot_div_uint256_t() -> void { using boost_ctrl_uint_type = boost::multiprecision::uint256_t; @@ -391,6 +458,21 @@ auto test_big_uints_shl() -> void } } +template +void test_digit_counting() +{ + constexpr auto max_power {std::is_same::value ? 77 : 38 }; + + T current_power {1}; + int current_digits {1}; + for (int i {}; i <= max_power; ++i) + { + BOOST_TEST_EQ(num_digits(current_power), current_digits); + current_power = current_power * UINT64_C(10); + ++current_digits; + } +} + int main() { test_big_uints_mul(); @@ -399,6 +481,8 @@ int main() test_big_uints_div(); test_big_uints_div(); + test_various_spots(); + test_spot_div_uint256_t(); test_p10_mul_uint256_t(); @@ -406,6 +490,9 @@ int main() test_big_uints_shl(); test_big_uints_shl(); + test_digit_counting(); + test_digit_counting(); + return boost::report_errors(); } diff --git a/test/test_cbrt.cpp b/test/test_cbrt.cpp index 0cc07a32a..6e447e6c9 100644 --- a/test/test_cbrt.cpp +++ b/test/test_cbrt.cpp @@ -3,12 +3,6 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#include -#include -#include -#include -#include - #include #if defined(__clang__) @@ -21,9 +15,10 @@ #include -#include - #include +#include +#include +#include #include namespace local @@ -364,6 +359,29 @@ int main() result_is_ok = (result_edge_is_ok && result_is_ok); } + { + using decimal_type = boost::decimal::decimal32_fast; + using float_type = float; + + const auto result_small_is_ok = local::test_cbrt(static_cast(INT32_C(16)), 1.0E-26L, 1.0E-01L); + const auto result_medium_is_ok = local::test_cbrt(static_cast(INT32_C(16)), 0.9E-01L, 1.1E+01L); + const auto result_large_is_ok = local::test_cbrt(static_cast(INT32_C(16)), 1.0E+01L, 1.0E+26L); + + BOOST_TEST(result_small_is_ok); + BOOST_TEST(result_medium_is_ok); + BOOST_TEST(result_large_is_ok); + + const auto result_edge_is_ok = local::test_cbrt_edge(); + + const auto result_ranges_is_ok = (result_small_is_ok && result_medium_is_ok && result_large_is_ok); + + result_is_ok = (result_ranges_is_ok && result_is_ok); + + BOOST_TEST(result_edge_is_ok); + + result_is_ok = (result_edge_is_ok && result_is_ok); + } + { using decimal_type = boost::decimal::decimal64; using float_type = double; diff --git a/test/test_cmath.cpp b/test/test_cmath.cpp index 5c6e0fcd5..3305c074b 100644 --- a/test/test_cmath.cpp +++ b/test/test_cmath.cpp @@ -311,7 +311,7 @@ void test_div_fmod() { std::uniform_real_distribution dist(0.0F, 1e30F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -349,7 +349,7 @@ void test_copysign() { std::uniform_real_distribution dist(0.0F, 1e30F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -391,7 +391,7 @@ void test_fma() std::uniform_real_distribution dist(-1e3, 1e3); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -445,7 +445,7 @@ void test_fdim() { std::uniform_real_distribution dist(1.0F, 1e5F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -479,15 +479,21 @@ void test_fdim() BOOST_TEST_EQ(fdim(Dec(1), Dec(1)), Dec(0)); } +// Macro if constexpr throws warning in C++14 mode +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4127) +#endif + template void test_ilogb() { - BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value) + BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value || std::is_same::value) { BOOST_TEST_EQ(ilogb(Dec(1, 0)), 101); BOOST_TEST_EQ(ilogb(Dec(10, 0)), 102); } - else BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value) + else BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value || std::is_same::value) { BOOST_TEST_EQ(ilogb(Dec(1, 0)), 398); BOOST_TEST_EQ(ilogb(Dec(10, 0)), 399); @@ -506,12 +512,12 @@ void test_ilogb() template void test_logb() { - BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value) + BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value || std::is_same::value) { BOOST_TEST_EQ(ilogb(Dec(1, 0)), Dec(101)); BOOST_TEST_EQ(ilogb(Dec(10, 0)), Dec(102)); } - else BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value) + else BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value || std::is_same::value) { BOOST_TEST_EQ(ilogb(Dec(1, 0)), Dec(398)); BOOST_TEST_EQ(ilogb(Dec(10, 0)), Dec(399)); @@ -527,13 +533,17 @@ void test_logb() BOOST_TEST(isnan(logb(std::numeric_limits::quiet_NaN()))); } +#ifdef _MSC_VER +# pragma warning(pop) +#endif + template void test_sqrt() { - using comp_type = std::conditional_t::value, float, double>; + using comp_type = std::conditional_t::value || std::is_same::value, float, double>; std::uniform_real_distribution dist(0, 1e5); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -571,7 +581,7 @@ void test_two_val_hypot() { std::uniform_real_distribution dist(1.0F, 1e5F); - constexpr auto max_iter {std::is_same::value ? 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -619,7 +629,7 @@ void test_mixed_two_val_hypot() { std::uniform_real_distribution dist(1.0F, 1e5F); - constexpr auto max_iter {std::is_same::value ? 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -657,7 +667,7 @@ void test_three_val_hypot() std::uniform_real_distribution dist(1.0F, 1e5F); - constexpr auto max_iter {std::is_same::value ? 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -712,7 +722,7 @@ void test_rint() { std::uniform_real_distribution dist(-1e20F, 1e20F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { @@ -780,7 +790,7 @@ void test_lrint() std::uniform_real_distribution dist(-1e5F, 1e5F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -859,7 +869,7 @@ void test_llrint() { std::uniform_real_distribution dist(-1e20F, 1e20F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; std::uniform_real_distribution dist2(-1e5F, 1e5F); @@ -902,7 +912,7 @@ void test_nearbyint() { std::uniform_real_distribution dist(-1e20F, 1e20F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -966,7 +976,7 @@ void test_round() { std::uniform_real_distribution dist(-1e5F, 1e5F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -1008,7 +1018,7 @@ void test_lround() { std::uniform_real_distribution dist(-1e20F, 1e20F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; std::uniform_real_distribution dist2(-1e5F, 1e5F); @@ -1051,7 +1061,7 @@ void test_llround() { std::uniform_real_distribution dist(-1e20F, 1e20F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; std::uniform_real_distribution dist2(-1e5F, 1e5F); @@ -1094,7 +1104,7 @@ void test_nextafter() { std::uniform_real_distribution dist(1.0F, 1e5F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -1137,7 +1147,7 @@ void test_nexttoward() { std::uniform_real_distribution dist(1.0F, 1e5F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -1213,7 +1223,7 @@ void test_log2() { std::uniform_real_distribution dist(-0.5F, 0.5F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -1246,7 +1256,7 @@ void test_log10() { std::uniform_real_distribution dist(-0.5F, 0.5F); - constexpr auto max_iter {std::is_same::value ? N / 4 : N}; + constexpr auto max_iter {std::is_same::value || std::is_same::value ? N / 4 : N}; for (std::size_t n {}; n < max_iter; ++n) { const auto val1 {dist(rng)}; @@ -1284,6 +1294,16 @@ int main() test_islessequal(); test_islessgreater(); test_isunordered(); + + test_fmax(); + test_isgreater(); + test_isgreaterequal(); + test_fmin(); + test_isless(); + test_islessequal(); + test_islessgreater(); + test_isunordered(); + test_fmax(); test_isgreater(); test_isgreaterequal(); @@ -1292,6 +1312,16 @@ int main() test_islessequal(); test_islessgreater(); test_isunordered(); + + test_fmax(); + test_isgreater(); + test_isgreaterequal(); + test_fmin(); + test_isless(); + test_islessequal(); + test_islessgreater(); + test_isunordered(); + test_fmax(); test_isgreater(); test_isgreaterequal(); @@ -1301,23 +1331,55 @@ int main() test_islessgreater(); test_isunordered(); + test_fmax(); + test_isgreater(); + test_isgreaterequal(); + test_fmin(); + test_isless(); + test_islessequal(); + test_islessgreater(); + test_isunordered(); + test_floor(); test_ceil(); test_trunc(); + + test_floor(); + test_ceil(); + test_trunc(); + test_floor(); test_ceil(); test_trunc(); + + test_floor(); + test_ceil(); + test_trunc(); + test_floor(); test_ceil(); test_trunc(); + test_floor(); + test_ceil(); + test_trunc(); + test_frexp10(); test_scalbn(); test_scalbln(); + + test_frexp10(); + test_scalbn(); + test_scalbln(); + test_frexp10(); test_scalbn(); test_scalbln(); + test_frexp10(); + test_scalbn(); + test_scalbln(); + test_div_fmod(); test_div_fmod(); @@ -1325,35 +1387,61 @@ int main() test_copysign(); test_fma(); + test_fma(); test_fma(); + test_fma(); test_fma(); + test_fma(); test_modf(); + test_modf(); test_modf(); + test_modf(); test_fdim(); + test_fdim(); test_fdim(); + test_fdim(); test_ilogb(); + test_ilogb(); test_ilogb(); + test_ilogb(); test_ilogb(); + test_ilogb(); test_logb(); + test_logb(); test_logb(); + test_logb(); test_logb(); + test_logb(); test_sqrt(); + test_sqrt(); test_sqrt(); + test_sqrt(); test_two_val_hypot(); test_three_val_hypot(); + test_two_val_hypot(); + test_three_val_hypot(); test_two_val_hypot(); test_three_val_hypot(); + test_two_val_hypot(); + test_three_val_hypot(); #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) + test_sqrt(); + test_sqrt(); + test_two_val_hypot(); test_three_val_hypot(); test_mixed_two_val_hypot(); + + test_two_val_hypot(); + test_three_val_hypot(); + test_mixed_two_val_hypot(); #endif test_mixed_two_val_hypot(); @@ -1362,27 +1450,57 @@ int main() test_lrint(); test_llrint(); test_nearbyint(); + + test_rint(); + test_lrint(); + test_llrint(); + test_nearbyint(); + test_rint(); test_lrint(); test_llrint(); test_nearbyint(); + test_rint(); + test_lrint(); + test_llrint(); + test_nearbyint(); + test_round(); test_lround(); test_llround(); + + test_round(); + test_lround(); + test_llround(); + test_round(); test_lround(); test_llround(); + test_round(); + test_lround(); + test_llround(); + test_nextafter(); test_nexttoward(); + + test_nextafter(); + test_nexttoward(); + test_nextafter(); test_nexttoward(); + test_nextafter(); + test_nexttoward(); + test_pow(); + test_pow(); test_pow(); + test_pow(); test_exp2(); + test_exp2(); test_exp2(); #if !defined(BOOST_DECIMAL_DISABLE_CLIB) @@ -1392,14 +1510,21 @@ int main() #endif test_log2(); + test_log2(); test_log2(); + test_log2(); test_log10(); + test_log10(); test_log10(); + test_log10(); #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) test_log2(); test_log10(); + + test_log2(); + test_log10(); #endif return boost::report_errors(); diff --git a/test/test_constants.cpp b/test/test_constants.cpp index b6c710a44..691bbdbed 100644 --- a/test/test_constants.cpp +++ b/test/test_constants.cpp @@ -93,6 +93,7 @@ void test_defaults() int main() { test_constants(); + test_constants(); test_constants(); test_constants(); test_defaults(); diff --git a/test/test_decimal32.cpp b/test/test_decimal32.cpp index dec6929c2..23060d3c2 100644 --- a/test/test_decimal32.cpp +++ b/test/test_decimal32.cpp @@ -447,21 +447,21 @@ void test_construct_from_float() decimal32 float_one(T(1)); if(!BOOST_TEST_EQ(one, float_one)) { - debug_pattern(float_one); + debug_pattern(float_one); // LCOV_EXCL_LINE } constexpr decimal32 fraction(12345, -4); decimal32 float_frac(T(1.2345)); if(!BOOST_TEST_EQ(fraction, float_frac)) { - debug_pattern(float_frac); + debug_pattern(float_frac); // LCOV_EXCL_LINE } constexpr decimal32 neg_frac(98123, -4, true); decimal32 neg_float_frac(T(-9.8123)); if(!BOOST_TEST_EQ(neg_frac, neg_float_frac)) { - debug_pattern(neg_float_frac); + debug_pattern(neg_float_frac); // LCOV_EXCL_LINE } } @@ -516,9 +516,12 @@ int main() test_construct_from_float(); test_construct_from_float(); + + #if BOOST_DECIMAL_LDBL_BITS != 128 test_construct_from_float(); + #endif #ifdef BOOST_DECIMAL_HAS_FLOAT128 - test_construct_from_float<__float128>(); + //test_construct_from_float<__float128>(); #endif test_comp(); diff --git a/test/test_decimal32_fast_basis.cpp b/test/test_decimal32_fast_basis.cpp index 7e86b7ed9..33fcff601 100644 --- a/test/test_decimal32_fast_basis.cpp +++ b/test/test_decimal32_fast_basis.cpp @@ -160,12 +160,22 @@ void test_non_finite_values() BOOST_TEST(isinf(detail::check_non_finite(std::numeric_limits::infinity() * dist(rng), one))); } +#if !defined(__GNUC__) || (__GNUC__ != 7 && __GNUC__ != 8) void test_unary_arithmetic() { - constexpr decimal32_fast one(0b1, -100); + constexpr decimal32_fast one(1); BOOST_TEST(+one == one); - BOOST_TEST(-one != one); + if(!BOOST_TEST(-one != one)) + { + // LCOV_EXCL_START + std::cerr << "One: " << one + << "\nNeg: " << -one + << "\n Bid: " << to_bid(one) + << "\nNeg Bid: " << to_bid(-one) << std::endl; + // LCOV_EXCL_STOP + } } +#endif void test_addition() { @@ -415,7 +425,12 @@ int main() { test_decimal_constructor(); test_non_finite_values(); + + // GCC 7 and 8 get the correct BID bit patterns but the result is wrong for negation + // Everything else works just fine + #if !defined(__GNUC__) || (__GNUC__ != 7 && __GNUC__ != 8) test_unary_arithmetic(); + #endif test_construct_from_integer(); test_construct_from_integer(); @@ -423,9 +438,14 @@ int main() test_construct_from_float(); test_construct_from_float(); + + #if BOOST_DECIMAL_LDBL_BITS != 128 test_construct_from_float(); - #ifdef BOOST_DECIMAL_HAS_FLOAT128 - test_construct_from_float<__float128>(); + #endif + + // Clang < 13 yields failures from conversion + #if defined(BOOST_DECIMAL_HAS_FLOAT128) && (!defined(__clang_major__) || __clang_major__ >= 13) + //test_construct_from_float<__float128>(); #endif test_comp(); diff --git a/test/test_decimal32_fast_stream.cpp b/test/test_decimal32_fast_stream.cpp new file mode 100644 index 000000000..02efaee85 --- /dev/null +++ b/test/test_decimal32_fast_stream.cpp @@ -0,0 +1,156 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "mini_to_chars.hpp" +#include +#include +#include +#include +#include + +using namespace boost::decimal; + +void test_istream() +{ + decimal32_fast val; + std::stringstream in; + in.str("1.234567e+06"); + in >> val; + BOOST_TEST_EQ(val, decimal32_fast(1234567, 0)); + + errno = 0; + decimal32_fast val2; + std::stringstream in_zero; + in_zero.str("0"); + in_zero >> val2; + BOOST_TEST_EQ(val2, decimal32_fast(0, 0)) && BOOST_TEST_EQ(errno, 0); + + decimal32_fast val3; + std::stringstream bad; + bad.str(""); + bad >> val3; + BOOST_TEST_EQ(errno, EINVAL) && BOOST_TEST_NE(val3, std::numeric_limits::signaling_NaN()); + + errno = 0; + decimal32_fast inf_val; + std::stringstream inf; + inf.str("inf"); + inf >> inf_val; + BOOST_TEST_EQ(inf_val, std::numeric_limits::infinity()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal32_fast inf_val2; + std::stringstream inf2; + inf2.str("INFINITY"); + inf2 >> inf_val2; + BOOST_TEST_EQ(inf_val2, std::numeric_limits::infinity()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal32_fast snan_val; + std::stringstream snan; + snan.str("-nan(snan)"); + snan >> snan_val; + BOOST_TEST_NE(snan_val, std::numeric_limits::signaling_NaN()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal32_fast nan_val; + std::stringstream nan_str; + nan_str.str("nan"); + nan_str >> nan_val; + BOOST_TEST_NE(nan_val, std::numeric_limits::quiet_NaN()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal32_fast junk_val; + std::stringstream junk_str; + junk_str.str("r5"); + junk_str >> junk_val; + BOOST_TEST_NE(junk_val, std::numeric_limits::signaling_NaN()) && BOOST_TEST_EQ(errno, EINVAL); +} + +void test_ostream() +{ + decimal32_fast val {123456, 0}; + std::stringstream out; + out << val; + BOOST_TEST_CSTR_EQ(out.str().c_str(), "123456"); + + // Tests the default value of setprecision + decimal32_fast big_val {123456789, 0}; + std::stringstream big_out; + big_out << big_val; + BOOST_TEST_CSTR_EQ(big_out.str().c_str(), "1.234568e+08"); + + decimal32_fast zero {0, 0}; + std::stringstream zero_out; + zero_out << zero; + BOOST_TEST_CSTR_EQ(zero_out.str().c_str(), "0.0e+00"); + + std::stringstream inf; + inf << std::numeric_limits::infinity(); + BOOST_TEST_CSTR_EQ(inf.str().c_str(), "inf"); + + std::stringstream qnan; + qnan << std::numeric_limits::quiet_NaN(); + BOOST_TEST_CSTR_EQ(qnan.str().c_str(), "nan"); + + std::stringstream snan; + snan << std::numeric_limits::signaling_NaN(); + BOOST_TEST_CSTR_EQ(snan.str().c_str(), "nan(snan)"); + + std::stringstream neg_inf; + neg_inf << (-std::numeric_limits::infinity()); + BOOST_TEST_CSTR_EQ(neg_inf.str().c_str(), "-inf"); + + std::stringstream neg_qnan; + neg_qnan << (-std::numeric_limits::quiet_NaN()); + BOOST_TEST_CSTR_EQ(neg_qnan.str().c_str(), "-nan(ind)"); + + std::stringstream neg_snan; + neg_snan << (-std::numeric_limits::signaling_NaN()); + BOOST_TEST_CSTR_EQ(neg_snan.str().c_str(), "-nan(snan)"); +} + +void test_locales() +{ + const char buffer[] = "1,1897e+02"; + + try + { + #ifdef BOOST_MSVC + std::locale::global(std::locale("German")); + #else + std::locale::global(std::locale("de_DE.UTF-8")); + #endif + } + // LCOV_EXCL_START + catch (...) + { + std::cerr << "Locale not installed. Skipping test." << std::endl; + return; + } + // LCOV_EXCL_STOP + + std::stringstream in; + in.str(buffer); + decimal32_fast val; + in >> val; + BOOST_TEST_EQ(val, decimal32_fast(1.1897e+02)); + + std::stringstream out; + out << std::scientific << std::setprecision(4) << val; + BOOST_TEST_CSTR_EQ(out.str().c_str(), buffer); +} + +int main() +{ + test_istream(); + test_ostream(); + + // Homebrew GCC does not support locales + #if !(defined(__GNUC__) && __GNUC__ >= 5 && defined(__APPLE__)) + test_locales(); + #endif + + return boost::report_errors(); +} diff --git a/test/test_decimal64_fast_basis.cpp b/test/test_decimal64_fast_basis.cpp new file mode 100644 index 000000000..1306ad1b4 --- /dev/null +++ b/test/test_decimal64_fast_basis.cpp @@ -0,0 +1,435 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "mini_to_chars.hpp" + +#include +#include +#include +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wold-style-cast" +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wsign-conversion" +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wold-style-cast" +# pragma GCC diagnostic ignored "-Wundef" +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +using namespace boost::decimal; + +void test_comp() +{ + constexpr decimal64_fast small(1, -50); + + BOOST_TEST(small == small); + + constexpr decimal64_fast sig(123456, -50); + BOOST_TEST(sig != small); + + BOOST_TEST(small < sig); + BOOST_TEST(small <= sig); + BOOST_TEST(small <= small); + BOOST_TEST(sig > small); + BOOST_TEST(sig >= small); + + decimal64_fast zero {0, 0}; + decimal64_fast one {1, 0}; + decimal64_fast half {5, -1}; + BOOST_TEST(zero < one); + BOOST_TEST(zero < half); + BOOST_TEST(one > zero); + BOOST_TEST(half > zero); + BOOST_TEST(zero > -one); + BOOST_TEST(half > -one); + BOOST_TEST(-one < zero); + BOOST_TEST(-one < half); + + // Test cohorts + BOOST_TEST(small == decimal64_fast(10, -51)); + BOOST_TEST(small == decimal64_fast(100, -52)); + BOOST_TEST(small == decimal64_fast(1000, -53)); + BOOST_TEST(small == decimal64_fast(10000, -54)); + BOOST_TEST(small == decimal64_fast(100000, -55)); + BOOST_TEST(small == decimal64_fast(1000000, -56)); + + // Test non-finite comp + BOOST_TEST(small < std::numeric_limits::infinity()); + BOOST_TEST(small > -std::numeric_limits::infinity()); + BOOST_TEST(!(small == std::numeric_limits::infinity())); + BOOST_TEST(small != std::numeric_limits::infinity()); + + BOOST_TEST(!(small < std::numeric_limits::signaling_NaN())); + BOOST_TEST(!(small < std::numeric_limits::quiet_NaN())); + BOOST_TEST(small != std::numeric_limits::quiet_NaN()); + BOOST_TEST(std::numeric_limits::quiet_NaN() != std::numeric_limits::quiet_NaN()); + + BOOST_TEST(small <= std::numeric_limits::infinity()); + BOOST_TEST(small >= -std::numeric_limits::infinity()); + BOOST_TEST(!(small <= std::numeric_limits::signaling_NaN())); + BOOST_TEST(!(small <= std::numeric_limits::quiet_NaN())); +} + +void test_non_finite_values() +{ + constexpr decimal64_fast one(0b1, 0); + + BOOST_TEST(std::numeric_limits::has_infinity); + BOOST_TEST(isinf(std::numeric_limits::infinity())); + BOOST_TEST(isinf(-std::numeric_limits::infinity())); + BOOST_TEST(!isinf(one)); + BOOST_TEST(!isinf(std::numeric_limits::quiet_NaN())); + BOOST_TEST(!isinf(std::numeric_limits::signaling_NaN())); + + BOOST_TEST(std::numeric_limits::has_quiet_NaN); + BOOST_TEST(std::numeric_limits::has_signaling_NaN); + BOOST_TEST(isnan(std::numeric_limits::quiet_NaN())); + BOOST_TEST(isnan(std::numeric_limits::signaling_NaN())); + BOOST_TEST(!isnan(one)); + BOOST_TEST(!isnan(std::numeric_limits::infinity())); + BOOST_TEST(!isnan(-std::numeric_limits::infinity())); + + BOOST_TEST(!issignaling(std::numeric_limits::quiet_NaN())); + BOOST_TEST(issignaling(std::numeric_limits::signaling_NaN())); + BOOST_TEST(!issignaling(one)); + BOOST_TEST(!issignaling(std::numeric_limits::infinity())); + BOOST_TEST(!issignaling(-std::numeric_limits::infinity())); + + #ifdef _MSC_VER + + BOOST_TEST(boost::decimal::isfinite(one)); + BOOST_TEST(!boost::decimal::isfinite(std::numeric_limits::infinity())); + BOOST_TEST(!boost::decimal::isfinite(std::numeric_limits::quiet_NaN())); + BOOST_TEST(!boost::decimal::isfinite(std::numeric_limits::signaling_NaN())); + + #else + + BOOST_TEST(isfinite(one)); + BOOST_TEST(!isfinite(std::numeric_limits::infinity())); + BOOST_TEST(!isfinite(std::numeric_limits::quiet_NaN())); + BOOST_TEST(!isfinite(std::numeric_limits::signaling_NaN())); + + #endif + + BOOST_TEST(isnormal(one)); + BOOST_TEST(!isnormal(std::numeric_limits::infinity())); + BOOST_TEST(!isnormal(std::numeric_limits::quiet_NaN())); + BOOST_TEST(!isnormal(std::numeric_limits::signaling_NaN())); + + BOOST_TEST_EQ(fpclassify(one), FP_NORMAL); + BOOST_TEST_EQ(fpclassify(-one), FP_NORMAL); + BOOST_TEST_EQ(fpclassify(std::numeric_limits::quiet_NaN()), FP_NAN); + BOOST_TEST_EQ(fpclassify(std::numeric_limits::signaling_NaN()), FP_NAN); + BOOST_TEST_EQ(fpclassify(std::numeric_limits::infinity()), FP_INFINITE); + BOOST_TEST_EQ(fpclassify(-std::numeric_limits::infinity()), FP_INFINITE); + + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(1, 2); + + BOOST_TEST(isnan(detail::check_non_finite(one, std::numeric_limits::quiet_NaN() * dist(rng)))); + BOOST_TEST(isnan(detail::check_non_finite(std::numeric_limits::quiet_NaN() * dist(rng), one))); + BOOST_TEST(isinf(detail::check_non_finite(one, std::numeric_limits::infinity() * dist(rng)))); + BOOST_TEST(isinf(detail::check_non_finite(std::numeric_limits::infinity() * dist(rng), one))); +} + +#if !defined(__GNUC__) || (__GNUC__ != 7 && __GNUC__ != 8) +void test_unary_arithmetic() +{ + constexpr decimal64_fast one(1); + BOOST_TEST(+one == one); + if(!BOOST_TEST(-one != one)) + { + // LCOV_EXCL_START + std::cerr << "One: " << one + << "\nNeg: " << -one + << "\n Bid: " << to_bid(one) + << "\nNeg Bid: " << to_bid(-one) << std::endl; + // LCOV_EXCL_STOP + } +} +#endif + +void test_addition() +{ + // Case 1: The difference is more than the digits of accuracy + constexpr decimal64_fast big_num(0b1, 20); + constexpr decimal64_fast small_num(0b1, -20); + BOOST_TEST_EQ(big_num + small_num, big_num); + BOOST_TEST_EQ(small_num + big_num, big_num); + + // Case 2: Round the last digit of the significand + constexpr decimal64_fast full_length_num {UINT64_C(1000000000000000), 0}; + constexpr decimal64_fast rounded_full_length_num(UINT64_C(1000000000000001), 0); + constexpr decimal64_fast no_round(1, -1); + constexpr decimal64_fast round(9, -1); + BOOST_TEST_EQ(full_length_num + no_round, full_length_num); + BOOST_TEST_EQ(full_length_num + round, rounded_full_length_num); + + // Case 3: Add away + constexpr decimal64_fast one(1, 0); + constexpr decimal64_fast two(2, 0); + constexpr decimal64_fast three(3, 0); + decimal64_fast mutable_one(1, 0); + + BOOST_TEST_EQ(one + one, two); + BOOST_TEST_EQ(two + one, three); + BOOST_TEST_EQ(one + one + one, three); + + // Pre- and post- increment + BOOST_TEST_EQ(mutable_one, one); + BOOST_TEST_EQ(mutable_one++, two); + BOOST_TEST_EQ(++mutable_one, three); + + // Different orders of magnitude + constexpr decimal64_fast ten(10, 0); + constexpr decimal64_fast eleven(11, 0); + BOOST_TEST_EQ(ten + one, eleven); + + constexpr decimal64_fast max_sig(9'999'999, 0); + constexpr decimal64_fast max_plus_one(10'000'000, 0); + BOOST_TEST_EQ(max_sig + one, max_plus_one); + + // Non-finite values + constexpr decimal64_fast qnan_val(std::numeric_limits::quiet_NaN()); + constexpr decimal64_fast snan_val(std::numeric_limits::signaling_NaN()); + constexpr decimal64_fast inf_val(std::numeric_limits::infinity()); + BOOST_TEST(isnan(qnan_val + one)); + BOOST_TEST(isnan(snan_val + one)); + BOOST_TEST(isnan(one + qnan_val)); + BOOST_TEST(isnan(one + snan_val)); + BOOST_TEST(isinf(inf_val + one)); + BOOST_TEST(isinf(one + inf_val)); + BOOST_TEST(isnan(inf_val + qnan_val)); + BOOST_TEST(isnan(qnan_val + inf_val)); +} + +void test_subtraction() +{ + // Case 1: The difference is more than the digits of accuracy + constexpr decimal64_fast big_num(0b1, 20); + constexpr decimal64_fast small_num(0b1, -20); + BOOST_TEST_EQ(big_num - small_num, big_num); + BOOST_TEST_EQ(small_num - big_num, -big_num); + + constexpr decimal64_fast one(1, 0); + constexpr decimal64_fast two(2, 0); + constexpr decimal64_fast three(3, 0); + decimal64_fast mutable_three(3, 0); + + BOOST_TEST_EQ(two - one, one); + BOOST_TEST_EQ(three - one - one, one); + + // Pre- and post- increment + BOOST_TEST_EQ(mutable_three, three); + BOOST_TEST_EQ(mutable_three--, two); + BOOST_TEST_EQ(--mutable_three, one); + + // Different orders of magnitude + constexpr decimal64_fast ten(10, 0); + constexpr decimal64_fast eleven(11, 0); + BOOST_TEST_EQ(eleven - one, ten); + + constexpr decimal64_fast max(9'999'999, 0); + constexpr decimal64_fast max_plus_one(10'000'000, 0); + BOOST_TEST_EQ(max_plus_one - one, max); + + // Non-finite values + constexpr decimal64_fast qnan_val(std::numeric_limits::quiet_NaN()); + constexpr decimal64_fast snan_val(std::numeric_limits::signaling_NaN()); + constexpr decimal64_fast inf_val(std::numeric_limits::infinity()); + BOOST_TEST(isnan(qnan_val - one)); + BOOST_TEST(isnan(snan_val - one)); + BOOST_TEST(isnan(one - qnan_val)); + BOOST_TEST(isnan(one - snan_val)); + BOOST_TEST(isinf(inf_val - one)); + BOOST_TEST(isinf(one - inf_val)); + BOOST_TEST(isnan(inf_val - qnan_val)); + BOOST_TEST(isnan(qnan_val - inf_val)); +} + +void test_multiplicatiom() +{ + constexpr decimal64_fast zero {0, 0}; + constexpr decimal64_fast one {1, 0}; + constexpr decimal64_fast two {2, 0}; + constexpr decimal64_fast four {4, 0}; + constexpr decimal64_fast eight {8, 0}; + + BOOST_TEST_EQ(zero * one, zero); + BOOST_TEST_EQ(zero * -one, zero); + BOOST_TEST_EQ(one * two, two); + + decimal64_fast pow_two {1, 0}; + BOOST_TEST_EQ(pow_two *= two, two); + BOOST_TEST_EQ(pow_two *= two, four); + BOOST_TEST_EQ(pow_two *= -two, -eight); + + // Non-finite values + constexpr decimal64_fast qnan_val(std::numeric_limits::quiet_NaN()); + constexpr decimal64_fast snan_val(std::numeric_limits::signaling_NaN()); + constexpr decimal64_fast inf_val(std::numeric_limits::infinity()); + BOOST_TEST(isnan(qnan_val * one)); + BOOST_TEST(isnan(snan_val * one)); + BOOST_TEST(isnan(one * qnan_val)); + BOOST_TEST(isnan(one * snan_val)); + BOOST_TEST(isinf(inf_val * one)); + BOOST_TEST(isinf(one * inf_val)); + BOOST_TEST(isnan(inf_val * qnan_val)); + BOOST_TEST(isnan(qnan_val * inf_val)); +} + +void test_div_mod() +{ + constexpr decimal64_fast zero {0, 0}; + constexpr decimal64_fast one {1, 0}; + constexpr decimal64_fast two {2, 0}; + constexpr decimal64_fast three {3, 0}; + constexpr decimal64_fast four {4, 0}; + constexpr decimal64_fast eight {8, 0}; + constexpr decimal64_fast half {5, -1}; + constexpr decimal64_fast quarter {25, -2}; + constexpr decimal64_fast eighth {125, -3}; + + BOOST_TEST_EQ(two / one, two); + BOOST_TEST_EQ(two % one, zero); + BOOST_TEST_EQ(eight / four, two); + BOOST_TEST_EQ(four / eight, half); + BOOST_TEST_EQ(one / four, quarter); + BOOST_TEST_EQ(one / eight, eighth); + BOOST_TEST_EQ(three / two, one + half); + + // From https://en.cppreference.com/w/cpp/numeric/math/fmod + BOOST_TEST_EQ(decimal64_fast(51, -1) % decimal64_fast(30, -1), decimal64_fast(21, -1)); + + // Non-finite values + constexpr decimal64_fast qnan_val(std::numeric_limits::quiet_NaN()); + constexpr decimal64_fast snan_val(std::numeric_limits::signaling_NaN()); + constexpr decimal64_fast inf_val(std::numeric_limits::infinity()); + BOOST_TEST(isnan(qnan_val / one)); + BOOST_TEST(isnan(snan_val / one)); + BOOST_TEST(isnan(one / qnan_val)); + BOOST_TEST(isnan(one / snan_val)); + BOOST_TEST(isinf(inf_val / one)); + BOOST_TEST_EQ(one / inf_val, zero); + BOOST_TEST(isnan(inf_val / qnan_val)); + BOOST_TEST(isnan(qnan_val / inf_val)); + + // Mixed types + BOOST_TEST(isnan(qnan_val / 1)); + BOOST_TEST(isnan(snan_val / 1)); + BOOST_TEST(isnan(1 / qnan_val)); + BOOST_TEST(isnan(1 / snan_val)); + BOOST_TEST(isinf(inf_val / 1)); + BOOST_TEST_EQ(1 / inf_val, zero); +} + +template +void test_construct_from_integer() +{ + constexpr decimal64_fast one(1, 0); + BOOST_TEST_EQ(one, decimal64_fast(T(1))); + + constexpr decimal64_fast one_pow_eight(1, 8); + BOOST_TEST_EQ(one_pow_eight, decimal64_fast(T(100'000'000))); +} + +template +void test_construct_from_float() +{ + constexpr decimal64_fast one(1, 0); + decimal64_fast float_one(T(1)); + BOOST_TEST_EQ(one, float_one); + + constexpr decimal64_fast fraction(12345, -4); + decimal64_fast float_frac(T(1.2345)); + BOOST_TEST_EQ(fraction, float_frac); + + constexpr decimal64_fast neg_frac(98123, -4, true); + decimal64_fast neg_float_frac(T(-9.8123)); + BOOST_TEST_EQ(neg_frac, neg_float_frac); +} + +template +void spot_check_addition(T a, T b, T res) +{ + decimal64_fast dec_a {a}; + decimal64_fast dec_b {b}; + decimal64_fast dec_res {res}; + + if (!BOOST_TEST_EQ(dec_a + dec_b, dec_res)) + { + // LCOV_EXCL_START + std::cerr << "A + B: " << a + b + << "\nIn dec: " << decimal64_fast(a + b) << std::endl; + // LCOV_EXCL_STOP + } +} + +void test_hash() +{ + decimal64_fast one {1, 0}; + decimal64_fast zero {0, 0}; + + BOOST_TEST_NE(std::hash{}(one), std::hash{}(zero)); +} + +void test_shrink_significand() +{ + std::mt19937_64 rng(42); + std::uniform_int_distribution dist(100'000'000'000, 100'000'000'000); + std::int32_t pow {}; + std::uint64_t sig {dist(rng)}; + + detail::shrink_significand(sig, pow); + BOOST_TEST_EQ(pow, 3); +} + +int main() +{ + test_non_finite_values(); + + #if !defined(__GNUC__) || (__GNUC__ != 7 && __GNUC__ != 8) + test_unary_arithmetic(); + #endif + + test_construct_from_integer(); + test_construct_from_integer(); + test_construct_from_integer(); + + test_construct_from_float(); + test_construct_from_float(); + test_construct_from_float(); + #if defined(BOOST_DECIMAL_HAS_FLOAT128) && (!defined(__clang_major__) || __clang_major__ >= 13) + test_construct_from_float<__float128>(); + #endif + + test_comp(); + + test_addition(); + test_subtraction(); + test_multiplicatiom(); + test_div_mod(); + + test_hash(); + + spot_check_addition(-1054191000, -920209700, -1974400700); + spot_check_addition(353582500, -32044770, 321537730); + spot_check_addition(989629100, 58451350, 1048080450); + + test_shrink_significand(); + + return boost::report_errors(); +} diff --git a/test/test_decimal64_fast_stream.cpp b/test/test_decimal64_fast_stream.cpp new file mode 100644 index 000000000..9324346e9 --- /dev/null +++ b/test/test_decimal64_fast_stream.cpp @@ -0,0 +1,115 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "mini_to_chars.hpp" +#include +#include +#include +#include +#include +#include + +using namespace boost::decimal; + +void test_istream() +{ + decimal64_fast val; + std::stringstream in; + in.str("1.234567e+06"); + in >> val; + BOOST_TEST_EQ(val, decimal64_fast(1234567, 0)); + + errno = 0; + decimal64_fast val2; + std::stringstream in_zero; + in_zero.str("0"); + in_zero >> val2; + BOOST_TEST_EQ(val2, decimal64_fast(0, 0)) && BOOST_TEST_EQ(errno, 0); + + decimal64_fast val3; + std::stringstream bad; + bad.str(""); + bad >> val3; + BOOST_TEST_EQ(errno, EINVAL) && BOOST_TEST_NE(val3, std::numeric_limits::signaling_NaN()); + + errno = 0; + decimal64_fast inf_val; + std::stringstream inf; + inf.str("inf"); + inf >> inf_val; + BOOST_TEST_EQ(inf_val, std::numeric_limits::infinity()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal64_fast inf_val2; + std::stringstream inf2; + inf2.str("INFINITY"); + inf2 >> inf_val2; + BOOST_TEST_EQ(inf_val2, std::numeric_limits::infinity()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal64_fast snan_val; + std::stringstream snan; + snan.str("-nan(snan)"); + snan >> snan_val; + BOOST_TEST_NE(snan_val, std::numeric_limits::signaling_NaN()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal64_fast nan_val; + std::stringstream nan_str; + nan_str.str("nan"); + nan_str >> nan_val; + BOOST_TEST_NE(nan_val, std::numeric_limits::quiet_NaN()) && BOOST_TEST_EQ(errno, 0); + + errno = 0; + decimal64_fast junk_val; + std::stringstream junk_str; + junk_str.str("r5"); + junk_str >> junk_val; + BOOST_TEST_NE(junk_val, std::numeric_limits::signaling_NaN()) && BOOST_TEST_EQ(errno, EINVAL); +} + +void test_ostream() +{ + decimal64_fast val {123456, 0}; + std::stringstream out; + out << val; + BOOST_TEST_CSTR_EQ(out.str().c_str(), "123456"); + + decimal64_fast zero {0, 0}; + std::stringstream zero_out; + zero_out << zero; + BOOST_TEST_CSTR_EQ(zero_out.str().c_str(), "0.0e+00"); + + std::stringstream inf; + inf << std::numeric_limits::infinity(); + BOOST_TEST_CSTR_EQ(inf.str().c_str(), "inf"); + + std::stringstream qnan; + qnan << std::numeric_limits::quiet_NaN(); + BOOST_TEST_CSTR_EQ(qnan.str().c_str(), "nan"); + + std::stringstream snan; + snan << std::numeric_limits::signaling_NaN(); + BOOST_TEST_CSTR_EQ(snan.str().c_str(), "nan(snan)"); + + std::stringstream neg_inf; + neg_inf << (-std::numeric_limits::infinity()); + BOOST_TEST_CSTR_EQ(neg_inf.str().c_str(), "-inf"); + + std::stringstream neg_qnan; + neg_qnan << (-std::numeric_limits::quiet_NaN()); + BOOST_TEST_CSTR_EQ(neg_qnan.str().c_str(), "-nan(ind)"); + + std::stringstream neg_snan; + neg_snan << (-std::numeric_limits::signaling_NaN()); + BOOST_TEST_CSTR_EQ(neg_snan.str().c_str(), "-nan(snan)"); +} + +int main() +{ + test_istream(); + test_ostream(); + + return boost::report_errors(); +} diff --git a/test/test_decimal_quantum.cpp b/test/test_decimal_quantum.cpp index 40cb5413f..b7cacb3ae 100644 --- a/test/test_decimal_quantum.cpp +++ b/test/test_decimal_quantum.cpp @@ -12,6 +12,8 @@ #include #include #include +#include +#include using namespace boost::decimal; @@ -92,11 +94,15 @@ void test_quantexp() if (static_cast(i) + detail::bias_v > detail::max_biased_exp_v) { - if (!BOOST_TEST_EQ(quantexp(val1), detail::max_biased_exp_v)) + // Fast decimals have no concept of subnormals + BOOST_IF_CONSTEXPR (!std::is_same::value) { - // LCOV_EXCL_START - std::cerr << "Val: " << val1 << std::endl; - // LCOV_EXCL_STOP + if (!BOOST_TEST_EQ(quantexp(val1), detail::max_biased_exp_v)) + { + // LCOV_EXCL_START + std::cerr << "Val: " << val1 << std::endl; + // LCOV_EXCL_STOP + } } } else @@ -126,17 +132,22 @@ void test_nonfinite_quantexp() template void test_quantize() { - std::uniform_int_distribution sig(1'000'000, 9'999'999); - std::uniform_int_distribution exp(std::numeric_limits::min_exponent10 + 19, - std::numeric_limits::max_exponent10 - 19); + using sig_type = typename Dec::significand_type; + + std::uniform_int_distribution sig(1'000'000, 9'999'999); + std::uniform_int_distribution exp(std::numeric_limits::min_exponent10 + std::numeric_limits::digits10 + 1, + std::numeric_limits::max_exponent10 - std::numeric_limits::digits10 - 1); constexpr auto max_iter {std::is_same::value ? N / 4 : N}; for (std::size_t i {}; i < max_iter; ++i) { - const auto sig1 {sig(rng)}; - const auto sig2 {sig(rng)}; - const auto exp1 {exp(rng)}; - const auto exp2 {exp(rng)}; + auto sig1 {static_cast(sig(rng))}; + auto sig2 {static_cast(sig(rng))}; + auto exp1 {exp(rng)}; + auto exp2 {exp(rng)}; + + detail::normalize(sig1, exp1); + detail::normalize(sig1, exp1); const Dec val1 {sig1, exp1}; const Dec val2 {sig2, exp2}; @@ -145,9 +156,13 @@ void test_quantize() if (!BOOST_TEST_EQ(quantize(val1, val2), quantized_val)) { - std::cerr << "Val 1: " << val1 + // LCOV_EXCL_START + std::cerr << std::setprecision(std::numeric_limits::digits10) + << "Val 1: " << val1 << "\nVal 2: " << val2 - << "\nQuant: " << quantized_val << std::endl; + << "\nQuant: " << quantized_val + << "\n Func: " << quantize(val1, val2) << std::endl; + // LCOV_EXCL_STOP } } } @@ -177,6 +192,15 @@ int main() test_quantize(); test_nonfinite_quantize(); + test_same_quantum(); + test_nonfinite_samequantum(); + // Decimal32_fast normalizes its value in the constructor, + // so it will not match the values of the other types + //test_quantexp(); + test_nonfinite_quantexp(); + test_quantize(); + test_nonfinite_quantize(); + test_same_quantum(); test_nonfinite_samequantum(); test_quantexp(); diff --git a/test/test_exp.cpp b/test/test_exp.cpp index 7e4fbe2a6..4e912d701 100644 --- a/test/test_exp.cpp +++ b/test/test_exp.cpp @@ -128,13 +128,13 @@ namespace local if(!result_val_is_ok) { - // LCOV_EXCL_START + // LCOV_EXCL_START std::cerr << "x_flt : " << std::scientific << std::setprecision(std::numeric_limits::digits10) << x_flt << std::endl; std::cerr << "val_flt: " << std::scientific << std::setprecision(std::numeric_limits::digits10) << val_flt << std::endl; std::cerr << "val_dec: " << std::scientific << std::setprecision(std::numeric_limits::digits10) << val_dec << std::endl; break; - // LCOV_EXCL_STOP + // LCOV_EXCL_STOP } } @@ -177,9 +177,11 @@ namespace local { static_cast(i); - const auto val_inf_pos = exp(std::numeric_limits::infinity() * static_cast(dist(gen))); + const decimal_type arg_inf { std::numeric_limits::infinity() * static_cast(dist(gen)) }; - const auto result_val_inf_pos_is_ok = isinf(val_inf_pos); + const auto val_inf_pos = exp(arg_inf); + + const auto result_val_inf_pos_is_ok = (fpclassify(val_inf_pos) == FP_INFINITE); BOOST_TEST(result_val_inf_pos_is_ok); diff --git a/test/test_fast_math.cpp b/test/test_fast_math.cpp new file mode 100644 index 000000000..f2bc9a172 --- /dev/null +++ b/test/test_fast_math.cpp @@ -0,0 +1,200 @@ +// Copyright 2024 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define BOOST_DECIMAL_FAST_MATH + +#include +#include +#include +#include + +using namespace boost::decimal; + +#if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) +static constexpr auto N = static_cast(1024U); // Number of trials +#else +static constexpr auto N = static_cast(1024U >> 4U); // Number of trials +#endif + +// NOLINTNEXTLINE : Seed with a constant for repeatability +static std::mt19937_64 rng(42); // NOSONAR : Global rng is not const + +template +void random_addition() +{ + std::uniform_int_distribution dist(1, 1000); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {val1}; + const T dec2 {val2}; + + const T res = dec1 + dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 + val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } +} + +template +void random_subtraction() +{ + std::uniform_int_distribution dist(1, 1000); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {val1}; + const T dec2 {val2}; + + const T res = dec1 - dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 - val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } +} + +template +void random_multiplication() +{ + std::uniform_int_distribution dist(1, 1000); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {val1}; + const T dec2 {val2}; + + const T res = dec1 * dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 * val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } +} + +template +void random_division() +{ + std::uniform_int_distribution dist(1, 1000); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {val1}; + const T dec2 {val2}; + + const T res = dec1 / dec2; + const auto res_int = static_cast(res); + + if (!BOOST_TEST_EQ(res_int, val1 / val2)) + { + // LCOV_EXCL_START + std::cerr << "Val 1: " << val1 + << "\nDec 1: " << dec1 + << "\nVal 2: " << val2 + << "\nDec 2: " << dec2 + << "\nDec res: " << res + << "\nInt res: " << val1 + val2 << std::endl; + // LCOV_EXCL_STOP + } + } +} + +template +void test_comparisions() +{ + std::uniform_int_distribution dist(-1000, 1000); + + for (std::size_t i {}; i < N; ++i) + { + const T val1 {dist(rng)}; + const T val2 {dist(rng)}; + + const T dec1 {val1}; + const T dec2 {val2}; + + BOOST_TEST_EQ(val1 == val2, dec1 == dec2); + BOOST_TEST_EQ(val1 != val2, dec1 != dec2); + BOOST_TEST_EQ(val1 < val2, dec1 < dec2); + BOOST_TEST_EQ(val1 <= val2, dec1 <= dec2); + BOOST_TEST_EQ(val1 > val2, dec1 > dec2); + BOOST_TEST_EQ(val1 >= val2, dec1 >= dec2); + } +} + +int main() +{ + random_addition(); + random_subtraction(); + random_multiplication(); + random_division(); + test_comparisions(); + + random_addition(); + random_subtraction(); + random_multiplication(); + random_division(); + test_comparisions(); + + random_addition(); + random_subtraction(); + random_multiplication(); + random_division(); + test_comparisions(); + + random_addition(); + random_subtraction(); + random_multiplication(); + random_division(); + test_comparisions(); + + #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) + random_addition(); + random_subtraction(); + random_multiplication(); + random_division(); + test_comparisions(); + #endif + + return boost::report_errors(); +} diff --git a/test/test_fixed_width_trunc.cpp b/test/test_fixed_width_trunc.cpp new file mode 100644 index 000000000..b43f32134 --- /dev/null +++ b/test/test_fixed_width_trunc.cpp @@ -0,0 +1,59 @@ +// 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 +void test() +{ + constexpr T test_val {UINT32_C(1234567), 0}; + constexpr T validation_val_1 {UINT32_C(1), 6}; + constexpr T validation_val_2 {UINT32_C(12), 5}; + constexpr T validation_val_3 {UINT32_C(123), 4}; + constexpr T validation_val_4 {UINT32_C(1235), 3}; + constexpr T validation_val_5 {UINT32_C(12346), 2}; + constexpr T validation_val_6 {UINT32_C(123457), 1}; + constexpr T validation_val_7 {test_val}; + + BOOST_TEST_EQ(trunc_to(test_val, 0), trunc_to(test_val)); + BOOST_TEST_EQ(trunc_to(test_val, 1), validation_val_1); + BOOST_TEST_EQ(trunc_to(test_val, 2), validation_val_2); + BOOST_TEST_EQ(trunc_to(test_val, 3), validation_val_3); + BOOST_TEST_EQ(trunc_to(test_val, 4), validation_val_4); + BOOST_TEST_EQ(trunc_to(test_val, 5), validation_val_5); + BOOST_TEST_EQ(trunc_to(test_val, 6), validation_val_6); + BOOST_TEST_EQ(trunc_to(test_val, 7), validation_val_7); + BOOST_TEST_EQ(trunc_to(test_val, 100), test_val); + + // Non-finite values + for (int i = 0; i < 10; ++i) + { + BOOST_TEST(isinf(trunc_to(std::numeric_limits::infinity(), i))); + BOOST_TEST(isnan(trunc_to(std::numeric_limits::quiet_NaN(), i))); + BOOST_TEST(isnan(trunc_to(std::numeric_limits::signaling_NaN(), i))); + BOOST_TEST_EQ(trunc_to(T{0}, i), T{0}); + } + + // Big value + constexpr T big_val {1, 20}; + for (int i = 0; i < 10; ++i) + { + BOOST_TEST_EQ(trunc_to(big_val, i), big_val); + } +} + +int main() +{ + test(); + test(); + test(); + + test(); + + return boost::report_errors(); +} diff --git a/test/test_from_chars.cpp b/test/test_from_chars.cpp index 43e39503c..463ec4456 100644 --- a/test/test_from_chars.cpp +++ b/test/test_from_chars.cpp @@ -205,18 +205,28 @@ int main() { test_from_chars_scientific(); test_from_chars_scientific(); + test_from_chars_scientific(); + test_from_chars_scientific(); test_from_chars_fixed(); test_from_chars_fixed(); + test_from_chars_fixed(); + test_from_chars_fixed(); test_from_chars_general(); test_from_chars_general(); + test_from_chars_general(); + test_from_chars_general(); test_non_finite_values(); test_non_finite_values(); + test_non_finite_values(); + test_non_finite_values(); test_hex_values(); test_hex_values(); + test_hex_values(); + test_hex_values(); #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) test_from_chars_scientific(); diff --git a/test/test_lgamma.cpp b/test/test_lgamma.cpp index 78d6791d1..c12f4151a 100644 --- a/test/test_lgamma.cpp +++ b/test/test_lgamma.cpp @@ -448,7 +448,7 @@ auto main() -> int using decimal_type = boost::decimal::decimal32; using float_type = float; - const auto result_lgamma_is_ok = local::test_lgamma(512, 0.1L, 0.9L); + const auto result_lgamma_is_ok = local::test_lgamma(512, 0.01L, 0.9L); BOOST_TEST(result_lgamma_is_ok); @@ -481,7 +481,7 @@ auto main() -> int using decimal_type = boost::decimal::decimal64; using float_type = double; - const auto result_lgamma_is_ok = local::test_lgamma(4096, 0.1L, 0.9L); + const auto result_lgamma_is_ok = local::test_lgamma(4096, 0.01L, 0.9L); BOOST_TEST(result_lgamma_is_ok); @@ -530,7 +530,7 @@ auto main() -> int } { - const auto result_lgamma128_is_ok = local::test_lgamma_128(8092); + const auto result_lgamma128_is_ok = local::test_lgamma_128(4096); BOOST_TEST(result_lgamma128_is_ok); diff --git a/test/test_log.cpp b/test/test_log.cpp index 782fd9d98..83c4b11a0 100644 --- a/test/test_log.cpp +++ b/test/test_log.cpp @@ -21,10 +21,10 @@ #include -template auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_zero { 0, 0 }; return my_zero; } -template auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_one { 1, 0 }; return my_one; } -template auto my_inf () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_inf { std::numeric_limits::infinity() }; return my_inf; } -template auto my_nan () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_nan { std::numeric_limits::quiet_NaN() }; return my_nan; } +template auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_zero_val { 0, 0 }; return my_zero_val; } +template auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_one_val { 1, 0 }; return my_one_val; } +template auto my_inf () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_inf_val { std::numeric_limits::infinity() }; return my_inf_val; } +template auto my_nan () -> DecimalType& { using decimal_type = DecimalType; static decimal_type my_nan_val { std::numeric_limits::quiet_NaN() }; return my_nan_val; } namespace local { diff --git a/test/test_promotion.cpp b/test/test_promotion.cpp index 9c4dfabc5..8a046645f 100644 --- a/test/test_promotion.cpp +++ b/test/test_promotion.cpp @@ -90,5 +90,32 @@ int main() static_assert(std::is_same, decimal64>::value, "False"); static_assert(std::is_same, decimal32>::value, "False"); + static_assert(std::is_same, decimal32_fast>::value, "False"); + static_assert(std::is_same, decimal32_fast>::value, "False"); + static_assert(std::is_same, decimal64>::value, "False"); + static_assert(std::is_same, decimal64>::value, "False"); + static_assert(std::is_same, decimal128>::value, "False"); + static_assert(std::is_same, decimal128>::value, "False"); + static_assert(std::is_same, decimal64>::value, "False"); + static_assert(std::is_same, decimal32_fast>::value, "False"); + + static_assert(std::is_same, decimal64_fast>::value, "False"); + static_assert(std::is_same, decimal64_fast>::value, "False"); + static_assert(std::is_same, decimal64_fast>::value, "False"); + static_assert(std::is_same, decimal64_fast>::value, "False"); + static_assert(std::is_same, decimal128>::value, "False"); + static_assert(std::is_same, decimal128>::value, "False"); + static_assert(std::is_same, decimal64_fast>::value, "False"); + static_assert(std::is_same, decimal64_fast>::value, "False"); + + static_assert(std::is_same, decimal128_fast>::value, "False"); + static_assert(std::is_same, decimal128_fast>::value, "False"); + static_assert(std::is_same, decimal128_fast>::value, "False"); + static_assert(std::is_same, decimal128_fast>::value, "False"); + static_assert(std::is_same, decimal128_fast>::value, "False"); + static_assert(std::is_same, decimal128_fast>::value, "False"); + static_assert(std::is_same, decimal128_fast>::value, "False"); + static_assert(std::is_same, decimal128_fast>::value, "False"); + return 0; } diff --git a/test/test_sin_cos.cpp b/test/test_sin_cos.cpp index 6239d5953..5a87e4897 100644 --- a/test/test_sin_cos.cpp +++ b/test/test_sin_cos.cpp @@ -1,4 +1,5 @@ // Copyright 2024 Matt Borland +// Copyright 2024 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -145,6 +146,221 @@ void print_value(T value, const char* str) << "\nExp: " << ptr << "\n" << std::endl; } +namespace local +{ + template + auto is_close_fraction(const NumericType& a, + const NumericType& b, + const NumericType& tol) noexcept -> bool + { + using std::fabs; + + auto result_is_ok = bool { }; + + NumericType delta { }; + + if(b == static_cast(0)) + { + delta = fabs(a - b); // LCOV_EXCL_LINE + + result_is_ok = (delta < tol); // LCOV_EXCL_LINE + } + else + { + delta = fabs(1 - (a / b)); + + result_is_ok = (delta < tol); + } + + // LCOV_EXCL_START + if (!result_is_ok) + { + std::cerr << std::setprecision(std::numeric_limits::digits10) << "a: " << a + << "\nb: " << b + << "\ndelta: " << delta + << "\ntol: " << tol << std::endl; + } + // LCOV_EXCL_STOP + + return result_is_ok; + } + + auto test_sin_128(const int tol_factor) -> bool + { + using decimal_type = boost::decimal::decimal128; + + using str_ctrl_array_type = std::array; + + const str_ctrl_array_type ctrl_strings = + {{ + // Table[N[Sin[n + n/10], 36], {n, -20, 20, 1}] + "0.00885130929040387592169025681577233246", + "-0.887157528692350427205640661441011342", + "-0.813673737507104955433222744609065147", + "0.148999025814198104343982890664237216", + "0.948844497918124441518161248410867044", + "0.711785342369123065842340834512896188", + "-0.303118356745702602523931087729992333", + "-0.986771964274613470590033455846296362", + "-0.592073514707223565308069810796062123", + "0.449647464534601151267544078200296711", + "0.999990206550703457051564899025522107", + "0.457535893775321044413818107505363926", + "-0.584917192891762253530931311812375128", + "-0.988168233877000368552393618723663021", + "-0.311541363513378174354985105592593697", + "0.705540325570391906231919175522070079", + "0.951602073889515954035392333380387684", + "0.157745694143248382011654277602482371", + "-0.808496403819590184304036910416119065", + "-0.891207360061435339951802577871703538", + "0", + "0.891207360061435339951802577871703538", + "0.808496403819590184304036910416119065", + "-0.157745694143248382011654277602482371", + "-0.951602073889515954035392333380387684", + "-0.705540325570391906231919175522070079", + "0.311541363513378174354985105592593697", + "0.988168233877000368552393618723663021", + "0.584917192891762253530931311812375128", + "-0.457535893775321044413818107505363926", + "-0.999990206550703457051564899025522107", + "-0.449647464534601151267544078200296711", + "0.592073514707223565308069810796062123", + "0.986771964274613470590033455846296362", + "0.303118356745702602523931087729992333", + "-0.711785342369123065842340834512896188", + "-0.948844497918124441518161248410867044", + "-0.148999025814198104343982890664237216", + "0.813673737507104955433222744609065147", + "0.887157528692350427205640661441011342", + "-0.00885130929040387592169025681577233246", + }}; + + std::array::value> sin_values { }; + std::array::value> ctrl_values { }; + + int nx { -20 }; + + bool result_is_ok { true }; + + const decimal_type my_tol { std::numeric_limits::epsilon() * static_cast(tol_factor) }; + + for(auto i = static_cast(UINT8_C(0)); i < std::tuple_size::value; ++i) + { + const decimal_type x_arg = + decimal_type + { + decimal_type { nx } + + decimal_type { nx, -1 } + }; + + ++nx; + + sin_values[i] = sin(x_arg); + + static_cast + ( + from_chars(ctrl_strings[i], ctrl_strings[i] + std::strlen(ctrl_strings[i]), ctrl_values[i]) + ); + + const auto result_sin_is_ok = is_close_fraction(sin_values[i], ctrl_values[i], my_tol); + + result_is_ok = (result_sin_is_ok && result_is_ok); + } + + return result_is_ok; + } + + auto test_cos_128(const int tol_factor) -> bool + { + using decimal_type = boost::decimal::decimal128; + + using str_ctrl_array_type = std::array; + + const str_ctrl_array_type ctrl_strings = + {{ + // Table[N[Cos[n + n/10], 36], {n, -20, 20, 1}] + "-0.999960826394637126454174739212693774", + "-0.461466704415910626922141930570155132", + "0.581321811814436275127478838749985834", + "0.988837342694145995574183803962615751", + "0.315743754919241977341902454154186407", + "-0.702397057502713532361560769391904267", + "-0.952952916887180197669329573420619689", + "-0.162114436499717558295988827296285793", + "0.805883957640450316780870877627822774", + "0.893206111509322690144989864397000805", + "0.00442569798805078574835502472394157323", + "-0.889191152625361054634438698689106779", + "-0.811093014061655562889085504219324484", + "0.153373862037864525977384239572053515", + "0.950232591958529466219737721668197376", + "0.708669774291260000027421181325843735", + "-0.307332869978419683119139742217712371", + "-0.987479769908864883936591051102853311", + "-0.588501117255345708524142612654928416", + "0.453596121425577387771370051784716122", + "1", + "0.453596121425577387771370051784716122", + "-0.588501117255345708524142612654928416", + "-0.987479769908864883936591051102853311", + "-0.307332869978419683119139742217712371", + "0.708669774291260000027421181325843735", + "0.950232591958529466219737721668197376", + "0.153373862037864525977384239572053515", + "-0.811093014061655562889085504219324484", + "-0.889191152625361054634438698689106779", + "0.00442569798805078574835502472394157323", + "0.893206111509322690144989864397000805", + "0.805883957640450316780870877627822774", + "-0.162114436499717558295988827296285793", + "-0.952952916887180197669329573420619689", + "-0.702397057502713532361560769391904267", + "0.315743754919241977341902454154186407", + "0.988837342694145995574183803962615751", + "0.581321811814436275127478838749985834", + "-0.461466704415910626922141930570155132", + "-0.999960826394637126454174739212693774", + }}; + + std::array::value> cos_values { }; + std::array::value> ctrl_values { }; + + int nx { -20 }; + + bool result_is_ok { true }; + + const decimal_type my_tol { std::numeric_limits::epsilon() * static_cast(tol_factor) }; + + for(auto i = static_cast(UINT8_C(0)); i < std::tuple_size::value; ++i) + { + const decimal_type x_arg = + decimal_type + { + decimal_type { nx } + + decimal_type { nx, -1 } + }; + + ++nx; + + cos_values[i] = cos(x_arg); + + static_cast + ( + from_chars(ctrl_strings[i], ctrl_strings[i] + std::strlen(ctrl_strings[i]), ctrl_values[i]) + ); + + const auto result_cos_is_ok = is_close_fraction(cos_values[i], ctrl_values[i], my_tol); + + result_is_ok = (result_cos_is_ok && result_is_ok); + } + + return result_is_ok; + } + +} // namespace local + int main() { #ifdef BOOST_DECIMAL_GENERATE_CONSTANT_SIGS @@ -199,13 +415,20 @@ int main() test_sin(); test_cos(); + test_sin(); + test_cos(); test_sin(); test_cos(); + test_sin(); + test_cos(); - #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) - //test_sin(); - //test_cos(); - #endif + { + const auto result_sin128_is_ok = local::test_sin_128(0x8'000); + const auto result_cos128_is_ok = local::test_cos_128(0x8'000); + + BOOST_TEST(result_sin128_is_ok); + BOOST_TEST(result_cos128_is_ok); + } return boost::report_errors(); } diff --git a/test/test_tgamma.cpp b/test/test_tgamma.cpp index b867b10f5..24b0925ca 100644 --- a/test/test_tgamma.cpp +++ b/test/test_tgamma.cpp @@ -21,8 +21,9 @@ #include -template auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_zero { 0, 0 }; return val_zero; } -template auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_one { 1, 0 }; return val_one; } +template auto my_zero() -> DecimalType&; +template auto my_one () -> DecimalType&; +template auto my_nan () -> DecimalType&; namespace local { @@ -239,6 +240,8 @@ namespace local std::mt19937_64 gen; + gen.seed(time_point()); + std::uniform_real_distribution dist ( @@ -248,11 +251,11 @@ namespace local auto result_is_ok = true; - for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) { static_cast(i); - const auto val_nan = tgamma(std::numeric_limits::quiet_NaN() * static_cast(dist(gen))); + const auto val_nan = tgamma(::my_nan() * static_cast(dist(gen))); const auto result_val_nan_is_ok = isnan(val_nan); @@ -304,7 +307,7 @@ namespace local { static_cast(i); - const auto val_zero_neg = tgamma(-::my_zero()); + const auto val_zero_neg = tgamma(-(::my_zero() * static_cast(dist(gen)))); const auto result_val_zero_neg_is_ok = (isinf(val_zero_neg) && signbit(val_zero_neg)); @@ -315,13 +318,13 @@ namespace local for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(6)); ++i) { - const auto n_neg = static_cast(-static_cast(i) - 1); + decimal_type dnx { ::my_zero() * static_cast(dist(gen)) }; - const auto val_neg_int = tgamma( decimal_type { n_neg, 0 } ); + dnx -= static_cast(i + 1); - const auto is_odd = ((n_neg & 1) != 0); + const auto val_neg_int = tgamma(dnx); - const auto result_val_neg_int_is_ok = (is_odd ? isnan(val_neg_int) : (fpclassify(val_neg_int) == FP_NORMAL)); + const auto result_val_neg_int_is_ok = isnan(val_neg_int); BOOST_TEST(result_val_neg_int_is_ok); @@ -385,7 +388,7 @@ namespace local return result_is_ok; } - auto test_tgamma_128(const int tol_factor) -> bool + auto test_tgamma_128_lo(const int tol_factor) -> bool { using decimal_type = boost::decimal::decimal128; @@ -393,16 +396,16 @@ namespace local const str_ctrl_array_type ctrl_strings = {{ - // Table[N[Gamma[(100 n + 10 n + 1)/100], 33], {n, 1, 9, 1}] - "0.947395504039301942134227647281424", - "1.10784755653406415338349971053114", - "2.71139823924390323650711692085896", - "10.2754040920152050479188001843206", - "53.1934282525008207389522379291890", - "350.998609824200588801455504140098", - "2825.09453680418713613816084109635", - "26903.6719467497675679082571845063", - "296439.082102472192334520537379648" + // Table[N[Gamma[n/10 + n/100], 36], {n, 1, 9, 1}] + "8.61268640035729038303843315710385452", + "4.15048157959277857782635113344664974", + "2.70720622261519104902052213245593595", + "2.01319332601838966777117106234059403", + "1.61612426873357513405845849344452552", + "1.36616419875147485749818904751902063", + "1.19969237367745339749375337490556205", + "1.08530778746771950916024031037404015", + "1.00587197964410779193412655924290279" }}; std::array::value> tg_values { }; @@ -419,11 +422,9 @@ namespace local const decimal_type x_arg = decimal_type { - decimal_type { 1, 2 } * nx - + decimal_type { 1, 1 } * nx - + 1 - } - / decimal_type { 1, 2 }; + decimal_type { nx, -1 } + + decimal_type { nx, -2 } + }; ++nx; @@ -442,6 +443,77 @@ namespace local return result_is_ok; } + auto test_tgamma_128_hi(const int tol_factor) -> bool + { + using decimal_type = boost::decimal::decimal128; + + using str_ctrl_array_type = std::array; + + const str_ctrl_array_type ctrl_strings = + {{ + // Table[N[Gamma[n + n/10 + n/100 + n/1000], 36], {n, 1, 221, 10}] + "0.947008281162266001895790481785841941", + "6.86303089001025022525468906807854872E7", + "3.15793281780505944512262743601561476E21", + "4.09725124531962875389920397572482227E37", + "2.15936518595728901631037627967671095E55", + "1.81286283067020212427848823649632939E74", + "1.39061339788491577387400422516492967E94", + "6.73844979762045895677263594960237945E114", + "1.58535690838444528565837326081067457E136", + "1.48677291673153228216478665408262025E158", + "4.76763037027821868276349648015607359E180", + "4.62395515046183569847627307320617350E203", + "1.22680267570425015175034111397510637E227", + "8.18925182002285090986591692926519438E250", + "1.28134405415265103961333749220602490E275", + "4.42253704896092684478952587741331734E299", + "3.19451354412535995695298989136255493E324", + "4.61171076932972412633999770033353340E349", + "1.27758574231803927960543278875893523E375", + "6.55079490827236721494047351435261992E400", + "6.01906299656231025481256209731706244E426", + "9.62614024174757375775890458257288037E452", + "2.60989891797040728048724526392884050E479", + }}; + + std::array::value> tg_values { }; + std::array::value> ctrl_values { }; + + int nx { 1 }; + + bool result_is_ok { true }; + + const decimal_type my_tol { std::numeric_limits::epsilon() * static_cast(tol_factor) }; + + for(auto i = static_cast(UINT8_C(0)); i < std::tuple_size::value; ++i) + { + const decimal_type x_arg = + decimal_type + { + decimal_type { nx } + + decimal_type { nx, -1 } + + decimal_type { nx, -2 } + + decimal_type { nx, -3 } + }; + + nx += 10; + + tg_values[i] = tgamma(x_arg); + + static_cast + ( + from_chars(ctrl_strings[i], ctrl_strings[i] + std::strlen(ctrl_strings[i]), ctrl_values[i]) + ); + + const auto result_tgamma_is_ok = is_close_fraction(tg_values[i], ctrl_values[i], my_tol); + + result_is_ok = (result_tgamma_is_ok && result_is_ok); + } + + return result_is_ok; + } + } // namespace local auto main() -> int @@ -470,6 +542,17 @@ auto main() -> int result_is_ok = (result_tgamma_is_ok && result_is_ok); } + { + using decimal_type = boost::decimal::decimal32_fast; + using float_type = float; + + const auto result_tgamma_is_ok = local::test_tgamma(768, 2.1L, 23.4L); + + BOOST_TEST(result_tgamma_is_ok); + + result_is_ok = (result_tgamma_is_ok && result_is_ok); + } + { using decimal_type = boost::decimal::decimal64; using float_type = double; @@ -528,14 +611,20 @@ auto main() -> int } { - const auto result_tgamma128_is_ok = local::test_tgamma_128(8192); + const auto result_tgamma128_lo_is_ok = local::test_tgamma_128_lo(4096); + const auto result_tgamma128_hi_is_ok = local::test_tgamma_128_hi(0x20'000); - BOOST_TEST(result_tgamma128_is_ok); + BOOST_TEST(result_tgamma128_lo_is_ok); + BOOST_TEST(result_tgamma128_hi_is_ok); - result_is_ok = (result_tgamma128_is_ok && result_is_ok); + result_is_ok = (result_tgamma128_lo_is_ok && result_tgamma128_hi_is_ok && result_is_ok); } result_is_ok = ((boost::report_errors() == 0) && result_is_ok); return (result_is_ok ? 0 : -1); } + +template auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_zero { 0 }; return val_zero; } +template auto my_one () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_one { 1 }; return val_one; } +template auto my_nan () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_nan { std::numeric_limits::quiet_NaN() }; return val_nan; } diff --git a/test/test_to_chars.cpp b/test/test_to_chars.cpp index 78a29b831..37fcc0060 100644 --- a/test/test_to_chars.cpp +++ b/test/test_to_chars.cpp @@ -97,7 +97,7 @@ void test_small_values() template void test_large_values() { - constexpr double max_value = std::is_same::value ? 1e80 : 1e200; + constexpr double max_value = std::is_same::value || std::is_same::value ? 1e80 : 1e200; std::uniform_real_distribution dist(-max_value, max_value); for (std::size_t i {}; i < N; ++i) @@ -614,6 +614,30 @@ int main() test_434_hex(); #endif + test_non_finite_values(); + test_small_values(); + test_large_values(); + test_fixed_format(); + test_precision(); + test_buffer_overflow(); + zero_test(); + test_434_fixed(); + test_434_scientific(); + test_hex_format(); + test_434_hex(); + + test_non_finite_values(); + test_small_values(); + test_large_values(); + test_fixed_format(); + test_precision(); + test_buffer_overflow(); + zero_test(); + test_434_fixed(); + test_434_scientific(); + test_hex_format(); + test_434_hex(); + // Bugfixes test_value(decimal64{2657844750}, "2657844750", chars_format::general); diff --git a/test/test_zeta.cpp b/test/test_zeta.cpp new file mode 100644 index 000000000..62b9e4f6d --- /dev/null +++ b/test/test_zeta.cpp @@ -0,0 +1,423 @@ +// Copyright 2024 Matt Borland +// Copyright 2024 Christoper Kormanyos +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +// Propogates up from boost.math +#define _SILENCE_CXX23_DENORM_DEPRECATION_WARNING + +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wold-style-cast" +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wsign-conversion" +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wold-style-cast" +# pragma GCC diagnostic ignored "-Wundef" +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +#include +#include + +#include +#include +#include + +template auto my_zero() -> DecimalType&; +template auto my_nan () -> DecimalType&; +template auto my_inf () -> DecimalType&; + +namespace local +{ + template + auto time_point() noexcept -> IntegralTimePointType + { + using local_integral_time_point_type = IntegralTimePointType; + using local_clock_type = ClockType; + + const auto current_now = + static_cast + ( + std::chrono::duration_cast + ( + local_clock_type::now().time_since_epoch() + ).count() + ); + + return static_cast(current_now); + } + + template + auto is_close_fraction(const NumericType& a, + const NumericType& b, + const NumericType& tol) noexcept -> bool + { + using std::fabs; + + auto result_is_ok = bool { }; + + NumericType delta { }; + + if(b == static_cast(0)) + { + delta = fabs(a - b); // LCOV_EXCL_LINE + + result_is_ok = (delta < tol); // LCOV_EXCL_LINE + } + else + { + delta = fabs(1 - (a / b)); + + result_is_ok = (delta < tol); + } + + // LCOV_EXCL_START + if (!result_is_ok) + { + std::cerr << std::setprecision(std::numeric_limits::digits10) << "a: " << a + << "\nb: " << b + << "\ndelta: " << delta + << "\ntol: " << tol << std::endl; + } + // LCOV_EXCL_STOP + + return result_is_ok; + } + + template + auto test_riemann_zeta(const int tol_factor, const long double range_lo, const long double range_hi) -> bool + { + using decimal_type = DecimalType; + using float_type = FloatType; + + std::random_device rd; + std::mt19937_64 gen(rd()); + + gen.seed(time_point()); + + auto dis = + std::uniform_real_distribution + { + static_cast(range_lo), + static_cast(range_hi) + }; + + bool result_is_ok { true }; + + auto trials = static_cast(UINT8_C(0)); + + #if !defined(BOOST_DECIMAL_REDUCE_TEST_DEPTH) + constexpr auto count = (sizeof(decimal_type) == static_cast(UINT8_C(4))) ? static_cast(UINT32_C(0x80)) : static_cast(UINT32_C(0x20)); + #else + constexpr auto count = (sizeof(decimal_type) == static_cast(UINT8_C(4))) ? static_cast(UINT32_C(0x10)) : static_cast(UINT32_C(0x4)); + #endif + + for( ; trials < count; ++trials) + { + const auto x_flt = dis(gen); + const auto x_dec = static_cast(x_flt); + + const auto val_flt = boost::math::zeta(x_flt); + const auto val_dec = riemann_zeta(x_dec); + + const auto result_log_is_ok = is_close_fraction(val_flt, static_cast(val_dec), std::numeric_limits::epsilon() * static_cast(tol_factor)); + + result_is_ok = (result_log_is_ok && result_is_ok); + + if(!result_log_is_ok) + { + // LCOV_EXCL_START + std::cerr << "x_flt : " << std::scientific << std::setprecision(std::numeric_limits::digits10) << x_flt << std::endl; + std::cerr << "val_flt: " << std::scientific << std::setprecision(std::numeric_limits::digits10) << val_flt << std::endl; + std::cerr << "val_dec: " << std::scientific << std::setprecision(std::numeric_limits::digits10) << val_dec << std::endl; + + break; + // LCOV_EXCL_STOP + } + } + + BOOST_TEST(result_is_ok); + + return result_is_ok; + } + +template +auto test_riemann_zeta_edge() -> bool +{ + using decimal_type = DecimalType; + using float_type = FloatType; + + bool result_is_ok { true }; + + std::mt19937_64 gen; + + gen.seed(time_point()); + + std::uniform_real_distribution dist(static_cast(1.1L), static_cast(101.1L)); + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(10)); ++i) + { + static_cast(i); + + { + const decimal_type inf { ::my_inf () * decimal_type(dist(gen)) }; + + bool result_inf_is_ok { }; + + result_inf_is_ok = (riemann_zeta(inf) == decimal_type { 1 } ); + result_is_ok = (result_inf_is_ok && result_is_ok); + BOOST_TEST(result_is_ok); + + result_inf_is_ok = + ( + isinf(riemann_zeta(-inf)) && signbit(riemann_zeta(-inf)) + ); + result_is_ok = (result_inf_is_ok && result_is_ok); + BOOST_TEST(result_is_ok); + } + + { + const decimal_type nan { ::my_nan () * decimal_type(dist(gen)) }; + + bool result_nan_is_ok { }; + + result_nan_is_ok = (isnan(riemann_zeta(nan))); result_is_ok = (result_nan_is_ok && result_is_ok); BOOST_TEST(result_is_ok); + result_nan_is_ok = (isnan(riemann_zeta(-nan))); result_is_ok = (result_nan_is_ok && result_is_ok); BOOST_TEST(result_is_ok); + result_nan_is_ok = (isnan(riemann_zeta(decimal_type { 1 }))); result_is_ok = (result_nan_is_ok && result_is_ok); BOOST_TEST(result_is_ok); + } + + { + const decimal_type zero { ::my_zero() * decimal_type(dist(gen)) }; + + const decimal_type minus_half { -5, -1 }; + + const bool result_zero_is_ok = (riemann_zeta(zero) == minus_half); result_is_ok = (result_zero_is_ok && result_is_ok); BOOST_TEST(result_is_ok); + } + } + + for(auto n = static_cast(UINT8_C(2)); n < static_cast(UINT8_C(6)); ++n) + { + using ::boost::decimal::riemann_zeta; + + const decimal_type rzf = riemann_zeta(static_cast(n)); + const decimal_type rzn = riemann_zeta(n); + + const bool result_n_or_f_is_ok = (rzf == rzn); + + result_is_ok = (result_n_or_f_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + return result_is_ok; +} + +auto test_riemann_zeta_128_lo(const int tol_factor) -> bool +{ + using decimal_type = boost::decimal::decimal128; + + using str_ctrl_array_type = std::array; + + const str_ctrl_array_type ctrl_strings = + {{ + // Table[N[Zeta[1 + n/1000], 36], {n, 5, 7, 1}] + "200.577579622956683652084654605524346", + "167.244319052140751595350397994287573", + "143.434867995431699170218293588670480", + }}; + + std::array::value> rz_values { }; + std::array::value> ctrl_values { }; + + int nx { 5 }; + + bool result_is_ok { true }; + + const decimal_type my_tol { std::numeric_limits::epsilon() * static_cast(tol_factor) }; + + for(auto i = static_cast(UINT8_C(0)); i < std::tuple_size::value; ++i) + { + const decimal_type x_arg = + decimal_type + { + decimal_type { 1 } + + decimal_type { nx, -3 } + }; + + ++nx; + + rz_values[i] = riemann_zeta(x_arg); + + static_cast + ( + from_chars(ctrl_strings[i], ctrl_strings[i] + std::strlen(ctrl_strings[i]), ctrl_values[i]) + ); + + const auto result_rz_is_ok = is_close_fraction(rz_values[i], ctrl_values[i], my_tol); + + result_is_ok = (result_rz_is_ok && result_is_ok); + } + + return result_is_ok; +} + +auto test_riemann_zeta_128_hi(const int tol_factor) -> bool +{ + using decimal_type = boost::decimal::decimal128; + + using str_ctrl_array_type = std::array; + + const str_ctrl_array_type ctrl_strings = + {{ + // Table[N[Zeta[n + n/10], 36], {n, 1, 9, 1}] + "10.5844484649508098263864007917355230", + "1.49054325650689350825344649551165452", + "1.15194479472077368855082683374115056", + "1.05928172597983541766404502818685201", + "1.02520457995468569459240582819540529", + "1.01116101415427096427312532266653516", + "1.00504987929596499812178165124883599", + "1.00231277790982194674469422849347780", + "1.00106679698357801585766465214764188", + }}; + + std::array::value> rz_values { }; + std::array::value> ctrl_values { }; + + int nx { 1 }; + + bool result_is_ok { true }; + + const decimal_type my_tol { std::numeric_limits::epsilon() * static_cast(tol_factor) }; + + for(auto i = static_cast(UINT8_C(0)); i < std::tuple_size::value; ++i) + { + const decimal_type x_arg = + decimal_type + { + decimal_type { nx } + + decimal_type { nx, -1 } + }; + + ++nx; + + rz_values[i] = riemann_zeta(x_arg); + + static_cast + ( + from_chars(ctrl_strings[i], ctrl_strings[i] + std::strlen(ctrl_strings[i]), ctrl_values[i]) + ); + + const auto result_rz_is_ok = is_close_fraction(rz_values[i], ctrl_values[i], my_tol); + + result_is_ok = (result_rz_is_ok && result_is_ok); + } + + return result_is_ok; +} + +} // namespace local + +int main() +{ + bool result_is_ok { true }; + + { + using decimal_type = ::boost::decimal::decimal32; + + const bool result_edge_is_ok = local::test_riemann_zeta_edge(); + + result_is_ok = (result_edge_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + { + using decimal_type = ::boost::decimal::decimal32; + + const bool result_rz32_is_ok = local::test_riemann_zeta(128, 1.1L, 5.6L); + + result_is_ok = (result_rz32_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + { + using decimal_type = ::boost::decimal::decimal32; + + const bool result_rz32_is_ok = local::test_riemann_zeta(1024, 1.01L, 1.1L); + + result_is_ok = (result_rz32_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + { + using decimal_type = ::boost::decimal::decimal32_fast; + + const bool result_rz32_is_ok = local::test_riemann_zeta(1024, 1.01L, 1.1L); + + result_is_ok = (result_rz32_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + { + using decimal_type = ::boost::decimal::decimal32; + + const bool result_rz32_is_ok = local::test_riemann_zeta(512, -3.6L, -2.3L); + + result_is_ok = (result_rz32_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + { + using decimal_type = ::boost::decimal::decimal64; + + const bool result_rz64_is_ok = local::test_riemann_zeta(256, 1.1L, 12.3L); + + result_is_ok = (result_rz64_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + { + using decimal_type = ::boost::decimal::decimal64; + + const bool result_rz64_is_ok = local::test_riemann_zeta(1024, 1.01L, 1.1L); + + result_is_ok = (result_rz64_is_ok && result_is_ok); + + BOOST_TEST(result_is_ok); + } + + { + const auto result_rz_128_lo_is_ok = local::test_riemann_zeta_128_lo(4096); + const auto result_rz_128_hi_is_ok = local::test_riemann_zeta_128_hi(4096); + + BOOST_TEST(result_rz_128_lo_is_ok); + BOOST_TEST(result_rz_128_hi_is_ok); + + result_is_ok = (result_rz_128_lo_is_ok && result_rz_128_hi_is_ok && result_is_ok); + } + + result_is_ok = ((boost::report_errors() == 0) && result_is_ok); + + return (result_is_ok ? 0 : -1); +} + +template auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_zero { 0 }; return val_zero; } +template auto my_nan () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_nan { std::numeric_limits::quiet_NaN() }; return val_nan; } +template auto my_inf () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_inf { std::numeric_limits::infinity() }; return val_inf; }