From cb1eeaf86bac62e0b69cd4fd0b8c24e413603351 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:29:53 -0500 Subject: [PATCH 01/22] 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 63fb510f062eb3ca2d165b1baf19e004414e4321 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:34:21 -0500 Subject: [PATCH 02/22] 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 68f725cc38530dd9ba9a41f70812baaf809aca20 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:41:27 -0500 Subject: [PATCH 03/22] 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 c5387b0bf6837390cda0057184db5e9f4cef8527 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 10:51:20 -0500 Subject: [PATCH 04/22] 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 45b7946727b8cba381eca3efc7829e8a1b5eac11 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:05:01 -0500 Subject: [PATCH 05/22] 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 16362d8ae82cf23318add6ef8d69ba28ac00e44c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:23:58 -0500 Subject: [PATCH 06/22] 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 5d4e09cc7a19fd97ff3794f8f8ecd93edc9595cf Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:28:34 -0500 Subject: [PATCH 07/22] 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 17d47d0681deb7b34e9ce966706c6c55c379cd21 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:38:23 -0500 Subject: [PATCH 08/22] 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 0610800dbc8d0002214753c28252380665bb2093 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:43:27 -0500 Subject: [PATCH 09/22] 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 77fb6cc9deb40bfaf421a6b27985b9a44b0262be Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:48:20 -0500 Subject: [PATCH 10/22] 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 da0a26fbb68fb94fefd4b31d3defebbbe2302749 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 11:57:58 -0500 Subject: [PATCH 11/22] 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 931414c99e04af0ea66c4ff15fd72f56803d5af9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 13:04:03 -0500 Subject: [PATCH 12/22] 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 327f4f15ae08eaf25aaec042ddac53b18a7ab038 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 13:07:50 -0500 Subject: [PATCH 13/22] 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 7859501da682f41362656d1c0910fc344c0c1d7c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 13:10:55 -0500 Subject: [PATCH 14/22] 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 3bc6c9318be72f29e6382e4a610b137f74edd78a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 14:01:05 -0500 Subject: [PATCH 15/22] 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 18815f39bf81d27d1f191dd6a083d9e15caf86de Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 14:01:16 -0500 Subject: [PATCH 16/22] 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 7adac665f..966cd33d7 100644 --- a/test/test_big_uints.cpp +++ b/test/test_big_uints.cpp @@ -478,6 +478,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 5f97adc004dac5246e021125bff9415ce4ad0bfd Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:52:04 -0500 Subject: [PATCH 17/22] Add additional test case --- test/github_issue_1260.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index dc5af98f2..a699753f3 100644 --- a/test/github_issue_1260.cpp +++ b/test/github_issue_1260.cpp @@ -21,6 +21,20 @@ 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() { // TODO(mborland): GCC 12+ in 32 bit compilations only with release mode fail @@ -29,6 +43,9 @@ int main() test(); test(); + test2(); + test2(); + #endif return boost::report_errors(); From 1b3b7f8a2f9f0f4b48421cb47664683aa6aa042d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:58:52 -0500 Subject: [PATCH 18/22] 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 dba59a6de..b8ce1669e 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 b89d0c079379067004499708586e55009d77cac7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:59:02 -0500 Subject: [PATCH 19/22] 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 81eba554e3db034355a78a8819f3dd6346fc0571 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 16:59:09 -0500 Subject: [PATCH 20/22] More tests --- test/github_issue_1260.cpp | 44 +++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/test/github_issue_1260.cpp b/test/github_issue_1260.cpp index a699753f3..02552d59e 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() @@ -44,7 +72,13 @@ int main() test(); test2(); - test2(); + //test2(); + + test3(); + //test3(); + + test4(); + //test4(); #endif From df60370d0755df3c4c8c77a4c0aaa3a73ce6d8a4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 17:04:33 -0500 Subject: [PATCH 21/22] 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 6ff8f0c976a291f12e371a9fdda439f2fc27fa3e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 17 Dec 2025 17:06:02 -0500 Subject: [PATCH 22/22] 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 {