From 2c2772561799af1bd18944b22cc86b84c055478c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:29:53 -0500 Subject: [PATCH 01/47] Add simplified u256 add impl --- include/boost/decimal/detail/i256.hpp | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 include/boost/decimal/detail/i256.hpp diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp new file mode 100644 index 000000000..de5b7a80c --- /dev/null +++ b/include/boost/decimal/detail/i256.hpp @@ -0,0 +1,39 @@ +// Copyright 2023 - 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// This is not a fully featured implementation of a 256-bit integer like int128::uint128_t is +// i256 only contains the minimum amount that we need to perform operations like decimal128_t add/sub + +#ifndef BOOST_DECIMAL_DETAIL_I256_HPP +#define BOOST_DECIMAL_DETAIL_I256_HPP + +#include +#include +#include + +namespace boost { +namespace decimal { +namespace detail { + +constexpr u256 u256_add(const int128::uint128_t& lhs, const int128::uint128_t& rhs) noexcept +{ + u256 result; + std::uint64_t carry {}; + + auto sum {lhs.low + rhs.low}; + result[0] = sum; + carry = (sum < lhs.low) ? 1 : 0; + + sum = lhs.high + rhs.high + carry; + result[1] = sum; + result[2] = static_cast(sum < lhs.high || (sum == lhs.high && carry)); + + return result; +} + +} // namespace detail +} // namespace decimal +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_I256_HPP From 31deee4c6af8a7915ff2cd0accb8991799adad1a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:34:21 -0500 Subject: [PATCH 02/47] Add intrinsic powered impl --- include/boost/decimal/detail/i256.hpp | 28 ++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index de5b7a80c..e11449424 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -16,7 +16,10 @@ namespace boost { namespace decimal { namespace detail { -constexpr u256 u256_add(const int128::uint128_t& lhs, const int128::uint128_t& rhs) noexcept +namespace impl { + +// This impl works regardless of intrinsics or otherwise +constexpr u256 u256_add_impl(const int128::uint128_t& lhs, const int128::uint128_t& rhs) noexcept { u256 result; std::uint64_t carry {}; @@ -32,6 +35,29 @@ constexpr u256 u256_add(const int128::uint128_t& lhs, const int128::uint128_t& r return result; } +} // namespace impl + +#if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_ADD_CARRY) + +constexpr u256 u256_add(const int128::uint128_t& lhs, const int128::uint128_t& rhs) noexcept +{ + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::u256_add_impl(lhs, rhs); + } + else + { + unsigned long long result[3] {}; + unsigned char carry {}; + carry = BOOST_DECIMAL_ADD_CARRY(carry, lhs.low, rhs.low, &result[0]); + result[2] = static_cast(BOOST_DECIMAL_ADD_CARRY(carry, lhs.high, rhs.high, &result[1])); + + return {UINT64_C(0), result[2], result[1], result[0]}; + } +} + +#endif + } // namespace detail } // namespace decimal } // namespace boost From 8f7c92b5e60dbe3280b93a395c36aba338c03e97 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:41:27 -0500 Subject: [PATCH 03/47] Add second u256 addition impl --- include/boost/decimal/detail/i256.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index e11449424..9b3495c8a 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -56,6 +56,25 @@ constexpr u256 u256_add(const int128::uint128_t& lhs, const int128::uint128_t& r } } +#elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(__GNUC__) && !defined(BOOST_DECIMAL_ADD_CARRY) + +constexpr u256 u256_add(const int128::uint128_t& lhs, const int128::uint128_t& rhs) noexcept +{ + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::u256_add_impl(lhs, rhs); + } + else + { + unsigned long long result[3] {}; + bool carry {}; + carry = impl::add_carry_u64(carry, lhs.low, rhs.low, &result[0]); + result[2] = static_cast(impl::add_carry_u64(carry, lhs.high, rhs.high, &result[1])); + + return {UINT64_C(0), result[2], result[1], result[0]}; + } +} + #endif } // namespace detail From 5294424de1f0888966dc9a391bbb987da44917d6 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:51:20 -0500 Subject: [PATCH 04/47] Add i256_sub method --- include/boost/decimal/detail/i256.hpp | 35 +++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index 9b3495c8a..5d9f9d031 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -77,6 +77,41 @@ constexpr u256 u256_add(const int128::uint128_t& lhs, const int128::uint128_t& r #endif +namespace impl { + +constexpr std::uint64_t sub_borrow_u64(const std::uint64_t borrow_in, const std::uint64_t a, const std::uint64_t b, std::uint64_t& result) noexcept +{ + const auto diff {a - b}; + const auto b1 {static_cast(a < b)}; + result = diff - borrow_in; + const auto borrow_out {b1 | (diff < borrow_in)}; + + return borrow_out; +} + +} // namespace impl + +constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept +{ + if (a >= b) + { + std::uint64_t borrow {}; + result[0] = impl::sub_borrow_u64(0, a.low, b.low, borrow); + result[1] = impl::sub_borrow_u64(borrow, a.high, b.high, borrow); + + return false; + } + else + { + // a < b: negative result, |a - b| = b - a + std::uint64_t borrow {}; + result[0] = impl::sub_borrow_u64(0, b.low, a.low, borrow); + result[1] = impl::sub_borrow_u64(borrow, a.high, b.high, borrow); + + return true; + } +} + } // namespace detail } // namespace decimal } // namespace boost From 35b21fa12a175603056dc45a3f35cd5640c9f2e1 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:05:01 -0500 Subject: [PATCH 05/47] Add signed 256 x 256 addition --- include/boost/decimal/detail/add_impl.hpp | 1 + include/boost/decimal/detail/i256.hpp | 40 +++++++++++++++++++---- 2 files changed, 34 insertions(+), 7 deletions(-) diff --git a/include/boost/decimal/detail/add_impl.hpp b/include/boost/decimal/detail/add_impl.hpp index 8eb1d0ce3..c6dc7d731 100644 --- a/include/boost/decimal/detail/add_impl.hpp +++ b/include/boost/decimal/detail/add_impl.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "int128.hpp" #ifndef BOOST_DECIMAL_BUILD_MODULE diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index 5d9f9d031..b93d488d1 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -89,15 +89,13 @@ constexpr std::uint64_t sub_borrow_u64(const std::uint64_t borrow_in, const std: return borrow_out; } -} // namespace impl - -constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept +constexpr bool i256_sub_impl(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept { if (a >= b) { std::uint64_t borrow {}; - result[0] = impl::sub_borrow_u64(0, a.low, b.low, borrow); - result[1] = impl::sub_borrow_u64(borrow, a.high, b.high, borrow); + result[0] = sub_borrow_u64(0, a.low, b.low, borrow); + result[1] = sub_borrow_u64(borrow, a.high, b.high, borrow); return false; } @@ -105,13 +103,41 @@ constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, { // a < b: negative result, |a - b| = b - a std::uint64_t borrow {}; - result[0] = impl::sub_borrow_u64(0, b.low, a.low, borrow); - result[1] = impl::sub_borrow_u64(borrow, a.high, b.high, borrow); + result[0] = sub_borrow_u64(0, b.low, a.low, borrow); + result[1] = sub_borrow_u64(borrow, a.high, b.high, borrow); return true; } } +} // namespace impl + +constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept +{ + return impl::i256_sub_impl(a, b, result); +} + +constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept +{ + if (a >= b) + { + auto borrow {impl::sub_borrow_u64(0, a[0], b[0], result[0])}; + borrow = impl::sub_borrow_u64(borrow, a[1], b[1], result[1]); + borrow = impl::sub_borrow_u64(borrow, a[2], b[2], result[2]); + impl::sub_borrow_u64(borrow, a[3], b[3], result[3]); + return false; + } + else + { + // a < b: negative result, |a - b| = b - a + auto borrow {impl::sub_borrow_u64(0, b[0], a[0], result[0])}; + borrow = impl::sub_borrow_u64(borrow, b[1], a[1], result[1]); + borrow = impl::sub_borrow_u64(borrow, b[2], a[2], result[2]); + impl::sub_borrow_u64(borrow, b[3], a[3], result[3]); + return true; + } +} + } // namespace detail } // namespace decimal } // namespace boost From e9620b1415a640cdf6aa0f1ceb35fa8df8a90e27 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:23:58 -0500 Subject: [PATCH 06/47] Add intrinsic powered sub --- include/boost/decimal/detail/i256.hpp | 65 +++++++++++++++++++++------ 1 file changed, 52 insertions(+), 13 deletions(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index b93d488d1..d0e36aff9 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -110,6 +110,27 @@ constexpr bool i256_sub_impl(const int128::uint128_t& a, const int128::uint128_t } } +constexpr bool i256_sub_impl(const u256& a, const u256& b, u256& result) noexcept +{ + if (a >= b) + { + auto borrow {impl::sub_borrow_u64(0, a[3], b[3], result[3])}; + borrow = impl::sub_borrow_u64(borrow, a[2], b[2], result[2]); + borrow = impl::sub_borrow_u64(borrow, a[1], b[1], result[1]); + impl::sub_borrow_u64(borrow, a[0], b[0], result[0]); + return false; + } + else + { + // a < b: negative result, |a - b| = b - a + auto borrow {impl::sub_borrow_u64(0, b[3], a[3], result[3])}; + borrow = impl::sub_borrow_u64(borrow, b[2], a[2], result[2]); + borrow = impl::sub_borrow_u64(borrow, b[1], a[1], result[1]); + impl::sub_borrow_u64(borrow, b[0], a[0], result[0]); + return true; + } +} + } // namespace impl constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept @@ -117,27 +138,45 @@ constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, return impl::i256_sub_impl(a, b, result); } -constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept +#if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_ADD_CARRY) + +constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept { - if (a >= b) + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) { - auto borrow {impl::sub_borrow_u64(0, a[0], b[0], result[0])}; - borrow = impl::sub_borrow_u64(borrow, a[1], b[1], result[1]); - borrow = impl::sub_borrow_u64(borrow, a[2], b[2], result[2]); - impl::sub_borrow_u64(borrow, a[3], b[3], result[3]); - return false; + return impl::i256_sub_impl(lhs, rhs, result); } else { - // a < b: negative result, |a - b| = b - a - auto borrow {impl::sub_borrow_u64(0, b[0], a[0], result[0])}; - borrow = impl::sub_borrow_u64(borrow, b[1], a[1], result[1]); - borrow = impl::sub_borrow_u64(borrow, b[2], a[2], result[2]); - impl::sub_borrow_u64(borrow, b[3], a[3], result[3]); - return true; + if (a >= b) + { + unsigned long long result[4] {}; + unsigned char borrow {}; + + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[3], b[3], &result[3]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[2], b[2], &result[2]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[1], b[1], &result[1]); + BOOST_DECIMAL_SUB_BORROW(borrow, a[0], b[0], &result[0]); + + res = u256{result[3], result[2], result[1], result[0]}; + } + else + { + unsigned long long result[4] {}; + unsigned char borrow {}; + + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[3], a[3], &result[3]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[2], a[2], &result[2]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[1], a[1], &result[1]); + BOOST_DECIMAL_SUB_BORROW(borrow, b[0], a[0], &result[0]); + + res = u256{result[3], result[2], result[1], result[0]}; + } } } +#endif + } // namespace detail } // namespace decimal } // namespace boost From b38025a8331d4ccd86be81e01872d794fe765b4d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:28:34 -0500 Subject: [PATCH 07/47] Add second intrin powered sub --- include/boost/decimal/detail/i256.hpp | 52 ++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index d0e36aff9..a8a7e917a 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -144,7 +144,7 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept { if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) { - return impl::i256_sub_impl(lhs, rhs, result); + return impl::i256_sub_impl(a, b, result); } else { @@ -159,6 +159,7 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept BOOST_DECIMAL_SUB_BORROW(borrow, a[0], b[0], &result[0]); res = u256{result[3], result[2], result[1], result[0]}; + return false; } else { @@ -171,6 +172,55 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept BOOST_DECIMAL_SUB_BORROW(borrow, b[0], a[0], &result[0]); res = u256{result[3], result[2], result[1], result[0]}; + return true; + } + } +} + +#elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(__GNUC__) && !defined(BOOST_DECIMAL_ADD_CARRY) + +namespace impl { + +inline bool sub_borrow_u64_intrin(const bool borrow_in, const std::uint64_t a, const std::uint64_t b, std::uint64_t& diff) noexcept { + unsigned long long res; + auto c = __builtin_usubll_overflow(a, b, &res); + c |= __builtin_usubll_overflow(res, static_cast(borrow_in), &res); + diff = res; + + return c; +} + +} // namespace impl + +constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept +{ + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::i256_sub_impl(a, b, result); + } + else + { + if (a >= b) + { + bool borrow {}; + + borrow = impl::sub_borrow_u64_intrin(borrow, a[3], b[3], result[3]); + borrow = impl::sub_borrow_u64_intrin(borrow, a[2], b[2], result[2]); + borrow = impl::sub_borrow_u64_intrin(borrow, a[1], b[1], result[1]); + impl::sub_borrow_u64_intrin(borrow, a[0], b[0], result[0]); + + return false; + } + else + { + bool borrow {}; + + borrow = impl::sub_borrow_u64_intrin(borrow, b[3], a[3], result[3]); + borrow = impl::sub_borrow_u64_intrin(borrow, b[2], a[2], result[2]); + borrow = impl::sub_borrow_u64_intrin(borrow, b[1], a[1], result[1]); + impl::sub_borrow_u64_intrin(borrow, b[0], a[0], result[0]); + + return true; } } } From 0d4fd38ba21b7e694bab149159c8895a16d7cec2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:38:23 -0500 Subject: [PATCH 08/47] Add new decimal128_t addition impl with new paths --- include/boost/decimal/detail/add_impl.hpp | 174 ++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/include/boost/decimal/detail/add_impl.hpp b/include/boost/decimal/detail/add_impl.hpp index c6dc7d731..cba7e5485 100644 --- a/include/boost/decimal/detail/add_impl.hpp +++ b/include/boost/decimal/detail/add_impl.hpp @@ -189,6 +189,180 @@ constexpr auto add_impl(const T& lhs, const T& rhs) noexcept -> ReturnType return ReturnType{return_sig, lhs_exp, new_sig < 0}; } +template +constexpr auto d128_add_impl_new(const T& lhs, const T& rhs) noexcept -> ReturnType +{ + using promoted_sig_type = u256; + + auto big_lhs {lhs.full_significand()}; + auto big_rhs {rhs.full_significand()}; + auto lhs_exp {lhs.biased_exponent()}; + auto rhs_exp {rhs.biased_exponent()}; + promoted_sig_type promoted_lhs {big_lhs}; + promoted_sig_type promoted_rhs {big_rhs}; + + // Align to larger exponent + if (lhs_exp != rhs_exp) + { + constexpr auto max_shift {detail::make_positive_unsigned(std::numeric_limits::digits10 - detail::precision_v - 1)}; + const auto shift {detail::make_positive_unsigned(lhs_exp - rhs_exp)}; + + if (shift > max_shift) + { + auto round {_boost_decimal_global_rounding_mode}; + + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + round = fegetround(); + } + + #endif + + if (BOOST_DECIMAL_LIKELY(round != rounding_mode::fe_dec_downward && round != rounding_mode::fe_dec_upward)) + { + return big_lhs != 0U && (lhs_exp > rhs_exp) ? + ReturnType{lhs.full_significand(), lhs.biased_exponent(), lhs.isneg()} : + ReturnType{rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()}; + } + else if (round == rounding_mode::fe_dec_downward) + { + // If we are subtracting even disparate numbers we need to round down + // E.g. "5e+95"_DF - "4e-100"_DF == "4.999999e+95"_DF + const auto use_lhs {big_lhs != 0U && (lhs_exp > rhs_exp)}; + + // Need to check for the case where we have 1e+95 - anything = 9.99999... without losing a nine + if (use_lhs) + { + if (big_rhs != 0U && (lhs.isneg() != rhs.isneg())) + { + if (is_power_of_10(big_lhs)) + { + --big_lhs; + big_lhs *= 10U; + big_lhs += 9U; + --lhs_exp; + } + else + { + --big_lhs; + } + } + + return ReturnType{big_lhs, lhs_exp, lhs.isneg()}; + } + else + { + if (big_lhs != 0U && (lhs.isneg() != rhs.isneg())) + { + if (is_power_of_10(big_rhs)) + { + --big_rhs; + big_rhs *= 10U; + big_rhs += 9U; + --rhs_exp; + } + else + { + --big_rhs; + } + } + + return ReturnType{big_rhs, rhs_exp, rhs.isneg()}; + } + } + else + { + // rounding mode == fe_dec_upward + // Unconditionally round up. Could be 5e+95 + 4e-100 -> 5.000001e+95 + const bool use_lhs {big_lhs != 0U && (lhs_exp > rhs_exp)}; + + if (use_lhs) + { + if (big_rhs != 0U) + { + if (lhs.isneg() != rhs.isneg()) + { + if (is_power_of_10(big_lhs)) + { + --big_lhs; + big_lhs *= 10U; + big_lhs += 9U; + --lhs_exp; + } + else + { + --big_lhs; + } + } + else + { + ++big_lhs; + } + } + + return ReturnType{big_lhs, lhs_exp, lhs.isneg()} ; + } + else + { + if (big_lhs != 0U) + { + if (rhs.isneg() != lhs.isneg()) + { + --big_rhs; + big_rhs *= 10U; + big_rhs += 9U; + --rhs_exp; + } + else + { + ++big_rhs; + } + } + + return ReturnType{big_rhs, rhs_exp, rhs.isneg()}; + } + } + } + + if (lhs_exp < rhs_exp) + { + promoted_rhs *= detail::pow10(shift); + lhs_exp = rhs_exp - static_cast(shift); + } + else + { + promoted_lhs *= detail::pow10(shift); + lhs_exp -= static_cast(shift); + } + } + + // Perform signed addition with overflow protection + u256 return_sig {}; + bool return_sign {}; + const auto lhs_sign {lhs.isneg()}; + const auto rhs_sign {rhs.isneg()}; + + if (lhs_sign && !rhs_sign) + { + // -lhs + rhs = rhs - lhs + return_sign = i256_sub(promoted_rhs, promoted_lhs, return_sig); + } + else if (!lhs_sign && rhs_sign) + { + // lhs - rhs + return_sign = i256_sub(promoted_lhs, promoted_rhs, return_sig); + } + else + { + // lhs + rhs + return_sig = promoted_lhs + promoted_rhs; + } + + return ReturnType{return_sig, lhs_exp, return_sign}; +} + template constexpr auto d128_add_impl(T lhs_sig, U lhs_exp, bool lhs_sign, T rhs_sig, U rhs_exp, bool rhs_sign, From d7a5bd1b62414092fc1106bc33064494e8c06c8f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:43:27 -0500 Subject: [PATCH 09/47] Add compound mul operator --- include/boost/decimal/detail/u256.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/boost/decimal/detail/u256.hpp b/include/boost/decimal/detail/u256.hpp index f65ac96bb..e9e720bea 100644 --- a/include/boost/decimal/detail/u256.hpp +++ b/include/boost/decimal/detail/u256.hpp @@ -56,6 +56,8 @@ u256 constexpr u256& operator>>=(int amount) noexcept; constexpr u256& operator|=(const u256& rhs) noexcept; + constexpr u256& operator*=(const u256& rhs) noexcept; + constexpr u256& operator/=(const u256& rhs) noexcept; constexpr u256& operator/=(const int128::uint128_t& rhs) noexcept; constexpr u256& operator/=(std::uint64_t rhs) noexcept; @@ -910,6 +912,12 @@ constexpr u256 umul256(const int128::uint128_t& a, const int128::uint128_t& b) n return result; } +constexpr u256& u256::operator*=(const u256& rhs) noexcept +{ + *this = *this * rhs; + return *this; +} + //===================================== // Division Operators //===================================== From cf2da34698b3abf235369278a6bdc2f8c1343490 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:48:20 -0500 Subject: [PATCH 10/47] Add operator u256 > u128 --- include/boost/decimal/detail/u256.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/boost/decimal/detail/u256.hpp b/include/boost/decimal/detail/u256.hpp index e9e720bea..9e05361a1 100644 --- a/include/boost/decimal/detail/u256.hpp +++ b/include/boost/decimal/detail/u256.hpp @@ -367,6 +367,11 @@ constexpr bool operator>(const u256& lhs, const u256& rhs) noexcept return rhs < lhs; } +constexpr bool operator>(const u256& lhs, const int128::uint128_t& rhs) noexcept +{ + return lhs[3] > 0U || lhs[2] > 0U || int128::uint128_t{lhs[1], lhs[0]} > rhs; +} + //===================================== // Greater Equal Operator //===================================== From 238ac78c8f04057a3a6415efb94836588c5c8ef2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:57:58 -0500 Subject: [PATCH 11/47] Add pretty printer for u256 --- extra/decimal_printer_lldb.py | 51 +++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/extra/decimal_printer_lldb.py b/extra/decimal_printer_lldb.py index 95861ea6b..0748a308c 100644 --- a/extra/decimal_printer_lldb.py +++ b/extra/decimal_printer_lldb.py @@ -109,6 +109,26 @@ def decimal_fast128_summary(valobj, internal_dict): except Exception as e: return f"" +def u256_summary(valobj, internal_dict): + """ + Custom summary for u256 detail type + Displays in decimal notation + """ + + try: + val = valobj.GetNonSyntheticValue() + + bytes = val.GetChildMemberWithName("bytes") + b0 = bytes.GetChildAtIndex(0).GetValueAsUnsigned() + b1 = bytes.GetChildAtIndex(1).GetValueAsUnsigned() + b2 = bytes.GetChildAtIndex(2).GetValueAsUnsigned() + b3 = bytes.GetChildAtIndex(3).GetValueAsUnsigned() + + value = (b3 << 192) | (b2 << 128) | (b1 << 64) | b0 + return f"{value:,}" + except Exception as e: + return f"" + def __lldb_init_module(debugger, internal_dict): decimal32_pattern = r"^(const )?(boost::decimal::decimal32_t|(\w+::)*decimal32_t)( &| \*)?$" decimal64_pattern = r"^(const )?(boost::decimal::decimal64_t|(\w+::)*decimal64_t)( &| \*)?$" @@ -118,6 +138,8 @@ def __lldb_init_module(debugger, internal_dict): decimal_fast64_pattern = r"^(const )?(boost::decimal::decimal_fast64_t|(\w+::)*decimal_fast64_t)( &| \*)?$" decimal_fast128_pattern = r"^(const )?(boost::decimal::decimal_fast128_t|(\w+::)*decimal_fast128_t)( &| \*)?$" + u256_pattern = r"^(const )?(boost::decimal::detail::u256|(\w+::)*u256)( &| \*)?$" + debugger.HandleCommand( f'type summary add -x "{decimal32_pattern}" -e -F decimal_printer_lldb.decimal32_summary' ) @@ -172,6 +194,13 @@ def __lldb_init_module(debugger, internal_dict): print("decimal_fast128_t printer loaded successfully") + debugger.HandleCommand( + f'type summary add -x "{u256_pattern}" -e -F decimal_printer_lldb.u256_summary' + ) + debugger.HandleCommand( + f'type synthetic add -x "{u256_pattern}" -l decimal_printer_lldb.u256SyntheticProvider' + ) + class DecimalSyntheticProvider: def __init__(self, valobj, internal_dict): self.valobj = valobj @@ -228,3 +257,25 @@ def update(self): def has_children(self): return True +class u256SyntheticProvider: + def __init__(self, valobj, internal_dict): + self.valobj = valobj + + def num_children(self): + return 1 + + def get_child_index(self, name): + if name == "bytes": + return 0 + return -1 + + def get_child_at_index(self, index): + if index == 0: + return self.valobj.GetChildMemberWithName("bytes") + return None + + def update(self): + pass + + def has_children(self): + return True From 5a7b55dbe03186340f97161caa815070604e1033 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 13:04:03 -0500 Subject: [PATCH 12/47] Add proper constructor for uint128_t --- include/boost/decimal/detail/u256.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u256.hpp b/include/boost/decimal/detail/u256.hpp index 9e05361a1..dc4de1a77 100644 --- a/include/boost/decimal/detail/u256.hpp +++ b/include/boost/decimal/detail/u256.hpp @@ -33,7 +33,8 @@ u256 constexpr u256& operator=(u256&& other) noexcept = default; constexpr u256(std::uint64_t byte3, std::uint64_t byte2, std::uint64_t byte1, std::uint64_t byte0) noexcept; - constexpr u256(std::uint64_t x) { bytes[0] = x; } + constexpr u256(const int128::uint128_t x) noexcept { bytes[0] = x.low; bytes[1] = x.high; } + constexpr u256(const std::uint64_t x) noexcept { bytes[0] = x; } explicit constexpr operator std::uint64_t() const noexcept { return bytes[0]; } From 1b7801dd0323647d60dde602a8a20d8cdc48559c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 13:07:50 -0500 Subject: [PATCH 13/47] Add handling of double negative path --- include/boost/decimal/detail/add_impl.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/add_impl.hpp b/include/boost/decimal/detail/add_impl.hpp index cba7e5485..eda84a8ac 100644 --- a/include/boost/decimal/detail/add_impl.hpp +++ b/include/boost/decimal/detail/add_impl.hpp @@ -356,8 +356,9 @@ constexpr auto d128_add_impl_new(const T& lhs, const T& rhs) noexcept -> ReturnT } else { - // lhs + rhs + // lhs + rhs or -lhs + -rhs return_sig = promoted_lhs + promoted_rhs; + return_sign = lhs_sign && rhs_sign; } return ReturnType{return_sig, lhs_exp, return_sign}; From cfe2a44fcf0b648ddbcb0d99ef3e369f730a17a5 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 13:10:55 -0500 Subject: [PATCH 14/47] Add potential fast path for non-normalized addition and subtraction --- include/boost/decimal/detail/add_impl.hpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/add_impl.hpp b/include/boost/decimal/detail/add_impl.hpp index eda84a8ac..b81da40e7 100644 --- a/include/boost/decimal/detail/add_impl.hpp +++ b/include/boost/decimal/detail/add_impl.hpp @@ -337,8 +337,7 @@ constexpr auto d128_add_impl_new(const T& lhs, const T& rhs) noexcept -> ReturnT lhs_exp -= static_cast(shift); } } - - // Perform signed addition with overflow protection + u256 return_sig {}; bool return_sign {}; const auto lhs_sign {lhs.isneg()}; @@ -361,6 +360,16 @@ constexpr auto d128_add_impl_new(const T& lhs, const T& rhs) noexcept -> ReturnT return_sign = lhs_sign && rhs_sign; } + BOOST_DECIMAL_IF_CONSTEXPR (detail::decimal_val_v == 128) + { + // In the regular 128-bit case there's a chance the high words are empty, + // and we can just convert to 128-bit arithmetic now + if (return_sig[2] == 0U && return_sig[3] == 0U) + { + return ReturnType{static_cast(return_sig), lhs_exp, return_sign}; + } + } + return ReturnType{return_sig, lhs_exp, return_sign}; } From 068849b75a55d6ddcb1fa751c4e93b8e9481283b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 14:01:05 -0500 Subject: [PATCH 15/47] Fix 256 bit powers of 10 --- include/boost/decimal/detail/power_tables.hpp | 160 +++++++++--------- 1 file changed, 81 insertions(+), 79 deletions(-) diff --git a/include/boost/decimal/detail/power_tables.hpp b/include/boost/decimal/detail/power_tables.hpp index 327d5cd3d..36c62f362 100644 --- a/include/boost/decimal/detail/power_tables.hpp +++ b/include/boost/decimal/detail/power_tables.hpp @@ -124,84 +124,86 @@ BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE detail::builtin_uint128_t builtin_128_po #endif -BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE u256 u256_pow_10[78] = { - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[0]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[1]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[2]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[3]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[4]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[5]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[6]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[7]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[8]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[9]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[10]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[11]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[12]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[13]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[14]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[15]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[16]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[17]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[18]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[19]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[20]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[21]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[22]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[23]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[24]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[25]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[26]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[27]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[28]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[29]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[30]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[31]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[32]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[33]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[34]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[35]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[36]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[37]}, - u256{BOOST_DECIMAL_DETAIL_INT128_pow10[38]}, - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[1]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[2]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[3]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[4]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[5]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[6]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[7]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[8]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[9]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[10]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[11]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[12]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[13]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[14]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[15]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[16]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[17]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[18]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[19]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[20]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[21]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[22]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[23]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[24]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[25]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[26]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[27]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[28]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[29]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[30]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[31]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[32]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[33]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[34]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[35]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[36]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[37]), - umul256(BOOST_DECIMAL_DETAIL_INT128_pow10[38], BOOST_DECIMAL_DETAIL_INT128_pow10[38]) +BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE u256 u256_pow_10[79] = { + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(1)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(10)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(100)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(1000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(10000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(100000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(1000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(10000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(100000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(1000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(10000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(100000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(1000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(10000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(100000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(1000000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(10000000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(100000000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(1000000000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(0), UINT64_C(10000000000000000000)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(5), UINT64_C(7766279631452241920)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(54), UINT64_C(3875820019684212736)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(542), UINT64_C(1864712049423024128)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(5421), UINT64_C(200376420520689664)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(54210), UINT64_C(2003764205206896640)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(542101), UINT64_C(1590897978359414784)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(5421010), UINT64_C(15908979783594147840)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(54210108), UINT64_C(11515845246265065472)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(542101086), UINT64_C(4477988020393345024)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(5421010862), UINT64_C(7886392056514347008)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(54210108624), UINT64_C(5076944270305263616)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(542101086242), UINT64_C(13875954555633532928)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(5421010862427), UINT64_C(9632337040368467968)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(54210108624275), UINT64_C(4089650035136921600)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(542101086242752), UINT64_C(4003012203950112768)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(5421010862427522), UINT64_C(3136633892082024448)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(54210108624275221), UINT64_C(12919594847110692864)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(542101086242752217), UINT64_C(68739955140067328)}, + u256{UINT64_C(0), UINT64_C(0), UINT64_C(5421010862427522170), UINT64_C(687399551400673280)}, + u256{UINT64_C(0), UINT64_C(2), UINT64_C(17316620476856118468), UINT64_C(6873995514006732800)}, + u256{UINT64_C(0), UINT64_C(29), UINT64_C(7145508105175220139), UINT64_C(13399722918938673152)}, + u256{UINT64_C(0), UINT64_C(293), UINT64_C(16114848830623546549), UINT64_C(4870020673419870208)}, + u256{UINT64_C(0), UINT64_C(2938), UINT64_C(13574535716559052564), UINT64_C(11806718586779598848)}, + u256{UINT64_C(0), UINT64_C(29387), UINT64_C(6618148649623664334), UINT64_C(7386721425538678784)}, + u256{UINT64_C(0), UINT64_C(293873), UINT64_C(10841254275107988496), UINT64_C(80237960548581376)}, + u256{UINT64_C(0), UINT64_C(2938735), UINT64_C(16178822382532126880), UINT64_C(802379605485813760)}, + u256{UINT64_C(0), UINT64_C(29387358), UINT64_C(14214271235644855872), UINT64_C(8023796054858137600)}, + u256{UINT64_C(0), UINT64_C(293873587), UINT64_C(13015503840481697412), UINT64_C(6450984253743169536)}, + u256{UINT64_C(0), UINT64_C(2938735877), UINT64_C(1027829888850112811), UINT64_C(9169610316303040512)}, + u256{UINT64_C(0), UINT64_C(29387358770), UINT64_C(10278298888501128114), UINT64_C(17909126868192198656)}, + u256{UINT64_C(0), UINT64_C(293873587705), UINT64_C(10549268516463523069), UINT64_C(13070572018536022016)}, + u256{UINT64_C(0), UINT64_C(2938735877055), UINT64_C(13258964796087472617), UINT64_C(1578511669393358848)}, + u256{UINT64_C(0), UINT64_C(29387358770557), UINT64_C(3462439444907864858), UINT64_C(15785116693933588480)}, + u256{UINT64_C(0), UINT64_C(293873587705571), UINT64_C(16177650375369096972), UINT64_C(10277214349659471872)}, + u256{UINT64_C(0), UINT64_C(2938735877055718), UINT64_C(14202551164014556797), UINT64_C(10538423128046960640)}, + u256{UINT64_C(0), UINT64_C(29387358770557187), UINT64_C(12898303124178706663), UINT64_C(13150510911921848320)}, + u256{UINT64_C(0), UINT64_C(293873587705571876), UINT64_C(18302566799529756941), UINT64_C(2377900603251621888)}, + u256{UINT64_C(0), UINT64_C(2938735877055718769), UINT64_C(17004971331911604867), UINT64_C(5332261958806667264)}, + u256{UINT64_C(1), UINT64_C(10940614696847636083), UINT64_C(4029016655730084128), UINT64_C(16429131440647569408)}, + u256{UINT64_C(15), UINT64_C(17172426599928602752), UINT64_C(3396678409881738056), UINT64_C(16717361816799281152)}, + u256{UINT64_C(159), UINT64_C(5703569335900062977), UINT64_C(15520040025107828953), UINT64_C(1152921504606846976)}, + u256{UINT64_C(1593), UINT64_C(1695461137871974930), UINT64_C(7626447661401876602), UINT64_C(11529215046068469760)}, + u256{UINT64_C(15930), UINT64_C(16954611378719749304), UINT64_C(2477500319180559562), UINT64_C(4611686018427387904)}, + u256{UINT64_C(159309), UINT64_C(3525417123811528497), UINT64_C(6328259118096044006), UINT64_C(9223372036854775808)}, + u256{UINT64_C(1593091), UINT64_C(16807427164405733357), UINT64_C(7942358959831785217), UINT64_C(0)}, + u256{UINT64_C(15930919), UINT64_C(2053574980671369030), UINT64_C(5636613303479645706), UINT64_C(0)}, + u256{UINT64_C(159309191), UINT64_C(2089005733004138687), UINT64_C(1025900813667802212), UINT64_C(0)}, + u256{UINT64_C(1593091911), UINT64_C(2443313256331835254), UINT64_C(10259008136678022120), UINT64_C(0)}, + u256{UINT64_C(15930919111), UINT64_C(5986388489608800929), UINT64_C(10356360998232463120), UINT64_C(0)}, + u256{UINT64_C(159309191113), UINT64_C(4523652674959354447), UINT64_C(11329889613776873120), UINT64_C(0)}, + u256{UINT64_C(1593091911132), UINT64_C(8343038602174441244), UINT64_C(2618431695511421504), UINT64_C(0)}, + u256{UINT64_C(15930919111324), UINT64_C(9643409726906205977), UINT64_C(7737572881404663424), UINT64_C(0)}, + u256{UINT64_C(159309191113245), UINT64_C(4200376900514301694), UINT64_C(3588752519208427776), UINT64_C(0)}, + u256{UINT64_C(1593091911132452), UINT64_C(5110280857723913709), UINT64_C(17440781118374726144), UINT64_C(0)}, + u256{UINT64_C(15930919111324522), UINT64_C(14209320429820033867), UINT64_C(8387114520361296896), UINT64_C(0)}, + u256{UINT64_C(159309191113245227), UINT64_C(12965995782233477362), UINT64_C(10084168908774762496), UINT64_C(0)}, + u256{UINT64_C(1593091911132452277), UINT64_C(532749306367912313), UINT64_C(8607968719199866880), UINT64_C(0)}, + u256{UINT64_C(15930919111324522770), UINT64_C(5327493063679123134), UINT64_C(12292710897160462336), UINT64_C(0)}, + u256{UINT64_C(11735238523568814774), UINT64_C(16381442489372128114), UINT64_C(12246644529347313664), UINT64_C(0)}, }; } // namespace impl @@ -235,7 +237,7 @@ constexpr auto pow10(detail::builtin_uint128_t n) noexcept -> detail::builtin_ui constexpr auto pow10(const u256& n) noexcept -> u256 { - return impl::u256_pow_10[n[3]]; + return impl::u256_pow_10[static_cast(n)]; } #if defined(__GNUC__) && __GNUC__ >= 7 From e9bac5950619c686cb56e234bb2bdeb9a0b490f3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 14:01:16 -0500 Subject: [PATCH 16/47] Add additional testing of pre generated powers of ten --- test/test_big_uints.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_big_uints.cpp b/test/test_big_uints.cpp index 487f9f9a7..b8cefe275 100644 --- a/test/test_big_uints.cpp +++ b/test/test_big_uints.cpp @@ -479,6 +479,7 @@ void test_digit_counting() int current_digits {1}; for (int i {}; i <= max_power; ++i) { + BOOST_TEST_EQ(current_power, boost::decimal::detail::pow10(static_cast(i))); BOOST_TEST_EQ(num_digits(current_power), current_digits); current_power = current_power * UINT64_C(10); ++current_digits; From d4190857612e54dcc7118cff1df2d670f0b435d8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:52:04 -0500 Subject: [PATCH 17/47] Add additional test case --- test/github_issue_1260.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index 3a8e01663..f4e3d5f8d 100644 --- a/test/github_issue_1260.cpp +++ b/test/github_issue_1260.cpp @@ -21,10 +21,26 @@ void test() BOOST_TEST_EQ(sub_val, res); } +template +void test2() +{ + const T lhs {"1E34"}; + const T rhs {"-0.501"}; + const T res {"9999999999999999999999999999999999"}; + + const T add_val {lhs + rhs}; + BOOST_TEST_EQ(add_val, res); + + const T sub_val {lhs - boost::decimal::abs(rhs)}; + BOOST_TEST_EQ(sub_val, res); +} + int main() { test(); test(); + test2(); + test2(); return boost::report_errors(); } From cf8f156d4a09637d567640d7badf8616760effbd Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:58:52 -0500 Subject: [PATCH 18/47] Use new addition method --- include/boost/decimal/decimal128_t.hpp | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index c99987252..532b174f7 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1708,17 +1708,10 @@ constexpr auto operator+(const decimal128_t& lhs, const decimal128_t& rhs) noexc } #endif - auto lhs_sig {lhs.full_significand()}; - auto lhs_exp {lhs.biased_exponent()}; - detail::expand_significand(lhs_sig, lhs_exp); - - auto rhs_sig {rhs.full_significand()}; - auto rhs_exp {rhs.biased_exponent()}; - detail::expand_significand(rhs_sig, rhs_exp); + const auto lhs_components {lhs.to_components()}; + const auto rhs_components {rhs.to_components()}; - return detail::d128_add_impl(lhs_sig, lhs_exp, lhs.isneg(), - rhs_sig, rhs_exp, rhs.isneg(), - abs(lhs) > abs(rhs)); + return detail::d128_add_impl_new(lhs_components, rhs_components); } template From 544c25ae332baf78dc29f67cd604c82658c27e77 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:59:02 -0500 Subject: [PATCH 19/47] Fix pow10 generation --- include/boost/decimal/detail/add_impl.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/add_impl.hpp b/include/boost/decimal/detail/add_impl.hpp index b81da40e7..b3a51d2ae 100644 --- a/include/boost/decimal/detail/add_impl.hpp +++ b/include/boost/decimal/detail/add_impl.hpp @@ -333,7 +333,8 @@ constexpr auto d128_add_impl_new(const T& lhs, const T& rhs) noexcept -> ReturnT } else { - promoted_lhs *= detail::pow10(shift); + const auto shift_pow10 {detail::pow10(static_cast(shift))}; + promoted_lhs *= shift_pow10; lhs_exp -= static_cast(shift); } } From 8f356c767eb542d87b05260df1af4e829947fb79 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:59:09 -0500 Subject: [PATCH 20/47] More tests --- test/github_issue_1260.cpp | 45 +++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index f4e3d5f8d..f2a811908 100644 --- a/test/github_issue_1260.cpp +++ b/test/github_issue_1260.cpp @@ -17,8 +17,8 @@ void test() const T add_val {lhs + rhs}; BOOST_TEST_EQ(add_val, res); - const T sub_val {lhs - boost::decimal::abs(rhs)}; - BOOST_TEST_EQ(sub_val, res); + //const T sub_val {lhs - boost::decimal::abs(rhs)}; + //BOOST_TEST_EQ(sub_val, res); } template @@ -31,8 +31,36 @@ void test2() const T add_val {lhs + rhs}; BOOST_TEST_EQ(add_val, res); - const T sub_val {lhs - boost::decimal::abs(rhs)}; - BOOST_TEST_EQ(sub_val, res); + //const T sub_val {lhs - boost::decimal::abs(rhs)}; + //BOOST_TEST_EQ(sub_val, res); +} + +template +void test3() +{ + const T lhs {"1E34"}; + const T rhs {"-0.5001"}; + const T res {"9999999999999999999999999999999999"}; + + const T add_val {lhs + rhs}; + BOOST_TEST_EQ(add_val, res); + + //const T sub_val {lhs - boost::decimal::abs(rhs)}; + //BOOST_TEST_EQ(sub_val, res); +} + +template +void test4() +{ + const T lhs {"1E34"}; + const T rhs {"-0.50001"}; + const T res {"9999999999999999999999999999999999"}; + + const T add_val {lhs + rhs}; + BOOST_TEST_EQ(add_val, res); + + //const T sub_val {lhs - boost::decimal::abs(rhs)}; + //BOOST_TEST_EQ(sub_val, res); } int main() @@ -41,6 +69,13 @@ int main() test(); test2(); - test2(); + //test2(); + + test3(); + //test3(); + + test4(); + //test4(); + return boost::report_errors(); } From 76a03def91472dc2a7199a0a902d1393af8ea87c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 17:04:33 -0500 Subject: [PATCH 21/47] Fix missing sub borrow instrinsic --- include/boost/decimal/detail/config.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index 3855e7a62..aad650b84 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -97,8 +97,10 @@ # endif # if defined(__ADX__) && defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) # define BOOST_DECIMAL_ADD_CARRY _addcarryx_u64 +# define BOOST_DECIMAL_SUB_BORROW _subborrow_u64 # elif defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) # define BOOST_DECIMAL_ADD_CARRY _addcarry_u64 +# define BOOST_DECIMAL_SUB_BORROW _subborrow_u64 # endif #elif defined(__x86_64__) # ifndef BOOST_DECIMAL_BUILD_MODULE @@ -107,8 +109,10 @@ # define BOOST_DECIMAL_HAS_X64_INTRINSICS # ifdef __ADX__ # define BOOST_DECIMAL_ADD_CARRY _addcarryx_u64 +# define BOOST_DECIMAL_SUB_BORROW _subborrow_u64 # else # define BOOST_DECIMAL_ADD_CARRY _addcarry_u64 +# define BOOST_DECIMAL_SUB_BORROW _subborrow_u64 # endif #elif defined(__ARM_NEON__) # ifndef BOOST_DECIMAL_BUILD_MODULE From b5f0865bf3093f9596dc946f756c606d490ac057 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 17:06:02 -0500 Subject: [PATCH 22/47] Fix missing variable --- include/boost/decimal/detail/i256.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index a8a7e917a..8620df64e 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -144,7 +144,7 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept { if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) { - return impl::i256_sub_impl(a, b, result); + return impl::i256_sub_impl(a, b, res); } else { From a20dbc2d1c921d3330831b2ed587246e211da7de Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 10:28:01 -0500 Subject: [PATCH 23/47] Explicitly construct u256 for u128 --- include/boost/decimal/detail/i256.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index 8620df64e..0ea129998 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -135,7 +135,7 @@ constexpr bool i256_sub_impl(const u256& a, const u256& b, u256& result) noexcep constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept { - return impl::i256_sub_impl(a, b, result); + return impl::i256_sub_impl(u256{a}, u256{b}, result); } #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_ADD_CARRY) @@ -181,7 +181,8 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept namespace impl { -inline bool sub_borrow_u64_intrin(const bool borrow_in, const std::uint64_t a, const std::uint64_t b, std::uint64_t& diff) noexcept { +inline bool sub_borrow_u64_intrin(const bool borrow_in, const std::uint64_t a, const std::uint64_t b, std::uint64_t& diff) noexcept +{ unsigned long long res; auto c = __builtin_usubll_overflow(a, b, &res); c |= __builtin_usubll_overflow(res, static_cast(borrow_in), &res); From 4f300235eb8bf412e71c7af8014a926b43cac4c8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 11:02:16 -0500 Subject: [PATCH 24/47] Expand the significands prior to addition --- include/boost/decimal/decimal128_t.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index 532b174f7..8ce64aca9 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1708,8 +1708,10 @@ constexpr auto operator+(const decimal128_t& lhs, const decimal128_t& rhs) noexc } #endif - const auto lhs_components {lhs.to_components()}; - const auto rhs_components {rhs.to_components()}; + auto lhs_components {lhs.to_components()}; + detail::expand_significand(lhs_components.sig, lhs_components.exp); + auto rhs_components {rhs.to_components()}; + detail::expand_significand(rhs_components.sig, rhs_components.exp); return detail::d128_add_impl_new(lhs_components, rhs_components); } From c2f56521ad7b173cedd781ccbf5565abfd668531 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 12:14:13 -0500 Subject: [PATCH 25/47] Add cheaper accessor function --- include/boost/decimal/detail/power_tables.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/boost/decimal/detail/power_tables.hpp b/include/boost/decimal/detail/power_tables.hpp index 36c62f362..8317144fc 100644 --- a/include/boost/decimal/detail/power_tables.hpp +++ b/include/boost/decimal/detail/power_tables.hpp @@ -240,6 +240,11 @@ constexpr auto pow10(const u256& n) noexcept -> u256 return impl::u256_pow_10[static_cast(n)]; } +constexpr auto pow10_256(const std::size_t n) noexcept -> u256 +{ + return impl::u256_pow_10[n]; +} + #if defined(__GNUC__) && __GNUC__ >= 7 # pragma GCC diagnostic pop #endif From 9ac3a4172cab2baa57c1d4617f8ada62b3b713fd Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 12:14:27 -0500 Subject: [PATCH 26/47] Explicitly test for zero --- include/boost/decimal/decimal128_t.hpp | 11 +++++++++++ include/boost/decimal/detail/add_impl.hpp | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index 8ce64aca9..07dcc5995 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1708,6 +1708,17 @@ constexpr auto operator+(const decimal128_t& lhs, const decimal128_t& rhs) noexc } #endif + constexpr decimal128_t zero {0, 0}; + + if (lhs == zero) + { + return rhs; + } + else if (rhs == zero) + { + return lhs; + } + auto lhs_components {lhs.to_components()}; detail::expand_significand(lhs_components.sig, lhs_components.exp); auto rhs_components {rhs.to_components()}; diff --git a/include/boost/decimal/detail/add_impl.hpp b/include/boost/decimal/detail/add_impl.hpp index b3a51d2ae..a2e834d57 100644 --- a/include/boost/decimal/detail/add_impl.hpp +++ b/include/boost/decimal/detail/add_impl.hpp @@ -326,14 +326,15 @@ constexpr auto d128_add_impl_new(const T& lhs, const T& rhs) noexcept -> ReturnT } } + const auto shift_pow10 {detail::pow10_256(shift)}; + if (lhs_exp < rhs_exp) { - promoted_rhs *= detail::pow10(shift); + promoted_rhs *= shift_pow10; lhs_exp = rhs_exp - static_cast(shift); } else { - const auto shift_pow10 {detail::pow10(static_cast(shift))}; promoted_lhs *= shift_pow10; lhs_exp -= static_cast(shift); } From d113ffe565fe6b251234373dde4f9f237e27c241 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 12:19:27 -0500 Subject: [PATCH 27/47] Test seemingly trivial value --- test/github_issue_1260.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index f2a811908..f7efec82f 100644 --- a/test/github_issue_1260.cpp +++ b/test/github_issue_1260.cpp @@ -63,6 +63,17 @@ void test4() //BOOST_TEST_EQ(sub_val, res); } +template +void test5() +{ + const T lhs {"5"}; + const T rhs {"-3"}; + const T res {"2"}; + + const T add_val {lhs + rhs}; + BOOST_TEST_EQ(add_val, res); +} + int main() { test(); @@ -77,5 +88,7 @@ int main() test4(); //test4(); + test5(); + return boost::report_errors(); } From 6b0cac832e706d43d1fbfb6a50f16877eb2b0e32 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 13:57:15 -0500 Subject: [PATCH 28/47] Fix subtraction word order --- include/boost/decimal/detail/i256.hpp | 36 +++++++++++++-------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index 0ea129998..1132bdb56 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -114,19 +114,19 @@ constexpr bool i256_sub_impl(const u256& a, const u256& b, u256& result) noexcep { if (a >= b) { - auto borrow {impl::sub_borrow_u64(0, a[3], b[3], result[3])}; - borrow = impl::sub_borrow_u64(borrow, a[2], b[2], result[2]); + auto borrow {impl::sub_borrow_u64(0, a[0], b[0], result[0])}; borrow = impl::sub_borrow_u64(borrow, a[1], b[1], result[1]); - impl::sub_borrow_u64(borrow, a[0], b[0], result[0]); + borrow = impl::sub_borrow_u64(borrow, a[2], b[2], result[2]); + impl::sub_borrow_u64(borrow, a[3], b[3], result[3]); return false; } else { // a < b: negative result, |a - b| = b - a - auto borrow {impl::sub_borrow_u64(0, b[3], a[3], result[3])}; - borrow = impl::sub_borrow_u64(borrow, b[2], a[2], result[2]); + auto borrow {impl::sub_borrow_u64(0, b[0], a[0], result[0])}; borrow = impl::sub_borrow_u64(borrow, b[1], a[1], result[1]); - impl::sub_borrow_u64(borrow, b[0], a[0], result[0]); + borrow = impl::sub_borrow_u64(borrow, b[2], a[2], result[2]); + impl::sub_borrow_u64(borrow, b[3], a[3], result[3]); return true; } } @@ -153,10 +153,10 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept unsigned long long result[4] {}; unsigned char borrow {}; - borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[3], b[3], &result[3]); - borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[2], b[2], &result[2]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[0], b[0], &result[0]); borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[1], b[1], &result[1]); - BOOST_DECIMAL_SUB_BORROW(borrow, a[0], b[0], &result[0]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[2], b[2], &result[2]); + BOOST_DECIMAL_SUB_BORROW(borrow, a[3], b[3], &result[3]); res = u256{result[3], result[2], result[1], result[0]}; return false; @@ -166,10 +166,10 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept unsigned long long result[4] {}; unsigned char borrow {}; - borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[3], a[3], &result[3]); - borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[2], a[2], &result[2]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[0], a[0], &result[0]); borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[1], a[1], &result[1]); - BOOST_DECIMAL_SUB_BORROW(borrow, b[0], a[0], &result[0]); + borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[2], a[2], &result[2]); + BOOST_DECIMAL_SUB_BORROW(borrow, b[3], a[3], &result[3]); res = u256{result[3], result[2], result[1], result[0]}; return true; @@ -205,10 +205,10 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept { bool borrow {}; - borrow = impl::sub_borrow_u64_intrin(borrow, a[3], b[3], result[3]); - borrow = impl::sub_borrow_u64_intrin(borrow, a[2], b[2], result[2]); + borrow = impl::sub_borrow_u64_intrin(borrow, a[0], b[0], result[0]); borrow = impl::sub_borrow_u64_intrin(borrow, a[1], b[1], result[1]); - impl::sub_borrow_u64_intrin(borrow, a[0], b[0], result[0]); + borrow = impl::sub_borrow_u64_intrin(borrow, a[2], b[2], result[2]); + impl::sub_borrow_u64_intrin(borrow, a[3], b[3], result[3]); return false; } @@ -216,10 +216,10 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept { bool borrow {}; - borrow = impl::sub_borrow_u64_intrin(borrow, b[3], a[3], result[3]); - borrow = impl::sub_borrow_u64_intrin(borrow, b[2], a[2], result[2]); + borrow = impl::sub_borrow_u64_intrin(borrow, b[0], a[0], result[0]); borrow = impl::sub_borrow_u64_intrin(borrow, b[1], a[1], result[1]); - impl::sub_borrow_u64_intrin(borrow, b[0], a[0], result[0]); + borrow = impl::sub_borrow_u64_intrin(borrow, b[2], a[2], result[2]); + impl::sub_borrow_u64_intrin(borrow, b[3], a[3], result[3]); return true; } From ffcaafde67b8d7aeb9982bc5848f93cd6cf505f8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 14:16:45 -0500 Subject: [PATCH 29/47] Improve use of intrinsics --- include/boost/decimal/detail/i256.hpp | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index 1132bdb56..575dfe121 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -203,29 +203,36 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept { if (a >= b) { - bool borrow {}; + unsigned long long borrow {}; - borrow = impl::sub_borrow_u64_intrin(borrow, a[0], b[0], result[0]); - borrow = impl::sub_borrow_u64_intrin(borrow, a[1], b[1], result[1]); - borrow = impl::sub_borrow_u64_intrin(borrow, a[2], b[2], result[2]); - impl::sub_borrow_u64_intrin(borrow, a[3], b[3], result[3]); + result[0] = __builtin_subcll(a[0], b[0], borrow, &borrow); + result[1] = __builtin_subcll(a[1], b[1], borrow, &borrow); + result[2] = __builtin_subcll(a[2], b[2], borrow, &borrow); + result[3] = __builtin_subcll(a[3], b[3], borrow, &borrow); return false; } else { - bool borrow {}; + unsigned long long borrow {}; - borrow = impl::sub_borrow_u64_intrin(borrow, b[0], a[0], result[0]); - borrow = impl::sub_borrow_u64_intrin(borrow, b[1], a[1], result[1]); - borrow = impl::sub_borrow_u64_intrin(borrow, b[2], a[2], result[2]); - impl::sub_borrow_u64_intrin(borrow, b[3], a[3], result[3]); + result[0] = __builtin_subcll(b[0], a[0], borrow, &borrow); + result[1] = __builtin_subcll(b[1], a[1], borrow, &borrow); + result[2] = __builtin_subcll(b[2], a[2], borrow, &borrow); + result[3] = __builtin_subcll(b[3], a[3], borrow, &borrow); return true; } } } +#else + +constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept +{ + return impl::i256_sub_impl(a, b, result); +} + #endif } // namespace detail From 57e593e672eb48f24c0555c739824fd507cc8518 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 14:17:14 -0500 Subject: [PATCH 30/47] Remove unneeded functions --- include/boost/decimal/detail/i256.hpp | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index 575dfe121..c8b9749be 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -133,11 +133,6 @@ constexpr bool i256_sub_impl(const u256& a, const u256& b, u256& result) noexcep } // namespace impl -constexpr bool i256_sub(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept -{ - return impl::i256_sub_impl(u256{a}, u256{b}, result); -} - #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_ADD_CARRY) constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept @@ -179,20 +174,6 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept #elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(__GNUC__) && !defined(BOOST_DECIMAL_ADD_CARRY) -namespace impl { - -inline bool sub_borrow_u64_intrin(const bool borrow_in, const std::uint64_t a, const std::uint64_t b, std::uint64_t& diff) noexcept -{ - unsigned long long res; - auto c = __builtin_usubll_overflow(a, b, &res); - c |= __builtin_usubll_overflow(res, static_cast(borrow_in), &res); - diff = res; - - return c; -} - -} // namespace impl - constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept { if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) From 6a8dbde419f3d803785f5c4fc830e94a802c447b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 14:51:16 -0500 Subject: [PATCH 31/47] Ignore impossible sign conversion warning --- include/boost/decimal/detail/fenv_rounding.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/boost/decimal/detail/fenv_rounding.hpp b/include/boost/decimal/detail/fenv_rounding.hpp index 61ac9aa48..4674033e0 100644 --- a/include/boost/decimal/detail/fenv_rounding.hpp +++ b/include/boost/decimal/detail/fenv_rounding.hpp @@ -189,6 +189,9 @@ constexpr auto fenv_round(T& val, bool is_neg = false, bool sticky = false) noex #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4127) +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-conversion" #endif template @@ -221,6 +224,7 @@ constexpr auto coefficient_rounding(T1& coeff, T2& exp, T3& biased_exp, const bo } // Do shifting + BOOST_DECIMAL_ASSERT(shift >= 0); const auto shift_pow_ten {detail::pow10(static_cast(shift))}; // In the synthetic integer cases it's inexpensive to see if we can demote the type @@ -267,6 +271,8 @@ constexpr auto coefficient_rounding(T1& coeff, T2& exp, T3& biased_exp, const bo #ifdef _MSC_VER # pragma warning(pop) +#elif defined(__GNUC__) +# pragma GCC diagnostic pop #endif } // namespace detail From f8a8e6f203ddadca75c1d3d33253c1d7ce7ae4f6 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 15:38:36 -0500 Subject: [PATCH 32/47] Test existence of builtin --- include/boost/decimal/detail/i256.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index c8b9749be..b0b095af1 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -172,7 +172,7 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept } } -#elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(__GNUC__) && !defined(BOOST_DECIMAL_ADD_CARRY) +#elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(__GNUC__) && !defined(BOOST_DECIMAL_ADD_CARRY) && BOOST_DECIMAL_HAS_BUILTIN(__builtin_subcll) constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept { From 464e3fbd4e5e4144b538a978763609819b28af25 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 12 Jan 2026 15:39:31 -0500 Subject: [PATCH 33/47] Remove unused function --- include/boost/decimal/detail/i256.hpp | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index b0b095af1..2d56b3e06 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -89,27 +89,6 @@ constexpr std::uint64_t sub_borrow_u64(const std::uint64_t borrow_in, const std: return borrow_out; } -constexpr bool i256_sub_impl(const int128::uint128_t& a, const int128::uint128_t& b, u256& result) noexcept -{ - if (a >= b) - { - std::uint64_t borrow {}; - result[0] = sub_borrow_u64(0, a.low, b.low, borrow); - result[1] = sub_borrow_u64(borrow, a.high, b.high, borrow); - - return false; - } - else - { - // a < b: negative result, |a - b| = b - a - std::uint64_t borrow {}; - result[0] = sub_borrow_u64(0, b.low, a.low, borrow); - result[1] = sub_borrow_u64(borrow, a.high, b.high, borrow); - - return true; - } -} - constexpr bool i256_sub_impl(const u256& a, const u256& b, u256& result) noexcept { if (a >= b) From fa8b2d64022e62666912aaf1900715e69799e0e8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 09:59:26 -0500 Subject: [PATCH 34/47] Add detection of i386 intrin --- include/boost/decimal/detail/config.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index aad650b84..2eb885cdb 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -119,6 +119,11 @@ # include # endif # define BOOST_DECIMAL_HAS_ARM_INTRINSICS +#elif defined(__i386__) +# ifndef BOOST_DECIMAL_BUILD_MODULE +# include +# endif +# define BOOST_DECIMAL_HAS_x86_INTRINSICS #else # define BOOST_DECIMAL_HAS_NO_INTRINSICS #endif From aae9a8c19d3e471091e14116ae9b670fd0017873 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 09:59:36 -0500 Subject: [PATCH 35/47] Fix macros being detected --- include/boost/decimal/detail/i256.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index 2d56b3e06..ab9aa7f3b 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -112,7 +112,7 @@ constexpr bool i256_sub_impl(const u256& a, const u256& b, u256& result) noexcep } // namespace impl -#if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_ADD_CARRY) +#if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_SUB_BORROW) constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept { @@ -151,7 +151,7 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& res) noexcept } } -#elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(__GNUC__) && !defined(BOOST_DECIMAL_ADD_CARRY) && BOOST_DECIMAL_HAS_BUILTIN(__builtin_subcll) +#elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(__GNUC__) && !defined(BOOST_DECIMAL_SUB_BORROW) && BOOST_DECIMAL_HAS_BUILTIN(__builtin_subcll) constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept { From ad01581ab7e9fb6e8cdacf67a1e46380f3c2d3e5 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 09:59:57 -0500 Subject: [PATCH 36/47] Implement subcll workaround for i386 --- include/boost/decimal/detail/i256.hpp | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/include/boost/decimal/detail/i256.hpp b/include/boost/decimal/detail/i256.hpp index ab9aa7f3b..d15e4cb6f 100644 --- a/include/boost/decimal/detail/i256.hpp +++ b/include/boost/decimal/detail/i256.hpp @@ -186,6 +186,58 @@ constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept } } +#elif !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_HAS_x86_INTRINSICS) + +namespace impl { + +// __builtin_subcll is missing from some platforms but __builtin_sub_overflow is present. +// Implement exactly as shown on the GCC page: https://gcc.gnu.org/onlinedocs/gcc/Integer-Overflow-Builtins.html + +inline std::uint64_t subcll(const std::uint64_t a, const std::uint64_t b, const std::uint64_t carry_in, std::uint64_t* carry_out) noexcept +{ + std::uint64_t s; + const auto c1 {__builtin_sub_overflow(a, b, &s)}; + const auto c2 {__builtin_sub_overflow(s, carry_in, &s)}; + *carry_out = static_cast(c1 | c2); + + return s; +} + +} // namespace impl + +constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept +{ + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::i256_sub_impl(a, b, result); + } + else + { + if (a >= b) + { + unsigned long long borrow {}; + + result[0] = impl::subcll(a[0], b[0], borrow, &borrow); + result[1] = impl::subcll(a[1], b[1], borrow, &borrow); + result[2] = impl::subcll(a[2], b[2], borrow, &borrow); + result[3] = impl::subcll(a[3], b[3], borrow, &borrow); + + return false; + } + else + { + unsigned long long borrow {}; + + result[0] = impl::subcll(b[0], a[0], borrow, &borrow); + result[1] = impl::subcll(b[1], a[1], borrow, &borrow); + result[2] = impl::subcll(b[2], a[2], borrow, &borrow); + result[3] = impl::subcll(b[3], a[3], borrow, &borrow); + + return true; + } + } +} + #else constexpr bool i256_sub(const u256& a, const u256& b, u256& result) noexcept From 9dc581a4af69e75308437bd0304e5abe1b6c2db3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 10:13:17 -0500 Subject: [PATCH 37/47] Add GDB printer for u256 internal type --- extra/decimal_printer_gdb.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/extra/decimal_printer_gdb.py b/extra/decimal_printer_gdb.py index e58feab54..69b7c739d 100644 --- a/extra/decimal_printer_gdb.py +++ b/extra/decimal_printer_gdb.py @@ -138,6 +138,27 @@ def children(self): yield ('sign_', self.val['sign_']) +class U256Printer: + """Pretty printer for u256 internal type""" + + def __init__(self, val): + self.val = val + + def to_string(self): + try: + bytes = self.val['bytes'] + byte0 = int(bytes[0]) & 0xFFFFFFFFFFFFFFFF + byte1 = int(bytes[1]) & 0xFFFFFFFFFFFFFFFF + byte2 = int(bytes[2]) & 0xFFFFFFFFFFFFFFFF + byte3 = int(bytes[3]) & 0xFFFFFFFFFFFFFFFF + + value = (byte0 << 192) | (byte1 << 128) | (byte2 << 64) | byte3 + return f"{value:,}" + except Exception as e: + return f"" + + + def build_pretty_printer(): """Build and return the pretty printer collection""" pp = gdb.printing.RegexpCollectionPrettyPrinter("boost_decimal") @@ -164,6 +185,11 @@ def build_pretty_printer(): r'^(const )?(boost::decimal::)?decimal_fast128_t( &| \*)?$', DecimalFast128Printer) + # Debug internal types + pp.add_printer('u256', + r'^(const )?(boost::decimal::detail::)?u256( &| \*)?$', + U256Printer) + return pp From df7d97769cea7a7be17a1092c8d220b40c14adf3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 10:51:28 -0500 Subject: [PATCH 38/47] Add testing failure from 32 bit platforms --- test/github_issue_1260.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index f7efec82f..41d5e0115 100644 --- a/test/github_issue_1260.cpp +++ b/test/github_issue_1260.cpp @@ -6,6 +6,8 @@ #include #include +#include +#include template void test() @@ -74,8 +76,22 @@ void test5() BOOST_TEST_EQ(add_val, res); } +template +void test6() +{ + // Only an issue on 32-bit platforms + const T lhs {"10000e+9"}; + const T rhs {"7000"}; + const T res {"10000000007000"}; + + const T add_val {lhs + rhs}; + BOOST_TEST_EQ(add_val, res); +} + int main() { + std::cerr << std::setprecision(std::numeric_limits::max_digits10); + test(); test(); @@ -89,6 +105,10 @@ int main() //test4(); test5(); + //test5(); + + test6(); + //test6(); return boost::report_errors(); } From 7237e34e9be54ec4837dc6e88fc510936c18eb93 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 11:15:33 -0500 Subject: [PATCH 39/47] Fix byte order --- extra/decimal_printer_gdb.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extra/decimal_printer_gdb.py b/extra/decimal_printer_gdb.py index 69b7c739d..751816945 100644 --- a/extra/decimal_printer_gdb.py +++ b/extra/decimal_printer_gdb.py @@ -147,10 +147,10 @@ def __init__(self, val): def to_string(self): try: bytes = self.val['bytes'] - byte0 = int(bytes[0]) & 0xFFFFFFFFFFFFFFFF - byte1 = int(bytes[1]) & 0xFFFFFFFFFFFFFFFF - byte2 = int(bytes[2]) & 0xFFFFFFFFFFFFFFFF - byte3 = int(bytes[3]) & 0xFFFFFFFFFFFFFFFF + byte0 = int(bytes[3]) & 0xFFFFFFFFFFFFFFFF + byte1 = int(bytes[2]) & 0xFFFFFFFFFFFFFFFF + byte2 = int(bytes[1]) & 0xFFFFFFFFFFFFFFFF + byte3 = int(bytes[0]) & 0xFFFFFFFFFFFFFFFF value = (byte0 << 192) | (byte1 << 128) | (byte2 << 64) | byte3 return f"{value:,}" From 627d376fc7d9eb93b05711c9d62bf96e6200225c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 11:47:11 -0500 Subject: [PATCH 40/47] Add special handling for u256 divided by a single word --- include/boost/decimal/detail/u256.hpp | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u256.hpp b/include/boost/decimal/detail/u256.hpp index dc4de1a77..edfd7eab3 100644 --- a/include/boost/decimal/detail/u256.hpp +++ b/include/boost/decimal/detail/u256.hpp @@ -1077,7 +1077,25 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto div_mod(const u256& lhs, const Unsigne BOOST_DECIMAL_DETAIL_INT128_ASSERT(m >= n); - int128::detail::impl::knuth_divide(u, m, v, n, q); + // Simplify handling of single word division + // We run into this case with dividing by powers of 10 while rounding u256 + if (n == 1U) + { + std::uint64_t remainder {}; + + for (std::size_t j = m; j-- > 0;) + { + const auto dividend {(remainder << 32) | u[j]}; + q[j] = static_cast(dividend / v[0]); + remainder = dividend % v[0]; + } + + u[0] = static_cast(remainder); + } + else + { + int128::detail::impl::knuth_divide(u, m, v, n, q); + } return {from_words(q), from_words(u)}; } From 87fc521d7ae833c6360d445faa1c01adb796bd1c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 13:16:12 -0500 Subject: [PATCH 41/47] Remove branch testing for zero --- include/boost/decimal/decimal128_t.hpp | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index 07dcc5995..8ce64aca9 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1708,17 +1708,6 @@ constexpr auto operator+(const decimal128_t& lhs, const decimal128_t& rhs) noexc } #endif - constexpr decimal128_t zero {0, 0}; - - if (lhs == zero) - { - return rhs; - } - else if (rhs == zero) - { - return lhs; - } - auto lhs_components {lhs.to_components()}; detail::expand_significand(lhs_components.sig, lhs_components.exp); auto rhs_components {rhs.to_components()}; From cd3cc9cf05e9b1d1f46fad928bbd64e20b8d9c5b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 13:18:54 -0500 Subject: [PATCH 42/47] Use and test new impl with decimal_fast128_t --- include/boost/decimal/decimal_fast128_t.hpp | 8 ++++---- test/github_issue_1260.cpp | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/boost/decimal/decimal_fast128_t.hpp b/include/boost/decimal/decimal_fast128_t.hpp index 2903da964..3a2494304 100644 --- a/include/boost/decimal/decimal_fast128_t.hpp +++ b/include/boost/decimal/decimal_fast128_t.hpp @@ -225,6 +225,9 @@ BOOST_DECIMAL_EXPORT class alignas(16) decimal_fast128_t final template friend constexpr Decimal detail::check_non_finite(Decimal x) noexcept; + template + friend constexpr auto detail::d128_add_impl_new(const T& lhs, const T& rhs) noexcept -> ReturnType; + public: constexpr decimal_fast128_t() noexcept = default; @@ -1032,10 +1035,7 @@ constexpr auto operator+(const decimal_fast128_t& lhs, const decimal_fast128_t& } #endif - return detail::d128_add_impl( - lhs.significand_, lhs.biased_exponent(), lhs.sign_, - rhs.significand_, rhs.biased_exponent(), rhs.sign_, - (abs(lhs) > abs(rhs))); + return detail::d128_add_impl_new(lhs, rhs); } template diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index 41d5e0115..cb28fac20 100644 --- a/test/github_issue_1260.cpp +++ b/test/github_issue_1260.cpp @@ -96,19 +96,19 @@ int main() test(); test2(); - //test2(); + test2(); test3(); - //test3(); + test3(); test4(); - //test4(); + test4(); test5(); - //test5(); + test5(); test6(); - //test6(); + test6(); return boost::report_errors(); } From 6fc97f9cedf7d3fe4d0045e2674764c6fbf6ae95 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 13:26:34 -0500 Subject: [PATCH 43/47] Implement and test subtraction --- include/boost/decimal/decimal128_t.hpp | 17 ++++++----------- include/boost/decimal/decimal_fast128_t.hpp | 9 +++++---- test/github_issue_1260.cpp | 16 ++++++++-------- 3 files changed, 19 insertions(+), 23 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index 8ce64aca9..c595c6134 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1766,18 +1766,13 @@ constexpr auto operator-(const decimal128_t& lhs, const decimal128_t& rhs) noexc } #endif - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::expand_significand(sig_lhs, exp_lhs); - - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::expand_significand(sig_rhs, exp_rhs); + auto lhs_components {lhs.to_components()}; + detail::expand_significand(lhs_components.sig, lhs_components.exp); + auto rhs_components {rhs.to_components()}; + detail::expand_significand(rhs_components.sig, rhs_components.exp); + rhs_components.sign = !rhs_components.sign; - return detail::d128_add_impl( - sig_lhs, exp_lhs, lhs.isneg(), - sig_rhs, exp_rhs, !rhs.isneg(), - abs(lhs) > abs(rhs)); + return detail::d128_add_impl_new(lhs_components, rhs_components); } template diff --git a/include/boost/decimal/decimal_fast128_t.hpp b/include/boost/decimal/decimal_fast128_t.hpp index 3a2494304..0743f6777 100644 --- a/include/boost/decimal/decimal_fast128_t.hpp +++ b/include/boost/decimal/decimal_fast128_t.hpp @@ -1083,10 +1083,11 @@ constexpr auto operator-(const decimal_fast128_t& lhs, const decimal_fast128_t& } #endif - return detail::d128_add_impl( - lhs.significand_, lhs.biased_exponent(), lhs.sign_, - rhs.significand_, rhs.biased_exponent(), !rhs.sign_, - abs(lhs) > abs(rhs)); + const auto lhs_components {lhs.to_components()}; + auto rhs_components {rhs.to_components()}; + rhs_components.sign = !rhs_components.sign; + + return detail::d128_add_impl_new(lhs_components, rhs_components); } template diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index cb28fac20..63ce8cdb3 100644 --- a/test/github_issue_1260.cpp +++ b/test/github_issue_1260.cpp @@ -19,8 +19,8 @@ void test() const T add_val {lhs + rhs}; BOOST_TEST_EQ(add_val, res); - //const T sub_val {lhs - boost::decimal::abs(rhs)}; - //BOOST_TEST_EQ(sub_val, res); + const T sub_val {lhs - boost::decimal::abs(rhs)}; + BOOST_TEST_EQ(sub_val, res); } template @@ -33,8 +33,8 @@ void test2() const T add_val {lhs + rhs}; BOOST_TEST_EQ(add_val, res); - //const T sub_val {lhs - boost::decimal::abs(rhs)}; - //BOOST_TEST_EQ(sub_val, res); + const T sub_val {lhs - boost::decimal::abs(rhs)}; + BOOST_TEST_EQ(sub_val, res); } template @@ -47,8 +47,8 @@ void test3() const T add_val {lhs + rhs}; BOOST_TEST_EQ(add_val, res); - //const T sub_val {lhs - boost::decimal::abs(rhs)}; - //BOOST_TEST_EQ(sub_val, res); + const T sub_val {lhs - boost::decimal::abs(rhs)}; + BOOST_TEST_EQ(sub_val, res); } template @@ -61,8 +61,8 @@ void test4() const T add_val {lhs + rhs}; BOOST_TEST_EQ(add_val, res); - //const T sub_val {lhs - boost::decimal::abs(rhs)}; - //BOOST_TEST_EQ(sub_val, res); + const T sub_val {lhs - boost::decimal::abs(rhs)}; + BOOST_TEST_EQ(sub_val, res); } template From 3e923c5344eb8491118e1b922a45e2beb97a6785 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 14:13:58 -0500 Subject: [PATCH 44/47] Use new function for mixed type add and sub --- include/boost/decimal/decimal128_t.hpp | 59 +++++++------------------- 1 file changed, 15 insertions(+), 44 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index c595c6134..e32c3823f 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1721,6 +1721,7 @@ constexpr auto operator+(const decimal128_t lhs, const Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_t) { using exp_type = decimal128_t::biased_exponent_type; + using sig_type = decimal128_t::significand_type; #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(lhs)) @@ -1729,19 +1730,15 @@ constexpr auto operator+(const decimal128_t lhs, const Integer rhs) noexcept } #endif - auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; - bool abs_lhs_bigger {abs(lhs) > sig_rhs}; - - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::expand_significand(sig_lhs, exp_lhs); + auto lhs_components {lhs.to_components()}; + detail::expand_significand(lhs_components.sig, lhs_components.exp); + auto positive_rhs {static_cast(detail::make_positive_unsigned(rhs))}; exp_type exp_rhs {0}; - detail::normalize(sig_rhs, exp_rhs); + detail::normalize(positive_rhs, exp_rhs); + const detail::decimal128_t_components rhs_components {positive_rhs, exp_rhs, rhs < 0}; - return detail::d128_add_impl(sig_lhs, exp_lhs, lhs.isneg(), - sig_rhs, exp_rhs, (rhs < 0), - abs_lhs_bigger); + return detail::d128_add_impl_new(lhs_components, rhs_components); } template @@ -1780,6 +1777,7 @@ constexpr auto operator-(const decimal128_t lhs, const Integer rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_t) { using exp_type = decimal128_t::biased_exponent_type; + using sig_type = decimal128_t::significand_type; #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(lhs)) @@ -1788,49 +1786,22 @@ constexpr auto operator-(const decimal128_t lhs, const Integer rhs) noexcept } #endif - auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; - const bool abs_lhs_bigger {abs(lhs) > sig_rhs}; - - auto sig_lhs {lhs.full_significand()}; - auto exp_lhs {lhs.biased_exponent()}; - detail::expand_significand(sig_lhs, exp_lhs); + auto lhs_components {lhs.to_components()}; + detail::expand_significand(lhs_components.sig, lhs_components.exp); + auto positive_rhs {static_cast(detail::make_positive_unsigned(rhs))}; exp_type exp_rhs {0}; - detail::normalize(sig_rhs, exp_rhs); + detail::normalize(positive_rhs, exp_rhs); + const detail::decimal128_t_components rhs_components {positive_rhs, exp_rhs, !(rhs < 0)}; - return detail::d128_add_impl( - sig_lhs, exp_lhs, lhs.isneg(), - sig_rhs, exp_rhs, !(rhs < 0), - abs_lhs_bigger); + return detail::d128_add_impl_new(lhs_components, rhs_components); } template constexpr auto operator-(const Integer lhs, const decimal128_t rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_t) { - using exp_type = decimal128_t::biased_exponent_type; - - #ifndef BOOST_DECIMAL_FAST_MATH - if (not_finite(rhs)) - { - return detail::check_non_finite(rhs); - } - #endif - - auto sig_lhs {static_cast(detail::make_positive_unsigned(lhs))}; - const bool abs_lhs_bigger {sig_lhs > abs(rhs)}; - - exp_type exp_lhs {0}; - detail::normalize(sig_lhs, exp_lhs); - - auto sig_rhs {rhs.full_significand()}; - auto exp_rhs {rhs.biased_exponent()}; - detail::expand_significand(sig_rhs, exp_rhs); - - return detail::d128_add_impl( - sig_lhs, exp_lhs, (lhs < 0), - sig_rhs, exp_rhs, !rhs.isneg(), - abs_lhs_bigger); + return -rhs + lhs; } constexpr auto operator*(const decimal128_t& lhs, const decimal128_t& rhs) noexcept -> decimal128_t From 72c5c5a59ed7ef2b642e81598d3c73d4c645ad3f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 14:19:41 -0500 Subject: [PATCH 45/47] Use new function for fast math mixed types --- include/boost/decimal/decimal128_t.hpp | 6 +-- include/boost/decimal/decimal_fast128_t.hpp | 47 +++++---------------- 2 files changed, 13 insertions(+), 40 deletions(-) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index e32c3823f..5fd7b4db6 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1789,10 +1789,10 @@ constexpr auto operator-(const decimal128_t lhs, const Integer rhs) noexcept auto lhs_components {lhs.to_components()}; detail::expand_significand(lhs_components.sig, lhs_components.exp); - auto positive_rhs {static_cast(detail::make_positive_unsigned(rhs))}; + auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; exp_type exp_rhs {0}; - detail::normalize(positive_rhs, exp_rhs); - const detail::decimal128_t_components rhs_components {positive_rhs, exp_rhs, !(rhs < 0)}; + detail::normalize(sig_rhs, exp_rhs); + const detail::decimal128_t_components rhs_components {sig_rhs, exp_rhs, !(rhs < 0)}; return detail::d128_add_impl_new(lhs_components, rhs_components); } diff --git a/include/boost/decimal/decimal_fast128_t.hpp b/include/boost/decimal/decimal_fast128_t.hpp index 0743f6777..d63d8fa17 100644 --- a/include/boost/decimal/decimal_fast128_t.hpp +++ b/include/boost/decimal/decimal_fast128_t.hpp @@ -1043,6 +1043,7 @@ constexpr auto operator+(const decimal_fast128_t& lhs, const Integer rhs) noexce BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal_fast128_t) { using exp_type = decimal_fast128_t::biased_exponent_type; + using sig_type = decimal_fast128_t::significand_type; #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(lhs)) @@ -1051,15 +1052,12 @@ constexpr auto operator+(const decimal_fast128_t& lhs, const Integer rhs) noexce } #endif - auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; - bool abs_lhs_bigger {abs(lhs) > sig_rhs}; - + auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; exp_type exp_rhs {0}; detail::normalize(sig_rhs, exp_rhs); + const detail::decimal_fast128_t_components rhs_components {sig_rhs, exp_rhs, rhs < 0}; - return detail::d128_add_impl(lhs.significand_, lhs.biased_exponent(), lhs.sign_, - sig_rhs, exp_rhs, (rhs < 0), - abs_lhs_bigger); + return detail::d128_add_impl_new(lhs.to_components(), rhs_components); } template @@ -1083,11 +1081,7 @@ constexpr auto operator-(const decimal_fast128_t& lhs, const decimal_fast128_t& } #endif - const auto lhs_components {lhs.to_components()}; - auto rhs_components {rhs.to_components()}; - rhs_components.sign = !rhs_components.sign; - - return detail::d128_add_impl_new(lhs_components, rhs_components); + return detail::d128_add_impl_new(lhs, -rhs); } template @@ -1095,6 +1089,7 @@ constexpr auto operator-(const decimal_fast128_t& lhs, const Integer rhs) noexce BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal_fast128_t) { using exp_type = decimal_fast128_t::biased_exponent_type; + using sig_type = decimal_fast128_t::significand_type; #ifndef BOOST_DECIMAL_FAST_MATH if (not_finite(lhs)) @@ -1103,41 +1098,19 @@ constexpr auto operator-(const decimal_fast128_t& lhs, const Integer rhs) noexce } #endif - auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; - const bool abs_lhs_bigger {abs(lhs) > sig_rhs}; - + auto sig_rhs {static_cast(detail::make_positive_unsigned(rhs))}; exp_type exp_rhs {0}; detail::normalize(sig_rhs, exp_rhs); + const detail::decimal_fast128_t_components rhs_components {sig_rhs, exp_rhs, !(rhs < 0)}; - return detail::d128_add_impl( - lhs.significand_, lhs.biased_exponent(), lhs.sign_, - sig_rhs, exp_rhs, !(rhs < 0), - abs_lhs_bigger); + return detail::d128_add_impl_new(lhs.to_components(), rhs_components); } template constexpr auto operator-(const Integer lhs, const decimal_fast128_t& rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal_fast128_t) { - using exp_type = decimal_fast128_t::biased_exponent_type; - - #ifndef BOOST_DECIMAL_FAST_MATH - if (not_finite(rhs)) - { - return detail::check_non_finite(rhs); - } - #endif - - auto sig_lhs {static_cast(detail::make_positive_unsigned(lhs))}; - const bool abs_lhs_bigger {sig_lhs > abs(rhs)}; - - exp_type exp_lhs {0}; - detail::normalize(sig_lhs, exp_lhs); - - return detail::d128_add_impl( - sig_lhs, exp_lhs, (lhs < 0), - rhs.significand_, rhs.biased_exponent(), !rhs.sign_, - abs_lhs_bigger); + return -rhs + lhs; } constexpr auto operator*(const decimal_fast128_t& lhs, const decimal_fast128_t& rhs) noexcept -> decimal_fast128_t From 065913792afa7696449e4460ebcc9b2fbbe702fe Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 14:24:30 -0500 Subject: [PATCH 46/47] Fix mixed nans handling --- include/boost/decimal/decimal128_t.hpp | 7 +++++++ include/boost/decimal/decimal_fast128_t.hpp | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/include/boost/decimal/decimal128_t.hpp b/include/boost/decimal/decimal128_t.hpp index 5fd7b4db6..3017612a7 100644 --- a/include/boost/decimal/decimal128_t.hpp +++ b/include/boost/decimal/decimal128_t.hpp @@ -1801,6 +1801,13 @@ template constexpr auto operator-(const Integer lhs, const decimal128_t rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal128_t) { + #ifndef BOOST_DECIMAL_FAST_MATH + if (not_finite(rhs)) + { + return detail::check_non_finite(rhs); + } + #endif + return -rhs + lhs; } diff --git a/include/boost/decimal/decimal_fast128_t.hpp b/include/boost/decimal/decimal_fast128_t.hpp index d63d8fa17..39b0974a3 100644 --- a/include/boost/decimal/decimal_fast128_t.hpp +++ b/include/boost/decimal/decimal_fast128_t.hpp @@ -1110,6 +1110,13 @@ template constexpr auto operator-(const Integer lhs, const decimal_fast128_t& rhs) noexcept BOOST_DECIMAL_REQUIRES_RETURN(detail::is_integral_v, Integer, decimal_fast128_t) { + #ifndef BOOST_DECIMAL_FAST_MATH + if (not_finite(rhs)) + { + return detail::check_non_finite(rhs); + } + #endif + return -rhs + lhs; } From 653aa3c47037772b768208da28e88e0624682a41 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 13 Jan 2026 15:49:38 -0500 Subject: [PATCH 47/47] Ignore GCC 7 warning --- include/boost/decimal/detail/normalize.hpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/boost/decimal/detail/normalize.hpp b/include/boost/decimal/detail/normalize.hpp index 5af8890d1..02c6ba9ba 100644 --- a/include/boost/decimal/detail/normalize.hpp +++ b/include/boost/decimal/detail/normalize.hpp @@ -15,6 +15,11 @@ namespace boost { namespace decimal { namespace detail { +#if defined(__GNUC__) && __GNUC__ == 7 +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif + // Converts the significand to full precision to remove the effects of cohorts template constexpr auto normalize(T1& significand, T2& exp, bool sign = false) noexcept -> void @@ -25,6 +30,7 @@ constexpr auto normalize(T1& significand, T2& exp, bool sign = false) noexcept - if (digits < target_precision) { const auto zeros_needed {target_precision - digits}; + BOOST_DECIMAL_ASSERT(zeros_needed >= 0); significand *= pow10(static_cast(zeros_needed)); exp -= zeros_needed; } @@ -35,6 +41,10 @@ constexpr auto normalize(T1& significand, T2& exp, bool sign = false) noexcept - } } +#if defined(__GNUC__) && __GNUC__ == 7 +# pragma GCC diagnostic pop +#endif + // This is a branchless version of the above which is used for implementing basic operations, // since we know that the values in the decimal type are never larger than target_precision template