// Copyright 2025 Matt Borland // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt #ifndef BOOST_DECIMAL_DETAIL_INT128_NUMERIC_HPP #define BOOST_DECIMAL_DETAIL_INT128_NUMERIC_HPP #include "bit.hpp" #include "detail/traits.hpp" #include #include namespace boost { namespace int128 { namespace detail { template struct reduced_integers { static constexpr bool value {std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value}; }; #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) template BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE bool is_reduced_integer_v {reduced_integers::value || std::is_same::value || std::is_same::value}; #else template BOOST_DECIMAL_INLINE_CONSTEXPR_VARIABLE bool is_reduced_integer_v {reduced_integers::value}; #endif // 128-bit } // namespace detail constexpr uint128_t add_sat(const uint128_t x, const uint128_t y) noexcept { const auto z {x + y}; if (z < x) { return (std::numeric_limits::max)(); } return z; } constexpr uint128_t sub_sat(const uint128_t x, const uint128_t y) noexcept { const auto z {x - y}; if (z > x) { return (std::numeric_limits::min)(); } return z; } constexpr int128_t add_sat(int128_t x, int128_t y) noexcept; constexpr int128_t sub_sat(int128_t x, int128_t y) noexcept; #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable : 4307) // Addition Overflow # pragma warning(disable : 4146) // Unary minus applied to unsigned type #endif constexpr int128_t add_sat(const int128_t x, const int128_t y) noexcept { if (x >= 0 && y >= 0) { constexpr auto max_value {static_cast((std::numeric_limits::max)())}; const auto big_x {static_cast(x)}; const auto big_y {static_cast(y)}; const auto big_res {big_x + big_y}; return big_res > max_value ? (std::numeric_limits::max)() : static_cast(big_res); } else if ((x < 0 && y > 0) || (x > 0 && y < 0)) { return x + y; } else { // x < 0 and y < 0 // Nearly the same technique as the positive values case constexpr auto max_value {-static_cast((std::numeric_limits::min)())}; const auto big_x {static_cast(abs(x))}; const auto big_y {static_cast(abs(y))}; const auto big_res {big_x + big_y}; return big_res > max_value ? (std::numeric_limits::min)() : -static_cast(big_res); } } constexpr int128_t sub_sat(const int128_t x, const int128_t y) noexcept { if (x <= 0 && y >= 0) { // Underflow case const auto res {x - y}; return res > x ? (std::numeric_limits::min)() : res; } else if (x > 0 && y < 0) { // Overflow Case constexpr auto max_val {static_cast((std::numeric_limits::max)())}; const auto big_x {static_cast(x)}; const auto big_y {-static_cast(y)}; const auto res {big_x + big_y}; return (res > max_val || res < big_x) ? (std::numeric_limits::max)() : static_cast(res); } else { return x - y; } } #ifdef _MSC_VER # pragma warning(pop) #endif constexpr uint128_t mul_sat(const uint128_t x, const uint128_t y) noexcept { const auto x_bits {bit_width(x)}; const auto y_bits {bit_width(y)}; if ((x_bits + y_bits) > std::numeric_limits::digits) { return (std::numeric_limits::max)(); } return x * y; } constexpr int128_t mul_sat(const int128_t& x, const int128_t& y) noexcept { const auto x_bits {bit_width(static_cast(abs(x)))}; const auto y_bits {bit_width(static_cast(abs(y)))}; if ((x_bits + y_bits) > std::numeric_limits::digits) { if ((x < 0) != (y < 0)) { return (std::numeric_limits::min)(); } else { return (std::numeric_limits::max)(); } } const int128_t res {x * y}; return res; } constexpr uint128_t div_sat(const uint128_t x, const uint128_t y) noexcept { return x / y; } constexpr int128_t div_sat(const int128_t x, const int128_t y) noexcept { if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(x == (std::numeric_limits::min)() && y == -1)) { // This is the only possible case of overflow return (std::numeric_limits::max)(); } return x / y; } template , bool> = true> constexpr TargetType saturate_cast(const uint128_t value) noexcept { BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) { return value; } else { if (value > static_cast((std::numeric_limits::max)())) { return (std::numeric_limits::max)(); } return static_cast(value); } } template , bool> = true> constexpr TargetType saturate_cast(const int128_t value) noexcept { BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) { return value; } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) else BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value || std::is_same::value) #else else BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) #endif { // We can't possibly have overflow in this case return value < 0 ? static_cast(0) : static_cast(value); } else { if (value > static_cast((std::numeric_limits::max)())) { return (std::numeric_limits::max)(); } else if (value < static_cast((std::numeric_limits::min)())) { return (std::numeric_limits::min)(); } return static_cast(value); } } } // namespace int128 } // namespace boost #endif // BOOST_DECIMAL_DETAIL_INT128_NUMERIC_HPP