// 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 { 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 {}; 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 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]}; } } #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 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; } 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[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 impl #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 { if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) { return impl::i256_sub_impl(a, b, res); } else { if (a >= b) { unsigned long long result[4] {}; unsigned char borrow {}; borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[0], b[0], &result[0]); borrow = BOOST_DECIMAL_SUB_BORROW(borrow, a[1], b[1], &result[1]); 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; } else { unsigned long long result[4] {}; unsigned char borrow {}; borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[0], a[0], &result[0]); borrow = BOOST_DECIMAL_SUB_BORROW(borrow, b[1], a[1], &result[1]); 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; } } } #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 { 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] = __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 { unsigned long long borrow {}; 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; } } } #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 { return impl::i256_sub_impl(a, b, result); } #endif } // namespace detail } // namespace decimal } // namespace boost #endif // BOOST_DECIMAL_DETAIL_I256_HPP