diff --git a/include/boost/decimal/detail/fenv_rounding.hpp b/include/boost/decimal/detail/fenv_rounding.hpp index eae09a6ea..3a684478c 100644 --- a/include/boost/decimal/detail/fenv_rounding.hpp +++ b/include/boost/decimal/detail/fenv_rounding.hpp @@ -645,12 +645,15 @@ BOOST_DECIMAL_CUDA_CONSTEXPR auto fenv_round(T& val, bool is_neg = false, bool s #endif -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable : 4127) +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-conversion" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wsign-conversion" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4127) #endif template @@ -743,10 +746,12 @@ BOOST_DECIMAL_CUDA_CONSTEXPR auto coefficient_rounding(T1& coeff, T2& exp, T3& b return coeff_digits; } -#ifdef _MSC_VER -# pragma warning(pop) +#if defined(__clang__) +# pragma clang diagnostic pop #elif defined(__GNUC__) # pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) #endif } // namespace detail diff --git a/include/boost/decimal/detail/int128/bit.hpp b/include/boost/decimal/detail/int128/bit.hpp index 4ad05a62e..996cd0fac 100644 --- a/include/boost/decimal/detail/int128/bit.hpp +++ b/include/boost/decimal/detail/int128/bit.hpp @@ -13,70 +13,106 @@ namespace boost { namespace int128 { -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool has_single_bit(const uint128_t x) noexcept +namespace impl { + +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countl_zero_impl(const uint128 x) noexcept { - return x && !(x & (x - 1U)); + return x.high == 0 ? 64 + detail::countl_zero(x.low) : detail::countl_zero(x.high); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countl_zero(const uint128_t x) noexcept +} // namespace impl + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countl_zero(const uint128 x) noexcept { - return x.high == 0 ? 64 + detail::countl_zero(x.low) : detail::countl_zero(x.high); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) && BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_clzg) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + + if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) + { + return impl::countl_zero_impl(x); + } + + // The second argument is the result for x == 0, which is undefined without it + return __builtin_clzg(static_cast(x), 128); + + #else + + return impl::countl_zero_impl(x); + + #endif } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countl_one(const uint128_t x) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countl_one(const uint128 x) noexcept { return countl_zero(~x); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int bit_width(const uint128_t x) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int bit_width(const uint128 x) noexcept { return x ? 128 - countl_zero(x) : 0; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t bit_ceil(const uint128_t x) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 bit_ceil(const uint128 x) noexcept { - return x <= 1U ? static_cast(1) : static_cast(1) << bit_width(x - 1U); + // __builtin_stdc_bit_ceil not available, but this is equivalent + return x <= 1U ? static_cast(1) : static_cast(2) << (127 - countl_zero(x - 1)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t bit_floor(const uint128_t x) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 bit_floor(const uint128 x) noexcept { - return x > 0U ? static_cast(1) << (bit_width(x) - 1U) : static_cast(0); + // __builtin_stdc_bit_floor not available, but this is equivalent + return x == 0U ? static_cast(0) : static_cast(1) << (127 - countl_zero(x)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countr_zero(const uint128_t x) noexcept +namespace impl { + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countr_zero_impl(const uint128 x) noexcept { return x.low == 0 ? 64 + detail::countr_zero(x.high) : detail::countr_zero(x.low); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countr_one(const uint128_t x) noexcept +} // namespace impl + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countr_zero(const uint128 x) noexcept +{ + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) && BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_ctzg) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + + if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) + { + return impl::countr_zero_impl(x); + } + + // The second argument is the result for x == 0, which is undefined without it + return __builtin_ctzg(static_cast(x), 128); + + #else + + return impl::countr_zero_impl(x); + + #endif +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countr_one(const uint128 x) noexcept { return countr_zero(~x); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t rotl(const uint128_t x, const int s) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 rotl(const uint128 x, const int s) noexcept { + // __builtin_stdc_rotate_left not available constexpr auto mask {127U}; return x << (static_cast(s) & mask) | x >> (static_cast(-s) & mask); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t rotr(const uint128_t x, const int s) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 rotr(const uint128 x, const int s) noexcept { + // __builtin_stdc_rotate_right not available constexpr auto mask {127U}; return x >> (static_cast(s) & mask) | x << (static_cast(-s) & mask); } -#if BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_popcountll) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount(const uint128_t x) noexcept -{ - return __builtin_popcountll(x.high) + __builtin_popcountll(x.low); -} - -#endif - namespace impl { -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount_impl(std::uint64_t x) noexcept +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount_impl(std::uint64_t x) noexcept { x = x - ((x >> 1U) & UINT64_C(0x5555555555555555)); x = (x & UINT64_C(0x3333333333333333)) + ((x >> 2U) & UINT64_C(0x3333333333333333)); @@ -85,15 +121,34 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount_impl(std::uint64_ return static_cast((x * UINT64_C(0x0101010101010101)) >> 56U); } -} // namespace impl +// The exact-match overload above is selected for the 64-bit halves +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount_impl(const uint128 x) noexcept +{ + return popcount_impl(x.high) + popcount_impl(x.low); +} -#if defined(_M_AMD64) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && !BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_popcountll) +} // namespace impl -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount(const uint128_t x) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount(const uint128 x) noexcept { + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) && BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_popcountg) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) { - return impl::popcount_impl(x.high) + impl::popcount_impl(x.low); // LCOV_EXCL_LINE + return impl::popcount_impl(x); + } + + return __builtin_popcountg(static_cast(x)); + + #elif BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_popcountll) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) + + return __builtin_popcountll(x.high) + __builtin_popcountll(x.low); + + #elif defined(_M_AMD64) && !defined(__GNUC__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + + if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) + { + return impl::popcount_impl(x); // LCOV_EXCL_LINE } else { @@ -107,15 +162,12 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #endif } -} -#elif defined(_M_IX86) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && !BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_popcountll) + #elif defined(_M_IX86) && !defined(__GNUC__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount(const uint128_t x) noexcept -{ if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) { - return impl::popcount_impl(x.high) + impl::popcount_impl(x.low); // LCOV_EXCL_LINE + return impl::popcount_impl(x); // LCOV_EXCL_LINE } else { @@ -137,26 +189,14 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #endif } -} - -#elif !BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_popcountll) || (defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int popcount(const uint128_t x) noexcept -{ - return impl::popcount_impl(x.high) + impl::popcount_impl(x.low); -} -#endif + #else -#if BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_bswap64) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) + return impl::popcount_impl(x); -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t byteswap(const uint128_t x) noexcept -{ - return {__builtin_bswap64(x.low), __builtin_bswap64(x.high)}; + #endif } -#endif - namespace impl { BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::uint64_t byteswap_impl(const std::uint64_t x) noexcept @@ -166,17 +206,45 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const return (step16 & UINT64_C(0x00FF00FF00FF00FF)) << 8U | (step16 & UINT64_C(0xFF00FF00FF00FF00)) >> 8U; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t byteswap_impl(const uint128_t x) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 byteswap_impl(const uint128 x) noexcept { return {byteswap_impl(x.low), byteswap_impl(x.high)}; } } // namespace impl -#if defined(_MSC_VER) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && !BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_bswap64) - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t byteswap(const uint128_t x) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 byteswap(const uint128 x) noexcept { + // The whole-width builtins are deliberately ranked below the paired 64-bit form. + // Measured today (7/29/2026) they are a regression: the 128-bit value blocks the loop vectorization + // the paired __builtin_bswap64 receives, costing up to 1.5x on arm64, and on x86-64 both + // forms emit identical code. Revisit if the codegen improves. + #if BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_bswap64) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) + + return {__builtin_bswap64(x.low), __builtin_bswap64(x.high)}; + + // __builtin_bswapg is clang-only (LLVM 22.1) and __builtin_bswap128 is GCC-only (GCC 11), + // so at most one of the two whole-width branches is ever live + #elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) && BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_bswapg) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + + if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) + { + return impl::byteswap_impl(x); + } + + return static_cast(__builtin_bswapg(static_cast(x))); + + #elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) && BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_bswap128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + + if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) + { + return impl::byteswap_impl(x); + } + + return static_cast(__builtin_bswap128(static_cast(x))); + + #elif defined(_MSC_VER) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) { return impl::byteswap_impl(x); // LCOV_EXCL_LINE @@ -185,16 +253,18 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const { return {_byteswap_uint64(x.low), _byteswap_uint64(x.high)}; } -} -#elif !BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_bswap64) || (defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) + #else -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t byteswap(const uint128_t x) noexcept -{ return impl::byteswap_impl(x); + + #endif } -#endif +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool has_single_bit(const uint128 x) noexcept +{ + return popcount(x) == 1; +} } // namespace int128 } // namespace boost diff --git a/include/boost/decimal/detail/int128/byte_conversions.hpp b/include/boost/decimal/detail/int128/byte_conversions.hpp new file mode 100644 index 000000000..654affc4e --- /dev/null +++ b/include/boost/decimal/detail/int128/byte_conversions.hpp @@ -0,0 +1,366 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_INT128_BYTE_CONVERSIONS_HPP +#define BOOST_DECIMAL_DETAIL_INT128_BYTE_CONVERSIONS_HPP + +#include +#include +#include + +#ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE + +#include +#include +#include +#include + +#endif + +namespace boost { +namespace int128 { + +//===================================== +// Whole value byte order conversions +//===================================== + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 to_be(const uint128 value) noexcept +{ + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_BIG_BYTE + + return value; + + #else + + return byteswap(value); + + #endif +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 to_be(const int128 value) noexcept +{ + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_BIG_BYTE + + return value; + + #else + + // Reversing the two's complement bit pattern is the same operation for both signs + return static_cast(byteswap(static_cast(value))); + + #endif +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 from_be(const uint128 value) noexcept +{ + // Self-inverse + return to_be(value); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 from_be(const int128 value) noexcept +{ + // Self-inverse + return to_be(value); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 to_le(const uint128 value) noexcept +{ + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + + return value; + + #else + + return byteswap(value); + + #endif +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 to_le(const int128 value) noexcept +{ + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + + return value; + + #else + + // Reversing the two's complement bit pattern is the same operation for both signs + return static_cast(byteswap(static_cast(value))); + + #endif +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 from_le(const uint128 value) noexcept +{ + // Self-inverse + return to_le(value); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 from_le(const int128 value) noexcept +{ + // Self-inverse + return to_le(value); +} + +namespace detail { + +// The byte-like element types that the byte array functions operate on. +// std::byte only exists from C++17, and the library is usable from C++14. +template +struct byte_like +{ + static constexpr bool value = std::is_same::value || + std::is_same::value || + std::is_same::value + #if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L + || std::is_same::value + #endif + ; +}; + +template +BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_byte_like_v = byte_like::value; + +// Reads the byte sitting at bit offset shift of a 64-bit word +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr ByteType extract_byte(const std::uint64_t word, const unsigned shift) noexcept +{ + return static_cast(static_cast((word >> shift) & UINT64_C(0xFF))); +} + +// Places a byte at bit offset shift of a 64-bit word +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::uint64_t insert_byte(const ByteType value, const unsigned shift) noexcept +{ + return static_cast(static_cast(value)) << shift; +} + +// Rebuilds either library type from the raw two's complement words +template +struct word_builder; + +template <> +struct word_builder +{ + static BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 build(const std::uint64_t hi, const std::uint64_t lo) noexcept + { + return uint128{hi, lo}; + } +}; + +template <> +struct word_builder +{ + static BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 build(const std::uint64_t hi, const std::uint64_t lo) noexcept + { + return from_bits(hi, lo); + } +}; + +// The byte arrays are built from shifts rather than from the object representation, +// so all four functions below are exact on either endianness. Bytes is anything +// that can be indexed with the subscript operator: a std::array or a pointer. + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_be_bytes_impl(const std::uint64_t hi, const std::uint64_t lo) noexcept +{ + return {{extract_byte(hi, 56U), extract_byte(hi, 48U), + extract_byte(hi, 40U), extract_byte(hi, 32U), + extract_byte(hi, 24U), extract_byte(hi, 16U), + extract_byte(hi, 8U), extract_byte(hi, 0U), + extract_byte(lo, 56U), extract_byte(lo, 48U), + extract_byte(lo, 40U), extract_byte(lo, 32U), + extract_byte(lo, 24U), extract_byte(lo, 16U), + extract_byte(lo, 8U), extract_byte(lo, 0U)}}; +} + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_le_bytes_impl(const std::uint64_t hi, const std::uint64_t lo) noexcept +{ + return {{extract_byte(lo, 0U), extract_byte(lo, 8U), + extract_byte(lo, 16U), extract_byte(lo, 24U), + extract_byte(lo, 32U), extract_byte(lo, 40U), + extract_byte(lo, 48U), extract_byte(lo, 56U), + extract_byte(hi, 0U), extract_byte(hi, 8U), + extract_byte(hi, 16U), extract_byte(hi, 24U), + extract_byte(hi, 32U), extract_byte(hi, 40U), + extract_byte(hi, 48U), extract_byte(hi, 56U)}}; +} + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_be_bytes_impl(const Bytes& bytes) noexcept +{ + return word_builder::build(insert_byte(bytes[0], 56U) | insert_byte(bytes[1], 48U) | + insert_byte(bytes[2], 40U) | insert_byte(bytes[3], 32U) | + insert_byte(bytes[4], 24U) | insert_byte(bytes[5], 16U) | + insert_byte(bytes[6], 8U) | insert_byte(bytes[7], 0U), + insert_byte(bytes[8], 56U) | insert_byte(bytes[9], 48U) | + insert_byte(bytes[10], 40U) | insert_byte(bytes[11], 32U) | + insert_byte(bytes[12], 24U) | insert_byte(bytes[13], 16U) | + insert_byte(bytes[14], 8U) | insert_byte(bytes[15], 0U)); +} + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_le_bytes_impl(const Bytes& bytes) noexcept +{ + return word_builder::build(insert_byte(bytes[8], 0U) | insert_byte(bytes[9], 8U) | + insert_byte(bytes[10], 16U) | insert_byte(bytes[11], 24U) | + insert_byte(bytes[12], 32U) | insert_byte(bytes[13], 40U) | + insert_byte(bytes[14], 48U) | insert_byte(bytes[15], 56U), + insert_byte(bytes[0], 0U) | insert_byte(bytes[1], 8U) | + insert_byte(bytes[2], 16U) | insert_byte(bytes[3], 24U) | + insert_byte(bytes[4], 32U) | insert_byte(bytes[5], 40U) | + insert_byte(bytes[6], 48U) | insert_byte(bytes[7], 56U)); +} + +} // namespace detail + +//===================================== +// Byte array conversions +//===================================== + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_be_bytes(const uint128 value) noexcept +{ + return detail::to_be_bytes_impl(value.high, value.low); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_be_bytes(const int128 value) noexcept +{ + return detail::to_be_bytes_impl(value.high, value.low); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_be_bytes(const std::array& bytes) noexcept +{ + static_assert(detail::is_valid_overload_v, + "The target type must be boost::int128::uint128 or boost::int128::int128"); + static_assert(detail::is_byte_like_v, + "The source bytes must be char, signed char, unsigned char, or std::byte"); + static_assert(N == sizeof(T), "The number of bytes provided, and the target type number of bytes do not match"); + + return detail::from_be_bytes_impl(bytes); +} + +// Reads sizeof(T) bytes starting at bytes +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_be_bytes(const ByteType* bytes) noexcept +{ + static_assert(detail::is_valid_overload_v, + "The target type must be boost::int128::uint128 or boost::int128::int128"); + static_assert(detail::is_byte_like_v, + "The source bytes must be char, signed char, unsigned char, or std::byte"); + + return detail::from_be_bytes_impl(bytes); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_le_bytes(const uint128 value) noexcept +{ + return detail::to_le_bytes_impl(value.high, value.low); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_le_bytes(const int128 value) noexcept +{ + return detail::to_le_bytes_impl(value.high, value.low); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_le_bytes(const std::array& bytes) noexcept +{ + static_assert(detail::is_valid_overload_v, + "The target type must be boost::int128::uint128 or boost::int128::int128"); + static_assert(detail::is_byte_like_v, + "The source bytes must be char, signed char, unsigned char, or std::byte"); + static_assert(N == sizeof(T), "The number of bytes provided, and the target type number of bytes do not match"); + + return detail::from_le_bytes_impl(bytes); +} + +// Reads sizeof(T) bytes starting at bytes +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_le_bytes(const ByteType* bytes) noexcept +{ + static_assert(detail::is_valid_overload_v, + "The target type must be boost::int128::uint128 or boost::int128::int128"); + static_assert(detail::is_byte_like_v, + "The source bytes must be char, signed char, unsigned char, or std::byte"); + + return detail::from_le_bytes_impl(bytes); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_ne_bytes(const uint128 value) noexcept +{ + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + + return detail::to_le_bytes_impl(value.high, value.low); + + #else + + return detail::to_be_bytes_impl(value.high, value.low); + + #endif +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::array to_ne_bytes(const int128 value) noexcept +{ + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + + return detail::to_le_bytes_impl(value.high, value.low); + + #else + + return detail::to_be_bytes_impl(value.high, value.low); + + #endif +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_ne_bytes(const std::array& bytes) noexcept +{ + static_assert(detail::is_valid_overload_v, + "The target type must be boost::int128::uint128 or boost::int128::int128"); + static_assert(detail::is_byte_like_v, + "The source bytes must be char, signed char, unsigned char, or std::byte"); + static_assert(N == sizeof(T), "The number of bytes provided, and the target type number of bytes do not match"); + + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + + return detail::from_le_bytes_impl(bytes); + + #else + + return detail::from_be_bytes_impl(bytes); + + #endif +} + +// Reads sizeof(T) bytes starting at bytes +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T from_ne_bytes(const ByteType* bytes) noexcept +{ + static_assert(detail::is_valid_overload_v, + "The target type must be boost::int128::uint128 or boost::int128::int128"); + static_assert(detail::is_byte_like_v, + "The source bytes must be char, signed char, unsigned char, or std::byte"); + + #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + + return detail::from_le_bytes_impl(bytes); + + #else + + return detail::from_be_bytes_impl(bytes); + + #endif +} + +} // namespace int128 +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_INT128_BYTE_CONVERSIONS_HPP diff --git a/include/boost/decimal/detail/int128/climits.hpp b/include/boost/decimal/detail/int128/climits.hpp index 607e0dade..30a9c91c9 100644 --- a/include/boost/decimal/detail/int128/climits.hpp +++ b/include/boost/decimal/detail/int128/climits.hpp @@ -9,9 +9,9 @@ #include #include -#define BOOST_DECIMAL_DETAIL_INT128_UINT128_MAX boost::int128::uint128_t{UINT64_MAX, UINT64_MAX} +#define BOOST_DECIMAL_DETAIL_INT128_UINT128_MAX boost::int128::uint128{UINT64_MAX, UINT64_MAX} -#define BOOST_DECIMAL_DETAIL_INT128_INT128_MIN boost::int128::int128_t{INT64_MIN, 0} -#define BOOST_DECIMAL_DETAIL_INT128_INT128_MAX boost::int128::int128_t{INT64_MAX, UINT64_MAX} +#define BOOST_DECIMAL_DETAIL_INT128_INT128_MIN boost::int128::int128{INT64_MIN, 0} +#define BOOST_DECIMAL_DETAIL_INT128_INT128_MAX boost::int128::int128{INT64_MAX, UINT64_MAX} #endif // BOOST_DECIMAL_DETAIL_INT128_CLIMITS_HPP diff --git a/include/boost/decimal/detail/int128/cstdlib.hpp b/include/boost/decimal/detail/int128/cstdlib.hpp index 439006034..284bbac8e 100644 --- a/include/boost/decimal/detail/int128/cstdlib.hpp +++ b/include/boost/decimal/detail/int128/cstdlib.hpp @@ -12,21 +12,21 @@ namespace int128 { BOOST_DECIMAL_DETAIL_INT128_EXPORT struct u128div_t { - uint128_t quot; - uint128_t rem; + uint128 quot; + uint128 rem; }; BOOST_DECIMAL_DETAIL_INT128_EXPORT struct i128div_t { - int128_t quot; - int128_t rem; + int128 quot; + int128 rem; }; -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr u128div_t div(const uint128_t x, const uint128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr u128div_t div(const uint128 x, const uint128 y) noexcept { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(x == 0U || y == 0U)) + if (y == 0U) { - return u128div_t{0U, 0U}; + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } if (x < y) @@ -54,43 +54,44 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const } } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr i128div_t div(const int128_t x, const int128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr i128div_t div(const int128 x, const int128 y) noexcept { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(x == 0 || y == 0)) + if (y == 0) { - return i128div_t{0, 0}; + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) + const auto abs_lhs {static_cast(abs(x))}; + const auto abs_rhs {static_cast(abs(y))}; - const auto builtin_x {static_cast(x)}; - const auto builtin_y {static_cast(y)}; - return i128div_t{static_cast(builtin_x / builtin_y), - static_cast(builtin_x % builtin_y)}; + if (abs_rhs > abs_lhs) + { + return {0, x}; + } - #else + const auto negative_quot {(x.signed_high() < 0) != (y.signed_high() < 0)}; + const auto negative_rem {x.signed_high() < 0}; - const auto abs_lhs {static_cast(abs(x))}; - const auto abs_rhs {static_cast(abs(y))}; + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - if (abs_rhs > abs_lhs) + if (abs_rhs.high != 0) { - return {0, x}; + const auto builtin_x {static_cast(x)}; + const auto builtin_y {static_cast(y)}; + return i128div_t{static_cast(builtin_x / builtin_y), + static_cast(builtin_x % builtin_y)}; } - const auto unsigned_res {div(abs_lhs, abs_rhs)}; + #endif - const auto negative_quot {(x.high < 0) != (y.high < 0)}; - const auto negative_rem {x.high < 0}; + const auto unsigned_res {div(abs_lhs, abs_rhs)}; - i128div_t res {static_cast(unsigned_res.quot), static_cast(unsigned_res.rem)}; + i128div_t res {static_cast(unsigned_res.quot), static_cast(unsigned_res.rem)}; res.quot = negative_quot ? -res.quot : res.quot; res.rem = negative_rem ? -res.rem : res.rem; return res; - - #endif } } // namespace int128 diff --git a/include/boost/decimal/detail/int128/detail/clz.hpp b/include/boost/decimal/detail/int128/detail/clz.hpp index 690aa997a..de18566be 100644 --- a/include/boost/decimal/detail/int128/detail/clz.hpp +++ b/include/boost/decimal/detail/int128/detail/clz.hpp @@ -18,7 +18,10 @@ namespace boost { namespace int128 { namespace detail { -namespace impl { +// The whole impl namespace is exported when building the module for testing so +// the low-level bit helpers can be exercised directly; it is an ordinary +// namespace in every other build. +BOOST_int128EST_EXPORT namespace impl { #if !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) @@ -119,7 +122,7 @@ constexpr int countl_impl(unsigned long long x) noexcept return x ? __builtin_clzll(x) : std::numeric_limits::digits; } -#elif (defined(_M_AMD64) || defined(_M_ARM64)) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) +#elif (defined(_M_AMD64) || defined(_M_ARM64)) && !defined(__GNUC__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) constexpr int countl_impl(std::uint32_t x) noexcept { @@ -163,7 +166,7 @@ constexpr int countl_impl(std::uint64_t x) noexcept } } -#elif defined(_M_IX86) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) +#elif defined(_M_IX86) && !defined(__GNUC__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) constexpr int countl_impl(std::uint32_t x) noexcept { diff --git a/include/boost/decimal/detail/int128/detail/common_div.hpp b/include/boost/decimal/detail/int128/detail/common_div.hpp index d5d5c828b..27894b678 100644 --- a/include/boost/decimal/detail/int128/detail/common_div.hpp +++ b/include/boost/decimal/detail/int128/detail/common_div.hpp @@ -7,6 +7,7 @@ #include #include +#include #ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE @@ -67,6 +68,242 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE quotient.low |= (remainder / rhs) & UINT32_MAX; } +// Portable 128-bit by 64-bit unsigned division producing a 64-bit quotient and remainder. +// This is the classic Hacker's Delight divlu (two 32-bit "digit" steps over 64-bit words). +// Precondition: u1 < d so the quotient is guaranteed to fit in 64 bits. It is constexpr-safe +// and serves as the fallback for udiv_2by1 on every target without a hardware 128/64 divide. +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr std::uint64_t divlu(std::uint64_t u1, std::uint64_t u0, std::uint64_t d, std::uint64_t& r) noexcept +{ + constexpr std::uint64_t b {UINT64_C(1) << 32U}; // Number base (2^32) + + BOOST_DECIMAL_DETAIL_INT128_ASSUME(u1 < d); // LCOV_EXCL_LINE + + // D.1: normalize so that the divisor's most significant bit is set + const auto s {countl_zero(d)}; + d <<= s; + + const auto vn1 {d >> 32U}; + const auto vn0 {d & UINT32_MAX}; + + // Shift the dividend left by s. The (64 - s) shift is undefined when s == 0, so guard it. + const auto un32 {s == 0 ? u1 : ((u1 << s) | (u0 >> (64 - s)))}; + const auto un10 {u0 << s}; + + const auto un1 {un10 >> 32U}; + const auto un0 {un10 & UINT32_MAX}; + + // First quotient digit + auto q1 {un32 / vn1}; + auto rhat {un32 - (q1 * vn1)}; + + while (q1 >= b || (q1 * vn0) > ((b * rhat) + un1)) + { + --q1; + rhat += vn1; + if (rhat >= b) + { + break; + } + } + + const auto un21 {(un32 * b) + un1 - (q1 * d)}; + + // Second quotient digit + auto q0 {un21 / vn1}; + rhat = un21 - (q0 * vn1); + + while (q0 >= b || (q0 * vn0) > ((b * rhat) + un0)) + { + --q0; + rhat += vn1; + if (rhat >= b) + { + break; + } + } + + // The remainder is shifted back down by the normalization amount + r = ((un21 * b) + un0 - (q0 * d)) >> s; + return (q1 * b) + q0; +} + +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ) + +// Inline asm cannot appear in a constexpr function body before C++20, so the x86-64 DIV +// instruction is wrapped in a non-constexpr helper that udiv_2by1 only calls at runtime. +BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE std::uint64_t udiv_2by1_divq(const std::uint64_t u1, const std::uint64_t u0, const std::uint64_t d, std::uint64_t& r) noexcept +{ + std::uint64_t q {}; + __asm__("divq %[d]" : "=a"(q), "=d"(r) : [d] "r"(d), "a"(u0), "d"(u1) : "cc"); + return q; +} + +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ + +// Divides the 128-bit value (u1:u0) by d, returning a 64-bit quotient and the true remainder. +// Precondition: u1 < d. Mirrors common_mul.hpp::umul: a hardware instruction at runtime where +// one exists, and the portable divlu in constexpr evaluation and everywhere else. +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr std::uint64_t udiv_2by1(const std::uint64_t u1, const std::uint64_t u0, const std::uint64_t d, std::uint64_t& r) noexcept +{ + BOOST_DECIMAL_DETAIL_INT128_ASSUME(u1 < d); // LCOV_EXCL_LINE + + #if (defined(BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ) || (defined(_M_AMD64) && !defined(__GNUC__) && !defined(__clang__) && _MSC_VER >= 1920)) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + + if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(u1)) + { + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ) + + return udiv_2by1_divq(u1, u0, d, r); + + #else + + return _udiv128(u1, u0, d, &r); + + #endif + } + + #endif + + return divlu(u1, u0, d, r); +} + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4127) // Pre c++17 the if constexpr remainder part will hit this +#endif + +// Divides the 128-bit value (uh:ul) by the 128-bit divisor (vh:vl) where vh != 0. Because the +// divisor is >= 2^64 the quotient is guaranteed to fit in a single 64-bit word, which is +// returned. When need_remainder is true the 128-bit remainder is written to (rem_hi:rem_lo). +// +// This is one normalized quotient digit (Knuth Algorithm D specialized to a 2-word divisor). +// The top-limb estimate qhat (reusing udiv_2by1, a hardware divq on x86-64) is bounded by +// Knuth Theorem B to q <= qhat <= q + 2; the D3 refinement against d0 tightens it to q <= qhat +// <= q + 1, and the conditional add-back then corrects the remaining off-by-one. +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr std::uint64_t div3by2(const std::uint64_t uh, const std::uint64_t ul, + const std::uint64_t vh, const std::uint64_t vl, std::uint64_t& rem_hi, std::uint64_t& rem_lo) noexcept +{ + BOOST_DECIMAL_DETAIL_INT128_ASSUME(vh != 0); // LCOV_EXCL_LINE + + // D.1: normalize so the divisor's most significant bit is set + const auto s {countl_zero(vh)}; + const auto cs {64 - s}; + + std::uint64_t d1 {}; + std::uint64_t d0 {}; + std::uint64_t u2 {}; + std::uint64_t u1 {}; + std::uint64_t u0 {}; + + if (s == 0) + { + d1 = vh; + d0 = vl; + u2 = 0; + u1 = uh; + u0 = ul; + } + else + { + d1 = (vh << s) | (vl >> cs); + d0 = vl << s; + u2 = uh >> cs; + u1 = (uh << s) | (ul >> cs); + u0 = ul << s; + } + + BOOST_DECIMAL_DETAIL_INT128_ASSUME(u2 <= d1); // LCOV_EXCL_LINE + + // D.3: estimate the single quotient digit qhat = floor((u2:u1) / d1), clamped to 2^64 - 1. + // rhat is the remainder of that estimate. + std::uint64_t qhat {}; + std::uint64_t rhat {}; + bool rhat_overflow {false}; + if (u2 < d1) + { + qhat = udiv_2by1(u2, u1, d1, rhat); + } + else + { + // u2 == d1: floor((u2:u1)/d1) clamps to 2^64 - 1, leaving rhat == u1 + d1 (may carry). + qhat = UINT64_MAX; + rhat = u1 + d1; + rhat_overflow = rhat < u1; + } + + std::uint64_t qd0_hi {}; + auto qd0_lo {umul(qhat, d0, qd0_hi)}; + + // Refine qhat against d0 (Knuth D3). The top-limb estimate alone can exceed the true quotient + // by up to 2; this brings it down to at most one too large, which the add-back below corrects. + // At most two iterations run, and only while the running remainder rhat stays below 2^64. + if (!rhat_overflow) + { + while (qd0_hi > rhat || (qd0_hi == rhat && qd0_lo > u0)) + { + --qhat; + rhat += d1; + const auto rhat_carry {rhat < d1}; + qd0_lo = umul(qhat, d0, qd0_hi); + if (rhat_carry) + { + break; + } + } + } + + // D.4: multiply and subtract (u2:u1:u0) - qhat * (d1:d0). qd0 already holds qhat * d0. + std::uint64_t qd1_hi {}; + const auto qd1_lo {umul(qhat, d1, qd1_hi)}; + + const auto p0 {qd0_lo}; + const auto p1 {qd0_hi + qd1_lo}; + const auto p2 {qd1_hi + static_cast(p1 < qd0_hi)}; + + const auto r0 {u0 - p0}; + const auto borrow0 {static_cast(u0 < p0)}; + const auto t1 {u1 - p1}; + auto r1 {t1 - borrow0}; + const auto borrow1 {static_cast(u1 < p1) + static_cast(t1 < borrow0)}; + + // D.5/D.6: if the top limb borrowed, qhat was one too large. Correct it and add the divisor + // back into the remainder. The probability of this branch is small. + auto r0_final {r0}; + if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY((u2 < p2) || ((u2 - p2) < borrow1))) + { + --qhat; // LCOV_EXCL_LINE + const auto sum0 {r0 + d0}; // LCOV_EXCL_LINE + r0_final = sum0; // LCOV_EXCL_LINE + r1 = r1 + d1 + static_cast(sum0 < r0); // LCOV_EXCL_LINE + } + + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (need_remainder) + { + if (s == 0) + { + rem_hi = r1; + rem_lo = r0_final; + } + else + { + rem_lo = (r0_final >> s) | (r1 << cs); + rem_hi = r1 >> s; + } + } + else + { + static_cast(rem_hi); + static_cast(rem_lo); + } + + return qhat; +} + +#if defined(_MSC_VER) +# pragma warning(pop) +#endif + namespace impl { #if defined(_MSC_VER) @@ -92,46 +329,6 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE vn[0] = needs_shift ? (v[0] << s) : v[0]; } -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void unpack_v(std::uint32_t (&vn)[8], const std::uint32_t (&v)[v_size], - const bool needs_shift, const int s, const int complement_s, const std::integral_constant&) noexcept -{ - vn[7] = needs_shift ? ((v[7] << s) | (v[6] >> complement_s)) : v[7]; - vn[6] = needs_shift ? ((v[6] << s) | (v[5] >> complement_s)) : v[6]; - vn[5] = needs_shift ? ((v[5] << s) | (v[4] >> complement_s)) : v[5]; - vn[4] = needs_shift ? ((v[4] << s) | (v[3] >> complement_s)) : v[4]; - vn[3] = needs_shift ? ((v[3] << s) | (v[2] >> complement_s)) : v[3]; - vn[2] = needs_shift ? ((v[2] << s) | (v[1] >> complement_s)) : v[2]; - vn[1] = needs_shift ? ((v[1] << s) | (v[0] >> complement_s)) : v[1]; - vn[0] = needs_shift ? (v[0] << s) : v[0]; -} - -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void unpack_u(std::uint32_t (&un)[un_size], const std::uint32_t (&u)[u_size], - const bool needs_shift, const int s, const int complement_s, const std::integral_constant&) noexcept -{ - un[4] = needs_shift ? (u[3] >> complement_s) : 0; - un[3] = needs_shift ? ((u[3] << s) | (u[2] >> complement_s)) : u[3]; - un[2] = needs_shift ? ((u[2] << s) | (u[1] >> complement_s)) : u[2]; - un[1] = needs_shift ? ((u[1] << s) | (u[0] >> complement_s)) : u[1]; - un[0] = needs_shift ? (u[0] << s) : u[0]; -} - -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void unpack_u(std::uint32_t (&un)[un_size], const std::uint32_t (&u)[u_size], - const bool needs_shift, const int s, const int complement_s, const std::integral_constant&) noexcept -{ - un[8] = needs_shift ? (u[7] >> complement_s) : 0; - un[7] = needs_shift ? ((u[7] << s) | (u[6] >> complement_s)) : u[7]; - un[6] = needs_shift ? ((u[6] << s) | (u[5] >> complement_s)) : u[6]; - un[5] = needs_shift ? ((u[5] << s) | (u[4] >> complement_s)) : u[5]; - un[4] = needs_shift ? ((u[4] << s) | (u[3] >> complement_s)) : u[4]; - un[3] = needs_shift ? ((u[3] << s) | (u[2] >> complement_s)) : u[3]; - un[2] = needs_shift ? ((u[2] << s) | (u[1] >> complement_s)) : u[2]; - un[1] = needs_shift ? ((u[1] << s) | (u[0] >> complement_s)) : u[1]; - un[0] = needs_shift ? (u[0] << s) : u[0]; -} - // See: The Art of Computer Programming Volume 2 (Semi-numerical algorithms) section 4.3.1 // Algorithm D: Division of Non-negative integers template @@ -145,13 +342,16 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr void knuth_divide(std::uint32_ const bool needs_shift {s > 0}; // Create normalized versions of u and v - constexpr std::size_t un_size {u_size == 8 ? 9 : 5}; - constexpr std::size_t vn_size {v_size == 8 ? 8 : 4}; - std::uint32_t un[un_size] {}; - std::uint32_t vn[vn_size] {}; + std::uint32_t un[5] {}; + std::uint32_t vn[4] {}; - static_assert(v_size == 8 || v_size == 4 || v_size == 2, "Unknown size for denominator"); - unpack_u(un, u, needs_shift, s, complement_s, std::integral_constant{}); + un[4] = needs_shift ? (u[3] >> complement_s) : 0; + un[3] = needs_shift ? ((u[3] << s) | (u[2] >> complement_s)) : u[3]; + un[2] = needs_shift ? ((u[2] << s) | (u[1] >> complement_s)) : u[2]; + un[1] = needs_shift ? ((u[1] << s) | (u[0] >> complement_s)) : u[1]; + un[0] = needs_shift ? (u[0] << s) : u[0]; + + static_assert(v_size == 4 || v_size == 2, "Unknown size for denominator"); unpack_v(vn, v, needs_shift, s, complement_s, std::integral_constant{}); // D.2 @@ -296,451 +496,63 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE template BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr T from_words(const std::uint32_t (&words)[4]) noexcept { - using high_word_type = decltype(T{}.high); - const auto low {static_cast(words[0]) | (static_cast(words[1]) << 32)}; const auto high {static_cast(words[2]) | (static_cast(words[3]) << 32)}; - return {static_cast(high), low}; + return {static_cast>(high), low}; } -// HAS_FAST_DIV128 is true on platforms where we expose a hardware 128-by-64 -// division primitive: MSVC x64 via _udiv128, and GCC/Clang on x86-64 via -// inline divq. Both flavors share the same Moller-Granlund-style 2x2 division -// scaffolding below, parameterized over portable umul128/udiv128 helpers. -// -// AArch64 stays on the __udivti3 / Knuth-D path -- UDIV is already 7c/0.5 -// there and the inline-divq port is x86-only. CUDA device code is excluded -// because inline asm and _udiv128 are not available in that environment. -#if defined(_M_AMD64) && !defined(__GNUC__) && !defined(__clang__) && _MSC_VER >= 1920 -# define BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128 -#elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) \ - && (defined(__GNUC__) || defined(__clang__)) \ - && defined(__x86_64__) \ - && !defined(__CUDA_ARCH__) -# define BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128 -#endif - -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128 - -// MSVC's _addcarry_u64 takes std::uint64_t* (unsigned __int64*); GCC/Clang's -// _addcarry_u64 takes unsigned long long*, which on LP64 Linux is a distinct -// type from std::uint64_t (typedef'd to unsigned long there). Wrap the macros -// with helpers that accept std::uint64_t* and convert internally. -BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE unsigned char int128_addcarry_u64(unsigned char c, std::uint64_t x, std::uint64_t y, std::uint64_t* out) noexcept -{ -#if defined(_M_AMD64) && !defined(__GNUC__) && !defined(__clang__) - return BOOST_DECIMAL_DETAIL_INT128_ADD_CARRY(c, x, y, out); -#else - unsigned long long tmp {static_cast(*out)}; - const unsigned char result {BOOST_DECIMAL_DETAIL_INT128_ADD_CARRY(c, x, y, &tmp)}; - *out = static_cast(tmp); - return result; -#endif -} - -BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE unsigned char int128_subborrow_u64(unsigned char c, std::uint64_t x, std::uint64_t y, std::uint64_t* out) noexcept -{ -#if defined(_M_AMD64) && !defined(__GNUC__) && !defined(__clang__) - return BOOST_DECIMAL_DETAIL_INT128_SUB_BORROW(c, x, y, out); -#else - unsigned long long tmp {static_cast(*out)}; - const unsigned char result {BOOST_DECIMAL_DETAIL_INT128_SUB_BORROW(c, x, y, &tmp)}; - *out = static_cast(tmp); - return result; -#endif -} - -// 64x64 -> 128-bit unsigned multiply. MSVC uses the _umul128 intrinsic; -// GCC/Clang use the native __uint128_t multiply which the compiler lowers -// to mul/mulx on x86-64. Neither path is usable in a constant expression -// (callers gate via IS_CONSTANT_EVALUATED), so this is not constexpr. -BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE std::uint64_t int128_umul128_intrinsic(std::uint64_t a, std::uint64_t b, std::uint64_t* hi) noexcept -{ -#if defined(_M_AMD64) && !defined(__GNUC__) && !defined(__clang__) - return _umul128(a, b, hi); -#else - const __uint128_t prod {static_cast<__uint128_t>(a) * b}; - *hi = static_cast(prod >> 64); - return static_cast(prod); -#endif -} - -// 128-bit-by-64-bit unsigned divide. MSVC uses _udiv128; GCC/Clang use -// inline divq. Caller must guarantee `high < divisor` so divq does not -// raise #DE (the Moller-Granlund normalization in div_mod_intrinsic -// below ensures this). Not constexpr because inline asm and _udiv128 -// are runtime-only; div_mod_intrinsic gates its call with -// IS_CONSTANT_EVALUATED. -BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE std::uint64_t int128_udiv128_intrinsic(std::uint64_t high, std::uint64_t low, std::uint64_t divisor, std::uint64_t* remainder) noexcept -{ -#if defined(_M_AMD64) && !defined(__GNUC__) && !defined(__clang__) && _MSC_VER >= 1920 - return _udiv128(high, low, divisor, remainder); -#else - std::uint64_t q; - std::uint64_t r; - __asm__ ("divq %4" - : "=a"(q), "=d"(r) - : "0"(low), "1"(high), "r"(divisor) - : "cc"); - *remainder = r; - return q; -#endif -} - -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T div_mod_intrinsic(T dividend, T divisor, T& remainder) -{ - using high_word_type = decltype(T{}.high); - - // Skip normalization if divisor is already large enough - // use direct division and intrinsic - // This is only possible in the unsigned case. The check uses - // std::is_unsigned on the high-word type rather than - // std::numeric_limits::is_signed to avoid forcing implicit - // instantiation of std::numeric_limits from inside this header (the - // explicit specialization sits later in uint128_imp.hpp; instantiating - // first via numeric_limits here causes clang to reject the - // specialization as "explicit specialization after instantiation"). - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_unsigned::value) - { - constexpr auto divisor_lower_bound{UINT64_MAX >> 1}; - if (divisor.high >= divisor_lower_bound) - { - T quotient{}; - - quotient.low = static_cast(dividend.high / divisor.high); - - std::uint64_t product0_high{}; - auto product0_low{int128_umul128_intrinsic(quotient.low, divisor.low, &product0_high)}; - - std::uint64_t product1_high{}; - auto product1_low{int128_umul128_intrinsic(quotient.low, static_cast(divisor.high), &product1_high)}; - - T product{}; - product.low = product0_low; - std::uint64_t product_high_tmp {static_cast(product.high)}; - auto carry{int128_addcarry_u64(0, product0_high, product1_low, &product_high_tmp)}; - product.high = static_cast(product_high_tmp); - product1_high += static_cast(carry); - - if (product1_high > 0 || product > dividend) - { - --quotient.low; - - // Recalculate with adjusted quotient - product0_low = int128_umul128_intrinsic(quotient.low, divisor.low, &product0_high); - product1_low = int128_umul128_intrinsic(quotient.low, static_cast(divisor.high), &product1_high); - - product.low = product0_low; - product_high_tmp = static_cast(product.high); - carry = int128_addcarry_u64(0, product0_high, product1_low, &product_high_tmp); - product.high = static_cast(product_high_tmp); - product1_high += static_cast(carry); - } - - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR(needs_mod) - { - auto borrow{int128_subborrow_u64(0, dividend.low, product.low, &remainder.low)}; - std::uint64_t remainder_high_tmp {static_cast(remainder.high)}; - int128_subborrow_u64(borrow, static_cast(dividend.high), static_cast(product.high), &remainder_high_tmp); - remainder.high = static_cast(remainder_high_tmp); - } - - return quotient; - } - } - - const auto shift_amount {countl_zero(static_cast(divisor.high))}; - divisor <<= shift_amount; - - auto high_digit {static_cast(shift_amount == 0 ? 0 : dividend.high >> (64 - shift_amount))}; - dividend <<= shift_amount; - - // Initial quotient estimate - T quotient {}; - const bool high_digit_gte_divisor {high_digit >= static_cast(divisor.high)}; - quotient.high = high_digit_gte_divisor ? 1 : 0; - std::uint64_t remainder_estimate {}; - - quotient.low = int128_udiv128_intrinsic(high_digit_gte_divisor ? high_digit - divisor.high : high_digit, - dividend.high, static_cast(divisor.high), &remainder_estimate); - - // Bounded correction loop with early exit - // Typically 2 is the most number of corrections we need since this is only for 2x2 division - // Other cases have been filtered out well before we've made it this far - int correction_steps {}; - constexpr int max_corrections {2}; - - while (correction_steps < max_corrections) - { - T product{}; - std::uint64_t product_high_tmp {}; - product.low = int128_umul128_intrinsic(quotient.low, divisor.low, &product_high_tmp); - product.high = static_cast(product_high_tmp); - if (product <= T{static_cast(remainder_estimate), dividend.low}) - { - break; - } - - --quotient.low; - const auto sum {remainder_estimate + divisor.high}; - if (remainder_estimate > sum) - { - break; - } - remainder_estimate = sum; - - correction_steps++; - } - - // Final verification and adjustment - std::uint64_t product0_high{}; - auto product_low {int128_umul128_intrinsic(quotient.low, divisor.low, &product0_high)}; - auto borrow {int128_subborrow_u64(0, dividend.low, product_low, ÷nd.low)}; - - std::uint64_t product1_high{}; - product_low = int128_umul128_intrinsic(quotient.low, static_cast(divisor.high), &product1_high); - product1_high += static_cast(int128_addcarry_u64(0, product_low, product0_high, &product_low)); - - std::uint64_t dividend_high_tmp {static_cast(dividend.high)}; - borrow = int128_subborrow_u64(borrow, static_cast(dividend.high), product_low, ÷nd_high_tmp); - dividend.high = static_cast(dividend_high_tmp); - borrow = int128_subborrow_u64(borrow, high_digit, product1_high, &high_digit); - quotient.low -= static_cast(borrow); - - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (needs_mod) - { - if (borrow) - { - auto carry { int128_addcarry_u64(0, dividend.low, divisor.low, ÷nd.low) }; - std::uint64_t dividend_high_tmp2 {static_cast(dividend.high)}; - int128_addcarry_u64(carry, static_cast(dividend.high), static_cast(divisor.high), ÷nd_high_tmp2); - dividend.high = static_cast(dividend_high_tmp2); - } - - dividend >>= shift_amount; - remainder = dividend; - } - - return quotient; -} - -// Moller-Granlund 3/2 reciprocal division for u256 by uint128. -// -// Computes (q, r) such that u = q*d + r, where u is 256-bit (3 limbs effective), -// d is 128-bit (2 limbs, normalized so d.high MSB is set), and v is the -// 64-bit reciprocal of d. -// -// The algorithm requires `d` to be normalized (d.high >= 2^63) and the top -// 128 bits of `u` to be strictly less than `d` so the quotient fits in one -// limb. The d128 division flow satisfies both: the divisor is post- -// expand_significand, so it is in [10^33, 10^34) and shifts to fill 128 bits; -// the dividend top 128 bits after the same shift remain well under 2^127. -// -// The reciprocal v is from reciprocal_2by1 below and must be precomputed. -// References: Moller & Granlund, "Improved Division by Invariant Integers" -// (2010), Algorithm 4. -template -BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE std::uint64_t mg32_div_3by2( - U128 u_high, std::uint64_t u_low, U128 d, std::uint64_t v, U128& r) noexcept -{ - // q = u_high.high * v + u_high (a 128-bit fused-multiply-add). - // int128_umul128_intrinsic returns the low 64 bits and writes the high - // 64 bits through the out pointer. - std::uint64_t q_high{}; - std::uint64_t q_low {int128_umul128_intrinsic(u_high.high, v, &q_high)}; - // Add u_high to (q_high, q_low). Carry from low into high. - { - const std::uint64_t low_sum {q_low + u_high.low}; - const unsigned char carry_into_high {static_cast(low_sum < q_low ? 1U : 0U)}; - q_low = low_sum; - q_high += u_high.high + carry_into_high; - } - - // r1 = u_high.low - q_high * d.high (single 64-bit limb) - const std::uint64_t r1 {u_high.low - q_high * d.high}; - - // (t_high, t_low) = q_high * d.low (full 128-bit product) - std::uint64_t t_high{}; - const std::uint64_t t_low {int128_umul128_intrinsic(q_high, d.low, &t_high)}; - - // r = (r1, u_low) - (t_high, t_low) - d - std::uint64_t r_low {u_low}; - std::uint64_t r_high {r1}; - unsigned char borrow {int128_subborrow_u64(0, r_low, t_low, &r_low)}; - int128_subborrow_u64(borrow, r_high, t_high, &r_high); - borrow = int128_subborrow_u64(0, r_low, d.low, &r_low); - int128_subborrow_u64(borrow, r_high, d.high, &r_high); - - // q' = q_high + 1 - std::uint64_t quotient {q_high + 1U}; - - // First correction: if r_high >= q_low, q'-- and r += d. - if (r_high >= q_low) - { - --quotient; - const unsigned char carry {int128_addcarry_u64(0, r_low, d.low, &r_low)}; - int128_addcarry_u64(carry, r_high, d.high, &r_high); - } - - // Second correction (very rare): if r >= d, q'++ and r -= d. - const bool r_ge_d {r_high > d.high || (r_high == d.high && r_low >= d.low)}; - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(r_ge_d)) - { - ++quotient; - const unsigned char b2 {int128_subborrow_u64(0, r_low, d.low, &r_low)}; - int128_subborrow_u64(b2, r_high, d.high, &r_high); - } - - r.low = r_low; - r.high = static_cast(r_high); - return quotient; -} - -// Precompute the 64-bit reciprocal for a normalized 128-bit divisor. -// Returns v such that floor((2^192 - 1) / d) = 2^128 + v. -// Precondition: d.high has its MSB set (d >= 2^127). -// Reference: Moller & Granlund 2010, Algorithm 2. -template -BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE std::uint64_t mg32_reciprocal_2by1(U128 d) noexcept -{ - // v_1 = floor((2^128 - 1) / d.high) - 2^64, computed via one 128/64 divide. - // The trick: divq(UINT64_MAX - d.high, UINT64_MAX, d.high) sidesteps the - // overflow trap (the divq precondition that high < divisor) because - // d.high >= 2^63 guarantees UINT64_MAX - d.high < d.high. - std::uint64_t dummy{}; - std::uint64_t v {int128_udiv128_intrinsic(~d.high, UINT64_MAX, d.high, &dummy)}; - - // p = d.high * v + d.low (modular, low 64 bits only; the high part is - // implicit in the residue analysis below). - std::uint64_t p {d.high * v + d.low}; - - // Adjust if the addition wrapped (p < d.low). - if (p < d.low) - { - --v; - if (p >= d.high) - { - --v; - p -= d.high; - } - p -= d.high; - } - - // (t_high, t_low) = v * d.low. - // int128_umul128_intrinsic returns the low limb and writes the high - // limb through the out pointer. - std::uint64_t t_high{}; - const std::uint64_t t_low {int128_umul128_intrinsic(v, d.low, &t_high)}; - - // p += t_high (with overflow detection) - const std::uint64_t p_new {p + t_high}; - if (p_new < p) - { - // Overflow; adjust. - --v; - // If (p_new, t_low) >= d, decrement again. - if (p_new > d.high || (p_new == d.high && t_low >= d.low)) - { - --v; - } - } - return v; -} - -#endif - } // namespace impl // We only need to take the time to process the remainder in the modulo case // In the division case it is a waste of cycles +// +// 128/64 -> 128-bit quotient (and optional 64-bit remainder) by two-step long division. +// The leading 64/64 yields the high quotient word and a remainder r < rhs, which satisfies +// the udiv_2by1 precondition for the low quotient word. This covers every rhs (including +// rhs <= UINT32_MAX) through the single hardware-or-portable udiv_2by1 primitive. template BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void one_word_div(const T& lhs, const std::uint64_t rhs, T& quotient) noexcept { - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) - - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) - { - using high_word_type = decltype(T{}.high); - - quotient.high = static_cast(static_cast(lhs.high) / rhs); - auto remainder {static_cast(lhs.high) % rhs}; - quotient.low = impl::int128_udiv128_intrinsic(remainder, lhs.low, rhs, &remainder); - return; - } - - #endif - - if (rhs <= UINT32_MAX) - { - half_word_div(lhs, static_cast(rhs), quotient); - } - else - { - std::uint32_t u[4] {}; - std::uint32_t v[2] {}; - std::uint32_t q[4] {}; + using high_word_type = decltype(T{}.high); - const auto m {impl::to_words(lhs, u)}; - const auto n {impl::to_words(rhs, v)}; + BOOST_DECIMAL_DETAIL_INT128_ASSUME(rhs != 0); // LCOV_EXCL_LINE - impl::knuth_divide(u, m, v, n, q); + const auto u_high {static_cast(lhs.high)}; - quotient = impl::from_words(q); - } + quotient.high = static_cast(u_high / rhs); + auto r {u_high % rhs}; + quotient.low = udiv_2by1(r, lhs.low, rhs, r); } template BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void one_word_div(const T& lhs, const std::uint64_t rhs, T& quotient, T& remainder) noexcept { - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) - - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) - { - using high_word_type = decltype(T{}.high); - - quotient.high = static_cast(static_cast(lhs.high) / rhs); - remainder.low = static_cast(lhs.high) % rhs; - quotient.low = impl::int128_udiv128_intrinsic(remainder.low, lhs.low, rhs, &remainder.low); - return; - } - - #else - - if (rhs <= UINT32_MAX) - { - half_word_div(lhs, static_cast(rhs), quotient, remainder); - } - else - { - std::uint32_t u[4] {}; - std::uint32_t v[2] {}; - std::uint32_t q[4] {}; + using high_word_type = decltype(T{}.high); - const auto m {impl::to_words(lhs, u)}; - const auto n {impl::to_words(rhs, v)}; + BOOST_DECIMAL_DETAIL_INT128_ASSUME(rhs != 0); // LCOV_EXCL_LINE - impl::knuth_divide(u, m, v, n, q); + const auto u_high {static_cast(lhs.high)}; - quotient = impl::from_words(q); - remainder = impl::from_words(u); - } + quotient.high = static_cast(u_high / rhs); + auto r {u_high % rhs}; + quotient.low = udiv_2by1(r, lhs.low, rhs, r); - #endif + remainder.high = static_cast(0); + remainder.low = r; } template BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void one_word_div(const T& lhs, const std::uint32_t rhs, T& quotient, T& remainder) noexcept { - half_word_div(lhs, rhs, quotient, remainder); + one_word_div(lhs, static_cast(rhs), quotient, remainder); } template BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void one_word_div(const T& lhs, const std::uint32_t rhs, T& quotient) noexcept { - half_word_div(lhs, rhs, quotient); + one_word_div(lhs, static_cast(rhs), quotient); } #ifdef _MSC_VER @@ -754,30 +566,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE { BOOST_DECIMAL_DETAIL_INT128_ASSUME(divisor != static_cast(0)); - #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128 + std::uint64_t rem_hi {}; + std::uint64_t rem_lo {}; - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR(std::is_unsigned::value) - { - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(dividend)) - { - T remainder{}; - return impl::div_mod_intrinsic(dividend, divisor, remainder); - } - } - - #endif - - std::uint32_t u[4]{}; - std::uint32_t v[4]{}; - std::uint32_t q[4]{}; - - const auto m{ impl::to_words(dividend, u) }; - const auto n{ impl::to_words(divisor, v) }; - - impl::knuth_divide(u, m, v, n, q); - - return impl::from_words(q); + const auto q {div3by2(static_cast(dividend.high), dividend.low, + static_cast(divisor.high), divisor.low, rem_hi, rem_lo)}; + return T{static_cast>(0), q}; } template @@ -785,31 +580,15 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE { BOOST_DECIMAL_DETAIL_INT128_ASSUME(divisor != static_cast(0)); - #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128 - - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR(std::is_unsigned::value) - { - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(dividend)) - { - return impl::div_mod_intrinsic(dividend, divisor, remainder); - } - } - - - #endif - - std::uint32_t u[4]{}; - std::uint32_t v[4]{}; - std::uint32_t q[4]{}; - - const auto m{ impl::to_words(dividend, u) }; - const auto n{ impl::to_words(divisor, v) }; + std::uint64_t rem_hi {}; + std::uint64_t rem_lo {}; - impl::knuth_divide(u, m, v, n, q); + const auto q {div3by2(static_cast(dividend.high), dividend.low, + static_cast(divisor.high), divisor.low, rem_hi, rem_lo)}; - remainder = impl::from_words(u); + remainder = T{static_cast>(rem_hi), rem_lo}; - return impl::from_words(q); + return T{static_cast>(0), q}; } #ifdef _MSC_VER diff --git a/include/boost/decimal/detail/int128/detail/common_mul.hpp b/include/boost/decimal/detail/int128/detail/common_mul.hpp index 5aeac3b55..714cdb914 100644 --- a/include/boost/decimal/detail/int128/detail/common_mul.hpp +++ b/include/boost/decimal/detail/int128/detail/common_mul.hpp @@ -6,11 +6,11 @@ #define BOOST_DECIMAL_DETAIL_INT128_DETAIL_COMMON_MUL_HPP #include +#include #ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE #include -#include #endif @@ -18,85 +18,100 @@ namespace boost { namespace int128 { namespace detail { -// See: The Art of Computer Programming Volume 2 (Semi-numerical algorithms) section 4.3.1 -// Algorithm M: Multiplication of Non-negative integers -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr ReturnType knuth_multiply(const std::uint32_t (&u)[u_size], - const std::uint32_t (&v)[v_size]) noexcept +template +struct ctor_high_word { - using high_word_type = decltype(ReturnType{}.high); + using type = std::uint64_t; +}; - std::uint32_t w[u_size + v_size] {}; +template <> +struct ctor_high_word +{ + using type = std::int64_t; +}; - // M.1 - for (std::size_t j {}; j < v_size; ++j) - { - // M.2 - if (v[j] == 0) - { - w[j + u_size] = 0; - continue; - } - - // M.3 - std::uint64_t t {}; - for (std::size_t i {}; i < u_size; ++i) - { - // M.4 - t += static_cast(u[i]) * v[j] + w[i + j]; - w[i + j] = static_cast(t); - t >>= 32u; - } - - // M.5 - w[j + u_size] = static_cast(t); - } +template +using ctor_high_word_t = typename ctor_high_word::type; + +// High 64 bits of the 64x64 -> 128 product, computed with four 32-bit partial products +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr std::uint64_t umulh_generic(const std::uint64_t a, const std::uint64_t b) noexcept +{ + const std::uint64_t a_lo {a & UINT32_MAX}; + const std::uint64_t a_hi {a >> 32U}; + const std::uint64_t b_lo {b & UINT32_MAX}; + const std::uint64_t b_hi {b >> 32U}; - const auto low {static_cast(w[0]) | (static_cast(w[1]) << 32)}; - const auto high {static_cast(w[2]) | (static_cast(w[3]) << 32)}; + const std::uint64_t lo_lo {a_lo * b_lo}; + const std::uint64_t hi_lo {a_hi * b_lo}; + const std::uint64_t lo_hi {a_lo * b_hi}; + const std::uint64_t hi_hi {a_hi * b_hi}; - return {static_cast(high), low}; + const std::uint64_t cross {(lo_lo >> 32U) + (hi_lo & UINT32_MAX) + (lo_hi & UINT32_MAX)}; + + return hi_hi + (hi_lo >> 32U) + (lo_hi >> 32U) + (cross >> 32U); } -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void to_words(const T& x, std::uint32_t (&words)[4]) noexcept +// Full 64x64 -> 128 product +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr std::uint64_t umul(const std::uint64_t a, const std::uint64_t b, std::uint64_t& hi) noexcept { #ifndef BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) + if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(a)) { - std::memcpy(&words, &x, sizeof(T)); - return; + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) + + const detail::builtin_u128 product {static_cast(a) * static_cast(b)}; + hi = static_cast(product >> 64U); + return static_cast(product); + + #elif defined(_M_AMD64) && !defined(__GNUC__) && !defined(__CUDA_ARCH__) && !defined(__SYCL_DEVICE_ONLY__) + + return _umul128(a, b, &hi); + + #elif defined(_M_ARM64) && !defined(__GNUC__) && !defined(__CUDA_ARCH__) && !defined(__SYCL_DEVICE_ONLY__) + + hi = __umulh(a, b); + return a * b; + + #endif } #endif - words[0] = static_cast(x.low & UINT32_MAX); // LCOV_EXCL_LINE - words[1] = static_cast(x.low >> 32); // LCOV_EXCL_LINE - words[2] = static_cast(static_cast(x.high) & UINT32_MAX); // LCOV_EXCL_LINE - words[3] = static_cast(static_cast(x.high) >> 32); // LCOV_EXCL_LINE + hi = umulh_generic(a, b); + return a * b; } - -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void to_words(const std::uint64_t x, std::uint32_t (&words)[2]) noexcept +// Low 128 bits of a 128x128 product +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr ReturnType low_word_mul(const T& lhs, const T& rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION + std::uint64_t result_high {}; + const std::uint64_t result_low {umul(lhs.low, rhs.low, result_high)}; - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) - { - std::memcpy(&words, &x, sizeof(std::uint64_t)); - return; - } + result_high += lhs.low * static_cast(rhs.high); + result_high += static_cast(lhs.high) * rhs.low; - #endif + return ReturnType{static_cast>(result_high), result_low}; +} + +// Low 128 bits of a 128x64 product +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr ReturnType low_word_mul(const T& lhs, const std::uint64_t rhs) noexcept +{ + std::uint64_t result_high {}; + const std::uint64_t result_low {umul(lhs.low, rhs, result_high)}; + + result_high += static_cast(lhs.high) * rhs; - words[0] = static_cast(x & UINT32_MAX); // LCOV_EXCL_LINE - words[1] = static_cast(x >> 32); // LCOV_EXCL_LINE + return ReturnType{static_cast>(result_high), result_low}; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr void to_words(const std::uint32_t x, std::uint32_t (&words)[1]) noexcept +// Low 128 bits of a 128x32 product +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr ReturnType low_word_mul(const T& lhs, const std::uint32_t rhs) noexcept { - words[0] = x; + return low_word_mul(lhs, static_cast(rhs)); } } // namespace detail diff --git a/include/boost/decimal/detail/int128/detail/config.hpp b/include/boost/decimal/detail/int128/detail/config.hpp index 6118b06be..979ee0ce6 100644 --- a/include/boost/decimal/detail/int128/detail/config.hpp +++ b/include/boost/decimal/detail/int128/detail/config.hpp @@ -5,12 +5,27 @@ #ifndef BOOST_DECIMAL_DETAIL_INT128_DETAIL_CONFIG_HPP #define BOOST_DECIMAL_DETAIL_INT128_DETAIL_CONFIG_HPP -#if defined(BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION) && !defined(BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE) -# define BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE +// A handful of detail-namespace entities are exercised directly by the module +// test suite. BOOST_int128EST_EXPORT exports them only when the module is built +// for testing (BOOST_DECIMAL_DETAIL_INT128_EXPORT_TESTING), so the normal module API stays limited +// to the public interface. It expands to nothing in ordinary (header) builds. +#if defined(BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE) && defined(BOOST_DECIMAL_DETAIL_INT128_EXPORT_TESTING) +# define BOOST_int128EST_EXPORT export +#else +# define BOOST_int128EST_EXPORT +#endif + +// The SYCL device target (spir64) has no native 128-bit integer, so force the portable +// code path on the device pass. This mirrors a user-supplied BOOST_DECIMAL_DETAIL_INT128_NO_BUILTIN_INT128 +// and keeps host/device selection consistent even though __x86_64__ stays defined on device. +#if defined(__SYCL_DEVICE_ONLY__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_BUILTIN_INT128) +# define BOOST_DECIMAL_DETAIL_INT128_NO_BUILTIN_INT128 #endif -// Use 128-bit integers -#if defined(BOOST_HAS_INT128) || (defined(__SIZEOF_INT128__) && !defined(_MSC_VER)) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_BUILTIN_INT128) +// Use 128-bit integers. +// The SYCL device target (spir64) has no native 128-bit integer, so on the device pass +// we fall back to the portable path (the same one used on platforms without __int128). +#if (defined(BOOST_HAS_INT128) || (defined(__SIZEOF_INT128__) && !defined(_MSC_VER))) && !defined(__SYCL_DEVICE_ONLY__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_BUILTIN_INT128) #define BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -20,24 +35,31 @@ namespace boost { namespace int128 { namespace detail { +// A module consumer receives these aliases from the import, so only declare them +// in ordinary builds and in the module interface unit itself; declaring them again +// in a consumer would give a second, distinct type and break overload resolution. +#if !defined(BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE) || defined(BOOST_DECIMAL_DETAIL_INT128_INTERFACE_UNIT) + // Avoids pedantic warnings #ifdef __GNUC__ -__extension__ using builtin_i128 = __int128 ; -__extension__ using builtin_u128 = unsigned __int128 ; +BOOST_int128EST_EXPORT __extension__ using builtin_i128 = __int128 ; +BOOST_int128EST_EXPORT __extension__ using builtin_u128 = unsigned __int128 ; #else -using builtin_i128 = __int128 ; -using builtin_u128 = unsigned __int128; +BOOST_int128EST_EXPORT using builtin_i128 = __int128 ; +BOOST_int128EST_EXPORT using builtin_u128 = unsigned __int128; #endif +#endif // declare builtin aliases + } // namespace detail } // namespace int128 } // namespace boost -#elif __has_include(<__msvc_int128.hpp>) && _MSVC_LANG >= 202002L +#elif __has_include(<__msvc_int128.hpp>) && _MSVC_LANG >= 202002L && !defined(__SYCL_DEVICE_ONLY__) #ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE #include <__msvc_int128.hpp> @@ -45,14 +67,23 @@ using builtin_u128 = unsigned __int128; #define BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 +#if _MSC_VER >= 1945 +#define BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR constexpr +#else #define BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR inline +#endif namespace boost { namespace int128 { namespace detail { -using builtin_i128 = std::_Signed128; -using builtin_u128 = std::_Unsigned128; +// See the note above: skip the re-declaration in a module consumer. +#if !defined(BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE) || defined(BOOST_DECIMAL_DETAIL_INT128_INTERFACE_UNIT) + +BOOST_int128EST_EXPORT using builtin_i128 = std::_Signed128; +BOOST_int128EST_EXPORT using builtin_u128 = std::_Unsigned128; + +#endif } // namespace detail } // namespace int128 @@ -126,6 +157,10 @@ using builtin_u128 = std::_Unsigned128; # define BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE inline #endif +// MinGW defines the MSVC platform macros (_M_AMD64, _M_IX86, _M_ARM64) for source +// compatibility, but it provides the GNU intrinsics rather than the MSVC ones. Every +// guard selecting an MSVC-only intrinsic (__shiftleft128, _umul128, __umulh, _BitScan*, +// __popcnt*, ...) therefore has to exclude GNU-mode compilers with !defined(__GNUC__). #ifdef __x86_64__ #ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE @@ -169,6 +204,12 @@ using builtin_u128 = std::_Unsigned128; #endif // Platform macros +// Hardware 128-bit by 64-bit unsigned division via the x86-64 DIV instruction +// Excluded on the CUDA and SYCL device passes (the device target is not x86-64) +#if defined(__x86_64__) && (defined(__GNUC__) || defined(__clang__)) && !defined(_MSC_VER) && !defined(__CUDA_ARCH__) && !defined(__SYCL_DEVICE_ONLY__) +# define BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ +#endif + // The builtin is only constexpr from clang-7 or GCC-10 #ifdef __has_builtin # if __has_builtin(__builtin_sub_overflow) && ((defined(__clang__) && __clang_major__ >= 7) || (defined(__GNUC__) && __GNUC__ >= 10)) @@ -285,10 +326,26 @@ using builtin_u128 = std::_Unsigned128; # endif #endif +// GPU device support. CUDA is auto-detected via __CUDACC__ (opt-in with +// BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA). SYCL is fully opt-in via BOOST_DECIMAL_DETAIL_INT128_ENABLE_SYCL; +// must be included before so SYCL_EXTERNAL exists. #if defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA) -# define BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE __host__ __device__ -#else -# define BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE +# define BOOST_DECIMAL_DETAIL_INT128_CUDA_ENABLED __host__ __device__ +# define BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT +#elif defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_SYCL) +# define BOOST_DECIMAL_DETAIL_INT128_SYCL_ENABLED SYCL_EXTERNAL +# define BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT #endif +#ifndef BOOST_DECIMAL_DETAIL_INT128_CUDA_ENABLED +# define BOOST_DECIMAL_DETAIL_INT128_CUDA_ENABLED +#endif +#ifndef BOOST_DECIMAL_DETAIL_INT128_SYCL_ENABLED +# define BOOST_DECIMAL_DETAIL_INT128_SYCL_ENABLED +#endif + +// Exactly one sub-macro is ever non-empty; expands to "__host__ __device__" (CUDA), +// "SYCL_EXTERNAL" (SYCL), or nothing (host). +#define BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_CUDA_ENABLED BOOST_DECIMAL_DETAIL_INT128_SYCL_ENABLED + #endif // BOOST_DECIMAL_DETAIL_INT128_DETAIL_CONFIG_HPP diff --git a/include/boost/decimal/detail/int128/detail/constants.hpp b/include/boost/decimal/detail/int128/detail/constants.hpp index 7299ed16f..9fbc87e26 100644 --- a/include/boost/decimal/detail/int128/detail/constants.hpp +++ b/include/boost/decimal/detail/int128/detail/constants.hpp @@ -19,7 +19,7 @@ namespace detail { BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR std::uint64_t low_word_mask {(std::numeric_limits::max)()}; template -BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR T offset_value_v = static_cast((std::numeric_limits::max)()); +BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR T offset_value_v = static_cast(18446744073709551616.0); // UINT64_MAX as double } // namespace detail } // namespace int128 diff --git a/include/boost/decimal/detail/int128/detail/conversions.hpp b/include/boost/decimal/detail/int128/detail/conversions.hpp index db2f2df8f..f3c3de319 100644 --- a/include/boost/decimal/detail/int128/detail/conversions.hpp +++ b/include/boost/decimal/detail/int128/detail/conversions.hpp @@ -16,7 +16,7 @@ namespace detail { template struct valid_overload { - static constexpr bool value = std::is_same::value || std::is_same::value; + static constexpr bool value = std::is_same::value || std::is_same::value; }; template @@ -26,32 +26,18 @@ BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_valid_overload_v = valid_ov #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t::int128_t(const uint128_t& v) noexcept : low {v.low}, high {static_cast(v.high)} {} +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128::int128(const uint128& v) noexcept : low {v.low}, high {v.high} {} -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t::uint128_t(const int128_t& v) noexcept : low {v.low}, high {static_cast(v.high)} {} +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128::uint128(const int128& v) noexcept : low {v.low}, high {v.high} {} #else -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t::int128_t(const uint128_t& v) noexcept : high {static_cast(v.high)}, low {v.low} {} +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128::int128(const uint128& v) noexcept : high {v.high}, low {v.low} {} -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t::uint128_t(const int128_t& v) noexcept : high {static_cast(v.high)}, low {v.low} {} +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128::uint128(const int128& v) noexcept : high {v.high}, low {v.low} {} #endif // BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE -//===================================== -// Conversion Operators -//===================================== - -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t::operator uint128_t() const noexcept -{ - return uint128_t{static_cast(this->high), static_cast(this->low)}; -} - -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t::operator int128_t() const noexcept -{ - return int128_t{static_cast(this->high), static_cast(this->low)}; -} - //===================================== // Comparison Operators //===================================== @@ -64,291 +50,288 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t::operator int128_t() template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const T lhs, const U rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - static_assert(std::is_same::value, "Sign Compare Error, cast one type to the other for this operation"); - static_cast(lhs); - static_cast(rhs); - return true; - - #else - - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) - { - if (lhs < T{0}) - { - return false; - } - - return static_cast(lhs) == rhs; - } - else - { - if (rhs < T{0}) - { - return false; - } - - return lhs == static_cast(rhs); - } - - #endif + return static_cast(lhs) == static_cast(rhs); } template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const T lhs, const U rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - static_assert(std::is_same::value, "Sign Compare Error, cast one type to the other for this operation"); - static_cast(lhs); - static_cast(rhs); - return true; - - #else - - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) - { - if (lhs < T{0}) - { - return true; - } - - return static_cast(lhs) != rhs; - } - else - { - if (rhs < T{0}) - { - return true; - } + return static_cast(lhs) != static_cast(rhs); +} - return lhs != static_cast(rhs); - } +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) < static_cast(rhs); +} - #endif +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) <= static_cast(rhs); } template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const T lhs, const U rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE + return static_cast(lhs) > static_cast(rhs); +} - static_assert(std::is_same::value, "Sign Compare Error, cast one type to the other for this operation"); - static_cast(lhs); - static_cast(rhs); - return true; +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) >= static_cast(rhs); +} - #else +#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) - { - if (lhs < T{0}) - { - return true; - } +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) <=> static_cast(rhs); +} - return static_cast(lhs) < rhs; - } - else - { - if (rhs < T{0}) - { - return false; - } +#endif - return lhs < static_cast(rhs); - } +//===================================== +// Arithmetic Operators +//===================================== - #endif +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator+(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) + static_cast(rhs); } template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator-(const T lhs, const U rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - static_assert(std::is_same::value, "Sign Compare Error, cast one type to the other for this operation"); - static_cast(lhs); - static_cast(rhs); - return true; + return static_cast(lhs) - static_cast(rhs); +} - #else +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator*(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) * static_cast(rhs); +} - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) - { - if (lhs < T{0}) - { - return true; - } +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) / static_cast(rhs); +} - return static_cast(lhs) <= rhs; - } - else - { - if (rhs < T{0}) - { - return false; - } +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) % static_cast(rhs); +} - return lhs <= static_cast(rhs); - } +//===================================== +// Cross-type Bitwise Operators +//===================================== - #endif +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator|(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) | static_cast(rhs); } template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator&(const T lhs, const U rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - static_assert(std::is_same::value, "Sign Compare Error, cast one type to the other for this operation"); - static_cast(lhs); - static_cast(rhs); - return true; + return static_cast(lhs) & static_cast(rhs); +} - #else +template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator^(const T lhs, const U rhs) noexcept +{ + return static_cast(lhs) ^ static_cast(rhs); +} - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) - { - if (lhs < T{0}) - { - return false; - } +//===================================== +// Cross-type Shift Operators +//===================================== - return static_cast(lhs) > rhs; - } - else - { - if (rhs < T{0}) - { - return true; - } +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator<<(const int128 lhs, const uint128 rhs) noexcept +{ + return lhs << static_cast(rhs); +} - return lhs > static_cast(rhs); - } +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator<<(const uint128 lhs, const int128 rhs) noexcept +{ + return lhs << static_cast(rhs); +} - #endif +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator>>(const int128 lhs, const uint128 rhs) noexcept +{ + return lhs >> static_cast(rhs); } -template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator>>(const uint128 lhs, const int128 rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE + return lhs >> static_cast(rhs); +} - static_assert(std::is_same::value, "Sign Compare Error, cast one type to the other for this operation"); - static_cast(lhs); - static_cast(rhs); - return true; +//===================================== +// int128 with builtin unsigned __int128 comparison operators +// +// These live here (not in int128_imp.hpp) +// to avoid C++20 rewritten-candidate ambiguity on MSVC +//===================================== - #else +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) - { - if (lhs < T{0}) - { - return false; - } +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) == rhs; +} - return static_cast(lhs) >= rhs; - } - else - { - if (rhs < T{0}) - { - return true; - } +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs == static_cast(rhs); +} - return lhs >= static_cast(rhs); - } +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) != rhs; +} - #endif +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs != static_cast(rhs); } -//===================================== -// Arithmetic Operators -//===================================== +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) < rhs; +} -template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_u128 lhs, const int128 rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION + return lhs < static_cast(rhs); +} - static_assert(std::is_same::value, "Sign Conversion Error, cast one type to the other for this operation"); - static_cast(rhs); - return static_cast(lhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) <= rhs; +} - #else +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs <= static_cast(rhs); +} - return static_cast(lhs) + static_cast(rhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) > rhs; +} - #endif +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs > static_cast(rhs); } -template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const int128 lhs, const detail::builtin_u128 rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION + return static_cast(lhs) >= rhs; +} - static_assert(std::is_same::value, "Sign Conversion Error, cast one type to the other for this operation"); - static_cast(rhs); - return static_cast(lhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs >= static_cast(rhs); +} - #else +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - return static_cast(lhs) - static_cast(rhs); +//===================================== +// int128 with builtin unsigned __int128 binary operators +//===================================== - #endif +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) + +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator|(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) | rhs; } -template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator|(const detail::builtin_u128 lhs, const int128 rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION + return lhs | static_cast(rhs); +} - static_assert(std::is_same::value, "Sign Conversion Error, cast one type to the other for this operation"); - static_cast(rhs); - return static_cast(lhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator&(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) & rhs; +} - #else +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator&(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs & static_cast(rhs); +} - return static_cast(lhs) * static_cast(rhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator^(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) ^ rhs; +} - #endif +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator^(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs ^ static_cast(rhs); } -template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator+(const int128 lhs, const detail::builtin_u128 rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION + return static_cast(lhs) + rhs; +} - static_assert(std::is_same::value, "Sign Conversion Error, cast one type to the other for this operation"); - static_cast(rhs); - return static_cast(lhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator+(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs + static_cast(rhs); +} - #else +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator-(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) - rhs; +} - return static_cast(lhs) / static_cast(rhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator-(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs - static_cast(rhs); +} - #endif +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator*(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) * rhs; } -template && detail::is_valid_overload_v && !std::is_same::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const T lhs, const U rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator*(const detail::builtin_u128 lhs, const int128 rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION + return lhs * static_cast(rhs); +} - static_assert(std::is_same::value, "Sign Conversion Error, cast one type to the other for this operation"); - static_cast(rhs); - return static_cast(lhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator/(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) / rhs; +} - #else +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator/(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs / static_cast(rhs); +} - return static_cast(lhs) % static_cast(rhs); +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator%(const int128 lhs, const detail::builtin_u128 rhs) noexcept +{ + return static_cast(lhs) % rhs; +} - #endif +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator%(const detail::builtin_u128 lhs, const int128 rhs) noexcept +{ + return lhs % static_cast(rhs); } +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 + #ifdef _MSC_VER #pragma warning(pop) #endif diff --git a/include/boost/decimal/detail/int128/detail/ctz.hpp b/include/boost/decimal/detail/int128/detail/ctz.hpp index f05baf027..6b0c3c944 100644 --- a/include/boost/decimal/detail/int128/detail/ctz.hpp +++ b/include/boost/decimal/detail/int128/detail/ctz.hpp @@ -109,7 +109,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int countr_impl(std::uint32_t #endif -#if (defined(_M_AMD64) || defined(_M_ARM64)) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && !BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_ctz) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) +#if (defined(_M_AMD64) || defined(_M_ARM64)) && !defined(__GNUC__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && !BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN(__builtin_ctz) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) constexpr int countr_impl(std::uint64_t x) noexcept { diff --git a/include/boost/decimal/detail/int128/detail/float_conversion.hpp b/include/boost/decimal/detail/int128/detail/float_conversion.hpp new file mode 100644 index 000000000..66d0656bc --- /dev/null +++ b/include/boost/decimal/detail/int128/detail/float_conversion.hpp @@ -0,0 +1,133 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_INT128_DETAIL_FLOAT_CONVERSION_HPP +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_FLOAT_CONVERSION_HPP + +#include +#include +#include + +#ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE + +#include +#include +#include + +#endif + +namespace boost { +namespace int128 { +namespace detail { + +// 2^exp as a T, exactly, for 0 <= exp <= 127. Splitting at 2^64 keeps both factors inside the +// range of an exact conversion from a 64-bit integer, so no rounding happens here +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T exact_power_of_two(const int exp) noexcept +{ + return exp < 64 ? static_cast(UINT64_C(1) << exp) + : static_cast(UINT64_C(1) << (exp - 64)) * offset_value_v; +} + +// A significand of 64 bits or more holds each word exactly, and holds the scaled high word +// exactly as well, so the addition is the only rounding and the two term form is already +// correctly rounded. x87 80-bit (64), IEEE binary128 (113) and IBM double-double (106) all +// take this path +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T unsigned_words_to_float_impl(const std::uint64_t high, const std::uint64_t low, + std::true_type) noexcept +{ + return static_cast(high) * offset_value_v + static_cast(low); +} + +// float and double cannot hold the high word or the sum exactly, so high * 2^64 + low rounds +// as many as three times and lands up to one ulp away from the correctly rounded result. +// Round the 128-bit value to exactly digits bits here instead, once, then apply an exact +// power of two. See the note on ties in the body +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T unsigned_words_to_float_impl(const std::uint64_t high, const std::uint64_t low, + std::false_type) noexcept +{ + // Anything below 2^64 is one conversion the compiler already rounds correctly + if (high == UINT64_C(0)) + { + return static_cast(low); + } + + constexpr int digits {std::numeric_limits::digits}; + constexpr int residue_bits {64 - digits}; + constexpr std::uint64_t residue_mask {(UINT64_C(1) << residue_bits) - UINT64_C(1)}; + constexpr std::uint64_t halfway {UINT64_C(1) << (residue_bits - 1)}; + + // Normalize so bit 127 of the pair is set, which puts the significand at the top of the + // high word. high is non-zero here, so the distance is always less than 64 + const auto shift {static_cast(countl_zero(high))}; + const auto norm_high {shift == 0U ? high : ((high << shift) | (low >> (64U - shift)))}; + const auto norm_low {shift == 0U ? low : (low << shift)}; + + const auto significand {norm_high >> residue_bits}; + const auto residue {norm_high & residue_mask}; + + // Round to nearest, ties to even. The discarded part is residue * 2^64 + norm_low, so it + // is above the halfway point when residue is, and it is exactly the halfway point only + // when residue equals halfway and every lower bit is clear + const bool round_up {residue > halfway || + (residue == halfway && (norm_low != UINT64_C(0) || (significand & UINT64_C(1)) != UINT64_C(0)))}; + + const auto rounded {significand + (round_up ? UINT64_C(1) : UINT64_C(0))}; + + // rounded holds digits bits, or digits + 1 when it carried, in which case it is a power of + // two. Either way the conversion and the scaling are exact, so the product is the value + // rounded exactly once. It overflows to infinity precisely when the correctly rounded + // result does, which is the required behavior for round to nearest + return static_cast(rounded) * exact_power_of_two(static_cast(64U - shift) + residue_bits); +} + +// Converts the 128-bit value (high, low) to T, correctly rounded to nearest with ties to even +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T unsigned_words_to_float(const std::uint64_t high, const std::uint64_t low) noexcept +{ + return unsigned_words_to_float_impl(high, low, + std::integral_constant::digits >= 64)>{}); +} + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr T signed_words_to_float(const std::int64_t high, const std::uint64_t low) noexcept +{ + if (high < 0) + { + // Two's complement magnitude of the full 128-bit value. + // INT128_MIN needs no special case since it yields high = 2^63, low = 0 + const auto abs_low {~low + UINT64_C(1)}; + const auto abs_high {~static_cast(high) + (abs_low == UINT64_C(0) ? UINT64_C(1) : UINT64_C(0))}; + + // The magnitude is rounded once and the negation is exact + return -unsigned_words_to_float(abs_high, abs_low); + } + + return unsigned_words_to_float(static_cast(high), low); +} + +// The other direction: a value in [0, 2^64) truncated toward zero into a 64-bit word. +// A cast straight to an unsigned type takes the compiler's unsigned conversion path, which +// subtracts 2^63 and puts the top bit back afterwards. Clang 7 emits that subtraction inside the +// window where it has already set the x87 control word to single precision, so an 80-bit long +// double loses every significand bit past the 24th. Splitting at 2^63 here keeps both conversions +// inside the signed range, where the conversion is one instruction on every compiler +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::uint64_t float_to_uint64(const T value) noexcept +{ + constexpr T two_63 {static_cast(UINT64_C(1) << 63)}; + + // value - two_63 is exact: it is a multiple of the ulp of value, and it is below 2^63 + return value < two_63 + ? static_cast(static_cast(value)) + : static_cast(static_cast(value - two_63)) | (UINT64_C(1) << 63); +} + +} // namespace detail +} // namespace int128 +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_INT128_DETAIL_FLOAT_CONVERSION_HPP diff --git a/include/boost/decimal/detail/int128/detail/fwd.hpp b/include/boost/decimal/detail/int128/detail/fwd.hpp index 5ae031a20..a60efb2fc 100644 --- a/include/boost/decimal/detail/int128/detail/fwd.hpp +++ b/include/boost/decimal/detail/int128/detail/fwd.hpp @@ -10,8 +10,12 @@ namespace boost { namespace int128 { -BOOST_DECIMAL_DETAIL_INT128_EXPORT struct uint128_t; -BOOST_DECIMAL_DETAIL_INT128_EXPORT struct int128_t; +BOOST_DECIMAL_DETAIL_INT128_EXPORT struct uint128; +BOOST_DECIMAL_DETAIL_INT128_EXPORT struct int128; + +// Decimal-local: Boost.Decimal refers to the types by their pre-rename names +BOOST_DECIMAL_DETAIL_INT128_EXPORT using uint128_t = uint128; +BOOST_DECIMAL_DETAIL_INT128_EXPORT using int128_t = int128; } // namespace int128 } // namespace boost diff --git a/include/boost/decimal/detail/int128/detail/int128_imp.hpp b/include/boost/decimal/detail/int128/detail/int128_imp.hpp index dd053e398..3aad6a374 100644 --- a/include/boost/decimal/detail/int128/detail/int128_imp.hpp +++ b/include/boost/decimal/detail/int128/detail/int128_imp.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE @@ -27,11 +28,11 @@ struct #if (defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128)) && !defined(_M_IX86) alignas(alignof(detail::builtin_i128)) #endif -int128_t +int128 { #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE std::uint64_t low {}; - std::int64_t high {}; + std::uint64_t high {}; #else #ifdef __GNUC__ @@ -39,7 +40,7 @@ int128_t # pragma GCC diagnostic ignored "-Wreorder" #endif - std::int64_t high {}; + std::uint64_t high {}; std::uint64_t low {}; #ifdef __GNUC__ @@ -49,288 +50,432 @@ int128_t #endif // Defaulted basic construction - constexpr int128_t() noexcept = default; - constexpr int128_t(const int128_t&) noexcept = default; - constexpr int128_t(int128_t&&) noexcept = default; - constexpr int128_t& operator=(const int128_t&) noexcept = default; - constexpr int128_t& operator=(int128_t&&) noexcept = default; + constexpr int128() noexcept = default; + constexpr int128(const int128&) noexcept = default; + constexpr int128(int128&&) noexcept = default; + constexpr int128& operator=(const int128&) noexcept = default; + constexpr int128& operator=(int128&&) noexcept = default; // Requires a conversion file to be implemented - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr int128_t(const uint128_t& v) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator uint128_t() const noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(const uint128& v) noexcept; // Construct from integral types #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const std::int64_t hi, const std::uint64_t lo) noexcept : low{lo}, high{hi} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(const std::int64_t hi, const std::uint64_t lo) noexcept : low{lo}, high{static_cast(hi)} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const SignedInteger v) noexcept : low {static_cast(v)}, high {v < 0 ? -1 : 0} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(const SignedInteger v) noexcept : low {static_cast(v)}, high {v < 0 ? ~UINT64_C(0) : UINT64_C(0)} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const UnsignedInteger v) noexcept : low {static_cast(v)}, high {} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(const UnsignedInteger v) noexcept : low {static_cast(v)}, high {} {} #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t(const detail::builtin_i128 v) noexcept : low {static_cast(v & static_cast(detail::low_word_mask))}, high {static_cast(v >> static_cast(64U))} {} - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t(const detail::builtin_u128 v) noexcept : low {static_cast(v & static_cast(detail::low_word_mask))}, high {static_cast(v >> static_cast(64U))} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128(const detail::builtin_i128 v) noexcept : low {static_cast(v & static_cast(detail::low_word_mask))}, high {static_cast(v >> static_cast(64U))} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128(const detail::builtin_u128 v) noexcept : low {static_cast(v & static_cast(detail::low_word_mask))}, high {static_cast(v >> static_cast(64U))} {} #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 #else // Big endian - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const std::int64_t hi, const std::uint64_t lo) noexcept : high{hi}, low{lo} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(const std::int64_t hi, const std::uint64_t lo) noexcept : high{static_cast(hi)}, low{lo} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const SignedInteger v) noexcept : high{v < 0 ? -1 : 0}, low{static_cast(v)} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(const SignedInteger v) noexcept : high{v < 0 ? ~UINT64_C(0) : UINT64_C(0)}, low{static_cast(v)} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const UnsignedInteger v) noexcept : high {}, low {static_cast(v)} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(const UnsignedInteger v) noexcept : high {}, low {static_cast(v)} {} - #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const detail::builtin_i128 v) noexcept : high {static_cast(v >> 64U)}, low {static_cast(v & detail::low_word_mask)} {} - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t(const detail::builtin_u128 v) noexcept : high {static_cast(v >> 64U)}, low {static_cast(v & detail::low_word_mask)} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128(const detail::builtin_i128 v) noexcept : high {static_cast(v >> 64U)}, low {static_cast(v & detail::low_word_mask)} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128(const detail::builtin_u128 v) noexcept : high {static_cast(v >> 64U)}, low {static_cast(v & detail::low_word_mask)} {} #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 #endif // BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + // Construct from floating-point types + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128(Float f) noexcept; + + // The high word read as a signed value. + // Every operation whose meaning depends on the sign of the value goes through + // this rather than reading high directly. + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::int64_t signed_high() const noexcept { return static_cast(high); } + // Integer Conversion operators BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator bool() const noexcept { return low || high; } template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator SignedInteger() const noexcept { return static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator SignedInteger() const noexcept { return static_cast(low); } + + #ifdef _MSC_VER + # pragma warning(push) + # pragma warning(disable:4127) + #endif template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator UnsignedInteger() const noexcept { return static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator UnsignedInteger() const noexcept + { + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) + { + return low || high; + } + else + { + return static_cast(low); + } + } + + #ifdef _MSC_VER + # pragma warning(pop) + #endif #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_i128() const noexcept { return static_cast(static_cast(high) << static_cast(64)) | static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_i128() const noexcept { return static_cast(static_cast(high) << static_cast(64)) | static_cast(low); } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_u128() const noexcept { return (static_cast(high) << static_cast(64)) | static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_u128() const noexcept { return (static_cast(high) << static_cast(64)) | static_cast(low); } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 // Conversion to float - // This is basically the same as ldexp(static_cast(high), 64) + static_cast(low), - // but can be constexpr at C++11 instead of C++26 - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator float() const noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator double() const noexcept; - - // Long double does not exist on device - #if !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) - explicit constexpr operator long double() const noexcept; + // Uses the builtin 128-bit conversion where one exists, and otherwise converts + // the unsigned magnitude as high * 2^64 + low before applying the sign. + // See detail/float_conversion.hpp for why the sign handling is required + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator float() const noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator double() const noexcept; + + // Long double does not exist on the CUDA or SYCL (spir64) device + #if !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) + constexpr operator long double() const noexcept; #endif // Compound Or template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator|=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator|=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator|=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator|=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator|=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator|=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound And template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator&=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator&=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator&=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator&=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator&=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator&=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound XOR template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator^=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator^=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator^=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator^=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator^=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator^=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Left Shift template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator<<=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator<<=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator<<=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator<<=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator<<=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator<<=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Right Shift template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator>>=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator>>=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator>>=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator>>=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator>>=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator>>=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Prefix and postfix increment - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator++() noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator++(int) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator++() noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator++(int) noexcept; // Prefix and postfix decrment - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator--() noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator--(int) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator--() noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator--(int) noexcept; // Compound Addition template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator+=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator+=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator+=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator+=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator+=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator+=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Subtraction template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator-=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator-=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator-=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator-=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator-=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator-=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Multiplication template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator*=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator*=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator*=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator*=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator*=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator*=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Division template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator/=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator/=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator/=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator/=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator/=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator/=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Modulo template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator%=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator%=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& operator%=(int128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator%=(int128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& operator%=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& operator%=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 + + // Compound assignment with floating point types. + // Matches the builtin: this value is converted to Float, the operation is applied in + // floating point, and the result is converted back, truncating toward zero. + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator+=(Float rhs) noexcept; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator-=(Float rhs) noexcept; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator*=(Float rhs) noexcept; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& operator/=(Float rhs) noexcept; + + // The builtin does not allow a floating point operand for these, so neither do we. + // Without these the implicit floating point constructor would silently truncate rhs + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128& operator%=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128& operator&=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128& operator|=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128& operator^=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128& operator<<=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128& operator>>=(Float rhs) = delete; }; -//===================================== -// Absolute Value function -//===================================== +namespace detail { -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t abs(int128_t value) noexcept +// Builds an int128 from the raw two's complement words +// Enables vectorization +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 from_bits(const std::uint64_t hi, const std::uint64_t lo) noexcept { - if (value.high < 0) - { - value.low = ~value.low + 1U; - value.high = static_cast(~static_cast(value.high) + static_cast(value.low == 0 ? 1 : 0)); - } - - return value; + int128 result {}; + result.high = hi; + result.low = lo; + return result; } +} // namespace detail + //===================================== // Float Conversion Operators //===================================== -// The most correct way to do this would be std::ldexp(static_cast(high), 64) + static_cast(low); -// Since std::ldexp is not constexpr until C++23 we can work around this by multiplying the high word -// by 0xFFFFFFFF in order to generally replicate what ldexp is doing in the constexpr context. -// We also avoid pulling in for the __float128 case where we would need ldexpq +// When the builtin 128-bit type exists we convert through it since the compiler +// runtime (__floattisf and friends) is correctly rounded. The portable fallback +// converts the unsigned magnitude and applies the sign; see detail/float_conversion.hpp +// for why the raw words can not be composed directly for negative values -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t::operator float() const noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128::operator float() const noexcept { - return static_cast(high) * detail::offset_value_v + static_cast(low); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) + + return static_cast(static_cast(*this)); + + #else + + return detail::signed_words_to_float(signed_high(), low); + + #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t::operator double() const noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128::operator double() const noexcept { - return static_cast(high) * detail::offset_value_v + static_cast(low); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) + + return static_cast(static_cast(*this)); + + #else + + return detail::signed_words_to_float(signed_high(), low); + + #endif } -#if !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) +#if !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) -constexpr int128_t::operator long double() const noexcept +constexpr int128::operator long double() const noexcept { - return static_cast(high) * detail::offset_value_v + static_cast(low); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) + + return static_cast(static_cast(*this)); + + #else + + return detail::signed_words_to_float(signed_high(), low); + + #endif } #endif +//===================================== +// Float Construction +//===================================== + +// Inverse of operator(Float). +// NaN -> 0; +// f >= 2^127 -> INT128_MAX; +// f < -2^127 -> INT128_MIN. +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128::int128(Float f) noexcept +{ + constexpr Float two_32 {static_cast(UINT64_C(1) << 32)}; + constexpr Float two_64 {two_32 * two_32}; + constexpr Float two_127 {two_64 * static_cast(UINT64_C(1) << 63)}; + + // NaN: leave default-initialized (zero). NaN compares false to everything, + // so neither >= 0 nor <= 0 holds. + if (!(f >= Float{0}) && !(f <= Float{0})) + { + return; + } + + if (f >= two_127) + { + high = UINT64_C(0x7FFFFFFFFFFFFFFF); + low = UINT64_MAX; + return; + } + + if (f <= -two_127) + { + high = UINT64_C(0x8000000000000000); + low = UINT64_C(0); + return; + } + + const bool negative {f < Float{0}}; + const Float abs_f {negative ? -f : f}; + + std::uint64_t h {detail::float_to_uint64(abs_f / two_64)}; + const Float remainder {abs_f - static_cast(h) * two_64}; + std::uint64_t l {detail::float_to_uint64(remainder)}; + + if (negative) + { + // Two's complement negation of (h, l): new_l = -l (with wraparound), + // new_h = ~h if a borrow occurred (l != 0), else ~h + 1. + const bool low_was_zero {l == UINT64_C(0)}; + l = UINT64_C(0) - l; + h = ~h + (low_was_zero ? UINT64_C(1) : UINT64_C(0)); + } + + high = h; + low = l; +} + //===================================== // Unary Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator+(const int128_t value) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator+(const int128 value) noexcept { return value; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator-(const int128_t value) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator-(const int128 value) noexcept { - return (value.low == 0) ? int128_t{static_cast(0ULL - static_cast(value.high)), 0} : - int128_t{~value.high, ~value.low + 1}; + // Spelled with the constructor rather than from_bits: clang folds the low word + // of the low == 0 arm away here, and loses that if the members are written. + return (value.low == 0) ? int128{static_cast(UINT64_C(0) - value.high), 0} : + int128{static_cast(~value.high), ~value.low + 1}; } //===================================== // Equality Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128_t lhs, const bool rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128 lhs, const bool rhs) noexcept { return lhs.high == 0 && lhs.low == static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const bool lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const bool lhs, const int128 rhs) noexcept { return rhs.high == 0 && rhs.low == static_cast(lhs); } @@ -345,7 +490,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const # pragma GCC diagnostic ignored "-Wsign-compare" #endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128 lhs, const int128 rhs) noexcept { // x64 and ARM64 like the values in opposite directions @@ -361,100 +506,48 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128 lhs, const SignedInteger rhs) noexcept { - return lhs.high == (rhs < 0 ? -1 : 0) && lhs.low == static_cast(rhs); + return lhs.high == (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)) && lhs.low == static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const SignedInteger lhs, const int128 rhs) noexcept { - return rhs.high == (lhs < 0 ? -1 : 0) && rhs.low == static_cast(lhs); + return rhs.high == (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)) && rhs.low == static_cast(lhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - return lhs.high == 0 && lhs.low == static_cast(rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - return rhs.high == 0 && rhs.low == static_cast(lhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs == static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) == rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs.high < 0 ? false : lhs == static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return rhs.high < 0 ? false : static_cast(lhs) == rhs; -} - -#else - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const int128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return lhs == static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) == rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 //===================================== // Inequality Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128 lhs, const int128 rhs) noexcept { // x64 and ARM64 like the values in opposite directions @@ -486,111 +579,59 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #endif } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128_t lhs, const bool rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128 lhs, const bool rhs) noexcept { return lhs.high != 0 || lhs.low != static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const bool lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const bool lhs, const int128 rhs) noexcept { return rhs.high != 0 || rhs.low != static_cast(lhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128 lhs, const SignedInteger rhs) noexcept { - return lhs.high != (rhs < 0 ? -1 : 0) || lhs.low != static_cast(rhs); + return lhs.high != (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)) || lhs.low != static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const SignedInteger lhs, const int128 rhs) noexcept { - return rhs.high != (lhs < 0 ? -1 : 0) || rhs.low != static_cast(lhs); + return rhs.high != (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)) || rhs.low != static_cast(lhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - return lhs.high != 0 || lhs.low != static_cast(rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - return rhs.high != 0 || rhs.low != static_cast(lhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs != static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) != rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs.high < 0 ? true : lhs != static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return rhs.high < 0 ? true : static_cast(lhs) != rhs; -} - -#else - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const int128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return lhs != static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) != rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 //===================================== // Less than Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const int128 lhs, const int128 rhs) noexcept { // On ARM macs only with the clang compiler is casting to __int128 uniformly better (and seemingly cost free) #if defined(__aarch64__) && defined(__APPLE__) && defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -601,7 +642,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) { - return lhs.high == rhs.high ? lhs.low < rhs.low : lhs.high < rhs.high; + return lhs.high == rhs.high ? lhs.low < rhs.low : lhs.signed_high() < rhs.signed_high(); } else { @@ -616,54 +657,32 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #else - return lhs.high == rhs.high ? lhs.low < rhs.low : lhs.high < rhs.high; + return lhs.high == rhs.high ? lhs.low < rhs.low : lhs.signed_high() < rhs.signed_high(); #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs.high < 0 ? true : lhs.low < static_cast(rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return lhs.signed_high() < 0 || (lhs.high == 0 && lhs.low < static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs.high < 0 ? false : static_cast(lhs) < rhs.low; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return rhs.signed_high() > 0 || (rhs.high == 0 && static_cast(lhs) < rhs.low); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const int128 lhs, const SignedInteger rhs) noexcept { - if (lhs.high < 0) + if (lhs.signed_high() < 0) { - return rhs >= 0 ? true : lhs < static_cast(rhs); + return rhs >= 0 ? true : lhs < static_cast(rhs); } - if (lhs.high > 0 || rhs < 0) + if (lhs.signed_high() > 0 || rhs < 0) { return false; } @@ -672,15 +691,15 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const int128_t } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const SignedInteger lhs, const int128 rhs) noexcept { - if (rhs.high < 0) + if (rhs.signed_high() < 0) { - return lhs >= 0 ? false : static_cast(lhs) < rhs; + return lhs >= 0 ? false : static_cast(lhs) < rhs; } // rhs is positive - if (rhs.high > 0 || lhs < 0) + if (rhs.signed_high() > 0 || lhs < 0) { return true; } @@ -690,53 +709,23 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const SignedInt #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs < static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) < rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs.high < 0 ? false : lhs < static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return rhs.high < 0 ? true : static_cast(lhs) < rhs; -} - -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const int128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return lhs < static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) < rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 //===================================== // Greater than Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const int128 lhs, const int128 rhs) noexcept { // On ARM macs only with the clang compiler is casting to __int128 uniformly better (and seemingly cost free) #if defined(__aarch64__) && defined(__APPLE__) && defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -747,7 +736,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) { - return lhs.high == rhs.high ? lhs.low > rhs.low : lhs.high > rhs.high; + return lhs.high == rhs.high ? lhs.low > rhs.low : lhs.signed_high() > rhs.signed_high(); } else { @@ -762,106 +751,54 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #else - return lhs.high == rhs.high ? lhs.low > rhs.low : lhs.high > rhs.high; + return lhs.high == rhs.high ? lhs.low > rhs.low : lhs.signed_high() > rhs.signed_high(); #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const int128 lhs, const SignedInteger rhs) noexcept { return !(lhs < rhs) && !(lhs == rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const SignedInteger lhs, const int128 rhs) noexcept { return !(lhs < rhs) && !(lhs == rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs.high > 0 ? true : lhs.low > static_cast(rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return lhs.signed_high() > 0 || (lhs.high == 0 && lhs.low > static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs.high < 0 ? true : static_cast(lhs) > rhs.low; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return rhs.signed_high() < 0 || (rhs.high == 0 && static_cast(lhs) > rhs.low); } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs > static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) > rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs.high < 0 ? false : lhs > static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return rhs.high < 0 ? true : static_cast(lhs) > rhs; + return lhs > static_cast(rhs); } -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const int128_t, const T) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) > rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 //===================================== // Less Equal Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const int128 lhs, const int128 rhs) noexcept { // On ARM macs only with the clang compiler is casting to __int128 uniformly better (and seemingly cost free) #if defined(__aarch64__) && defined(__APPLE__) && defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -872,7 +809,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) { - return lhs.high == rhs.high ? lhs.low <= rhs.low : lhs.high <= rhs.high; + return lhs.high == rhs.high ? lhs.low <= rhs.low : lhs.signed_high() <= rhs.signed_high(); } else { @@ -887,106 +824,54 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #else - return lhs.high == rhs.high ? lhs.low <= rhs.low : lhs.high <= rhs.high; + return lhs.high == rhs.high ? lhs.low <= rhs.low : lhs.signed_high() <= rhs.signed_high(); #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const int128 lhs, const SignedInteger rhs) noexcept { return !(lhs > rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const SignedInteger lhs, const int128 rhs) noexcept { return !(lhs > rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs.high < 0 ? true : lhs.low <= static_cast(rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return lhs.signed_high() < 0 || (lhs.high == 0 && lhs.low <= static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs.high < 0 ? false : static_cast(lhs) <= rhs.low; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return rhs.signed_high() > 0 || (rhs.high == 0 && static_cast(lhs) <= rhs.low); } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs <= static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) <= rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const int128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return lhs.high < 0 ? true : lhs <= static_cast(rhs); + return lhs <= static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - return rhs.high < 0 ? false : static_cast(lhs) <= rhs; + return static_cast(lhs) <= rhs; } -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const int128_t, const T) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const T, const int128_t) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; -} - -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 //===================================== // Greater Equal Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const int128 lhs, const int128 rhs) noexcept { // On ARM macs only with the clang compiler is casting to __int128 uniformly better (and seemingly cost free) #if defined(__aarch64__) && defined(__APPLE__) && defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -997,7 +882,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) { - return lhs.high == rhs.high ? lhs.low >= rhs.low : lhs.high >= rhs.high; + return lhs.high == rhs.high ? lhs.low >= rhs.low : lhs.signed_high() >= rhs.signed_high(); } else { @@ -1012,99 +897,47 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #else - return lhs.high == rhs.high ? lhs.low >= rhs.low : lhs.high >= rhs.high; + return lhs.high == rhs.high ? lhs.low >= rhs.low : lhs.signed_high() >= rhs.signed_high(); #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const int128 lhs, const SignedInteger rhs) noexcept { return !(lhs < rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const SignedInteger lhs, const int128 rhs) noexcept { return !(lhs < rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs.high < 0 ? false : lhs.low >= static_cast(rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return lhs.signed_high() > 0 || (lhs.high == 0 && lhs.low >= static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs.high < 0 ? true : static_cast(lhs) >= rhs.low; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return rhs.signed_high() < 0 || (rhs.high == 0 && static_cast(lhs) >= rhs.low); } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs >= static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) >= rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs.high < 0 ? false : lhs >= static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return rhs.high < 0 ? true : static_cast(lhs) >= rhs; -} - -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const int128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return lhs >= static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) >= rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 //===================================== @@ -1113,7 +946,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONS #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int128 lhs, const int128 rhs) noexcept { if (lhs < rhs) { @@ -1130,7 +963,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int128 lhs, const SignedInteger rhs) noexcept { if (lhs < rhs) { @@ -1147,7 +980,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const SignedInteger lhs, const int128 rhs) noexcept { if (lhs < rhs) { @@ -1164,10 +997,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - if (lhs < rhs) { return std::strong_ordering::less; @@ -1180,22 +1011,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< { return std::strong_ordering::greater; } - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return std::strong_ordering::less; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - if (lhs < rhs) { return std::strong_ordering::less; @@ -1208,15 +1028,6 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< { return std::strong_ordering::greater; } - - #else - - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return std::strong_ordering::less; - - #endif } #endif @@ -1225,107 +1036,56 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< // Not Operator //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator~(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator~(const int128 rhs) noexcept { - return {~rhs.high, ~rhs.low}; + return detail::from_bits(~rhs.high, ~rhs.low); } //===================================== // Or Operator //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator|(const int128 lhs, const int128 rhs) noexcept { - return {lhs.high | rhs.high, lhs.low | rhs.low}; + return detail::from_bits(lhs.high | rhs.high, lhs.low | rhs.low); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator|(const int128 lhs, const SignedInteger rhs) noexcept { - return {lhs.high | (rhs < 0 ? -1 : 0), lhs.low | static_cast(rhs)}; + return detail::from_bits(lhs.high | (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low | static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator|(const SignedInteger lhs, const int128 rhs) noexcept { - return {rhs.high | (lhs < 0 ? -1 : 0), static_cast(lhs) | rhs.low}; + return detail::from_bits(rhs.high | (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), static_cast(lhs) | rhs.low); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator|(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - - return {lhs.high, lhs.low | static_cast(rhs)}; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif -} + return detail::from_bits(lhs.high, lhs.low | static_cast(rhs)); +} BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const UnsignedInteger lhs, const int128_t rhs) noexcept -{ - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - - return {rhs.high, static_cast(lhs) | rhs.low}; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs | static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) | rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const int128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator|(const UnsignedInteger lhs, const int128 rhs) noexcept { - return lhs | static_cast(rhs); + return detail::from_bits(rhs.high, static_cast(lhs) | rhs.low); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) | rhs; -} - -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const int128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator|(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + return lhs | static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator|(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + return static_cast(lhs) | rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -1334,17 +1094,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator|(const T, co //===================================== template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator|=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator|=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif - - *this = *this | rhs; + *this = static_cast(*this | rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator|=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator|=(const int128 rhs) noexcept { *this = *this | rhs; return *this; @@ -1353,13 +1109,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator|= #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator|=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator|=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - - *this = *this | rhs; + *this = static_cast(*this | rhs); return *this; } @@ -1369,111 +1121,56 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator|=(co // And Operator //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator&(const int128 lhs, const int128 rhs) noexcept { - return {lhs.high & rhs.high, lhs.low & rhs.low}; + return detail::from_bits(lhs.high & rhs.high, lhs.low & rhs.low); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator&(const int128 lhs, const SignedInteger rhs) noexcept { - return {lhs.high & (rhs < 0 ? -1 : 0), lhs.low & static_cast(rhs)}; + return detail::from_bits(lhs.high & (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low & static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator&(const SignedInteger lhs, const int128 rhs) noexcept { - return {rhs.high & (lhs < 0 ? -1 : 0), static_cast(lhs) & rhs.low}; + return detail::from_bits(rhs.high & (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), static_cast(lhs) & rhs.low); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator&(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - - return {lhs.high, lhs.low & static_cast(rhs)}; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif + return {0, lhs.low & static_cast(rhs)}; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const UnsignedInteger lhs, const int128_t rhs) noexcept -{ - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - - return {rhs.high, static_cast(lhs) & rhs.low}; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs & static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) & rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs & static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator&(const UnsignedInteger lhs, const int128 rhs) noexcept { - return static_cast(lhs) & rhs; + return {0, static_cast(lhs) & rhs.low}; } -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const int128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator&(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + return lhs & static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator&(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator&(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + return static_cast(lhs) & rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator&=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator&=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - - *this = *this & rhs; + *this = static_cast(*this & rhs); return *this; } @@ -1484,17 +1181,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator&=(co //===================================== template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator&=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator&=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif - - *this = *this & rhs; + *this = static_cast(*this & rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator&=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator&=(const int128 rhs) noexcept { *this = *this & rhs; return *this; @@ -1504,98 +1197,47 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator&= // XOR Operator //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator^(const int128 lhs, const int128 rhs) noexcept { - return {lhs.high ^ rhs.high, lhs.low ^ rhs.low}; + return detail::from_bits(lhs.high ^ rhs.high, lhs.low ^ rhs.low); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator^(const int128 lhs, const SignedInteger rhs) noexcept { - return {lhs.high ^ (rhs < 0 ? -1 : 0), lhs.low ^ static_cast(rhs)}; + return detail::from_bits(lhs.high ^ (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low ^ static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator^(const SignedInteger lhs, const int128 rhs) noexcept { - return {rhs.high ^ (lhs < 0 ? -1 : 0), static_cast(lhs) ^ rhs.low}; + return detail::from_bits(rhs.high ^ (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), static_cast(lhs) ^ rhs.low); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator^(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - - return {lhs.high, lhs.low ^ static_cast(rhs)}; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return detail::from_bits(lhs.high, lhs.low ^ static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const UnsignedInteger lhs, const int128_t rhs) noexcept -{ - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - - return {rhs.high, static_cast(lhs) ^ rhs.low}; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return int128_t{}; - - #endif -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs ^ static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) ^ rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs ^ static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator^(const UnsignedInteger lhs, const int128 rhs) noexcept { - return static_cast(lhs) ^ rhs; + return detail::from_bits(rhs.high, static_cast(lhs) ^ rhs.low); } -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const int128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator^(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + return lhs ^ static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator^(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + return static_cast(lhs) ^ rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -1604,17 +1246,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator^(const T, co //===================================== template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator^=(Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator^=(Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif - - *this = *this ^ rhs; + *this = static_cast(*this ^ rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator^=(int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator^=(int128 rhs) noexcept { *this = *this ^ rhs; return *this; @@ -1623,13 +1261,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator^= #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator^=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator^=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - - *this = *this ^ rhs; + *this = static_cast(*this ^ rhs); return *this; } @@ -1642,25 +1276,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator^=(co namespace detail { template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t default_ls_impl(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 default_ls_impl(const int128 lhs, const Integer rhs) noexcept { static_assert(std::is_integral::value, "Only builtin types allowed"); - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (rhs < 0 || rhs >= 128) - { - return {0, 0}; - } - } - else - { - if (rhs >= 128) - { - return {0, 0}; - } - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators. In a + // constant expression the compiler diagnoses it; at runtime it is unspecified. if (rhs == 0) { return lhs; @@ -1668,44 +1290,30 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t default_ls_impl(const if (rhs == 64) { - return {static_cast(lhs.low), 0}; + return detail::from_bits(lhs.low, 0); } if (rhs > 64) { - return {static_cast(lhs.low << (rhs - 64)), 0}; + return detail::from_bits(lhs.low << (rhs - 64), 0); } // For shifts < 64 - std::uint64_t high_part = (static_cast(lhs.high) << rhs) | + std::uint64_t high_part = (lhs.high << rhs) | (lhs.low >> (64 - rhs)); - return { - static_cast(high_part), - lhs.low << rhs - }; + return detail::from_bits(high_part, lhs.low << rhs); } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_ls_impl(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128 intrinsic_ls_impl(const int128 lhs, const Integer rhs) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs >= 128 || rhs < 0)) - { - return {0, 0}; - } - } - else - { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs >= 128)) - { - return {0, 0}; - } - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators; delegate + // straight to the native type so we produce identical results. #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 + // Left-shifting a negative builtin_i128 is UB pre-C++20 # if defined(__aarch64__) #if defined(__GNUC__) && __GNUC__ >= 8 @@ -1713,12 +1321,12 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_ls_impl(const int128_ # pragma GCC diagnostic ignored "-Wclass-memaccess" #endif - builtin_i128 value; - std::memcpy(&value, &lhs, sizeof(builtin_i128)); + builtin_u128 value; + std::memcpy(&value, &lhs, sizeof(builtin_u128)); const auto res {value << rhs}; - int128_t return_value; - std::memcpy(&return_value, &res, sizeof(int128_t)); + int128 return_value; + std::memcpy(&return_value, &res, sizeof(int128)); return return_value; #if defined(__GNUC__) && __GNUC__ >= 8 @@ -1727,20 +1335,20 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_ls_impl(const int128_ # else - return static_cast(lhs) << rhs; + return int128{static_cast(lhs) << rhs}; # endif - #elif defined(_M_AMD64) + #elif defined(_M_AMD64) && !defined(__GNUC__) if (rhs >= 64) { - return {static_cast(lhs.low << (rhs - 64)), 0}; + return detail::from_bits(lhs.low << (rhs - 64), 0); } else { - int128_t res; - res.high = static_cast(__shiftleft128(lhs.low, static_cast(lhs.high), static_cast(rhs))); + int128 res; + res.high = __shiftleft128(lhs.low, lhs.high, static_cast(rhs)); res.low = lhs.low << rhs; return res; @@ -1754,22 +1362,19 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_ls_impl(const int128_ } if (rhs == 64) { - return {static_cast(lhs.low), 0}; + return detail::from_bits(lhs.low, 0); } if (rhs > 64) { - return {static_cast(lhs.low << (rhs - 64)), 0}; + return detail::from_bits(lhs.low << (rhs - 64), 0); } // For shifts < 64 - const auto high_part = (static_cast(lhs.high) << rhs) | + const auto high_part = (lhs.high << rhs) | (lhs.low >> (64 - rhs)); - return { - static_cast(high_part), - lhs.low << rhs - }; + return detail::from_bits(high_part, lhs.low << rhs); #endif } @@ -1777,7 +1382,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_ls_impl(const int128_ } // namespace detail BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator<<(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator<<(const int128 lhs, const Integer rhs) noexcept { #ifndef BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION @@ -1797,68 +1402,37 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator<<(const int1 #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator<<(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator<<(const int128 lhs, const int128 rhs) noexcept { - if (rhs.high != 0 || rhs.low >= 128) - { - return 0; - } - + // Out-of-range counts (negative, >= 128, or with the high word set) are + // undefined, matching the built-in operators; forward to the scalar overload. return lhs << rhs.low; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_u128 operator<<(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_u128 operator<<(const detail::builtin_u128 lhs, const int128 rhs) noexcept { - constexpr auto bit_width {sizeof(detail::builtin_u128) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return lhs << rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs << static_cast(rhs.low); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_i128 operator<<(const detail::builtin_i128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_i128 operator<<(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - constexpr auto bit_width {sizeof(detail::builtin_i128) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return lhs << rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs << static_cast(rhs.low); } #endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(SignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int operator<<(const SignedInteger lhs, const int128_t rhs) noexcept -{ - constexpr auto bit_width {sizeof(SignedInteger) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return static_cast(lhs) << rhs.low; -} +// A shift takes its value and its result type from the left operand after integral promotion, +// and only the count from the right, exactly as the builtin does -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr unsigned operator<<(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(Integer) * 8 <= 64), bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::promoted_t operator<<(const Integer lhs, const int128 rhs) noexcept { - constexpr auto bit_width {sizeof(UnsignedInteger) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return static_cast(lhs) << rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return static_cast>(lhs) << rhs.low; } #ifdef _MSC_VER @@ -1867,13 +1441,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr unsigned operator<<(const Unsi #endif // _MSC_VER template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator<<=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator<<=(const Integer rhs) noexcept { - *this = *this << rhs; + *this = static_cast(*this << rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator<<=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator<<=(const int128 rhs) noexcept { *this = *this << rhs; return *this; @@ -1882,13 +1456,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator<< #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator<<=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator<<=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - - *this = *this << rhs; + *this = static_cast(*this << rhs); return *this; } @@ -1905,23 +1475,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator<<=(c namespace detail { template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t default_rs_impl(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 default_rs_impl(const int128 lhs, const Integer rhs) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (rhs >= 128 || rhs < 0) - { - return lhs.high < 0 ? int128_t{-1, UINT64_MAX} : int128_t{0, 0}; - } - } - else - { - if (rhs >= 128) - { - return lhs.high < 0 ? int128_t{-1, UINT64_MAX} : int128_t{0, 0}; - } - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators. In a + // constant expression the compiler diagnoses it; at runtime it is unspecified. if (rhs == 0) { return lhs; @@ -1929,38 +1487,24 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t default_rs_impl(const if (rhs >= 64) { - return {lhs.high < 0 ? -1 : 0, static_cast(lhs.high >> (rhs - 64))}; + return detail::from_bits(lhs.signed_high() < 0 ? ~UINT64_C(0) : UINT64_C(0), + static_cast(lhs.signed_high() >> (rhs - 64))); } // For shifts < 64 - const auto high_to_low {static_cast(lhs.high) << (64 - rhs)}; + const auto high_to_low {lhs.high << (64 - rhs)}; const auto low_shifted {lhs.low >> rhs}; const auto low_part {high_to_low | low_shifted}; - return { - lhs.high >> rhs, - low_part - }; + return detail::from_bits(static_cast(lhs.signed_high() >> rhs), low_part); } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_rs_impl(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128 intrinsic_rs_impl(const int128 lhs, const Integer rhs) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (rhs >= 128 || rhs < 0) - { - return lhs.high < 0 ? int128_t{-1, UINT64_MAX} : int128_t{0, 0}; - } - } - else - { - if (rhs >= 128) - { - return lhs.high < 0 ? int128_t{-1, UINT64_MAX} : int128_t{0, 0}; - } - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators; delegate + // straight to the native type so we produce identical results. #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 # if defined(__aarch64__) @@ -1974,8 +1518,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_rs_impl(const int128_ std::memcpy(&value, &lhs, sizeof(builtin_i128)); const auto res {value >> rhs}; - int128_t return_value; - std::memcpy(&return_value, &res, sizeof(int128_t)); + int128 return_value; + std::memcpy(&return_value, &res, sizeof(int128)); return return_value; #if defined(__GNUC__) && __GNUC__ >= 8 @@ -1988,17 +1532,18 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_rs_impl(const int128_ # endif - #elif defined(_M_AMD64) + #elif defined(_M_AMD64) && !defined(__GNUC__) if (rhs >= 64) { - return {lhs.high < 0 ? -1 : 0, static_cast(lhs.high >> (rhs - 64))}; + return detail::from_bits(lhs.signed_high() < 0 ? ~UINT64_C(0) : UINT64_C(0), + static_cast(lhs.signed_high() >> (rhs - 64))); } else { - int128_t res; - res.low = __shiftright128(lhs.low, static_cast(lhs.high), static_cast(rhs)); - res.high = lhs.high >> rhs; + int128 res; + res.low = __shiftright128(lhs.low, lhs.high, static_cast(rhs)); + res.high = static_cast(lhs.signed_high() >> rhs); return res; } @@ -2012,18 +1557,16 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_rs_impl(const int128_ if (rhs >= 64) { - return {lhs.high < 0 ? -1 : 0, static_cast(lhs.high >> (rhs - 64))}; + return detail::from_bits(lhs.signed_high() < 0 ? ~UINT64_C(0) : UINT64_C(0), + static_cast(lhs.signed_high() >> (rhs - 64))); } // For shifts < 64 - const auto high_to_low {static_cast(lhs.high) << (64 - rhs)}; + const auto high_to_low {lhs.high << (64 - rhs)}; const auto low_shifted {lhs.low >> rhs}; const auto low_part {high_to_low | low_shifted}; - return { - lhs.high >> rhs, - low_part - }; + return detail::from_bits(static_cast(lhs.signed_high() >> rhs), low_part); #endif } @@ -2031,7 +1574,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128_t intrinsic_rs_impl(const int128_ } // namespace detail BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator>>(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator>>(const int128 lhs, const Integer rhs) noexcept { #ifndef BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION @@ -2051,68 +1594,37 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator>>(const int1 #endif } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator>>(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator>>(const int128 lhs, const int128 rhs) noexcept { - if (rhs.high != 0 || rhs.low >= 128) - { - return 0; - } - + // Out-of-range counts (negative, >= 128, or with the high word set) are + // undefined, matching the built-in operators; forward to the scalar overload. return lhs >> rhs.low; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_u128 operator>>(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_u128 operator>>(const detail::builtin_u128 lhs, const int128 rhs) noexcept { - constexpr auto bit_width {sizeof(detail::builtin_u128) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return lhs >> rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs >> static_cast(rhs.low); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_i128 operator>>(const detail::builtin_i128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_i128 operator>>(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - constexpr auto bit_width {sizeof(detail::builtin_i128) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return lhs >> rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs >> static_cast(rhs.low); } #endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(SignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int operator>>(const SignedInteger lhs, const int128_t rhs) noexcept -{ - constexpr auto bit_width {sizeof(SignedInteger) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return static_cast(lhs) >> rhs.low; -} +// A shift takes its value and its result type from the left operand after integral promotion, +// and only the count from the right, exactly as the builtin does -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr unsigned operator>>(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(Integer) * 8 <= 64), bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::promoted_t operator>>(const Integer lhs, const int128 rhs) noexcept { - constexpr auto bit_width {sizeof(UnsignedInteger) * 8}; - - if (rhs.high != 0 || rhs.low >= bit_width) - { - return 0; - } - - return static_cast(lhs) >> rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return static_cast>(lhs) >> rhs.low; } #ifdef _MSC_VER @@ -2121,13 +1633,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr unsigned operator>>(const Unsi #endif // _MSC_VER template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator>>=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator>>=(const Integer rhs) noexcept { - *this = *this >> rhs; + *this = static_cast(*this >> rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator>>=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator>>=(const int128 rhs) noexcept { *this = *this >> rhs; return *this; @@ -2136,13 +1648,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator>> #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator>>=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator>>=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - - *this = *this >> rhs; + *this = static_cast(*this >> rhs); return *this; } @@ -2156,7 +1664,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator>>=(c // Increment Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator++() noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator++() noexcept { if (++low == UINT64_C(0)) { @@ -2166,7 +1674,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator++ return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t int128_t::operator++(int) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 int128::operator++(int) noexcept { const auto temp {*this}; ++(*this); @@ -2177,7 +1685,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t int128_t::operator++( // Decrement Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator--() noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator--() noexcept { if (low-- == UINT64_C(0)) { @@ -2187,7 +1695,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator-- return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t int128_t::operator--(int) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 int128::operator--(int) noexcept { const auto temp {*this}; --(*this); @@ -2200,30 +1708,31 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t int128_t::operator--( namespace detail { -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t library_add(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 library_add(const int128 lhs, const int128 rhs) noexcept { const auto new_low {lhs.low + rhs.low}; - const auto new_high {static_cast(lhs.high) + - static_cast(rhs.high) + + const auto new_high {lhs.high + + rhs.high + static_cast(new_low < lhs.low)}; - return int128_t{static_cast(new_high), new_low}; + return detail::from_bits(new_high, new_low); } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t default_add(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 default_add(const int128 lhs, const int128 rhs) noexcept { #if (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__))) && !defined(_WIN32) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - return static_cast(static_cast(lhs) + static_cast(rhs)); + // Compute in the unsigned domain so that overflow wraps modulo 2^128 + return int128{static_cast(lhs) + static_cast(rhs)}; #elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN_ADD_OVERFLOW) std::uint64_t result_low {}; std::uint64_t result_high {}; - result_high = static_cast(lhs.high) + static_cast(rhs.high) + __builtin_add_overflow(lhs.low, rhs.low, &result_low); + result_high = lhs.high + rhs.high + __builtin_add_overflow(lhs.low, rhs.low, &result_low); - return int128_t{static_cast(result_high), result_low}; + return detail::from_bits(result_high, result_low); #elif defined(_M_AMD64) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) @@ -2233,9 +1742,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE } else { - int128_t result {}; + int128 result {}; const auto carry {BOOST_DECIMAL_DETAIL_INT128_ADD_CARRY(0, lhs.low, rhs.low, &result.low)}; - BOOST_DECIMAL_DETAIL_INT128_ADD_CARRY(carry, static_cast(lhs.high), static_cast(rhs.high), reinterpret_cast(&result.high)); + BOOST_DECIMAL_DETAIL_INT128_ADD_CARRY(carry, lhs.high, rhs.high, &result.high); return result; } @@ -2248,35 +1757,36 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t default_add(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 default_add(const int128 lhs, const Integer rhs) noexcept { const auto new_low {lhs.low + rhs}; - const auto new_high {static_cast(lhs.high) + static_cast(new_low < lhs.low)}; + const auto new_high {lhs.high + static_cast(new_low < lhs.low)}; - return int128_t{static_cast(new_high), new_low}; + return detail::from_bits(new_high, new_low); } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t library_sub(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 library_sub(const int128 lhs, const int128 rhs) noexcept { const auto new_low {lhs.low - rhs.low}; - const auto new_high {static_cast(lhs.high) - static_cast(rhs.high) - static_cast(lhs.low < rhs.low)}; + const auto new_high {lhs.high - rhs.high - static_cast(lhs.low < rhs.low)}; - return int128_t{static_cast(new_high), new_low}; + return detail::from_bits(new_high, new_low); } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t default_sub(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 default_sub(const int128 lhs, const int128 rhs) noexcept { #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN_SUB_OVERFLOW) && (!defined(__aarch64__) || defined(__APPLE__) || !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128)) && !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) // __builtin_sub_overflow is marked constexpr so we don't need if consteval handling std::uint64_t result_low {}; - const auto result_high {static_cast(lhs.high) - static_cast(rhs.high) - static_cast(__builtin_sub_overflow(lhs.low, rhs.low, &result_low))}; + const auto result_high {lhs.high - rhs.high - static_cast(__builtin_sub_overflow(lhs.low, rhs.low, &result_low))}; - return int128_t{static_cast(result_high), result_low}; + return detail::from_bits(result_high, result_low); - #elif defined(__aarch64__) && !defined(__APPLE__) + #elif defined(__aarch64__) && !defined(__APPLE__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - return static_cast(static_cast(lhs) - static_cast(rhs)); + // Unsigned wrap for consistent two's-complement semantics + return int128{static_cast(lhs) - static_cast(rhs)}; #elif defined(_M_AMD64) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) @@ -2286,9 +1796,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE } else { - int128_t result {}; + int128 result {}; const auto borrow {BOOST_DECIMAL_DETAIL_INT128_SUB_BORROW(0, lhs.low, rhs.low, &result.low)}; - BOOST_DECIMAL_DETAIL_INT128_SUB_BORROW(borrow, static_cast(lhs.high), static_cast(rhs.high), reinterpret_cast(&result.high)); + BOOST_DECIMAL_DETAIL_INT128_SUB_BORROW(borrow, lhs.high, rhs.high, &result.high); return result; } @@ -2301,11 +1811,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t default_sub(const int128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 default_sub(const int128 lhs, const Integer rhs) noexcept { const auto new_low {lhs.low - rhs}; - const auto new_high {static_cast(lhs.high) - static_cast(new_low > lhs.low)}; - return int128_t{static_cast(new_high), new_low}; + const auto new_high {lhs.high - static_cast(new_low > lhs.low)}; + return detail::from_bits(new_high, new_low); } } @@ -2314,14 +1824,14 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE // doing addition via subtraction is >10% faster in the benchmarks #if defined(__s390__) || defined(__s390x__) -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator+(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator+(const int128 lhs, const int128 rhs) noexcept { return detail::default_sub(lhs, -rhs); } #else -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator+(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator+(const int128 lhs, const int128 rhs) noexcept { return detail::default_add(lhs, rhs); } @@ -2329,107 +1839,64 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator+(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator+(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return detail::default_add(lhs, rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator+(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator+(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return detail::default_add(rhs, lhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator+(const int128_t lhs, const SignedInteger rhs) noexcept -{ - return rhs > 0 ? detail::default_add(lhs, rhs) : detail::default_sub(lhs, -rhs); -} +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4146) // Unary minus applied to unsigned type +#endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator+(const SignedInteger lhs, const int128_t rhs) noexcept -{ - return lhs > 0 ? detail::default_add(rhs, lhs) : detail::default_sub(rhs, -lhs); -} - -#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator+(const int128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator+(const int128 lhs, const SignedInteger rhs) noexcept { - return detail::default_add(lhs, static_cast(rhs)); + // Negate in the unsigned domain so INT64_MIN does not overflow (UBSAN) + return rhs < 0 ? detail::default_sub(lhs, -static_cast(rhs)) : + detail::default_add(lhs, static_cast(rhs)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator+(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator+(const SignedInteger lhs, const int128 rhs) noexcept { - return detail::default_add(rhs, static_cast(lhs)); + return lhs < 0 ? detail::default_sub(rhs, -static_cast(lhs)) : + detail::default_add(rhs, static_cast(lhs)); } -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator+(const int128_t, const T) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} +#ifdef _MSC_VER +# pragma warning(pop) +#endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator+(const T, const int128_t) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator+(const int128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator+(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return detail::default_add(lhs, static_cast(rhs)); + return detail::default_add(lhs, static_cast(rhs)); } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator+(const detail::builtin_i128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator+(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - return detail::default_add(rhs, static_cast(lhs)); + return detail::default_add(rhs, static_cast(lhs)); } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator+=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator+=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif - - *this = *this + rhs; + *this = static_cast(*this + rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator+=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator+=(const int128 rhs) noexcept { *this = *this + rhs; return *this; @@ -2438,9 +1905,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator+= #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator+=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator+=(const Integer rhs) noexcept { - *this = *this + rhs; + *this = static_cast(*this + rhs); return *this; } @@ -2450,113 +1917,58 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator+=(co // Subtraction Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator-(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator-(const int128 lhs, const int128 rhs) noexcept { return detail::default_sub(lhs, rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator-(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator-(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return detail::default_sub(lhs, rhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator-(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator-(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return detail::default_add(-rhs, lhs); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator-(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator-(const int128 lhs, const SignedInteger rhs) noexcept { - return detail::default_sub(lhs, static_cast(rhs)); + return detail::default_sub(lhs, static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator-(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator-(const SignedInteger lhs, const int128 rhs) noexcept { - return detail::default_sub(static_cast(lhs), rhs); + return detail::default_sub(static_cast(lhs), rhs); } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator-(const int128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator-(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return lhs - static_cast(rhs); + return lhs - static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator-(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator-(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - return static_cast(lhs) - rhs; + return static_cast(lhs) - rhs; } -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator-(const int128_t, const T) noexcept +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator-=(const Integer rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + *this = static_cast(*this - rhs); + return *this; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator-(const T, const int128_t) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} - -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator-(const int128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs - static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128_t operator-(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) - rhs; -} - -#endif - -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator-=(const Integer rhs) noexcept -{ - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif - - *this = *this - rhs; - return *this; -} - -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator-=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator-=(const int128 rhs) noexcept { *this = *this - rhs; return *this; @@ -2565,79 +1977,59 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator-= #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator-=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator-=(const Integer rhs) noexcept { - *this = *this - rhs; + *this = static_cast(*this - rhs); return *this; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 //===================================== -// Multiplication Operators +// Absolute Value function //===================================== -namespace detail { - -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t signed_shift_left_32(const std::uint64_t low) noexcept +// Branch-free two's complement absolute value: (x ^ mask) - mask, where mask is all +// ones for a negative value and zero otherwise. abs(min()) is min(), which matches the +// behavior of the builtin signed integer types. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 abs(const int128 value) noexcept { - return {static_cast(low >> 32), low << 32}; -} + const auto sign_word {static_cast(value.signed_high() >> 63)}; + const auto mask {detail::from_bits(sign_word, sign_word)}; -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t library_mul(const int128_t lhs, const int128_t rhs) noexcept -{ - const auto a {lhs.low >> 32U}; - const auto b {lhs.low & UINT32_MAX}; - const auto c {rhs.low >> 32U}; - const auto d {rhs.low & UINT32_MAX}; - - int128_t result { static_cast(static_cast(lhs.high) * rhs.low + static_cast(lhs.low) * rhs.high + a * c), b * d }; - result += signed_shift_left_32(a * d) + signed_shift_left_32(b * c); - - return result; + return (value ^ mask) - mask; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t default_mul(const int128_t lhs, const std::uint64_t rhs) noexcept -{ - const auto low_res{lhs.low * rhs}; - - const auto a_lo{lhs.low & UINT32_MAX}; - const auto a_high{lhs.low >> 32U}; - const auto b_lo{rhs & UINT32_MAX}; - const auto b_high{rhs >> 32U}; +//===================================== +// Multiplication Operators +//===================================== - const auto lo_lo{a_lo * b_lo}; - const auto lo_hi{a_lo * b_high}; - const auto hi_lo{a_high * b_lo}; - const auto hi_hi{a_high * b_high}; +namespace detail { - const auto mid{(lo_lo >> 32U) + (lo_hi & UINT32_MAX) + (hi_lo & UINT32_MAX)}; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 default_mul(const int128 lhs, const std::uint64_t rhs) noexcept +{ + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(__s390__) && !defined(__s390x__) + + return int128{static_cast(lhs) * static_cast(rhs)}; - const auto carry{hi_hi + (lo_hi >> 32) + (hi_lo >> 32) + (mid >> 32)}; + #else - const auto high_res{lhs.high * static_cast(rhs) + static_cast(carry)}; + return low_word_mul(lhs, rhs); - return {high_res, low_res}; + #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t default_mul(const int128_t lhs, const std::uint32_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 default_mul(const int128 lhs, const std::uint32_t rhs) noexcept { - const auto low_res{lhs.low * rhs}; - - const auto a_hi{lhs.low >> 32U}; - const auto hi_lo{a_hi * rhs}; - - const auto high_res{lhs.high * static_cast(rhs) + static_cast(hi_lo)}; - - return {high_res, low_res}; + return default_mul(lhs, static_cast(rhs)); } #if defined(_M_AMD64) && !defined(__GNUC__) -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE int128_t msvc_amd64_mul(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE int128 msvc_amd64_mul(const int128 lhs, const int128 rhs) noexcept { - int128_t result {}; - result.low = _umul128(lhs.low, rhs.low, reinterpret_cast(&result.high)); + int128 result {}; + result.low = _umul128(lhs.low, rhs.low, &result.high); result.high += lhs.low * rhs.high; result.high += lhs.high * rhs.low; @@ -2646,7 +2038,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE #endif -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128_t default_mul(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr int128 default_mul(const int128 lhs, const int128 rhs) noexcept { #if ((defined(__aarch64__) && defined(__APPLE__)) || defined(__x86_64__) || defined(__PPC__) || defined(__powerpc__)) && defined(__GNUC__) && !defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -2654,7 +2046,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) { - return library_mul(lhs, rhs); + return low_word_mul(lhs, rhs); } else { @@ -2668,7 +2060,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE std::memcpy(&new_rhs, &rhs, sizeof(detail::builtin_u128)); const auto res {new_lhs * new_rhs}; - int128_t library_res {}; + int128 library_res {}; std::memcpy(&library_res, &res, sizeof(detail::builtin_u128)); @@ -2679,95 +2071,61 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE # elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - return static_cast(static_cast(lhs) * static_cast(rhs)); + // Unsigned wrap for consistent two's-complement semantics + return int128{static_cast(lhs) * static_cast(rhs)}; # else - return library_mul(lhs, rhs); + return low_word_mul(lhs, rhs); # endif #elif defined(__aarch64__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - return static_cast(static_cast(lhs) * static_cast(rhs)); + return int128{static_cast(lhs) * static_cast(rhs)}; #elif defined(_M_AMD64) && !defined(__GNUC__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(rhs)) { - return library_mul(lhs, rhs); // LCOV_EXCL_LINE + return low_word_mul(lhs, rhs); // LCOV_EXCL_LINE } else { return msvc_amd64_mul(lhs, rhs); } - #elif (defined(_M_IX86) || defined(_M_ARM) || defined(__arm__)) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + #elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(__s390__) && !defined(__s390x__) - if (BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(rhs)) - { - return library_mul(lhs, rhs); // LCOV_EXCL_LINE - } - else - { - std::uint32_t lhs_words[4] {}; - std::uint32_t rhs_words[4] {}; - - // Since in all likelihood this equates to memcpy we don't need to convert to non-negative integers and back - to_words(lhs, lhs_words); - to_words(rhs, rhs_words); - - return knuth_multiply(lhs_words, rhs_words); - } + // Multiply in the unsigned domain to avoid signed-overflow UB, then reinterpret the bits. + return int128{static_cast(lhs) * static_cast(rhs)}; #else - return library_mul(lhs, rhs); + return low_word_mul(lhs, rhs); #endif } } // namespace detail -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator*(const int128 lhs, const int128 rhs) noexcept { return detail::default_mul(lhs, rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator*(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using local_eval_type = detail::evaluation_type_t; return detail::default_mul(lhs, static_cast(rhs)); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator*(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using local_eval_type = detail::evaluation_type_t; return detail::default_mul(rhs, static_cast(lhs)); - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } #ifdef _MSC_VER @@ -2776,14 +2134,14 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const Unsig #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator*(const int128 lhs, const SignedInteger rhs) noexcept { return rhs < 0 ? -detail::default_mul(lhs, -static_cast(rhs)) : detail::default_mul(lhs, static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator*(const SignedInteger lhs, const int128 rhs) noexcept { return lhs < 0 ? -detail::default_mul(rhs, -static_cast(lhs)) : detail::default_mul(rhs, static_cast(lhs)); @@ -2793,62 +2151,28 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const Signe # pragma warning(pop) #endif -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return static_cast(static_cast(lhs) * rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return static_cast(static_cast(rhs) * lhs); -} - -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const int128_t, const T) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const T, const int128_t) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} - -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const int128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator*(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return detail::default_mul(lhs, static_cast(rhs)); + return detail::default_mul(lhs, static_cast(rhs)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator*(const detail::builtin_i128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator*(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - return detail::default_mul(rhs, static_cast(lhs)); + return detail::default_mul(rhs, static_cast(lhs)); } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator*=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator*=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif - - *this = *this * rhs; + *this = static_cast(*this * rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator*=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator*=(const int128 rhs) noexcept { *this = *this * rhs; return *this; @@ -2857,9 +2181,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator*= #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator*=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator*=(const Integer rhs) noexcept { - *this = *this * rhs; + *this = static_cast(*this * rhs); return *this; } @@ -2874,14 +2198,15 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator*=(co # pragma clang diagnostic ignored "-Wassume" #endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator/(const int128 lhs, const int128 rhs) noexcept { if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - constexpr int128_t min_val {INT64_MIN, 0}; + constexpr int128 min_val {INT64_MIN, 0}; const auto abs_lhs {abs(lhs)}; const auto abs_rhs {abs(rhs)}; @@ -2889,21 +2214,16 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const { return {0,0}; } - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - - return static_cast(static_cast(lhs) / static_cast(rhs)); - #else - - int128_t quotient {}; - const auto negative_res {(lhs.high < 0) != (rhs.high < 0)}; + const auto negative_res {(lhs.signed_high() < 0) != (rhs.signed_high() < 0)}; - if (abs_rhs.high != 0) - { - quotient = detail::knuth_div(abs_lhs, abs_rhs); - } - else + // Narrow fast path: when the divisor magnitude fits in 64 bits, divide the magnitudes with + // the hardware-accelerated one_word_div and reapply the sign. This reuses the abs values + // computed above and beats native signed division (the out-of-line __divti3) for this case. + if (abs_rhs.high == 0) { + int128 quotient {}; + if (abs_lhs.high == 0) { quotient = {0, abs_lhs.low / abs_rhs.low}; @@ -2912,87 +2232,89 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const { detail::one_word_div(abs_lhs, abs_rhs.low, quotient); } + + return negative_res ? -quotient : quotient; } + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) + + return static_cast(static_cast(lhs) / static_cast(rhs)); + + #else + + const auto quotient {detail::knuth_div(abs_lhs, abs_rhs)}; return negative_res ? -quotient : quotient; + #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator/(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } const auto abs_lhs {abs(lhs)}; - int128_t quotient {}; + int128 quotient {}; detail::one_word_div(abs_lhs, static_cast(rhs), quotient); return lhs < 0 ? -quotient : quotient; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator/(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - if (rhs.high != 0 && rhs.high != -1) + if (rhs.high != 0 && rhs.high != ~UINT64_C(0)) { return {0,0}; } else { auto abs_rhs {abs(rhs)}; + // rhs == -2^64 has |rhs| greater than any 64-bit lhs, so the quotient is 0 (also avoids /0) + if (abs_rhs.high != 0) + { + return {0, 0}; + } const auto res {static_cast(lhs) / abs_rhs.low}; - const int128_t result {0, res}; + const int128 result {0, res}; return rhs < 0 ? -result : result; } - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4146) // Unary minus applied to unsigned type +#endif + BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator/(const int128 lhs, const SignedInteger rhs) noexcept { using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - int128_t quotient {}; + int128 quotient {}; - constexpr int128_t min_val {INT64_MIN, 0}; - const auto negative_res {static_cast((lhs.high < 0) ^ (rhs < 0))}; - const auto abs_rhs {rhs < 0 ? -rhs : rhs}; + constexpr int128 min_val {INT64_MIN, 0}; + const auto negative_res {static_cast((lhs.signed_high() < 0) ^ (rhs < 0))}; + // Negate in the unsigned domain so INT64_MIN does not overflow (UBSAN) + const auto abs_rhs {rhs < 0 ? -static_cast(rhs) : static_cast(rhs)}; const auto abs_lhs {abs(lhs)}; if (lhs != min_val && abs_lhs < abs_rhs) @@ -3000,132 +2322,85 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const int12 return {0, 0}; } - detail::one_word_div(abs_lhs, static_cast(abs_rhs), quotient); + detail::one_word_div(abs_lhs, abs_rhs, quotient); return negative_res ? -quotient : quotient; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator/(const SignedInteger lhs, const int128 rhs) noexcept { if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - if (rhs.high != 0 && rhs.high != -1) + if (rhs.high != 0 && rhs.high != ~UINT64_C(0)) { return {0,0}; } else { - const auto negative_res {static_cast((rhs.high < 0) ^ (lhs < 0))}; + const auto negative_res {static_cast((rhs.signed_high() < 0) ^ (lhs < 0))}; const auto abs_rhs {abs(rhs)}; - const auto abs_lhs {lhs < 0 ? -lhs : lhs}; - const int128_t res {0, static_cast(abs_lhs) / abs_rhs.low}; + // rhs == -2^64 has |rhs| greater than any 64-bit lhs, so the quotient is 0 (also avoids /0) + if (abs_rhs.high != 0) + { + return {0, 0}; + } + // Negate in the unsigned domain so INT64_MIN does not overflow (UBSAN) + const auto abs_lhs {lhs < 0 ? -static_cast(lhs) : static_cast(lhs)}; + const int128 res {0, abs_lhs / abs_rhs.low}; return negative_res ? -res : res; } } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return static_cast(static_cast(lhs) / rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs / static_cast(rhs)); -} - -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#if defined(__clang__) +# pragma clang diagnostic pop +#endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t, const T) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} +#ifdef _MSC_VER +# pragma warning(pop) +#endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const T, const int128_t) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const int128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator/(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return static_cast(static_cast(lhs) / rhs); + return static_cast(static_cast(lhs) / rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator/(const detail::builtin_i128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator/(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - return static_cast(lhs / static_cast(rhs)); + return static_cast(lhs / static_cast(rhs)); } #elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator/(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs / static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator/(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) / rhs; -} - -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator/(const int128_t, const T) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator/(const T, const int128_t) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} - -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator/(const int128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128 operator/(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return lhs / static_cast(rhs); + return lhs / static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator/(const detail::builtin_i128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128 operator/(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - return static_cast(lhs) / rhs; + return static_cast(lhs) / rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator/=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator/=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif - - *this = *this / rhs; + *this = static_cast(*this / rhs); return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator/=(const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator/=(const int128 rhs) noexcept { *this = *this / rhs; return *this; @@ -3134,9 +2409,9 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator/= #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator/=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator/=(const Integer rhs) noexcept { - *this = *this / rhs; + *this = static_cast(*this / rhs); return *this; } @@ -3153,60 +2428,49 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator/=(co //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(int128_t lhs, UnsignedInteger rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(int128 lhs, UnsignedInteger rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(UnsignedInteger lhs, int128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(UnsignedInteger lhs, int128 rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(int128_t lhs, SignedInteger rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(int128 lhs, SignedInteger rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(SignedInteger lhs, int128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(SignedInteger lhs, int128 rhs) noexcept; -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(int128_t lhs, int128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(int128 lhs, int128 rhs) noexcept; template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(const int128 lhs, const UnsignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - int128_t quotient {}; - int128_t remainder {}; + int128 quotient {}; + int128 remainder {}; const auto abs_lhs {abs(lhs)}; detail::one_word_div(abs_lhs, static_cast(rhs), quotient, remainder); return lhs < 0 ? -remainder : remainder; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const UnsignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(const UnsignedInteger lhs, const int128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } const auto abs_rhs {abs(rhs)}; @@ -3216,40 +2480,32 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const Unsig return lhs; } - const int128_t remainder {0, static_cast(lhs) % abs_rhs.low}; + const int128 remainder {0, static_cast(lhs) % abs_rhs.low}; return remainder; - - #else - - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return {0, 0}; - - #endif } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(const int128 lhs, const SignedInteger rhs) noexcept { - return lhs % static_cast(rhs); + return lhs % static_cast(rhs); } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const SignedInteger lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(const SignedInteger lhs, const int128 rhs) noexcept { - return static_cast(lhs) % rhs; + return static_cast(lhs) % rhs; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator%(const int128 lhs, const int128 rhs) noexcept { if (rhs == 0) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - constexpr int128_t min_val {INT64_MIN, 0}; + constexpr int128 min_val {INT64_MIN, 0}; const auto abs_lhs {abs(lhs)}; const auto abs_rhs {abs(rhs)}; @@ -3257,153 +2513,232 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const int12 { return lhs; } - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - else - { - return static_cast(static_cast(lhs) % static_cast(rhs)); - } - #else - const auto is_neg{lhs < 0}; - - int128_t remainder {}; + const auto is_neg {lhs < 0}; - if (abs_rhs.high != 0) - { - detail::knuth_div(abs_lhs, abs_rhs, remainder); - } - else + // Narrow fast path: when the divisor magnitude fits in 64 bits, take the remainder of the + // magnitudes with the hardware-accelerated one_word_div and reapply the dividend's sign. + if (abs_rhs.high == 0) { + int128 remainder {}; + if (abs_lhs.high == 0) { - remainder = int128_t{0, abs_lhs.low % abs_rhs.low}; + remainder = int128{0, abs_lhs.low % abs_rhs.low}; } else { - int128_t quotient {}; - + int128 quotient {}; detail::one_word_div(abs_lhs, abs_rhs.low, quotient, remainder); } + + return is_neg ? -remainder : remainder; } + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) + + return static_cast(static_cast(lhs) % static_cast(rhs)); + + #else + + int128 remainder {}; + detail::knuth_div(abs_lhs, abs_rhs, remainder); return is_neg ? -remainder : remainder; #endif } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator%(const int128 lhs, const detail::builtin_i128 rhs) noexcept { return static_cast(lhs) % rhs; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const detail::builtin_i128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR int128 operator%(const detail::builtin_i128 lhs, const int128 rhs) noexcept { return lhs % static_cast(rhs); } -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t lhs, const detail::builtin_u128 rhs) noexcept +#elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128 operator%(const int128 lhs, const detail::builtin_i128 rhs) noexcept { - return static_cast(static_cast(lhs) % rhs); + return lhs % static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const detail::builtin_u128 lhs, const int128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128 operator%(const detail::builtin_i128 lhs, const int128 rhs) noexcept { - return static_cast(lhs % static_cast(rhs)); + return static_cast(lhs) % rhs; } -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const int128_t, const T) noexcept +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator%=(const Integer rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + *this = static_cast(*this % rhs); + return *this; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator%(const T, const int128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator%=(const int128 rhs) noexcept { - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; + *this = *this % rhs; + return *this; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -#elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) +#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator%(const int128_t lhs, const detail::builtin_i128 rhs) noexcept +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128& int128::operator%=(const Integer rhs) noexcept { - return lhs % static_cast(rhs); + *this = static_cast(*this % rhs); + return *this; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator%(const detail::builtin_i128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) % rhs; -} +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +//===================================== +// Built-in Integer Compound Assignment +//===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator%(const int128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs % static_cast(rhs); -} +// Compound assignment with a built-in integer on the left. +// The builtin applies the operation to the common type of the two operands and converts +// the result back to the type of the left operand, so each of these is the binary operator +// above followed by that conversion, which matches what the builtin 128-bit integer does. +// detail/traits.hpp defines which types Integer may be -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator%(const detail::builtin_u128 lhs, const int128_t rhs) noexcept -{ - return static_cast(lhs) % rhs; -} +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4804) // Unsafe use of type bool in operation +#endif -#else // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(op, compound_op) \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr Integer& operator compound_op(Integer& lhs, const int128 rhs) noexcept \ + { \ + lhs = static_cast(lhs op rhs); \ + return lhs; \ + } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator%(const int128_t, const T) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(|, |=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(&, &=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(^, ^=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(+, +=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(-, -=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(*, *=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(/, /=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(%, %=) -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t operator%(const T, const int128_t) noexcept -{ - static_assert(detail::is_signed_integer_v, "Sign Compare Error"); - return {0, 0}; -} +// The shifts take the value from the left operand alone, so only the count comes from rhs +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(<<, <<=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP(>>, >>=) -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_INTEGER_COMPOUND_OP -#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 +#ifdef _MSC_VER +# pragma warning(pop) +#endif -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator%=(const Integer rhs) noexcept -{ - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_signed_integer_v, "Sign Conversion Error"); - #endif +//===================================== +// Floating Point Operators +//===================================== - *this = *this % rhs; - return *this; -} +// The usual arithmetic conversions convert the integer operand to the floating point type +// before the operation is applied, so each of these computes exactly what the builtin +// 128-bit integer computes for the same expression. +// detail/traits.hpp defines which types Float may be -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t& int128_t::operator%=(const int128_t rhs) noexcept -{ - *this = *this % rhs; - return *this; -} +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(op, return_type) \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr return_type operator op(const int128 lhs, const Float rhs) noexcept \ + { \ + return static_cast(lhs) op rhs; \ + } \ + \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr return_type operator op(const Float lhs, const int128 rhs) noexcept \ + { \ + return lhs op static_cast(rhs); \ + } + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(+, Float) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(-, Float) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(*, Float) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(/, Float) + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(==, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(!=, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(<, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(<=, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(>, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(>=, bool) + +// Mixing an integer and a floating point type yields a partial ordering because of NaN +#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR -template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline int128_t& int128_t::operator%=(const Integer rhs) noexcept -{ - *this = *this % rhs; - return *this; -} +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP(<=>, std::partial_ordering) -#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR + +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_BINARY_OP + +// Compound assignment converts the result back to int128, truncating toward zero. +// A result that is NaN or outside the range of the type saturates as the floating point +// constructor does, rather than being undefined as it is for the builtin + +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_COMPOUND_OP(op, compound_op) \ + template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128& int128::operator compound_op(const Float rhs) noexcept \ + { \ + *this = static_cast(static_cast(*this) op rhs); \ + return *this; \ + } \ + \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr Float& operator compound_op(Float& lhs, const int128 rhs) noexcept \ + { \ + lhs compound_op static_cast(rhs); \ + return lhs; \ + } + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_COMPOUND_OP(+, +=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_COMPOUND_OP(-, -=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_COMPOUND_OP(*, *=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_COMPOUND_OP(/, /=) + +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_COMPOUND_OP + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +// The builtin allows no floating point operand for the modulo, bitwise and shift operators. +// Deleting them keeps that a compile error here, rather than letting the implicit floating +// point constructor silently truncate the operand + +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP(op) \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128 operator op(int128 lhs, Float rhs) = delete; \ + \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE int128 operator op(Float lhs, int128 rhs) = delete; + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP(%) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP(&) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP(|) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP(^) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP(<<) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP(>>) + +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_I128_FLOAT_DELETED_OP namespace detail { @@ -3455,15 +2790,15 @@ class numeric_limits_impl_i128 static constexpr bool tinyness_before = false; // Member functions - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (min) () -> boost::int128::int128_t { return {INT64_MIN, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto lowest () -> boost::int128::int128_t { return {INT64_MIN, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (max) () -> boost::int128::int128_t { return {INT64_MAX, UINT64_MAX}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto epsilon () -> boost::int128::int128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto round_error () -> boost::int128::int128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto infinity () -> boost::int128::int128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto quiet_NaN () -> boost::int128::int128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto signaling_NaN() -> boost::int128::int128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto denorm_min () -> boost::int128::int128_t { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (min) () -> boost::int128::int128 { return {INT64_MIN, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto lowest () -> boost::int128::int128 { return {INT64_MIN, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (max) () -> boost::int128::int128 { return {INT64_MAX, UINT64_MAX}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto epsilon () -> boost::int128::int128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto round_error () -> boost::int128::int128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto infinity () -> boost::int128::int128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto quiet_NaN () -> boost::int128::int128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto signaling_NaN() -> boost::int128::int128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto denorm_min () -> boost::int128::int128 { return {0, 0}; } }; #if !defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L @@ -3476,10 +2811,23 @@ template constexpr bool numeric_limits_impl_i128::has_infinity; template constexpr bool numeric_limits_impl_i128::has_quiet_NaN; template constexpr bool numeric_limits_impl_i128::has_signaling_NaN; -// These members were deprecated in C++23 -#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) +// These members were deprecated in C++23; suppress the deprecation warning rather +// than dropping the definitions. +#if defined(__GNUC__) && __cplusplus > 202002L +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4996) +#endif + template constexpr std::float_denorm_style numeric_limits_impl_i128::has_denorm; template constexpr bool numeric_limits_impl_i128::has_denorm_loss; + +#if defined(__GNUC__) && __cplusplus > 202002L +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) #endif template constexpr std::float_round_style numeric_limits_impl_i128::round_style; @@ -3512,7 +2860,7 @@ namespace std { #endif template <> -class numeric_limits : +class numeric_limits : public boost::int128::detail::numeric_limits_impl_i128 {}; #ifdef __clang__ diff --git a/include/boost/decimal/detail/int128/detail/literal_macros.hpp b/include/boost/decimal/detail/int128/detail/literal_macros.hpp new file mode 100644 index 000000000..3097c0cc9 --- /dev/null +++ b/include/boost/decimal/detail/int128/detail/literal_macros.hpp @@ -0,0 +1,17 @@ +// Copyright 2022 Peter Dimov +// 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_DETAIL_LITERAL_MACROS_HPP +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_LITERAL_MACROS_HPP + +// Convenience macros for the user-defined literals. This header intentionally has +// no includes and declares nothing, so module consumers can pull it in on its own +// to obtain the macros (macros are never part of a module's exported interface). + +#define BOOST_DECIMAL_DETAIL_INT128_STRINGIFY(x) #x +#define BOOST_DECIMAL_DETAIL_INT128_UINT128_C(x) boost::int128::literals::operator""_u128(BOOST_DECIMAL_DETAIL_INT128_STRINGIFY(x)) +#define BOOST_DECIMAL_DETAIL_INT128_INT128_C(x) boost::int128::literals::operator""_i128(BOOST_DECIMAL_DETAIL_INT128_STRINGIFY(x)) + +#endif // BOOST_DECIMAL_DETAIL_INT128_DETAIL_LITERAL_MACROS_HPP diff --git a/include/boost/decimal/detail/int128/detail/mini_from_chars.hpp b/include/boost/decimal/detail/int128/detail/mini_from_chars.hpp index 55b83bf33..265459b26 100644 --- a/include/boost/decimal/detail/int128/detail/mini_from_chars.hpp +++ b/include/boost/decimal/detail/int128/detail/mini_from_chars.hpp @@ -15,6 +15,10 @@ #include #include +#if !(defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) || defined(BOOST_DECIMAL_DETAIL_INT128_DISABLE_EXCEPTIONS)) +#include +#endif + #endif namespace boost { @@ -23,12 +27,12 @@ namespace detail { namespace impl { -#if !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) +#if !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR unsigned char uchar_values[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, @@ -47,15 +51,15 @@ static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 25 #endif // __NVCC__ -// Convert characters for 0-9, A-Z, a-z to 0-35. Anything else is 255 +// Convert characters for 0-9, A-Z, a-z to 0-35. The digit separator ' is 254. Anything else is 255 BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr auto digit_from_char(char val) noexcept -> unsigned char { - #if defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA) + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) constexpr unsigned char uchar_values[] = {255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255, 255, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 255, 255, 255, 255, 255, @@ -77,10 +81,10 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE return uchar_values[static_cast(val)]; } -template +template BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(const char* first, const char* last, Integer& value, int base) noexcept { - if (first >= last) + if (last - first <= 0) { return EINVAL; } @@ -127,7 +131,6 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(co overflow_value /= unsigned_base; - overflow_value <<= 1; max_digit %= unsigned_base; // If the only character was a sign abort now @@ -138,52 +141,80 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(co bool overflowed = false; - std::ptrdiff_t nc = last - next; - constexpr std::ptrdiff_t nd = std::numeric_limits::digits10; + const std::ptrdiff_t nc = last - next; - { - std::ptrdiff_t i = 0; + // For bases 2..10 the first digits10 characters always fit in the unsigned + // For bases above 10, the safe window is shorter, so we must check with each iteration + const std::ptrdiff_t nd { + base <= 10 + ? static_cast(std::numeric_limits::digits10) + : std::ptrdiff_t{0} + }; - for( ; i < nd && i < nc; ++i ) - { - // overflow is not possible in the first nd characters + const std::ptrdiff_t fast_limit {nd < nc ? nd : nc}; + std::ptrdiff_t i = 0; - const auto current_digit = static_cast(digit_from_char(*next)); + for (; i < fast_limit; ++i) + { + const auto raw_digit = digit_from_char(*next); - if (current_digit >= unsigned_base) + // When parsing a user-defined literal skip the digit separator ' (marked as 254) + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (is_literal_parse) + { + if (raw_digit == 254) { - break; + ++next; + continue; } - - result = static_cast(result * unsigned_base + current_digit); - ++next; } - for( ; i < nc; ++i ) + const auto current_digit = static_cast(raw_digit); + + if (current_digit >= unsigned_base) { - const auto current_digit = static_cast(digit_from_char(*next)); + break; + } - if (current_digit >= unsigned_base) - { - break; - } + result = static_cast(result * unsigned_base + current_digit); + ++next; + } - if (result < overflow_value || (result == overflow_value && current_digit <= max_digit)) - { - result = static_cast(result * unsigned_base + current_digit); - } - else + for (; i < nc; ++i) + { + const auto raw_digit = digit_from_char(*next); + + // When parsing a user-defined literal skip the digit separator ' (marked as 254) + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (is_literal_parse) + { + if (raw_digit == 254) { - overflowed = true; - break; + ++next; + continue; } + } - ++next; + const auto current_digit = static_cast(raw_digit); + + if (current_digit >= unsigned_base) + { + break; } + + if (result < overflow_value || (result == overflow_value && current_digit <= max_digit)) + { + result = static_cast(result * unsigned_base + current_digit); + } + else + { + overflowed = true; + break; + } + + ++next; } // Return the parsed value, adding the sign back if applicable - // If we have overflowed then we do not return the result + // If we have overflowed, then we do not return the result if (overflowed) { return EDOM; @@ -201,6 +232,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(co // This value will be negative to differentiate from errno values // since they are in the range of acceptable distances + + // This cast is useless on 32-bit platforms #if defined(__GNUC__) && !defined(__clang__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wuseless-cast" @@ -214,18 +247,145 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars_integer_impl(co } } // namespace impl -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars(const char* first, const char* last, uint128_t& value, int base = 10) noexcept +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars(const char* first, const char* last, uint128& value, int base = 10) noexcept +{ + return impl::from_chars_integer_impl(first, last, value, base); +} + +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars(const char* first, const char* last, int128& value, int base = 10) noexcept +{ + return impl::from_chars_integer_impl(first, last, value, base); +} + +// Parsing entry points for the user-defined literals. Unlike from_chars these skip the +// C++ digit separator ' so that literals such as 1'234'567_u128 are accepted. +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars_literal(const char* first, const char* last, uint128& value, int base = 10) noexcept +{ + return impl::from_chars_integer_impl(first, last, value, base); +} + +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars_literal(const char* first, const char* last, int128& value, int base = 10) noexcept +{ + return impl::from_chars_integer_impl(first, last, value, base); +} + +// Rejects an out of range literal +[[noreturn]] BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline void parse_literal_out_of_range() { - return impl::from_chars_integer_impl(first, last, value, base); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) || defined(BOOST_DECIMAL_DETAIL_INT128_DISABLE_EXCEPTIONS) + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; + #else + BOOST_DECIMAL_DETAIL_INT128_THROW_EXCEPTION(std::out_of_range("Literal is out of range of the target type")); + #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int from_chars(const char* first, const char* last, int128_t& value, int base = 10) noexcept +// Rejects an invalid literal +[[noreturn]] BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline void parse_invalid_literal() { - return impl::from_chars_integer_impl(first, last, value, base); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) || defined(BOOST_DECIMAL_DETAIL_INT128_DISABLE_EXCEPTIONS) + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; + #else + BOOST_DECIMAL_DETAIL_INT128_THROW_EXCEPTION(std::invalid_argument("Literal is not a valid integer")); + #endif +} + +// GCC before 6 rejects a constexpr function that contains a throw-expression or a +// call to a non-constexpr function anywhere in its body so we need to use unreachable in that case +#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 6 +# define BOOST_DECIMAL_DETAIL_INT128_REJECT_LITERAL(reporter) BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE +#else +# define BOOST_DECIMAL_DETAIL_INT128_REJECT_LITERAL(reporter) reporter() +#endif + +// Parse a user-defined literal, hard-failing on any malformed or out-of-range input. +// A C++ base prefix (0x/0X hex, 0b/0B binary, or a leading 0 for octal) is stripped and +// the digits parsed in that base, otherwise handled as base 10 +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr Integer parse_literal(const char* first, const char* last) noexcept +{ + Integer parse_value {}; + + // A leading sign stays with the digits; a base prefix, if present, follows it. + auto next = first; + const bool negative {next != last && *next == '-'}; + if (negative) + { + ++next; + } + + int base {10}; + bool prefixed {false}; + + if (last - next >= 2 && *next == '0') + { + const char marker {next[1]}; + if (marker == 'x' || marker == 'X') + { + base = 16; + next += 2; + prefixed = true; + } + else if (marker == 'b' || marker == 'B') + { + base = 2; + next += 2; + prefixed = true; + } + else + { + base = 8; + next += 1; + prefixed = true; + } + } + + // With no prefix, from_chars_literal handles the sign and the full decimal range. + // Overflow is reported as EDOM; anything else short of full consumption is malformed. + if (!prefixed) + { + const auto status = from_chars_literal(first, last, parse_value); + if (status == EDOM) + { + BOOST_DECIMAL_DETAIL_INT128_REJECT_LITERAL(parse_literal_out_of_range); + } + else if (status != first - last) + { + BOOST_DECIMAL_DETAIL_INT128_REJECT_LITERAL(parse_invalid_literal); + } + + return parse_value; + } + + // Prefixed: parse the magnitude in the detected base, then reapply the sign. + const auto status = from_chars_literal(next, last, parse_value, base); + if (status == EDOM) + { + BOOST_DECIMAL_DETAIL_INT128_REJECT_LITERAL(parse_literal_out_of_range); + } + else if (status != next - last) + { + BOOST_DECIMAL_DETAIL_INT128_REJECT_LITERAL(parse_invalid_literal); + } + + if (negative) + { + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) + { + parse_value = static_cast(-parse_value); + } + else + { + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; + } + } + + return parse_value; } } // namespace detail } // namespace int128 } // namespace boost +#undef BOOST_DECIMAL_DETAIL_INT128_REJECT_LITERAL + #endif //MINI_FROM_CHARS_HPP diff --git a/include/boost/decimal/detail/int128/detail/mini_to_chars.hpp b/include/boost/decimal/detail/int128/detail/mini_to_chars.hpp index e931e0628..360dabdb3 100644 --- a/include/boost/decimal/detail/int128/detail/mini_to_chars.hpp +++ b/include/boost/decimal/detail/int128/detail/mini_to_chars.hpp @@ -12,7 +12,11 @@ namespace boost { namespace int128 { namespace detail { -#if !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) +// A 128-bit integer needs up to 128 binary digits (base 2); allow for a leading sign and a +// null terminator so mini_to_chars is safe for every supported base (2, 8, 10, 16). +constexpr std::size_t mini_to_chars_buffer_size = 130; + +#if !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR char lower_case_digit_table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', @@ -30,9 +34,9 @@ static_assert(sizeof(upper_case_digit_table) == sizeof(char) * 16, "10 numbers, #endif // !__NVCC__ -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr char* mini_to_chars(char (&buffer)[64], uint128_t v, const int base, const bool uppercase) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr char* mini_to_chars(char (&buffer)[mini_to_chars_buffer_size], uint128 v, const int base, const bool uppercase) noexcept { - #if defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA) + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) constexpr char lower_case_digit_table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' @@ -44,7 +48,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr char* mini_to_chars(char (&buf }; #endif - char* last {buffer + 64U}; + char* last {buffer + sizeof(buffer)}; *--last = '\0'; if (v == 0U) @@ -97,28 +101,28 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr char* mini_to_chars(char (&buf return last; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr char* mini_to_chars(char (&buffer)[64], const int128_t v, const int base, const bool uppercase) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr char* mini_to_chars(char (&buffer)[mini_to_chars_buffer_size], const int128 v, const int base, const bool uppercase) noexcept { char* p {nullptr}; if (v < 0) { // We cant negate the min value inside the signed type, but we know what the result will be - if (v == (std::numeric_limits::min)()) + if (v == (std::numeric_limits::min)()) { - p = mini_to_chars(buffer, uint128_t{UINT64_C(0x8000000000000000), 0}, base, uppercase); + p = mini_to_chars(buffer, uint128{UINT64_C(0x8000000000000000), 0}, base, uppercase); } else { const auto neg_v {-v}; - p = mini_to_chars(buffer, static_cast(neg_v), base, uppercase); + p = mini_to_chars(buffer, static_cast(neg_v), base, uppercase); } *--p = '-'; } else { - p = mini_to_chars(buffer, static_cast(v), base, uppercase); + p = mini_to_chars(buffer, static_cast(v), base, uppercase); } return p; diff --git a/include/boost/decimal/detail/int128/detail/traits.hpp b/include/boost/decimal/detail/int128/detail/traits.hpp index 971c5dbc7..d441af50d 100644 --- a/include/boost/decimal/detail/int128/detail/traits.hpp +++ b/include/boost/decimal/detail/int128/detail/traits.hpp @@ -49,6 +49,39 @@ BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_unsigned_integer_v = unsign template BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_any_integer_v = signed_integer::value || unsigned_integer::value; +template +struct floating_point +{ + static constexpr bool value = std::is_same::value || std::is_same::value + #ifndef BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT + || std::is_same::value; + #else + ; + #endif +}; + +template +BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_floating_point_v = floating_point::value; + +// The type integral promotion gives an operand of type T, which is the result type of a +// shift with T on the left. The rule depends on the rank of T and not only on its size, so +// long and char32_t differ where both are the width of an int; asking the compiler is exact +template +struct promoted +{ + using type = decltype(+T{}); +}; + +// Unary plus on a bool draws a warning from MSVC, and the answer is always int +template <> +struct promoted +{ + using type = int; +}; + +template +using promoted_t = typename promoted::type; + // Decides if we can use a u32 or u64 implementation for some operations #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -71,10 +104,12 @@ using evaluation_type_t = std::conditional_t, bool> = true #define BOOST_DECIMAL_DETAIL_INT128_DEFAULTED_UNSIGNED_INTEGER_CONCEPT typename UnsignedInteger, std::enable_if_t, bool> = true #define BOOST_DECIMAL_DETAIL_INT128_DEFAULTED_INTEGER_CONCEPT typename Integer, std::enable_if_t, bool> = true +#define BOOST_DECIMAL_DETAIL_INT128_DEFAULTED_FLOATING_POINT_CONCEPT typename Float, std::enable_if_t, bool> = true #define BOOST_DECIMAL_DETAIL_INT128_SIGNED_INTEGER_CONCEPT typename SignedInteger, std::enable_if_t, bool> #define BOOST_DECIMAL_DETAIL_INT128_UNSIGNED_INTEGER_CONCEPT typename UnsignedInteger, std::enable_if_t, bool> #define BOOST_DECIMAL_DETAIL_INT128_INTEGER_CONCEPT typename Integer, std::enable_if_t, bool> +#define BOOST_DECIMAL_DETAIL_INT128_FLOATING_POINT_CONCEPT typename Float, std::enable_if_t, bool> #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) diff --git a/include/boost/decimal/detail/int128/detail/uint128_imp.hpp b/include/boost/decimal/detail/int128/detail/uint128_imp.hpp index 3f430fecb..f33aabf32 100644 --- a/include/boost/decimal/detail/int128/detail/uint128_imp.hpp +++ b/include/boost/decimal/detail/int128/detail/uint128_imp.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE @@ -28,7 +29,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT struct #if (defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128)) && !defined(_M_IX86) alignas(alignof(detail::builtin_u128)) #endif -uint128_t +uint128 { #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE std::uint64_t low {}; @@ -50,34 +51,33 @@ uint128_t #endif // BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE // Defaulted basic construction - constexpr uint128_t() noexcept = default; - constexpr uint128_t(const uint128_t&) noexcept = default; - constexpr uint128_t(uint128_t&&) noexcept = default; - constexpr uint128_t& operator=(const uint128_t&) noexcept = default; - constexpr uint128_t& operator=(uint128_t&&) noexcept = default; + constexpr uint128() noexcept = default; + constexpr uint128(const uint128&) noexcept = default; + constexpr uint128(uint128&&) noexcept = default; + constexpr uint128& operator=(const uint128&) noexcept = default; + constexpr uint128& operator=(uint128&&) noexcept = default; // Requires a conversion file to be implemented - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr uint128_t(const int128_t& v) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator int128_t() const noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(const int128& v) noexcept; // Construct from integral types #if BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const std::uint64_t hi, const std::uint64_t lo) noexcept : low {lo}, high {hi} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(const std::uint64_t hi, const std::uint64_t lo) noexcept : low {lo}, high {hi} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const SignedInteger v) noexcept : low {static_cast(v)}, high {v < 0 ? UINT64_MAX : UINT64_C(0)} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(const SignedInteger v) noexcept : low {static_cast(v)}, high {v < 0 ? UINT64_MAX : UINT64_C(0)} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const UnsignedInteger v) noexcept : low {static_cast(v)}, high {} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(const UnsignedInteger v) noexcept : low {static_cast(v)}, high {} {} #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t(const detail::builtin_i128 v) noexcept : + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128(const detail::builtin_i128 v) noexcept : low {static_cast(v)}, high {static_cast(static_cast(v) >> static_cast(64U))} {} - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t(const detail::builtin_u128 v) noexcept : + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128(const detail::builtin_u128 v) noexcept : low {static_cast(v)}, high {static_cast(v >> static_cast(64U))} {} @@ -85,21 +85,21 @@ uint128_t #else // Big endian - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const std::uint64_t hi, const std::uint64_t lo) noexcept : high {hi}, low {lo} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(const std::uint64_t hi, const std::uint64_t lo) noexcept : high {hi}, low {lo} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const SignedInteger v) noexcept : high {v < 0 ? UINT64_MAX : UINT64_C(0)}, low {static_cast(v)} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(const SignedInteger v) noexcept : high {v < 0 ? UINT64_MAX : UINT64_C(0)}, low {static_cast(v)} {} template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const UnsignedInteger v) noexcept : high {}, low {static_cast(v)} {} + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(const UnsignedInteger v) noexcept : high {}, low {static_cast(v)} {} - #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const detail::builtin_i128 v) noexcept : + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128(const detail::builtin_i128 v) noexcept : high {static_cast(static_cast(v) >> 64U)}, low {static_cast(v)} {} - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t(const detail::builtin_u128 v) noexcept : + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128(const detail::builtin_u128 v) noexcept : high {static_cast(v >> 64U)}, low {static_cast(v)} {} @@ -107,175 +107,234 @@ uint128_t #endif // BOOST_DECIMAL_DETAIL_INT128_ENDIAN_LITTLE_BYTE + // Construct from floating-point types + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128(Float f) noexcept; + // Integer conversion operators BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator bool() const noexcept {return low || high; } template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator SignedInteger() const noexcept { return static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator SignedInteger() const noexcept { return static_cast(low); } + + #ifdef _MSC_VER + # pragma warning(push) + # pragma warning(disable:4127) + #endif template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator UnsignedInteger() const noexcept { return static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator UnsignedInteger() const noexcept + { + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) + { + return low || high; + } + else + { + return static_cast(low); + } + } + + #ifdef _MSC_VER + # pragma warning(pop) + #endif #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_i128() const noexcept { return static_cast(static_cast(high) << static_cast(64)) | static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_i128() const noexcept { return static_cast(static_cast(high) << static_cast(64)) | static_cast(low); } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_u128() const noexcept { return (static_cast(high) << static_cast(64)) | static_cast(low); } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR operator detail::builtin_u128() const noexcept { return (static_cast(high) << static_cast(64)) | static_cast(low); } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 // Conversion to float - // This is basically the same as ldexp(static_cast(high), 64) + static_cast(low), - // but can be constexpr at C++11 instead of C++26 - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator float() const noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE explicit constexpr operator double() const noexcept; - - // long doubles do not exist on device - #if !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) - explicit constexpr operator long double() const noexcept; + // Uses the builtin 128-bit conversion where one exists, and otherwise composes + // the words as high * 2^64 + low with an exact 2^64 constant, which is the value + // ldexp(static_cast(high), 64) + static_cast(low) computes + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator float() const noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr operator double() const noexcept; + + // long doubles do not exist on the CUDA or SYCL (spir64) device + #if !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) + constexpr operator long double() const noexcept; #endif // Compound OR template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator|=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator|=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator|=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator|=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator|=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator|=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound AND template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator&=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator&=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator&=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator&=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator&=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator&=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound XOR template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator^=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator^=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator^=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator^=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator^=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator^=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Left Shift template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator<<=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator<<=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator<<=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator<<=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator<<=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator<<=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Right Shift template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator>>=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator>>=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator>>=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator>>=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator>>=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator>>=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator++() noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator++(int) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator--() noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator--(int) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator++() noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator++(int) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator--() noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator--(int) noexcept; // Compound Addition template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator+=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator+=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator+=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator+=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator+=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator+=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Subtraction template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator-=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator-=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator-=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator-=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator-=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator-=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Multiplication template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator*=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator*=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator*=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator*=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator*=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator*=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound Division template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator/=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator/=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator/=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator/=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator/=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator/=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 // Compound modulo template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator%=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator%=(Integer rhs) noexcept; - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& operator%=(uint128_t rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator%=(uint128 rhs) noexcept; #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& operator%=(Integer rhs) noexcept; + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& operator%=(Integer rhs) noexcept; #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 + + // Compound assignment with floating point types. + // Matches the builtin: this value is converted to Float, the operation is applied in + // floating point, and the result is converted back, truncating toward zero. + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator+=(Float rhs) noexcept; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator-=(Float rhs) noexcept; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator*=(Float rhs) noexcept; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& operator/=(Float rhs) noexcept; + + // The builtin does not allow a floating point operand for these, so neither do we. + // Without these the implicit floating point constructor would silently truncate rhs + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128& operator%=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128& operator&=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128& operator|=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128& operator^=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128& operator<<=(Float rhs) = delete; + + template + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128& operator>>=(Float rhs) = delete; }; //===================================== // Absolute Value function //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t abs(const uint128_t value) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 abs(const uint128 value) noexcept { return value; } @@ -284,40 +343,98 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const // Float Conversion Operators //===================================== -// The most correct way to do this would be std::ldexp(static_cast(high), 64) + static_cast(low); -// Since std::ldexp is not constexpr until C++23 we can work around this by multiplying the high word -// by 0xFFFFFFFF in order to generally replicate what ldexp is doing in the constexpr context. -// We also avoid pulling in for the __float128 case where we would need ldexpq +// When the builtin 128-bit type exists we convert through it since the compiler +// runtime (__floatuntisf and friends) is correctly rounded. The portable fallback +// composes the words as high * 2^64 + low; see detail/float_conversion.hpp -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t::operator float() const noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128::operator float() const noexcept { - return static_cast(high) * detail::offset_value_v + static_cast(low); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) + + return static_cast(static_cast(*this)); + + #else + + return detail::unsigned_words_to_float(high, low); + + #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t::operator double() const noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128::operator double() const noexcept { - return static_cast(high) * detail::offset_value_v + static_cast(low); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) + + return static_cast(static_cast(*this)); + + #else + + return detail::unsigned_words_to_float(high, low); + + #endif } -#if !(defined(__CUDACC__) && defined(BOOST_DECIMAL_DETAIL_INT128_ENABLE_CUDA)) +#if !defined(BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT) -constexpr uint128_t::operator long double() const noexcept +constexpr uint128::operator long double() const noexcept { - return static_cast(high) * detail::offset_value_v + static_cast(low); + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) + + return static_cast(static_cast(*this)); + + #else + + return detail::unsigned_words_to_float(high, low); + + #endif } -#endif // __NVCC__ +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_GPU_SUPPORT + +//===================================== +// Float Construction +//===================================== + +// Inverse of operator(Float): decompose f into (high, low) by dividing by 2^64. +// NaN/negative -> 0 +// overflow -> UINT128_MAX. +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128::uint128(Float f) noexcept +{ + constexpr Float two_32 {static_cast(UINT64_C(1) << 32)}; + constexpr Float two_64 {two_32 * two_32}; + + // !(f >= 0) catches both NaN and negative values without using + if (!(f >= Float{0})) + { + return; + } + + // Overflow test: f >= 2^128 iff f / 2^64 >= 2^64. Comparing scaled values + // avoids materializing 2^128 as a Float, which overflows to +infinity for + // `float` and is therefore not constant-evaluable on older compilers. + const Float scaled {f / two_64}; + if (scaled >= two_64) + { + high = UINT64_MAX; + low = UINT64_MAX; + return; + } + + high = detail::float_to_uint64(scaled); + const Float remainder {f - static_cast(high) * two_64}; + low = detail::float_to_uint64(remainder); +} //===================================== // Unary Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const uint128_t value) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator+(const uint128 value) noexcept { return value; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const uint128_t value) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator-(const uint128 value) noexcept { return {~value.high + static_cast(value.low == UINT64_C(0)), ~value.low + UINT64_C(1)}; } @@ -326,12 +443,12 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const // Equality Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128_t lhs, const bool rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128 lhs, const bool rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const bool lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const bool lhs, const uint128 rhs) noexcept { return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } @@ -345,58 +462,38 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 rhs_u {rhs}; + return lhs.high == rhs_u.high && lhs.low == rhs_u.low; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 lhs_u {lhs}; + return lhs_u.high == rhs.high && lhs_u.low == rhs.low; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const UnsignedInteger lhs, const uint128 rhs) noexcept { return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const uint128 lhs, const uint128 rhs) noexcept { #if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_AMD64) return lhs.low == rhs.low && lhs.high == rhs.high; - #elif defined (__x86_64__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_BUILTIN_INT128) + #elif defined(__x86_64__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) return static_cast(lhs) == static_cast(rhs); @@ -424,44 +521,24 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs == static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) == rhs; -} - -#else - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return lhs == static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) == rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs == static_cast(rhs); + return lhs == static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator==(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) == rhs; + return static_cast(lhs) == rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -470,69 +547,49 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST // Inequality Operators //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128_t lhs, const bool rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128 lhs, const bool rhs) noexcept { return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const bool lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const bool lhs, const uint128 rhs) noexcept { return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 rhs_u {rhs}; + return lhs.high != rhs_u.high || lhs.low != rhs_u.low; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 lhs_u {lhs}; + return lhs_u.high != rhs.high || lhs_u.low != rhs.low; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const UnsignedInteger lhs, const uint128 rhs) noexcept { return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const uint128 lhs, const uint128 rhs) noexcept { #if defined(__aarch64__) || defined(_M_ARM64) || defined(_M_AMD64) return lhs.low != rhs.low || lhs.high != rhs.high; - #elif defined(__x86_64__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_BUILTIN_INT128) + #elif defined(__x86_64__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) return static_cast(lhs) != static_cast(rhs); @@ -560,44 +617,26 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs != static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) != rhs; -} - -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return lhs != static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) != rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs != static_cast(rhs); + return lhs != static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator!=(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) != rhs; + return static_cast(lhs) != rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -607,52 +646,32 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 rhs_u {rhs}; + return lhs.high == rhs_u.high ? lhs.low < rhs_u.low : lhs.high < rhs_u.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs < 0 || rhs.high > UINT64_C(0) || static_cast(lhs) < rhs.low; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 lhs_u {lhs}; + return lhs_u.high == rhs.high ? lhs_u.low < rhs.low : lhs_u.high < rhs.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const uint128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const UnsignedInteger lhs, const uint128 rhs) noexcept { return rhs.high > UINT64_C(0) || static_cast(lhs) < rhs.low; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<(const uint128 lhs, const uint128 rhs) noexcept { // On ARM macs only with the clang compiler is casting to unsigned __int128 uniformly better (and seemingly cost free) #if defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -716,44 +735,26 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs < static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) < rhs; -} - -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return lhs < static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) < rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs < static_cast(rhs); + return lhs < static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) < rhs; + return static_cast(lhs) < rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -763,52 +764,32 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 rhs_u {rhs}; + return lhs.high == rhs_u.high ? lhs.low <= rhs_u.low : lhs.high < rhs_u.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs < 0 || rhs.high > UINT64_C(0) || static_cast(lhs) <= rhs.low; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 lhs_u {lhs}; + return lhs_u.high == rhs.high ? lhs_u.low <= rhs.low : lhs_u.high < rhs.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const uint128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const UnsignedInteger lhs, const uint128 rhs) noexcept { return rhs.high > UINT64_C(0) || static_cast(lhs) <= rhs.low; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator<=(const uint128 lhs, const uint128 rhs) noexcept { #if defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -871,45 +852,27 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs <= static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) <= rhs; -} -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - return lhs <= static_cast(rhs); + return lhs <= static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) <= rhs; + return static_cast(lhs) <= rhs; } -#else - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return lhs <= static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator<=(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) <= rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -918,52 +881,32 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONS //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs < 0 || lhs.high > UINT64_C(0) || lhs.low > static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 rhs_u {rhs}; + return lhs.high == rhs_u.high ? lhs.low > rhs_u.low : lhs.high > rhs_u.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) > rhs.low; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 lhs_u {lhs}; + return lhs_u.high == rhs.high ? lhs_u.low > rhs.low : lhs_u.high > rhs.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const uint128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high > UINT64_C(0) || lhs.low > static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const UnsignedInteger lhs, const uint128 rhs) noexcept { return rhs.high == UINT64_C(0) && static_cast(lhs) > rhs.low; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>(const uint128 lhs, const uint128 rhs) noexcept { #if defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -1026,45 +969,27 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs > static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) > rhs; -} -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - return lhs > static_cast(rhs); + return lhs > static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) > rhs; + return static_cast(lhs) > rhs; } -#else - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return lhs > static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) > rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -1073,52 +998,32 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONS //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return rhs < 0 || lhs.high > UINT64_C(0) || lhs.low >= static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 rhs_u {rhs}; + return lhs.high == rhs_u.high ? lhs.low >= rhs_u.low : lhs.high > rhs_u.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - - return lhs >= 0 && rhs.high == UINT64_C(0) && static_cast(lhs) >= rhs.low; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + const uint128 lhs_u {lhs}; + return lhs_u.high == rhs.high ? lhs_u.low >= rhs.low : lhs_u.high > rhs.high; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const uint128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high > UINT64_C(0) || lhs.low >= static_cast(rhs); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const UnsignedInteger lhs, const uint128 rhs) noexcept { return rhs.high == UINT64_C(0) && static_cast(lhs) >= rhs.low; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator>=(const uint128 lhs, const uint128 rhs) noexcept { #if defined(__clang__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -1181,45 +1086,27 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs >= static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) >= rhs; -} - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - return lhs >= static_cast(rhs); + return lhs >= static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) >= rhs; + return static_cast(lhs) >= rhs; } -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return lhs >= static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR bool operator>=(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - return true; + return static_cast(lhs) >= rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 @@ -1229,7 +1116,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONS #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const uint128 lhs, const uint128 rhs) noexcept { if (lhs < rhs) { @@ -1246,7 +1133,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const uint128 lhs, const UnsignedInteger rhs) noexcept { if (lhs < rhs) { @@ -1263,7 +1150,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const UnsignedInteger lhs, const uint128 rhs) noexcept { if (lhs < rhs) { @@ -1280,10 +1167,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - if (lhs < rhs) { return std::strong_ordering::less; @@ -1296,22 +1181,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< { return std::strong_ordering::greater; } - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return std::strong_ordering::less; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - if (lhs < rhs) { return std::strong_ordering::less; @@ -1324,15 +1198,6 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< { return std::strong_ordering::greater; } - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Compare Error"); - static_cast(lhs); - static_cast(rhs); - return std::strong_ordering::less; - - #endif } #endif @@ -1341,7 +1206,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator< // Not Operator //===================================== -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator~(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator~(const uint128 rhs) noexcept { return {~rhs.high, ~rhs.low}; } @@ -1351,111 +1216,67 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator|(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return {lhs.high | (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low | static_cast(rhs)}; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator|(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return {rhs.high | (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low | static_cast(lhs)}; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator|(const uint128 lhs, const UnsignedInteger rhs) noexcept { return {lhs.high, lhs.low | static_cast(rhs)}; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator|(const UnsignedInteger lhs, const uint128 rhs) noexcept { return {rhs.high, rhs.low | static_cast(lhs)}; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator|(const uint128 lhs, const uint128 rhs) noexcept { return {lhs.high | rhs.high, lhs.low | rhs.low}; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs | static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) | rhs; -} +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator|(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return lhs | static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator|(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return static_cast(lhs) | rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator|(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs | static_cast(rhs); + return lhs | static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator|(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator|(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) | rhs; + return static_cast(lhs) | rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator|=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator|=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this | rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator|=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator|=(const uint128 rhs) noexcept { *this = *this | rhs; return *this; @@ -1464,12 +1285,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator|=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator|=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - *this = *this | rhs; return *this; } @@ -1481,112 +1298,68 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator|=( //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator&(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return {lhs.high & (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low & static_cast(rhs)}; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator&(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return {rhs.high & (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low & static_cast(lhs)}; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator&(const uint128 lhs, const UnsignedInteger rhs) noexcept { - return {lhs.high, lhs.low & static_cast(rhs)}; + return {UINT64_C(0), lhs.low & static_cast(rhs)}; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator&(const UnsignedInteger lhs, const uint128 rhs) noexcept { - return {rhs.high, rhs.low & static_cast(lhs)}; + return {UINT64_C(0), rhs.low & static_cast(lhs)}; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator&(const uint128 lhs, const uint128 rhs) noexcept { return {lhs.high & rhs.high, lhs.low & rhs.low}; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs & static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) & rhs; -} +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator&(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return lhs & static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator&(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return static_cast(lhs) & rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator&(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs & static_cast(rhs); + return lhs & static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator&(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator&(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) & rhs; + return static_cast(lhs) & rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator&=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator&=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this & rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator&=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator&=(const uint128 rhs) noexcept { *this = *this & rhs; return *this; @@ -1595,12 +1368,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator&=(Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator&=(Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - *this = *this & rhs; return *this; } @@ -1613,112 +1382,68 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator&=( //===================================== BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator^(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return {lhs.high ^ (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low ^ static_cast(rhs)}; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator^(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return {rhs.high ^ (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low ^ static_cast(lhs)}; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator^(const uint128 lhs, const UnsignedInteger rhs) noexcept { return {lhs.high, lhs.low ^ static_cast(rhs)}; } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator^(const UnsignedInteger lhs, const uint128 rhs) noexcept { return {rhs.high, rhs.low ^ static_cast(lhs)}; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator^(const uint128 lhs, const uint128 rhs) noexcept { return {lhs.high ^ rhs.high, lhs.low ^ rhs.low}; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 - -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept -{ - return lhs ^ static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) ^ rhs; -} +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator^(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return lhs ^ static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator^(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return static_cast(lhs) ^ rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator^(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs ^ static_cast(rhs); + return lhs ^ static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator^(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator^(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) ^ rhs; + return static_cast(lhs) ^ rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator^=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator^=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this ^ rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator^=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator^=(const uint128 rhs) noexcept { *this = *this ^ rhs; return *this; @@ -1727,12 +1452,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator^=(Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator^=(Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - *this = *this ^ rhs; return *this; } @@ -1746,25 +1467,13 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator^=( namespace detail { template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t default_ls_impl(const uint128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 default_ls_impl(const uint128 lhs, const Integer rhs) noexcept { static_assert(std::is_integral::value, "Needs to be a builtin type"); - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (rhs < 0 || rhs >= 128) - { - return {0, 0}; - } - } - else - { - if (rhs >= 128) - { - return {0, 0}; - } - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators. In a + // constant expression the compiler diagnoses it; at runtime it is unspecified. if (rhs == 0) { return lhs; @@ -1787,28 +1496,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t default_ls_impl(cons } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_ls_impl(const uint128_t lhs, const T rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128 intrinsic_ls_impl(const uint128 lhs, const T rhs) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs >= 128 || rhs < 0)) - { - return {0, 0}; - } - } - else - { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs >= 128)) - { - return {0, 0}; - } - } - - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) - { - return lhs; - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators; delegate + // straight to the native type so we produce identical results. #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 # if defined(__aarch64__) @@ -1822,8 +1514,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_ls_impl(const uint12 std::memcpy(&value, &lhs, sizeof(builtin_u128)); const auto res {value << rhs}; - uint128_t return_value; - std::memcpy(&return_value, &res, sizeof(uint128_t)); + uint128 return_value; + std::memcpy(&return_value, &res, sizeof(uint128)); return return_value; #if defined(__GNUC__) && __GNUC__ >= 8 @@ -1838,6 +1530,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_ls_impl(const uint12 #else + if (rhs == 0) + { + return lhs; + } + if (rhs == 64) { return {lhs.low, 0}; @@ -1859,7 +1556,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_ls_impl(const uint12 } // namespace detail BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator<<(const uint128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator<<(const uint128 lhs, const Integer rhs) noexcept { #ifndef BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION @@ -1881,78 +1578,47 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator<<(const uin // A number of different overloads to ensure that we return the same type as the builtins would -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator<<(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator<<(const uint128 lhs, const uint128 rhs) noexcept { - if (rhs.high > UINT64_C(0) || rhs.low >= UINT64_C(128)) - { - return uint128_t{0}; - } - + // Out-of-range counts (>= 128 or with the high word set) are undefined, + // matching the built-in operators; forward the count to the scalar overload. return lhs << rhs.low; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_u128 operator<<(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_u128 operator<<(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - constexpr auto bit_width {sizeof(detail::builtin_u128 ) * 8}; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } - - return lhs << rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs << static_cast(rhs.low); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_i128 operator<<(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_i128 operator<<(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - constexpr auto bit_width {sizeof(detail::builtin_u128) * 8}; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } - - return lhs << rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs << static_cast(rhs.low); } #endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(SignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int operator<<(const SignedInteger lhs, const uint128_t rhs) noexcept -{ - constexpr auto bit_width {sizeof(SignedInteger) * 8}; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } +// A shift takes its value and its result type from the left operand after integral promotion, +// and only the count from the right, exactly as the builtin does - return static_cast(lhs) << rhs.low; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr unsigned int operator<<(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(Integer) * 8 <= 64), bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::promoted_t operator<<(const Integer lhs, const uint128 rhs) noexcept { - constexpr auto bit_width {sizeof(UnsignedInteger) * 8}; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } - - return static_cast(lhs) << rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return static_cast>(lhs) << rhs.low; } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator<<=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator<<=(const Integer rhs) noexcept { *this = *this << rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator<<=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator<<=(const uint128 rhs) noexcept { *this = *this << rhs; return *this; @@ -1961,7 +1627,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator<<=(Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator<<=(Integer rhs) noexcept { *this = *this << rhs; return *this; @@ -1976,23 +1642,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator<<= namespace detail { template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t default_rs_impl(const uint128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 default_rs_impl(const uint128 lhs, const Integer rhs) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (rhs < 0 || rhs >= 128) - { - return {0, 0}; - } - } - else - { - if (rhs >= 128) - { - return {0, 0}; - } - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators. In a + // constant expression the compiler diagnoses it; at runtime it is unspecified. if (rhs == 0) { return lhs; @@ -2015,28 +1669,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t default_rs_impl(cons } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_rs_impl(const uint128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128 intrinsic_rs_impl(const uint128 lhs, const Integer rhs) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) - { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs >= 128 || rhs < 0)) - { - return {0, 0}; - } - } - else - { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs >= 128)) - { - return {0, 0}; - } - } - - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0)) - { - return lhs; - } - + // A shift by a negative amount or by an amount >= 128 (the operand width) is + // undefined behavior, exactly as for the built-in shift operators; delegate + // straight to the native type so we produce identical results. #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 # ifdef __aarch64__ @@ -2050,8 +1687,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_rs_impl(const uint12 std::memcpy(&value, &lhs, sizeof(builtin_u128)); const auto res {value >> rhs}; - uint128_t return_value; - std::memcpy(&return_value, &res, sizeof(uint128_t)); + uint128 return_value; + std::memcpy(&return_value, &res, sizeof(uint128)); return return_value; #if defined(__GNUC__) && __GNUC__ >= 8 @@ -2064,6 +1701,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_rs_impl(const uint12 #else + if (rhs == 0) + { + return lhs; + } + if (rhs == 64) { return {0, lhs.high}; @@ -2084,7 +1726,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128_t intrinsic_rs_impl(const uint12 } // namespace detail BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator>>(const uint128_t lhs, const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator>>(const uint128 lhs, const Integer rhs) noexcept { #ifndef BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION @@ -2104,78 +1746,47 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator>>(const uin #endif } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator>>(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator>>(const uint128 lhs, const uint128 rhs) noexcept { - if (rhs.high > UINT64_C(0) || rhs.low >= UINT64_C(128)) - { - return uint128_t{0}; - } - + // Out-of-range counts (>= 128 or with the high word set) are undefined, + // matching the built-in operators; forward the count to the scalar overload. return lhs >> rhs.low; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_u128 operator>>(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_u128 operator>>(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - constexpr auto bit_width = sizeof(detail::builtin_u128) * 8; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } - - return lhs >> rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs >> static_cast(rhs.low); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::builtin_i128 operator>>(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR detail::builtin_i128 operator>>(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - constexpr auto bit_width = sizeof(detail::builtin_i128) * 8; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } - - return lhs >> rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return lhs >> static_cast(rhs.low); } #endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(SignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int operator>>(const SignedInteger lhs, const uint128_t rhs) noexcept -{ - constexpr auto bit_width = sizeof(SignedInteger) * 8; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } - - return static_cast(lhs) >> rhs.low; -} +// A shift takes its value and its result type from the left operand after integral promotion, +// and only the count from the right, exactly as the builtin does -BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr unsigned operator>>(UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT template && (sizeof(Integer) * 8 <= 64), bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr detail::promoted_t operator>>(const Integer lhs, const uint128 rhs) noexcept { - constexpr auto bit_width = sizeof(UnsignedInteger) * 8; - - if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) - { - return 0; - } - - return static_cast(lhs) >> rhs.low; + // Out-of-range counts are undefined, matching the built-in operators. + return static_cast>(lhs) >> rhs.low; } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator>>=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator>>=(const Integer rhs) noexcept { *this = *this >> rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator>>=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator>>=(const uint128 rhs) noexcept { *this = *this >> rhs; return *this; @@ -2184,7 +1795,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator>>=(Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator>>=(Integer rhs) noexcept { *this = *this >> rhs; return *this; @@ -2196,7 +1807,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator>>= // Increment Operator //===================================== -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator++() noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator++() noexcept { if (++low == UINT64_C(0)) { @@ -2206,7 +1817,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t uint128_t::operator++(int) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 uint128::operator++(int) noexcept { const auto temp {*this}; ++(*this); @@ -2217,7 +1828,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t uint128_t::operator+ // Decrement Operator //===================================== -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator--() noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator--() noexcept { if (--low == UINT64_MAX) { @@ -2227,7 +1838,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t uint128_t::operator--(int) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 uint128::operator--(int) noexcept { const auto temp {*this}; --(*this); @@ -2240,18 +1851,22 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t uint128_t::operator- namespace impl { -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128_t default_add(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128 default_add(const uint128 lhs, const uint128 rhs) noexcept { #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN_ADD_OVERFLOW) && (defined(__i386__) || (defined(__aarch64__) && !defined(__APPLE__)) || defined(__arm__) || (defined(__s390__) || defined(__s390x__))) - uint128_t res {}; + uint128 res {}; res.high = lhs.high + rhs.high + __builtin_add_overflow(lhs.low, rhs.low, &res.low); return res; + #elif (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__))) && !defined(_MSC_VER) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) + + return static_cast(static_cast(lhs) + static_cast(rhs)); + #else - uint128_t temp {lhs.high + rhs.high, lhs.low + rhs.low}; + uint128 temp {lhs.high + rhs.high, lhs.low + rhs.low}; if (temp.low < lhs.low) { @@ -2263,18 +1878,18 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128_t default_add(const uint128_t lhs, const std::uint64_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128 default_add(const uint128 lhs, const std::uint64_t rhs) noexcept { #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN_ADD_OVERFLOW) && (defined(__i386__) || (defined(__aarch64__) && !defined(__APPLE__)) || defined(__arm__) || (defined(__s390__) || defined(__s390x__))) - uint128_t res {}; + uint128 res {}; res.high = lhs.high + __builtin_add_overflow(lhs.low, rhs, &res.low); return res; #else - uint128_t temp {lhs.high, lhs.low + rhs}; + uint128 temp {lhs.high, lhs.low + rhs}; if (temp.low < lhs.low) { @@ -2286,22 +1901,22 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128_t default_sub(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128 default_sub(const uint128 lhs, const uint128 rhs) noexcept { #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN_SUB_OVERFLOW) && (defined(__i386__) || defined(__arm__) || (defined(__s390__) || defined(__s390x__))) - uint128_t res {}; + uint128 res {}; res.high = lhs.high - rhs.high - __builtin_sub_overflow(lhs.low, rhs.low, &res.low); return res; #elif (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__))) && !defined(_MSC_VER) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) - return static_cast(static_cast(lhs) - static_cast(rhs)); + return static_cast(static_cast(lhs) - static_cast(rhs)); #else - uint128_t temp {lhs.high - rhs.high, lhs.low - rhs.low}; + uint128 temp {lhs.high - rhs.high, lhs.low - rhs.low}; // Check for carry if (lhs.low < rhs.low) @@ -2314,18 +1929,18 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE #endif } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128_t default_sub(const uint128_t lhs, const std::uint64_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128 default_sub(const uint128 lhs, const std::uint64_t rhs) noexcept { #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_BUILTIN_SUB_OVERFLOW) && (defined(__i386__) || (defined(__aarch64__) && !defined(__APPLE__)) || defined(__arm__) || (defined(__s390__) || defined(__s390x__))) - uint128_t res {}; + uint128 res {}; res.high = lhs.high - __builtin_sub_overflow(lhs.low, rhs, &res.low); return res; #else - uint128_t temp {lhs.high, lhs.low - rhs}; + uint128 temp {lhs.high, lhs.low - rhs}; // Check for carry if (lhs.low < rhs) @@ -2346,39 +1961,17 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator+(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return rhs < 0 ? impl::default_sub(lhs, -static_cast(rhs)) : impl::default_add(lhs, static_cast(rhs)); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator+(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return lhs < 0 ? impl::default_sub(rhs, -static_cast(lhs)) : impl::default_add(rhs, static_cast(lhs)); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } #ifdef _MSC_VER @@ -2386,78 +1979,56 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const Sign #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator+(const uint128 lhs, const UnsignedInteger rhs) noexcept { return impl::default_add(lhs, static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator+(const UnsignedInteger lhs, const uint128 rhs) noexcept { return impl::default_add(rhs, static_cast(lhs)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator+(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator+(const uint128 lhs, const uint128 rhs) noexcept { return impl::default_add(lhs, rhs); } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator+(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator+(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - return impl::default_add(lhs, static_cast(rhs)); + return impl::default_add(lhs, static_cast(rhs)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator+(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator+(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - return impl::default_add(static_cast(lhs), rhs); + return impl::default_add(static_cast(lhs), rhs); } -#else - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator+(const uint128_t, const T) noexcept -{ - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; -} -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator+(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator+(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return impl::default_add(lhs, static_cast(rhs)); } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator+(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator+(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return impl::default_add(lhs, static_cast(rhs)); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator+(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept -{ - return impl::default_add(static_cast(lhs), rhs); + return impl::default_add(static_cast(lhs), rhs); } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator+=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator+=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this + rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator+=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator+=(const uint128 rhs) noexcept { *this = *this + rhs; return *this; @@ -2466,12 +2037,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator+=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator+=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - *this = *this + rhs; return *this; } @@ -2489,39 +2056,17 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator+=( #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator-(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return rhs < 0 ? impl::default_add(lhs, -static_cast(rhs)) : impl::default_sub(lhs, static_cast(rhs)); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator-(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - return lhs < 0 ? impl::default_sub(-rhs, -static_cast(lhs)) : impl::default_add(-rhs, static_cast(lhs)); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } #ifdef _MSC_VER @@ -2529,78 +2074,56 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const Sign #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator-(const uint128 lhs, const UnsignedInteger rhs) noexcept { return impl::default_sub(lhs, static_cast(rhs)); } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator-(const UnsignedInteger lhs, const uint128 rhs) noexcept { return impl::default_add(-rhs, static_cast(lhs)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator-(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator-(const uint128 lhs, const uint128 rhs) noexcept { return impl::default_sub(lhs, rhs); } #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator-(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator-(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - return lhs - static_cast(rhs); + return lhs - static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator-(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator-(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) - rhs; + return static_cast(lhs) - rhs; } -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator-(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator-(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return lhs - static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator-(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator-(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; -} - -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator-(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs - static_cast(rhs); -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator-(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) - rhs; + return static_cast(lhs) - rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator-=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator-=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this - rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator-=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator-=(const uint128 rhs) noexcept { *this = *this - rhs; return *this; @@ -2609,12 +2132,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator-=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator-=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - *this = *this - rhs; return *this; } @@ -2634,9 +2153,9 @@ namespace detail { #if defined(_M_AMD64) && !defined(__GNUC__) -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128_t msvc_mul(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128 msvc_mul(const uint128 lhs, const uint128 rhs) noexcept { - uint128_t result {}; + uint128 result {}; result.low = _umul128(lhs.low, rhs.low, &result.high); result.high += lhs.low * rhs.high; result.high += lhs.high * rhs.low; @@ -2644,27 +2163,27 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE return result; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128_t msvc_mul(const uint128_t lhs, const std::uint64_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128 msvc_mul(const uint128 lhs, const std::uint64_t rhs) noexcept { - uint128_t result {}; + uint128 result {}; result.low = _umul128(lhs.low, rhs, &result.high); result.high += lhs.high * rhs; return result; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128_t msvc_mul(const uint128_t lhs, const std::uint32_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128 msvc_mul(const uint128 lhs, const std::uint32_t rhs) noexcept { - uint128_t result {}; + uint128 result {}; result.low = _umul128(lhs.low, static_cast(rhs), &result.high); result.high += lhs.high * static_cast(rhs); return result; } -#elif defined(_M_ARM64) +#elif defined(_M_ARM64) && !defined(__GNUC__) -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128_t msvc_mul(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128 msvc_mul(const uint128 lhs, const uint128 rhs) noexcept { const auto low_low{lhs.low * rhs.low}; const auto high_low_low{__umulh(lhs.low, rhs.low)}; @@ -2677,7 +2196,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE return {high, low_low}; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128_t msvc_mul(const uint128_t lhs, const std::uint64_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128 msvc_mul(const uint128 lhs, const std::uint64_t rhs) noexcept { const auto low{lhs.low * rhs}; const auto high{__umulh(lhs.low, rhs) + (lhs.high * rhs)}; @@ -2685,7 +2204,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE return {high, low}; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128_t msvc_mul(const uint128_t lhs, const std::uint32_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE uint128 msvc_mul(const uint128 lhs, const std::uint32_t rhs) noexcept { const auto low{lhs.low * rhs}; const auto high{__umulh(lhs.low, static_cast(rhs)) + (lhs.high * rhs)}; @@ -2696,7 +2215,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE #endif // MSVC implementations template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128_t default_mul(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE constexpr uint128 default_mul(const uint128 lhs, const UnsignedInteger rhs) noexcept { #if (defined(__aarch64__) || defined(__x86_64__) || defined(__PPC__) || defined(__powerpc__)) && defined(__GNUC__) && defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) @@ -2707,14 +2226,14 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE detail::builtin_u128 new_lhs {}; detail::builtin_u128 new_rhs {}; - std::memcpy(&new_lhs, &lhs, sizeof(uint128_t)); + std::memcpy(&new_lhs, &lhs, sizeof(uint128)); std::memcpy(&new_rhs, &rhs, sizeof(UnsignedInteger)); const auto res {new_lhs * new_rhs}; - uint128_t library_res {}; + uint128 library_res {}; - std::memcpy(&library_res, &res, sizeof(uint128_t)); + std::memcpy(&library_res, &res, sizeof(uint128)); return library_res; } @@ -2722,39 +2241,31 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE # elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) # define BOOST_DECIMAL_DETAIL_INT128_HIDE_MUL - return static_cast(static_cast(lhs) * static_cast(rhs)); + return static_cast(static_cast(lhs) * static_cast(rhs)); # endif - #elif (defined(__s390x__) || defined(__s390x__)) && defined(__GNUC__) - # define BOOST_DECIMAL_DETAIL_INT128_HIDE_MUL - - return static_cast(static_cast(lhs) * static_cast(rhs)); - - #elif ((defined(_M_AMD64) && !defined(__GNUC__)) || defined(_M_ARM64)) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + // s390x intentionally falls through to the synthetic low_word_mul below. Casting to builtin_u128 + // makes GCC reconstruct the value through a vector-unit stack round-trip that is several times + // slower, and the memcpy path is unsafe for the narrow (scalar rhs) overloads on big-endian. + #elif (defined(_M_AMD64) || defined(_M_ARM64)) && !defined(__GNUC__) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) { return msvc_mul(lhs, rhs); } + #elif defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(__s390__) && !defined(__s390x__) + # define BOOST_DECIMAL_DETAIL_INT128_HIDE_MUL + + return static_cast(static_cast(lhs) * static_cast(rhs)); + #endif // We need to hide this if we use a non-const eval method above to avoid a litany of cross-platform warnings #ifndef BOOST_DECIMAL_DETAIL_INT128_HIDE_MUL - constexpr std::size_t rhs_words_needed {std::is_same::value ? 1 : - std::is_same::value ? 2 : - std::is_same::value ? 4 : 0}; - - static_assert(rhs_words_needed != 0, "Must be 32, 64 or 128 bit unsigned integer"); - - std::uint32_t lhs_words[4] {}; - std::uint32_t rhs_words[rhs_words_needed] {}; - to_words(lhs, lhs_words); - to_words(rhs, rhs_words); - - return knuth_multiply(lhs_words, rhs_words); + return low_word_mul(lhs, rhs); #else #undef BOOST_DECIMAL_DETAIL_INT128_HIDE_MUL @@ -2773,51 +2284,29 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_FORCE_INLINE #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator*(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; const auto abs_rhs {rhs < 0 ? -static_cast(rhs) : static_cast(rhs)}; const auto res {detail::default_mul(lhs, abs_rhs)}; return rhs < 0 ? -res : res; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator*(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; const auto abs_lhs {lhs < 0 ? -static_cast(lhs) : static_cast(lhs)}; const auto res {detail::default_mul(rhs, abs_lhs)}; return lhs < 0 ? -res : res; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif } BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator*(const uint128 lhs, const UnsignedInteger rhs) noexcept { return detail::default_mul(lhs, static_cast(rhs)); } @@ -2827,78 +2316,62 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const uint #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator*(const UnsignedInteger lhs, const uint128 rhs) noexcept { return detail::default_mul(rhs, static_cast(lhs)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator*(const uint128 lhs, const uint128 rhs) noexcept { return detail::default_mul(lhs, rhs); } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator*(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - const auto abs_rhs {rhs < 0 ? -static_cast(rhs) : static_cast(rhs)}; - const auto res {lhs * abs_rhs}; + const detail::builtin_u128 rhs_bits {static_cast(rhs)}; + const bool rhs_negative {static_cast(static_cast(rhs_bits >> static_cast(64U))) < 0}; + const uint128 rhs_u {rhs_bits}; + const uint128 abs_rhs {rhs_negative ? -rhs_u : rhs_u}; + const uint128 res {lhs * abs_rhs}; - return rhs < 0 ? -res : res; + return rhs_negative ? -res : res; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator*(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - const auto abs_lhs {lhs < 0 ? -static_cast(lhs) : static_cast(lhs)}; - const auto res {abs_lhs * rhs}; - - return lhs < 0 ? -res : res; -} - -#else - -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const uint128_t, const T) noexcept -{ - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; -} + const detail::builtin_u128 lhs_bits {static_cast(lhs)}; + const bool lhs_negative {static_cast(static_cast(lhs_bits >> static_cast(64U))) < 0}; + const uint128 lhs_u {lhs_bits}; + const uint128 abs_lhs {lhs_negative ? -lhs_u : lhs_u}; + const uint128 res {abs_lhs * rhs}; -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const T, const uint128_t) noexcept -{ - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return lhs_negative ? -res : res; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator*(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs * static_cast(rhs); + return lhs * static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator*(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator*(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) * rhs; + return static_cast(lhs) * rhs; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator*=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator*=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this * rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator*=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator*=(const uint128 rhs) noexcept { *this = *this * rhs; return *this; @@ -2907,12 +2380,8 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator*=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator*=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - *this = *this * rhs; return *this; } @@ -2925,63 +2394,42 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator*=( // For div we need forward declarations since we mix and match the arguments BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(uint128_t lhs, SignedInteger rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(uint128 lhs, SignedInteger rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(SignedInteger lhs, uint128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(SignedInteger lhs, uint128 rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(uint128_t lhs, UnsignedInteger rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(uint128 lhs, UnsignedInteger rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(UnsignedInteger lhs, uint128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(UnsignedInteger lhs, uint128 rhs) noexcept; -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(uint128_t lhs, uint128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(uint128 lhs, uint128 rhs) noexcept; template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; - return rhs < 0 ? lhs / static_cast(rhs) : lhs / static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return rhs < 0 ? lhs / static_cast(rhs) : lhs / static_cast(rhs); } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; - return lhs < 0 ? static_cast(lhs) / rhs : static_cast(lhs) / rhs; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return lhs < 0 ? static_cast(lhs) / rhs : static_cast(lhs) / rhs; } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(const uint128 lhs, const UnsignedInteger rhs) noexcept { using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0U)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } if (lhs < rhs) @@ -2989,7 +2437,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint return {0, 0}; } - uint128_t quotient {}; + uint128 quotient {}; detail::one_word_div(lhs, static_cast(rhs), quotient); @@ -2997,13 +2445,14 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(const UnsignedInteger lhs, const uint128 rhs) noexcept { using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0U)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } if (lhs < rhs) @@ -3014,125 +2463,87 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const Unsi return {0, static_cast(lhs) / rhs.low}; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator/(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator/(const uint128 lhs, const uint128 rhs) noexcept { if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0U)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } if (lhs < rhs) { return {0, 0}; } - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(__s390__) && !defined(__s390x__) - else - { - // On x86-64 GCC/Clang the __int128 builtin lowers to __udivti3 which - // runs Knuth-D (~70-90c). Route runtime calls to the Moller-Granlund - // 2x2 divide that backs the MSVC path (~25-30c). constexpr context - // still uses the builtin (inline asm is not constexpr). - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && (defined(__GNUC__) || defined(__clang__)) - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) - { - // uint128/uint64 short-circuit: div_mod_intrinsic's 2x2 path - // forces countl_zero(divisor.high)=64 -> shift by 64, producing a - // correct but extra-work result. one_word_div hits a single divq. - if (rhs.high == 0U) - { - if (lhs.high == 0U) - { - return {0, lhs.low / rhs.low}; - } - uint128_t quotient {}; - detail::one_word_div(lhs, rhs.low, quotient); - return quotient; - } - uint128_t remainder{}; - return detail::impl::div_mod_intrinsic(lhs, rhs, remainder); - } - #endif - return static_cast(static_cast(lhs) / static_cast(rhs)); - } - #else - else if (rhs.high != 0U) - { - return detail::knuth_div(lhs, rhs); - } - else + + // A divisor that fits in 64 bits is handled by the hardware-accelerated narrow path. This + // beats the native 128/128 divide for this common case on every platform (it avoids the + // out-of-line __udivti3 call on GCC/Clang and uses divq / _udiv128 directly where present). + if (rhs.high == 0U) { if (lhs.high == 0U) { return {0, lhs.low / rhs.low}; } - else - { - uint128_t quotient {}; - detail::one_word_div(lhs, rhs.low, quotient); + uint128 quotient {}; + detail::one_word_div(lhs, rhs.low, quotient); + return quotient; + } + + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(__s390__) && !defined(__s390x__) - return quotient; - } + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + // Decimal-local: at runtime the divq based knuth_div beats the out-of-line __udivti3 call + if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) + { + return detail::knuth_div(lhs, rhs); } #endif -} -#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) + return static_cast(static_cast(lhs) / static_cast(rhs)); -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator/(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs / static_cast(rhs); -} + #else -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator/(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) / rhs; + return detail::knuth_div(lhs, rhs); + + #endif } -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator/(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator/(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs / static_cast(rhs); + return lhs / static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator/(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator/(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) / rhs; + return static_cast(lhs) / rhs; } -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator/(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator/(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return lhs / static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator/(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator/(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return static_cast(lhs) / rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator/=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator/=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this / rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator/=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator/=(const uint128 rhs) noexcept { *this = *this / rhs; return *this; @@ -3141,87 +2552,68 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator/=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator/=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - *this = *this / rhs; return *this; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + //===================================== // Modulo Operator //===================================== // For div we need forward declarations since we mix and match the arguments BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(uint128_t lhs, SignedInteger rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(uint128 lhs, SignedInteger rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(SignedInteger lhs, uint128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(SignedInteger lhs, uint128 rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(uint128_t lhs, UnsignedInteger rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(uint128 lhs, UnsignedInteger rhs) noexcept; BOOST_DECIMAL_DETAIL_INT128_EXPORT template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(UnsignedInteger lhs, uint128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(UnsignedInteger lhs, uint128 rhs) noexcept; -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(uint128_t lhs, uint128_t rhs) noexcept; +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(uint128 lhs, uint128 rhs) noexcept; template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const uint128_t lhs, const SignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(const uint128 lhs, const SignedInteger rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; - return rhs < 0 ? lhs % static_cast(rhs) : lhs % static_cast(rhs); - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return rhs < 0 ? lhs % static_cast(rhs) : lhs % static_cast(rhs); } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const SignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(const SignedInteger lhs, const uint128 rhs) noexcept { - #ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - using eval_type = detail::evaluation_type_t; - return lhs < 0 ? static_cast(lhs) % rhs : static_cast(lhs) % rhs; - - #else - - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - static_cast(lhs); - static_cast(rhs); - return true; - - #endif + return lhs < 0 ? static_cast(lhs) % rhs : static_cast(lhs) % rhs; } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const uint128_t lhs, const UnsignedInteger rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(const uint128 lhs, const UnsignedInteger rhs) noexcept { using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0U)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } if (lhs.high != 0) { - uint128_t quotient {}; - uint128_t remainder {}; + uint128 quotient {}; + uint128 remainder {}; detail::one_word_div(lhs, static_cast(rhs), quotient, remainder); @@ -3234,13 +2626,14 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const uint } template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const UnsignedInteger lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(const UnsignedInteger lhs, const uint128 rhs) noexcept { using eval_type = detail::evaluation_type_t; if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0U)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } else if (rhs > lhs) { @@ -3250,125 +2643,90 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const Unsi return {0, static_cast(lhs) % rhs.low}; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator%(const uint128_t lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator%(const uint128 lhs, const uint128 rhs) noexcept { if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(rhs == 0U)) { - return {0, 0}; + // Division or remainder by zero is undefined behavior for the builtin __int128 types (a hardware trap). We match that: marking it unreachable keeps codegen branch-free and vectorizable. + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; } - else if (rhs > lhs) + if (rhs > lhs) { return lhs; } - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(__s390__) && !defined(__s390x__) - else - { - // See operator/ for the rationale on routing x86-64 GCC/Clang - // runtime calls through the Moller-Granlund 2x2 divide. - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) && (defined(__GNUC__) || defined(__clang__)) - if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) - { - // uint128/uint64 short-circuit (see operator/ for the rationale). - if (rhs.high == 0U) - { - if (lhs.high == 0U) - { - return {0, lhs.low % rhs.low}; - } - uint128_t quotient {}; - uint128_t remainder {}; - detail::one_word_div(lhs, rhs.low, quotient, remainder); - return remainder; - } - uint128_t remainder{}; - detail::impl::div_mod_intrinsic(lhs, rhs, remainder); - return remainder; - } - #endif - return static_cast(static_cast(lhs) % static_cast(rhs)); - } - #else - else if (rhs.high != 0U) - { - uint128_t remainder {}; - detail::knuth_div(lhs, rhs, remainder); - return remainder; - } - else + + // A divisor that fits in 64 bits is handled by the hardware-accelerated narrow path, which + // beats the native 128/128 divide for this common case on every platform. + if (rhs.high == 0U) { if (lhs.high == 0U) { return {0, lhs.low % rhs.low}; } - else - { - uint128_t quotient {}; - uint128_t remainder {}; - detail::one_word_div(lhs, rhs.low, quotient, remainder); + uint128 quotient {}; + uint128 remainder {}; + detail::one_word_div(lhs, rhs.low, quotient, remainder); + return remainder; + } - return remainder; - } + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) && !defined(__s390__) && !defined(__s390x__) + + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + // Decimal-local: at runtime the divq based knuth_div beats the out-of-line __udivti3 call + if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(lhs)) + { + uint128 remainder {}; + detail::knuth_div(lhs, rhs, remainder); + return remainder; } #endif -} -#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) + return static_cast(static_cast(lhs) % static_cast(rhs)); -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator%(const uint128_t lhs, const detail::builtin_u128 rhs) noexcept -{ - return lhs % static_cast(rhs); -} + #else -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator%(const detail::builtin_u128 lhs, const uint128_t rhs) noexcept -{ - return static_cast(lhs) % rhs; + uint128 remainder {}; + detail::knuth_div(lhs, rhs, remainder); + return remainder; + + #endif } -#ifdef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator%(const uint128_t lhs, const detail::builtin_i128 rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator%(const uint128 lhs, const detail::builtin_u128 rhs) noexcept { - return lhs % static_cast(rhs); + return lhs % static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator%(const detail::builtin_i128 lhs, const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator%(const detail::builtin_u128 lhs, const uint128 rhs) noexcept { - return static_cast(lhs) % rhs; + return static_cast(lhs) % rhs; } -#else -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator%(const uint128_t, const T) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator%(const uint128 lhs, const detail::builtin_i128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return lhs % static_cast(rhs); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT template ::value, bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128_t operator%(const T, const uint128_t) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE BOOST_DECIMAL_DETAIL_INT128_BUILTIN_CONSTEXPR uint128 operator%(const detail::builtin_i128 lhs, const uint128 rhs) noexcept { - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - return {0, 0}; + return static_cast(lhs) % rhs; } -#endif // BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator%=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator%=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(detail::is_unsigned_integer_v, "Sign Conversion Error"); - #endif - *this = *this % rhs; return *this; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator%=(const uint128_t rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator%=(const uint128 rhs) noexcept { *this = *this % rhs; return *this; @@ -3377,18 +2735,159 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t& uint128_t::operator #ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 template -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128_t& uint128_t::operator%=(const Integer rhs) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE inline uint128& uint128::operator%=(const Integer rhs) noexcept { - #ifndef BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION - static_assert(!std::numeric_limits::is_signed, "Sign Conversion Error"); - #endif - * this = *this % rhs; return *this; } #endif // BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128 +//===================================== +// Built-in Integer Compound Assignment +//===================================== + +// Compound assignment with a built-in integer on the left. +// The builtin applies the operation to the common type of the two operands and converts +// the result back to the type of the left operand, so each of these is the binary operator +// above followed by that conversion, which matches what the builtin 128-bit integer does. +// detail/traits.hpp defines which types Integer may be + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4804) // Unsafe use of type bool in operation +#endif + +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(op, compound_op) \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr Integer& operator compound_op(Integer& lhs, const uint128 rhs) noexcept \ + { \ + lhs = static_cast(lhs op rhs); \ + return lhs; \ + } + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(|, |=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(&, &=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(^, ^=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(+, +=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(-, -=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(*, *=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(/, /=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(%, %=) + +// The shifts take the value from the left operand alone, so only the count comes from rhs +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(<<, <<=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP(>>, >>=) + +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_INTEGER_COMPOUND_OP + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + +//===================================== +// Floating Point Operators +//===================================== + +// The usual arithmetic conversions convert the integer operand to the floating point type +// before the operation is applied, so each of these computes exactly what the builtin +// 128-bit integer computes for the same expression. +// detail/traits.hpp defines which types Float may be + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#endif + +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(op, return_type) \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr return_type operator op(const uint128 lhs, const Float rhs) noexcept \ + { \ + return static_cast(lhs) op rhs; \ + } \ + \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr return_type operator op(const Float lhs, const uint128 rhs) noexcept \ + { \ + return lhs op static_cast(rhs); \ + } + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(+, Float) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(-, Float) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(*, Float) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(/, Float) + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(==, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(!=, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(<, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(<=, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(>, bool) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(>=, bool) + +// Mixing an integer and a floating point type yields a partial ordering because of NaN +#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP(<=>, std::partial_ordering) + +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR + +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_BINARY_OP + +// Compound assignment converts the result back to uint128, truncating toward zero. +// A result that is NaN or outside the range of the type saturates as the floating point +// constructor does, rather than being undefined as it is for the builtin + +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_COMPOUND_OP(op, compound_op) \ + template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128& uint128::operator compound_op(const Float rhs) noexcept \ + { \ + *this = static_cast(static_cast(*this) op rhs); \ + return *this; \ + } \ + \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr Float& operator compound_op(Float& lhs, const uint128 rhs) noexcept \ + { \ + lhs compound_op static_cast(rhs); \ + return lhs; \ + } + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_COMPOUND_OP(+, +=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_COMPOUND_OP(-, -=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_COMPOUND_OP(*, *=) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_COMPOUND_OP(/, /=) + +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_COMPOUND_OP + +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#elif defined(__clang__) +# pragma clang diagnostic pop +#endif + +// The builtin allows no floating point operand for the modulo, bitwise and shift operators. +// Deleting them keeps that a compile error here, rather than letting the implicit floating +// point constructor silently truncate the operand + +#define BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP(op) \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128 operator op(uint128 lhs, Float rhs) = delete; \ + \ + BOOST_DECIMAL_DETAIL_INT128_EXPORT template \ + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE uint128 operator op(Float lhs, uint128 rhs) = delete; + +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP(%) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP(&) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP(|) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP(^) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP(<<) +BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP(>>) + +#undef BOOST_DECIMAL_DETAIL_INT128_DETAIL_U128_FLOAT_DELETED_OP + namespace detail { template @@ -3439,15 +2938,15 @@ class numeric_limits_impl_u128 static constexpr bool tinyness_before = false; // Member functions - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (min) () -> boost::int128::uint128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto lowest () -> boost::int128::uint128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (max) () -> boost::int128::uint128_t { return {UINT64_MAX, UINT64_MAX}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto epsilon () -> boost::int128::uint128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto round_error () -> boost::int128::uint128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto infinity () -> boost::int128::uint128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto quiet_NaN () -> boost::int128::uint128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto signaling_NaN() -> boost::int128::uint128_t { return {0, 0}; } - BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto denorm_min () -> boost::int128::uint128_t { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (min) () -> boost::int128::uint128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto lowest () -> boost::int128::uint128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto (max) () -> boost::int128::uint128 { return {UINT64_MAX, UINT64_MAX}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto epsilon () -> boost::int128::uint128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto round_error () -> boost::int128::uint128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto infinity () -> boost::int128::uint128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto quiet_NaN () -> boost::int128::uint128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto signaling_NaN() -> boost::int128::uint128 { return {0, 0}; } + BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE static constexpr auto denorm_min () -> boost::int128::uint128 { return {0, 0}; } }; #if !defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L @@ -3460,10 +2959,23 @@ template constexpr bool numeric_limits_impl_u128::has_infinity; template constexpr bool numeric_limits_impl_u128::has_quiet_NaN; template constexpr bool numeric_limits_impl_u128::has_signaling_NaN; -// These members were deprecated in C++23 -#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) +// These members were deprecated in C++23; suppress the deprecation warning rather +// than dropping the definitions. +#if defined(__GNUC__) && __cplusplus > 202002L +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable:4996) +#endif + template constexpr std::float_denorm_style numeric_limits_impl_u128::has_denorm; template constexpr bool numeric_limits_impl_u128::has_denorm_loss; + +#if defined(__GNUC__) && __cplusplus > 202002L +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) #endif template constexpr std::float_round_style numeric_limits_impl_u128::round_style; @@ -3497,7 +3009,7 @@ namespace std { #endif template <> -class numeric_limits : +class numeric_limits : public boost::int128::detail::numeric_limits_impl_u128 {}; #ifdef __clang__ diff --git a/include/boost/decimal/detail/int128/fmt_format.hpp b/include/boost/decimal/detail/int128/fmt_format.hpp index 44063f171..e2f427247 100644 --- a/include/boost/decimal/detail/int128/fmt_format.hpp +++ b/include/boost/decimal/detail/int128/fmt_format.hpp @@ -58,8 +58,7 @@ constexpr auto parse_impl(ParseContext& ctx) int padding_digits = 0; auto sign = sign_option::negative; bool prefix = false; - bool write_as_character = false; - bool character_debug_format = false; + bool zero_pad = false; char fill_char = ' '; auto align = alignment::none; @@ -139,14 +138,14 @@ constexpr auto parse_impl(ParseContext& ctx) ++it; } - // Character presentation type - if (it != ctx.end() && (*it == '?' || *it == 'c')) + // Zero-pad flag (std::format places '0' before the width) + if (it != ctx.end() && *it == '0') { - character_debug_format = *it == '?'; + zero_pad = true; ++it; } - // Check for a padding character + // Parse the width while (it != ctx.end() && *it >= '0' && *it <= '9') { padding_digits = padding_digits * 10 + (*it - '0'); @@ -166,10 +165,6 @@ constexpr auto parse_impl(ParseContext& ctx) is_upper = true; break; - case 'c': - write_as_character = true; - break; - case 'o': base = 8; break; @@ -198,13 +193,13 @@ constexpr auto parse_impl(ParseContext& ctx) BOOST_DECIMAL_DETAIL_INT128_THROW_EXCEPTION(std::logic_error("Expected '}' in format string")); // LCOV_EXCL_LINE } - return std::make_tuple(base, padding_digits, sign, is_upper, prefix, write_as_character, character_debug_format, fill_char, align, it); + return std::make_tuple(base, padding_digits, sign, is_upper, prefix, zero_pad, fill_char, align, it); } template struct is_library_type_impl { - static constexpr bool value {std::is_same::value || std::is_same::value}; + static constexpr bool value {std::is_same::value || std::is_same::value}; }; template @@ -218,8 +213,7 @@ struct formatter sign_option sign; bool is_upper; bool prefix; - bool write_as_character; - bool character_debug_format; + bool zero_pad; char fill_char; alignment align; @@ -228,8 +222,7 @@ struct formatter sign {sign_option::negative}, is_upper {false}, prefix {false}, - write_as_character {false}, - character_debug_format {false}, + zero_pad {false}, fill_char {' '}, align {alignment::none} {} @@ -243,44 +236,43 @@ struct formatter sign = std::get<2>(res); is_upper = std::get<3>(res); prefix = std::get<4>(res); - write_as_character = std::get<5>(res); - character_debug_format = std::get<6>(res); - fill_char = std::get<7>(res); - align = std::get<8>(res); + zero_pad = std::get<5>(res); + fill_char = std::get<6>(res); + align = std::get<7>(res); - return std::get<9>(res); + return std::get<8>(res); } template auto format(T v, FormatContext& ctx) const { - char buffer[64]; + char buffer[detail::mini_to_chars_buffer_size]; bool isneg {false}; - boost::int128::uint128_t abs_v {}; + boost::int128::uint128 abs_v {}; - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) { if (v < T{0}) { isneg = true; - // Can't negate int128_t::min(), handle specially + // Can't negate int128::min(), handle specially if (v == (std::numeric_limits::min)()) { - abs_v = boost::int128::uint128_t{UINT64_C(0x8000000000000000), 0}; + abs_v = boost::int128::uint128{UINT64_C(0x8000000000000000), 0}; } else { - abs_v = static_cast(-v); + abs_v = static_cast(-v); } } else { - abs_v = static_cast(v); + abs_v = static_cast(v); } } else { - abs_v = static_cast(v); + abs_v = static_cast(v); } const auto end = detail::mini_to_chars(buffer, abs_v, base, is_upper); @@ -311,9 +303,9 @@ struct formatter sign_len = 1; } - // Zero-padding only applies when no explicit alignment is set - // Account for prefix and sign in the padding calculation - if (align == alignment::none && padding_digits > 0) + // Zero-padding applies only with the '0' flag and no explicit alignment. + // Account for prefix and sign in the padding calculation. + if (zero_pad && align == alignment::none && padding_digits > 0) { auto target_digit_width {static_cast(padding_digits)}; if (target_digit_width > prefix_len + sign_len) @@ -384,7 +376,7 @@ struct formatter { s.insert(s.begin(), ' '); } - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) { if (isneg) { @@ -393,7 +385,7 @@ struct formatter } break; case sign_option::negative: - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) { if (isneg) { @@ -410,11 +402,15 @@ struct formatter s.erase(0, s.find_first_not_of('\0')); s.erase(s.find_last_not_of('\0') + 1); - // Apply alignment if specified - if (align != alignment::none && s.size() < static_cast(padding_digits)) + // Apply alignment. An explicit alignment uses fill_char; with no explicit + // alignment and no zero-padding, integer types default to right alignment + // with the fill character (matching std::format). + if (s.size() < static_cast(padding_digits) && + (align != alignment::none || !zero_pad)) { auto fill_count = static_cast(padding_digits) - s.size(); - switch (align) + const auto effective_align = (align == alignment::none) ? alignment::right : align; + switch (effective_align) { case alignment::left: s.append(fill_count, fill_char); @@ -448,10 +444,10 @@ struct formatter namespace fmt { template <> -struct formatter : public boost::int128::fmt_detail::formatter {}; +struct formatter : public boost::int128::fmt_detail::formatter {}; template <> -struct formatter : public boost::int128::fmt_detail::formatter {}; +struct formatter : public boost::int128::fmt_detail::formatter {}; } // namespace fmt diff --git a/include/boost/decimal/detail/int128/format.hpp b/include/boost/decimal/detail/int128/format.hpp index 15cd82bc2..bebcc94a2 100644 --- a/include/boost/decimal/detail/int128/format.hpp +++ b/include/boost/decimal/detail/int128/format.hpp @@ -5,17 +5,35 @@ #ifndef BOOST_DECIMAL_DETAIL_INT128_FORMAT_HPP #define BOOST_DECIMAL_DETAIL_INT128_FORMAT_HPP -#if __has_include() && defined(__cpp_lib_format) && __cpp_lib_format >= 201907L && !defined(BOOST_DECIMAL_DISABLE_CLIB) +// The feature-test macros below are only visible once has been seen. +// The module build gets them from the global module fragment instead. +#ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE +# if __has_include() +# include +# endif +#endif + +#if __has_include() && defined(__cpp_lib_format) && __cpp_lib_format >= 201907L #include #include #include + +#ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE #include #include #include +#endif #define BOOST_DECIMAL_DETAIL_INT128_HAS_FORMAT +#if defined(__cpp_lib_constexpr_format) && __cpp_lib_constexpr_format >= 202511L +# define BOOST_DECIMAL_DETAIL_INT128_HAS_CONSTEXPR_FORMAT +# define BOOST_DECIMAL_DETAIL_INT128_CONSTEXPR_FORMAT constexpr +#else +# define BOOST_DECIMAL_DETAIL_INT128_CONSTEXPR_FORMAT +#endif + namespace boost::int128::detail { enum class sign_option @@ -42,8 +60,7 @@ constexpr auto parse_impl(ParseContext& ctx) int padding_digits = 0; auto sign = sign_option::negative; bool prefix = false; - bool write_as_character = false; - bool character_debug_format = false; + bool zero_pad = false; char fill_char = ' '; auto align = alignment::none; @@ -123,14 +140,14 @@ constexpr auto parse_impl(ParseContext& ctx) ++it; } - // Character presentation type - if (it != ctx.end() && (*it == '?' || *it == 'c')) + // Zero-pad flag (std::format places '0' before the width) + if (it != ctx.end() && *it == '0') { - character_debug_format = *it == '?'; + zero_pad = true; ++it; } - // Check for a padding character + // Parse the width while (it != ctx.end() && *it >= '0' && *it <= '9') { padding_digits = padding_digits * 10 + (*it - '0'); @@ -150,10 +167,6 @@ constexpr auto parse_impl(ParseContext& ctx) is_upper = true; break; - case 'c': - write_as_character = true; - break; - case 'o': base = 8; break; @@ -180,13 +193,13 @@ constexpr auto parse_impl(ParseContext& ctx) BOOST_DECIMAL_DETAIL_INT128_THROW_EXCEPTION(std::format_error("Expected '}' in format string")); // LCOV_EXCL_LINE } - return std::make_tuple(base, padding_digits, sign, is_upper, prefix, write_as_character, character_debug_format, fill_char, align, it); + return std::make_tuple(base, padding_digits, sign, is_upper, prefix, zero_pad, fill_char, align, it); } template struct is_library_type_impl { - static constexpr bool value {std::is_same_v || std::is_same_v}; + static constexpr bool value {std::is_same_v || std::is_same_v}; }; template @@ -207,8 +220,7 @@ struct formatter boost::int128::detail::sign_option sign; bool is_upper; bool prefix; - bool write_as_character; - bool character_debug_format; + bool zero_pad; char fill_char; boost::int128::detail::alignment align; @@ -217,8 +229,7 @@ struct formatter sign {boost::int128::detail::sign_option::negative}, is_upper {false}, prefix {false}, - write_as_character {false}, - character_debug_format {false}, + zero_pad {false}, fill_char {' '}, align {boost::int128::detail::alignment::none} {} @@ -232,44 +243,43 @@ struct formatter sign = std::get<2>(res); is_upper = std::get<3>(res); prefix = std::get<4>(res); - write_as_character = std::get<5>(res); - character_debug_format = std::get<6>(res); - fill_char = std::get<7>(res); - align = std::get<8>(res); + zero_pad = std::get<5>(res); + fill_char = std::get<6>(res); + align = std::get<7>(res); - return std::get<9>(res); + return std::get<8>(res); } template - auto format(T v, FormatContext& ctx) const + BOOST_DECIMAL_DETAIL_INT128_CONSTEXPR_FORMAT auto format(T v, FormatContext& ctx) const { - char buffer[64]; + char buffer[boost::int128::detail::mini_to_chars_buffer_size]; bool isneg {false}; - boost::int128::uint128_t abs_v {}; + boost::int128::uint128 abs_v {}; - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { if (v < 0) { isneg = true; - // Can't negate int128_t::min(), handle specially + // Can't negate int128::min(), handle specially if (v == (std::numeric_limits::min)()) { - abs_v = boost::int128::uint128_t{UINT64_C(0x8000000000000000), 0}; + abs_v = boost::int128::uint128{UINT64_C(0x8000000000000000), 0}; } else { - abs_v = static_cast(-v); + abs_v = static_cast(-v); } } else { - abs_v = static_cast(v); + abs_v = static_cast(v); } } else { - abs_v = static_cast(v); + abs_v = static_cast(v); } const auto end = boost::int128::detail::mini_to_chars(buffer, abs_v, base, is_upper); @@ -300,9 +310,9 @@ struct formatter sign_len = 1; } - // Zero-padding only applies when no explicit alignment is set - // Account for prefix and sign in the padding calculation - if (align == boost::int128::detail::alignment::none && padding_digits > 0) + // Zero-padding applies only with the '0' flag and no explicit alignment. + // Account for prefix and sign in the padding calculation. + if (zero_pad && align == boost::int128::detail::alignment::none && padding_digits > 0) { auto target_digit_width {static_cast(padding_digits)}; if (target_digit_width > prefix_len + sign_len) @@ -373,7 +383,7 @@ struct formatter { s.insert(s.begin(), ' '); } - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { if (isneg) { @@ -382,7 +392,7 @@ struct formatter } break; case boost::int128::detail::sign_option::negative: - if constexpr (std::is_same_v) + if constexpr (std::is_same_v) { if (isneg) { @@ -390,20 +400,24 @@ struct formatter } } break; - // LCOV_EXCL_START - default: - BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; - // LCOV_EXCL_STOP + default: // LCOV_EXCL_LINE + BOOST_DECIMAL_DETAIL_INT128_UNREACHABLE; // LCOV_EXCL_LINE } s.erase(0, s.find_first_not_of('\0')); s.erase(s.find_last_not_of('\0') + 1); - // Apply alignment if specified - if (align != boost::int128::detail::alignment::none && s.size() < static_cast(padding_digits)) + // Apply alignment. An explicit alignment uses fill_char; with no explicit + // alignment and no zero-padding, integer types default to right alignment + // with the fill character (matching std::format). + if (s.size() < static_cast(padding_digits) && + (align != boost::int128::detail::alignment::none || !zero_pad)) { auto fill_count = static_cast(padding_digits) - s.size(); - switch (align) + const auto effective_align = (align == boost::int128::detail::alignment::none) + ? boost::int128::detail::alignment::right + : align; + switch (effective_align) { case boost::int128::detail::alignment::left: s.append(fill_count, fill_char); @@ -419,10 +433,8 @@ struct formatter s.append(right_fill, fill_char); break; } - // LCOV_EXCL_START - default: - break; - // LCOV_EXCL_STOP + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE } } diff --git a/include/boost/decimal/detail/int128/hash.hpp b/include/boost/decimal/detail/int128/hash.hpp new file mode 100644 index 000000000..5bf78fab7 --- /dev/null +++ b/include/boost/decimal/detail/int128/hash.hpp @@ -0,0 +1,95 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_INT128_HASH_HPP +#define BOOST_DECIMAL_DETAIL_INT128_HASH_HPP + +#include + +#ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE + +#include +#include +#include + +#endif + +namespace boost { +namespace int128 { +namespace detail { + +// The cast is only useless for 64-bit platforms +// Without we get an implicit conversion warning which is arguably worse +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wuseless-cast" +#endif + +// splitmix64 finalizer: mixes all 64 input bits into the result before any narrowing to size_t. +// This is required for correctness on platforms where size_t is 32 bits +inline std::size_t hash_finalize_64(std::uint64_t v) noexcept +{ + v ^= v >> 30; + v *= UINT64_C(0xbf58476d1ce4e5b9); + v ^= v >> 27; + v *= UINT64_C(0x94d049bb133111eb); + v ^= v >> 31; + return static_cast(v); +} + +#if defined(__GNUC__) && !defined(__clang__) +# pragma GCC diagnostic pop +#endif + +} // namespace detail +} // namespace int128 +} // namespace boost + +namespace std { + +template <> +struct hash +{ + auto operator()(const boost::int128::int128 v) const noexcept -> std::size_t + { + const std::size_t low_hash {boost::int128::detail::hash_finalize_64(v.low)}; + const std::size_t high_hash {boost::int128::detail::hash_finalize_64(v.high)}; + + // boost::hash_combine style mixing of the two finalized halves + return low_hash ^ (high_hash + static_cast(0x9e3779b9) + (low_hash << 6) + (low_hash >> 2)); + } +}; + +template <> +struct hash +{ + auto operator()(const boost::int128::uint128 v) const noexcept -> std::size_t + { + const std::size_t low_hash {boost::int128::detail::hash_finalize_64(v.low)}; + const std::size_t high_hash {boost::int128::detail::hash_finalize_64(v.high)}; + + // boost::hash_combine style mixing of the two finalized halves + return low_hash ^ (high_hash + static_cast(0x9e3779b9) + (low_hash << 6) + (low_hash >> 2)); + } +}; + +} // namespace std + +namespace boost { +namespace int128 { + +inline std::size_t hash_value(const uint128 v) noexcept +{ + return std::hash{}(v); +} + +inline std::size_t hash_value(const int128 v) noexcept +{ + return std::hash{}(v); +} + +} // namespace int128 +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_INT128_HASH_HPP diff --git a/include/boost/decimal/detail/int128/iostream.hpp b/include/boost/decimal/detail/int128/iostream.hpp index d3a2558c4..e9debe9ef 100644 --- a/include/boost/decimal/detail/int128/iostream.hpp +++ b/include/boost/decimal/detail/int128/iostream.hpp @@ -28,7 +28,7 @@ namespace detail { template struct streamable_overload { - static constexpr bool value = std::is_same::value || std::is_same::value; + static constexpr bool value = std::is_same::value || std::is_same::value; }; template @@ -103,7 +103,7 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT template & os, const LibIntegerType& v) -> std::enable_if_t, std::basic_ostream&> { - char buffer[64U] {}; + char buffer[detail::mini_to_chars_buffer_size] {}; const auto flags {os.flags()}; int base {10}; diff --git a/include/boost/decimal/detail/int128/literals.hpp b/include/boost/decimal/detail/int128/literals.hpp index d684d19d4..700db4e70 100644 --- a/include/boost/decimal/detail/int128/literals.hpp +++ b/include/boost/decimal/detail/int128/literals.hpp @@ -15,88 +15,50 @@ namespace boost { namespace int128 { namespace literals { -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator ""_u128(const char* str) noexcept { - uint128_t result {}; - detail::from_chars(str, str + detail::strlen(str), result); - return result; + return detail::parse_literal(str, str + detail::strlen(str)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator ""_U128(const char* str) noexcept { - uint128_t result {}; - detail::from_chars(str, str + detail::strlen(str), result); - return result; + return detail::parse_literal(str, str + detail::strlen(str)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(const char* str, std::size_t len) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator ""_u128(const char* str, std::size_t len) noexcept { - uint128_t result {}; - detail::from_chars(str, str + len, result); - return result; + return detail::parse_literal(str, str + len); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(const char* str, std::size_t len) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 operator ""_U128(const char* str, std::size_t len) noexcept { - uint128_t result {}; - detail::from_chars(str, str + len, result); - return result; + return detail::parse_literal(str, str + len); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator ""_u128(unsigned long long v) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator ""_i128(const char* str) noexcept { - return uint128_t{v}; + return detail::parse_literal(str, str + detail::strlen(str)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t operator ""_U128(unsigned long long v) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator ""_I128(const char* str) noexcept { - return uint128_t{v}; + return detail::parse_literal(str, str + detail::strlen(str)); } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator ""_i128(const char* str, std::size_t len) noexcept { - int128_t result {}; - detail::from_chars(str, str + detail::strlen(str), result); - return result; + return detail::parse_literal(str, str + len); } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 operator ""_I128(const char* str, std::size_t len) noexcept { - int128_t result {}; - detail::from_chars(str, str + detail::strlen(str), result); - return result; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(unsigned long long v) noexcept -{ - return int128_t{v}; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(unsigned long long v) noexcept -{ - return int128_t{v}; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator ""_i128(const char* str, std::size_t len) noexcept -{ - int128_t result {}; - detail::from_chars(str, str + len, result); - return result; -} - -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t operator ""_I128(const char* str, std::size_t len) noexcept -{ - int128_t result {}; - detail::from_chars(str, str + len, result); - return result; + return detail::parse_literal(str, str + len); } } // namespace literals } // namespace int128 } // namespace boost -#define BOOST_DECIMAL_DETAIL_INT128_STRINGIFY(x) #x -#define BOOST_DECIMAL_DETAIL_INT128_UINT128_C(x) boost::int128::literals::operator""_u128(BOOST_DECIMAL_DETAIL_INT128_STRINGIFY(x)) -#define BOOST_DECIMAL_DETAIL_INT128_INT128_C(x) boost::int128::literals::operator""_i128(BOOST_DECIMAL_DETAIL_INT128_STRINGIFY(x)) +#include #endif // BOOST_DECIMAL_DETAIL_INT128_LITERALS_HPP diff --git a/include/boost/decimal/detail/int128/numeric.hpp b/include/boost/decimal/detail/int128/numeric.hpp index 0ef1637d4..faba25b85 100644 --- a/include/boost/decimal/detail/int128/numeric.hpp +++ b/include/boost/decimal/detail/int128/numeric.hpp @@ -6,11 +6,14 @@ #define BOOST_DECIMAL_DETAIL_INT128_NUMERIC_HPP #include +#include #include +#include #ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE #include +#include #endif @@ -32,8 +35,8 @@ struct reduced_integers 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) @@ -52,25 +55,25 @@ BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_reduced_integer_v {reduced_ } // namespace detail -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t add_sat(const uint128_t x, const uint128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 saturating_add(const uint128 x, const uint128 y) noexcept { const auto z {x + y}; if (z < x) { - return (std::numeric_limits::max)(); + return (std::numeric_limits::max)(); } return z; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t sub_sat(const uint128_t x, const uint128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 saturating_sub(const uint128 x, const uint128 y) noexcept { const auto z {x - y}; if (z > x) { - return (std::numeric_limits::min)(); + return (std::numeric_limits::min)(); } return z; @@ -82,50 +85,50 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const # pragma warning(disable : 4146) // Unary minus applied to unsigned type #endif -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t add_sat(const int128_t x, const int128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 saturating_add(const int128 x, const int128 y) noexcept { // Detect overflow BEFORE the addition to avoid signed overflow UB. // When both are non-negative: overflow iff x > max - y (subtraction safe: max - non_negative >= 0) // When both are negative: overflow iff x < min - y (subtraction safe: min - negative > min) // Mixed signs: overflow is impossible. - if (x.high >= 0 && y.high >= 0) + if (x.signed_high() >= 0 && y.signed_high() >= 0) { - if (x > (std::numeric_limits::max)() - y) + if (x > (std::numeric_limits::max)() - y) { - return (std::numeric_limits::max)(); + return (std::numeric_limits::max)(); } } - else if (x.high < 0 && y.high < 0) + else if (x.signed_high() < 0 && y.signed_high() < 0) { - if (x < (std::numeric_limits::min)() - y) + if (x < (std::numeric_limits::min)() - y) { - return (std::numeric_limits::min)(); + return (std::numeric_limits::min)(); } } return x + y; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t sub_sat(const int128_t x, const int128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 saturating_sub(const int128 x, const int128 y) noexcept { // Detect overflow BEFORE the subtraction to avoid signed overflow UB. // Positive overflow: x >= 0 and y < 0 and x > max + y (safe: max + negative < max) // Negative overflow: x < 0 and y >= 0 and x < min + y (safe: min + non_negative > min) // Same signs: overflow is impossible. - if (x.high >= 0 && y.high < 0) + if (x.signed_high() >= 0 && y.signed_high() < 0) { - if (x > (std::numeric_limits::max)() + y) + if (x > (std::numeric_limits::max)() + y) { - return (std::numeric_limits::max)(); + return (std::numeric_limits::max)(); } } - else if (x.high < 0 && y.high >= 0) + else if (x.signed_high() < 0 && y.signed_high() >= 0) { - if (x < (std::numeric_limits::min)() + y) + if (x < (std::numeric_limits::min)() + y) { - return (std::numeric_limits::min)(); + return (std::numeric_limits::min)(); } } @@ -136,51 +139,36 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t sub_sat(const int128_ # pragma warning(pop) #endif -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t mul_sat(const uint128_t x, const uint128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 saturating_mul(const uint128 x, const uint128 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; + uint128 res {}; + return ckd_mul(&res, x, y) ? (std::numeric_limits::max)() : res; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t mul_sat(const int128_t& x, const int128_t& y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 saturating_mul(const int128 x, const int128 y) noexcept { - const auto x_bits {bit_width(static_cast(abs(x)))}; - const auto y_bits {bit_width(static_cast(abs(y)))}; + int128 res {}; + const auto overflowed {ckd_mul(&res, x, y)}; - if ((x_bits + y_bits) > std::numeric_limits::digits) + if (overflowed) { - if ((x < 0) != (y < 0)) - { - return (std::numeric_limits::min)(); - } - else - { - return (std::numeric_limits::max)(); - } + return (x < 0) != (y < 0) ? (std::numeric_limits::min)() : (std::numeric_limits::max)(); } - const int128_t res {x * y}; return res; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t div_sat(const uint128_t x, const uint128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 saturating_div(const uint128 x, const uint128 y) noexcept { return x / y; } -BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t div_sat(const int128_t x, const int128_t y) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 saturating_div(const int128 x, const int128 y) noexcept { - if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(x == (std::numeric_limits::min)() && y == -1)) + 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 (std::numeric_limits::max)(); } return x / y; @@ -192,15 +180,15 @@ BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE const #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const uint128_t value) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr TargetType saturating_cast(const uint128 value) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) { return static_cast(value); } else { - if (value > static_cast((std::numeric_limits::max)())) + if (value > static_cast((std::numeric_limits::max)())) { return (std::numeric_limits::max)(); } @@ -214,16 +202,16 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const #endif BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const int128_t value) noexcept +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr TargetType saturating_cast(const int128 value) noexcept { - BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) { return static_cast(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 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) + else BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::is_same::value) #endif { // We can't possibly have overflow in this case @@ -231,11 +219,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const } else { - if (value > static_cast((std::numeric_limits::max)())) + if (value > static_cast((std::numeric_limits::max)())) { return (std::numeric_limits::max)(); } - else if (value < static_cast((std::numeric_limits::min)())) + else if (value < static_cast((std::numeric_limits::min)())) { return (std::numeric_limits::min)(); } @@ -246,7 +234,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr TargetType saturate_cast(const namespace detail { -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::uint64_t gcd64(std::uint64_t x, std::uint64_t y) noexcept +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::uint64_t gcd64(std::uint64_t x, std::uint64_t y) noexcept { if (x == 0) { @@ -278,7 +266,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::uint64_t gcd64(std::uint6 } // namespace detail -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t gcd(uint128_t a, uint128_t b) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 gcd(uint128 a, uint128 b) noexcept { // Base case if (a == 0U) @@ -302,7 +290,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t gcd(uint128_t a, uin if (a > b) { - const uint128_t temp {a}; + const uint128 temp {a}; a = b; b = temp; } @@ -312,12 +300,12 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t gcd(uint128_t a, uin // Stop doing 128-bit math as soon as we can const auto g {detail::gcd64(a.low, b.low)}; - return uint128_t{0, g} << shift; + return uint128{0, g} << shift; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t gcd(const int128_t a, const int128_t b) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 gcd(const int128 a, const int128 b) noexcept { - return static_cast(gcd(static_cast(abs(a)), static_cast(abs(b)))); + return static_cast(gcd(static_cast(abs(a)), static_cast(abs(b)))); } // For unknown reasons this implementation fails for MSVC x86 only in release mode @@ -325,11 +313,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t gcd(const int128_t a, // but very slow impl that we know works. #if !(defined(_M_IX86) && !defined(_NDEBUG)) -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t lcm(const uint128_t a, const uint128_t b) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 lcm(const uint128 a, const uint128 b) noexcept { if (a == 0U || b == 0U) { - return static_cast(0); + return static_cast(0); } // Calculate GCD first @@ -341,11 +329,11 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t lcm(const uint128_t #else -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t lcm(uint128_t a, uint128_t b) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 lcm(uint128 a, uint128 b) noexcept { if (a == 0U || b == 0U) { - return uint128_t{0}; + return uint128{0}; } @@ -363,7 +351,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t lcm(uint128_t a, uin std::swap(a, b); } - uint128_t lcm{a}; + uint128 lcm{a}; while (lcm % b != 0U) { @@ -375,12 +363,12 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t lcm(uint128_t a, uin #endif -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t lcm(const int128_t a, const int128_t b) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 lcm(const int128 a, const int128 b) noexcept { - return static_cast(lcm(static_cast(abs(a)), static_cast(abs(b)))); + return static_cast(lcm(static_cast(abs(a)), static_cast(abs(b)))); } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t midpoint(const uint128_t a, const uint128_t b) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 midpoint(const uint128 a, const uint128 b) noexcept { // Bit manipulation formula works for unsigned integers auto mid {(a & b) + ((a ^ b) >> 1)}; @@ -394,7 +382,7 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128_t midpoint(const uint1 return mid; } -BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t midpoint(const int128_t a, const int128_t b) noexcept +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 midpoint(const int128 a, const int128 b) noexcept { // For signed integers, we use a + (b - a) / 2 or a - (a - b) / 2 // The subtraction is done in unsigned arithmetic to handle overflow correctly @@ -402,27 +390,424 @@ BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128_t midpoint(const int128 // // Use direct field access for both the uint128 construction and the // comparison to avoid NVCC host compiler issues with operator<= and - // static_cast on int128_t for large-magnitude values + // static_cast on int128 for large-magnitude values - const uint128_t ua {static_cast(a.high), a.low}; - const uint128_t ub {static_cast(b.high), b.low}; + const uint128 ua {a.high, a.low}; + const uint128 ub {b.high, b.low}; - const bool a_le_b {a.high == b.high ? a.low <= b.low : a.high < b.high}; + const bool a_le_b {a.high == b.high ? a.low <= b.low : a.signed_high() < b.signed_high()}; if (a_le_b) { // diff = b - a (computed in unsigned, handles wrap-around correctly) const auto diff {ub - ua}; - return a + static_cast(diff / 2U); + return a + static_cast(diff / 2U); } else { // diff = a - b (computed in unsigned, handles wrap-around correctly) const auto diff {ua - ub}; - return a - static_cast(diff / 2U); + return a - static_cast(diff / 2U); } } +// Quotient and remainder of a single division, following the div_result proposed for the +// standard library by P3724 (Integer division) +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +struct div_result +{ + T quotient; + T remainder; +}; + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator==(const div_result& lhs, const div_result& rhs) noexcept +{ + return lhs.quotient == rhs.quotient && lhs.remainder == rhs.remainder; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool operator!=(const div_result& lhs, const div_result& rhs) noexcept +{ + return !(lhs == rhs); +} + +#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::strong_ordering operator<=>(const div_result& lhs, const div_result& rhs) noexcept +{ + const auto quotient_order {lhs.quotient <=> rhs.quotient}; + return quotient_order != std::strong_ordering::equal ? quotient_order : lhs.remainder <=> rhs.remainder; +} + +#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_SPACESHIP_OPERATOR + +namespace detail { + +// -1 when the exact quotient of x / y is negative, and 1 otherwise +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int quotient_sign(const int128 x, const int128 y) noexcept +{ + return (x < 0) != (y < 0) ? -1 : 1; +} + +// Applies the quotient offset d (-1, 0, or 1) to a truncated division result, and returns the +// remainder matching the adjusted quotient. The remainder is evaluated in unsigned arithmetic +// so that the d * y term cannot overflow when y is INT128_MIN. +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result offset_quotient(const i128div_t truncated, const int128 y, const int d) noexcept +{ + const uint128 unsigned_rem {truncated.rem.high, truncated.rem.low}; + const uint128 unsigned_y {y.high, y.low}; + + uint128 rem {unsigned_rem}; + + if (d > 0) + { + rem = unsigned_rem - unsigned_y; + } + else if (d < 0) + { + rem = unsigned_rem + unsigned_y; + } + + return div_result{truncated.quot + d, static_cast(rem)}; +} + +// An unsigned quotient is never rounded down, so the only offsets are 0 and 1. The remainder +// of an incremented quotient is negative, and is returned reduced modulo 2^128. +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result offset_quotient(const u128div_t truncated, const uint128 y, const bool increment) noexcept +{ + return div_result{increment ? truncated.quot + 1U : truncated.quot, + increment ? truncated.rem - y : truncated.rem}; +} + +// Round-to-nearest comparison shared by the ties functions: the truncated quotient grows in +// magnitude when the remainder is more than half the divisor. truncate_ties selects the strict +// form, which both breaks an exact tie towards zero and recovers the bit that abs(y) / 2 drops +// when y is odd. +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool nearest_increment(const uint128 abs_rem, const uint128 abs_half_y, const bool truncate_ties) noexcept +{ + return truncate_ties ? abs_rem > abs_half_y : abs_rem >= abs_half_y; +} + +// Magnitude of the remainder of a truncated signed division. The magnitude is always less +// than abs(y), so it is representable for every valid divisor. +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 abs_remainder(const i128div_t truncated) noexcept +{ + return static_cast(abs(truncated.rem)); +} + +// floor(abs(y) / 2), exact for every y including INT128_MIN +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 abs_half_divisor(const int128 y) noexcept +{ + return static_cast(abs(y)) >> 1U; +} + +// An odd divisor cannot produce an exact tie, so every ties function truncates on it +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool is_odd(const int128 x) noexcept +{ + return (x.low & 1U) != 0U; +} + +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool is_odd(const uint128 x) noexcept +{ + return (x.low & 1U) != 0U; +} + +} // namespace detail + +// Rounds towards zero, which is what operator/ already does +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_to_zero(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return div_result{truncated.quot, truncated.rem}; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_to_zero(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + return div_result{truncated.quot, truncated.rem}; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_to_zero(const uint128 x, const uint128 y) noexcept +{ + return x / y; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_to_zero(const int128 x, const int128 y) noexcept +{ + return x / y; +} + +// Rounds away from zero, so the quotient grows in magnitude unless the division is exact +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_away_zero(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, truncated.rem != 0U); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_away_zero(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, truncated.rem != 0 ? detail::quotient_sign(x, y) : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_away_zero(const uint128 x, const uint128 y) noexcept +{ + return div_rem_away_zero(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_away_zero(const int128 x, const int128 y) noexcept +{ + return div_rem_away_zero(x, y).quotient; +} + +// Rounds towards positive infinity, which for an unsigned quotient is away from zero +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_to_pos_inf(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, truncated.rem != 0U); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_to_pos_inf(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto adjust {truncated.rem != 0 && detail::quotient_sign(x, y) > 0}; + return detail::offset_quotient(truncated, y, adjust ? 1 : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_to_pos_inf(const uint128 x, const uint128 y) noexcept +{ + return div_rem_to_pos_inf(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_to_pos_inf(const int128 x, const int128 y) noexcept +{ + return div_rem_to_pos_inf(x, y).quotient; +} + +// Rounds towards negative infinity, which for an unsigned quotient is truncation +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_to_neg_inf(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return div_result{truncated.quot, truncated.rem}; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_to_neg_inf(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto adjust {truncated.rem != 0 && detail::quotient_sign(x, y) < 0}; + return detail::offset_quotient(truncated, y, adjust ? -1 : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_to_neg_inf(const uint128 x, const uint128 y) noexcept +{ + return x / y; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_to_neg_inf(const int128 x, const int128 y) noexcept +{ + return div_rem_to_neg_inf(x, y).quotient; +} + +// Euclidean division, whose remainder is always in [0, abs(y)). Only a negative remainder +// needs fixing, and growing the quotient magnitude by one makes the remainder positive. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_euclid(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return div_result{truncated.quot, truncated.rem}; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_euclid(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, truncated.rem < 0 ? detail::quotient_sign(x, y) : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_euclid(const uint128 x, const uint128 y) noexcept +{ + return x / y; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_euclid(const int128 x, const int128 y) noexcept +{ + return div_rem_euclid(x, y).quotient; +} + +// Rounds to nearest, breaking an exact tie towards zero +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_zero(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, detail::nearest_increment(truncated.rem, y >> 1U, true)); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_zero(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto increment {detail::nearest_increment(detail::abs_remainder(truncated), detail::abs_half_divisor(y), true)}; + return detail::offset_quotient(truncated, y, increment ? detail::quotient_sign(x, y) : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_ties_to_zero(const uint128 x, const uint128 y) noexcept +{ + return div_rem_ties_to_zero(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_ties_to_zero(const int128 x, const int128 y) noexcept +{ + return div_rem_ties_to_zero(x, y).quotient; +} + +// Rounds to nearest, breaking an exact tie away from zero +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_away_zero(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, detail::nearest_increment(truncated.rem, y >> 1U, detail::is_odd(y))); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_away_zero(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto increment {detail::nearest_increment(detail::abs_remainder(truncated), detail::abs_half_divisor(y), detail::is_odd(y))}; + return detail::offset_quotient(truncated, y, increment ? detail::quotient_sign(x, y) : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_ties_away_zero(const uint128 x, const uint128 y) noexcept +{ + return div_rem_ties_away_zero(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_ties_away_zero(const int128 x, const int128 y) noexcept +{ + return div_rem_ties_away_zero(x, y).quotient; +} + +// Rounds to nearest, breaking an exact tie towards positive infinity. A tie only grows the +// magnitude when the quotient is positive. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_pos_inf(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, detail::nearest_increment(truncated.rem, y >> 1U, detail::is_odd(y))); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_pos_inf(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto sign {detail::quotient_sign(x, y)}; + const auto increment {detail::nearest_increment(detail::abs_remainder(truncated), detail::abs_half_divisor(y), detail::is_odd(y) || sign < 0)}; + return detail::offset_quotient(truncated, y, increment ? sign : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_ties_to_pos_inf(const uint128 x, const uint128 y) noexcept +{ + return div_rem_ties_to_pos_inf(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_ties_to_pos_inf(const int128 x, const int128 y) noexcept +{ + return div_rem_ties_to_pos_inf(x, y).quotient; +} + +// Rounds to nearest, breaking an exact tie towards negative infinity. A tie only grows the +// magnitude when the quotient is negative, so an unsigned tie always truncates. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_neg_inf(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + return detail::offset_quotient(truncated, y, detail::nearest_increment(truncated.rem, y >> 1U, true)); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_neg_inf(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto sign {detail::quotient_sign(x, y)}; + const auto increment {detail::nearest_increment(detail::abs_remainder(truncated), detail::abs_half_divisor(y), detail::is_odd(y) || sign > 0)}; + return detail::offset_quotient(truncated, y, increment ? sign : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_ties_to_neg_inf(const uint128 x, const uint128 y) noexcept +{ + return div_rem_ties_to_neg_inf(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_ties_to_neg_inf(const int128 x, const int128 y) noexcept +{ + return div_rem_ties_to_neg_inf(x, y).quotient; +} + +// Rounds to nearest, breaking an exact tie to the odd quotient, so a tie only grows the +// magnitude when truncation would have produced an even quotient +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_odd(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto increment {detail::nearest_increment(truncated.rem, y >> 1U, detail::is_odd(y) || detail::is_odd(truncated.quot))}; + return detail::offset_quotient(truncated, y, increment); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_odd(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto truncate_ties {detail::is_odd(y) || detail::is_odd(truncated.quot)}; + const auto increment {detail::nearest_increment(detail::abs_remainder(truncated), detail::abs_half_divisor(y), truncate_ties)}; + return detail::offset_quotient(truncated, y, increment ? detail::quotient_sign(x, y) : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_ties_to_odd(const uint128 x, const uint128 y) noexcept +{ + return div_rem_ties_to_odd(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_ties_to_odd(const int128 x, const int128 y) noexcept +{ + return div_rem_ties_to_odd(x, y).quotient; +} + +// Rounds to nearest, breaking an exact tie to the even quotient, so a tie only grows the +// magnitude when truncation would have produced an odd quotient +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_even(const uint128 x, const uint128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto increment {detail::nearest_increment(truncated.rem, y >> 1U, detail::is_odd(y) || !detail::is_odd(truncated.quot))}; + return detail::offset_quotient(truncated, y, increment); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr div_result div_rem_ties_to_even(const int128 x, const int128 y) noexcept +{ + const auto truncated {div(x, y)}; + const auto truncate_ties {detail::is_odd(y) || !detail::is_odd(truncated.quot)}; + const auto increment {detail::nearest_increment(detail::abs_remainder(truncated), detail::abs_half_divisor(y), truncate_ties)}; + return detail::offset_quotient(truncated, y, increment ? detail::quotient_sign(x, y) : 0); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 div_ties_to_even(const uint128 x, const uint128 y) noexcept +{ + return div_rem_ties_to_even(x, y).quotient; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 div_ties_to_even(const int128 x, const int128 y) noexcept +{ + return div_rem_ties_to_even(x, y).quotient; +} + +// The Euclidean remainder, which is always in [0, abs(y)). Only a negative remainder needs +// fixing, and abs(y) is added in unsigned arithmetic so that INT128_MIN is handled. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 rem_euclid(const uint128 x, const uint128 y) noexcept +{ + return x % y; +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 rem_euclid(const int128 x, const int128 y) noexcept +{ + const auto rem {x % y}; + + if (rem < 0) + { + const uint128 unsigned_rem {rem.high, rem.low}; + return static_cast(unsigned_rem + static_cast(abs(y))); + } + + return rem; +} + } // namespace int128 } // namespace boost diff --git a/include/boost/decimal/detail/int128/random.hpp b/include/boost/decimal/detail/int128/random.hpp index fad18f6c8..04c201c76 100644 --- a/include/boost/decimal/detail/int128/random.hpp +++ b/include/boost/decimal/detail/int128/random.hpp @@ -15,79 +15,79 @@ template struct make_unsigned_imp; template <> -struct make_unsigned_imp +struct make_unsigned_imp { - using type = int128::uint128_t; + using type = int128::uint128; }; template <> -struct make_unsigned_imp +struct make_unsigned_imp { - using type = int128::uint128_t; + using type = int128::uint128; }; template struct make_unsigned; template <> -struct make_unsigned +struct make_unsigned { - using type = int128::uint128_t; + using type = int128::uint128; }; template <> -struct make_unsigned +struct make_unsigned { - using type = int128::int128_t; + using type = int128::uint128; }; template struct make_unsigned_or_unbounded_imp; template <> -struct make_unsigned_or_unbounded_imp +struct make_unsigned_or_unbounded_imp { - using type = int128::uint128_t; + using type = int128::uint128; }; template <> -struct make_unsigned_or_unbounded_imp +struct make_unsigned_or_unbounded_imp { - using type = int128::uint128_t; + using type = int128::uint128; }; template struct make_unsigned_or_unbounded; template <> -struct make_unsigned_or_unbounded +struct make_unsigned_or_unbounded { - using type = int128::uint128_t; + using type = int128::uint128; }; template <> -struct make_unsigned_or_unbounded +struct make_unsigned_or_unbounded { - using type = int128::uint128_t; + using type = int128::uint128; }; template struct is_integral; template <> -struct is_integral : std::true_type {}; +struct is_integral : std::true_type {}; template <> -struct is_integral : std::true_type {}; +struct is_integral : std::true_type {}; template struct is_signed; template <> -struct is_signed : std::false_type {}; +struct is_signed : std::false_type {}; template <> -struct is_signed : std::true_type {}; +struct is_signed : std::true_type {}; } // namespace traits } // namespace random diff --git a/include/boost/decimal/detail/int128/string.hpp b/include/boost/decimal/detail/int128/string.hpp index db8adf5ed..eb93cb6ac 100644 --- a/include/boost/decimal/detail/int128/string.hpp +++ b/include/boost/decimal/detail/int128/string.hpp @@ -18,11 +18,19 @@ namespace boost { namespace int128 { template -auto to_string(const T& value) -> std::enable_if_t<(std::is_same::value || std::is_same::value), std::string> +auto to_string(const T& value) -> std::enable_if_t<(std::is_same::value || std::is_same::value), std::string> { - char buffer[64]; + char buffer[detail::mini_to_chars_buffer_size]; const auto last {detail::mini_to_chars(buffer, value, 10, false)}; - return std::string{last, buffer + sizeof(buffer)}; + return std::string{last, buffer + sizeof(buffer) - 1}; +} + +template +auto to_wstring(const T& value) -> std::enable_if_t<(std::is_same::value || std::is_same::value), std::wstring> +{ + char buffer[detail::mini_to_chars_buffer_size]; + const auto last {detail::mini_to_chars(buffer, value, 10, false)}; + return std::wstring{last, buffer + sizeof(buffer) - 1}; } } // namespace int128 diff --git a/include/boost/decimal/detail/int128/utilities.hpp b/include/boost/decimal/detail/int128/utilities.hpp new file mode 100644 index 000000000..220109b15 --- /dev/null +++ b/include/boost/decimal/detail/int128/utilities.hpp @@ -0,0 +1,602 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_DECIMAL_DETAIL_INT128_UTILITIES_HPP +#define BOOST_DECIMAL_DETAIL_INT128_UTILITIES_HPP + +#include +#include +#include + +#ifndef BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE + +#include +#include +#include +#include + +#endif + +namespace boost { +namespace int128 { + +namespace detail { + +// Modular addition for 128-bit operands assuming 0 <= a, b < m +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 addmod(const uint128 a, const uint128 b, const uint128 m) noexcept +{ + const uint128 s {a + b}; + + if (s < a || s >= m) + { + return s - m; + } + + return s; +} + +// Modular multiplication via shift-and-add for the full 128-bit modulus case +BOOST_int128EST_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 mulmod_shift(uint128 a, uint128 b, const uint128 m) noexcept +{ + uint128 result {0}; + + while (b != 0U) + { + if (static_cast(b.low & 1U)) + { + result = addmod(result, a, m); + } + + a = addmod(a, a, m); + b >>= 1; + } + + return result; +} + +// Modular multiplication when the modulus fits in 64 bits +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr std::uint64_t mulmod_word(const std::uint64_t a, const std::uint64_t b, const std::uint64_t m) noexcept +{ + return ((uint128{a} * uint128{b}) % uint128{m}).low; +} + +} // namespace detail + +// Computes (base ^ exp) mod m using fast modular exponentiation with +// optimizations specific to the boost::int128 library types +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 powm(uint128 base, uint128 exp, const uint128 m) noexcept +{ + if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(m == 0U)) + { + return uint128{0}; + } + + if (m == 1U) + { + return uint128{0}; + } + + if (exp == 0U) + { + return uint128{1}; + } + + base %= m; + + if (base == 0U) + { + return uint128{0}; + } + + // Power-of-two modulus: reduction is just a bitmask. + if (has_single_bit(m)) + { + const uint128 mask {m - 1U}; + uint128 result {1}; + + while (exp != 0U) + { + if (static_cast(exp.low & 1U)) + { + result = (result * base) & mask; + } + + base = (base * base) & mask; + exp >>= 1; + } + + return result; + } + + // Modulus fits in 64 bits: stay in 64-bit lanes. + if (m.high == 0U) + { + const auto mm {m.low}; + std::uint64_t result {1}; + auto b {base.low}; + + while (exp != 0U) + { + if (static_cast(exp.low & 1U)) + { + result = detail::mulmod_word(result, b, mm); + } + + b = detail::mulmod_word(b, b, mm); + exp >>= 1; + } + + return uint128{result}; + } + + // General 128-bit modulus: shift-and-add for each squaring keeps every + // intermediate strictly below m without needing a 256-bit product. + uint128 result {1}; + + while (exp != 0U) + { + if (static_cast(exp.low & 1U)) + { + result = detail::mulmod_shift(result, base, m); + } + + base = detail::mulmod_shift(base, base, m); + exp >>= 1; + } + + return result; +} + +// Signed overload. Returns the non-negative residue in [0, m) +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 powm(const int128 base, const int128 exp, const int128 m) noexcept +{ + if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(m <= 0 || exp < 0)) + { + return int128{0}; + } + + const uint128 um {static_cast(m)}; + + uint128 ub {}; + + if (base.signed_high() < 0) + { + const uint128 magnitude {static_cast(abs(base))}; + const uint128 r {magnitude % um}; + ub = r == 0U ? uint128{0} : um - r; + } + else + { + ub = static_cast(base) % um; + } + + return static_cast(powm(ub, static_cast(exp), um)); +} + +// Computes base^exp using exponentiation by squaring. The result is reduced +// modulo 2^128, mirroring the wrap-around behavior of operator*. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 ipow(uint128 base, std::uint64_t exp) noexcept +{ + uint128 result {1}; + + while (exp != 0U) + { + if (static_cast(exp & 1U)) + { + result *= base; + } + + exp >>= 1; + + if (exp != 0U) + { + base *= base; + } + } + + return result; +} + +// Signed overload. Wraps modulo 2^128 on overflow, matching operator*. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 ipow(int128 base, std::uint64_t exp) noexcept +{ + int128 result {1}; + + while (exp != 0U) + { + if (static_cast(exp & 1U)) + { + result *= base; + } + + exp >>= 1; + + if (exp != 0U) + { + base *= base; + } + } + + return result; +} + +// Integer square root: returns floor(sqrt(n)). +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 isqrt(const uint128 n) noexcept +{ + if (n < 2U) + { + return n; + } + + // 2^ceil(bit_width(n)/2) is the smallest power of two whose square exceeds n. + uint128 x {uint128{1} << ((bit_width(n) + 1) / 2)}; + + while (true) + { + const uint128 y {(x + n / x) >> 1}; + + if (y >= x) + { + return x; + } + + x = y; + } +} + +// Signed overload. Negative inputs are documented to return 0. +BOOST_DECIMAL_DETAIL_INT128_EXPORT BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr int128 isqrt(const int128 n) noexcept +{ + if (BOOST_DECIMAL_DETAIL_INT128_UNLIKELY(n < 0)) + { + return int128{0}; + } + + return static_cast(isqrt(static_cast(n))); +} + +namespace detail { + +// The C23 checked integer macros accept any integer type for their operands +// except bool, plain char, enumerated types, and bit-precise (_BitInt) types. +template +struct valid_checked_type : std::integral_constant::value && + !std::is_same::value && + !std::is_same::value> {}; + +template <> +struct valid_checked_type : std::true_type {}; + +template <> +struct valid_checked_type : std::true_type {}; + +// Widen an integer operand to its 128-bit two's complement bit pattern, returned as a uint128 +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr uint128 ckd_widen(const T value) noexcept +{ + BOOST_DECIMAL_DETAIL_INT128_IF_CONSTEXPR (std::numeric_limits::is_signed) + { + return static_cast(static_cast(value)); + } + else + { + return static_cast(value); + } +} + +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable : 4324) +#endif + +// Sign and magnitude of an operand together with its 128-bit two's complement +// image. magnitude is the absolute value; negative records the sign. +struct ckd_operand +{ + uint128 raw; + uint128 magnitude; + bool negative; +}; + +// Exact signed sum of two operands given as (magnitude, sign). carry marks a +// 129th bit, which no 128-bit or narrower target can represent. +struct ckd_sum_result +{ + uint128 magnitude; + bool negative; + bool carry; +}; + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr ckd_operand ckd_decompose(const T value) noexcept +{ + const uint128 raw {ckd_widen(value)}; + const bool negative {std::numeric_limits::is_signed && ((raw >> 127) != 0U)}; + return ckd_operand{raw, negative ? uint128{0} - raw : raw, negative}; +} + +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr ckd_sum_result ckd_signed_sum(const uint128 a_magnitude, const bool a_negative, + const uint128 b_magnitude, const bool b_negative) noexcept +{ + if (a_negative == b_negative) + { + // Equal signs: magnitudes add and may overflow into a 129th bit. + const uint128 magnitude {a_magnitude + b_magnitude}; + return ckd_sum_result{magnitude, a_negative, magnitude < a_magnitude}; + } + + // Opposite signs: the smaller magnitude is subtracted and never carries. + if (a_magnitude >= b_magnitude) + { + return ckd_sum_result{a_magnitude - b_magnitude, a_negative, false}; + } + + return ckd_sum_result{b_magnitude - a_magnitude, b_negative, false}; +} + +// Whether a result of the given sign and magnitude fits in T1. exceeds_width +// forces overflow when the true magnitude does not even fit in 128 bits. +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool ckd_overflows(const uint128 magnitude, const bool negative, const bool exceeds_width) noexcept +{ + if (exceeds_width) + { + return true; + } + + const uint128 max_magnitude {static_cast((std::numeric_limits::max)())}; + + if (negative) + { + const uint128 min_magnitude {std::numeric_limits::is_signed ? max_magnitude + uint128{1} : uint128{0}}; + return magnitude > min_magnitude; + } + + return magnitude > max_magnitude; +} + +} // namespace detail + +// Checked addition following the C23 ckd_add contract. +// +// Computes a + b as if both operands were represented in a signed integer +// type of infinite range and then converts that exact result to the type +// pointed to by result. *result always receives the exact result wrapped +// around to the width of *result. Returns false when *result represents the +// exact mathematical sum, and true when the sum did not fit and wrap-around +// occurred. +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool ckd_add(T1* result, const T2 a, const T3 b) noexcept +{ + static_assert(detail::valid_checked_type::value && + detail::valid_checked_type::value && + detail::valid_checked_type::value, + "ckd_add operands must be integer types other than bool and plain char."); + + const auto op_a {detail::ckd_decompose(a)}; + const auto op_b {detail::ckd_decompose(b)}; + + // The modular sum of the widened images is the exact sum mod 2^128, which + // is all the wrapped result needs for any target no wider than 128 bits. + *result = static_cast(op_a.raw + op_b.raw); + + const auto sum {detail::ckd_signed_sum(op_a.magnitude, op_a.negative, op_b.magnitude, op_b.negative)}; + return detail::ckd_overflows(sum.magnitude, sum.negative, sum.carry); +} + +// Checked subtraction following the C23 ckd_sub contract. +// +// Behaves as ckd_add for a - b: *result receives the exact difference wrapped +// to its width, and the return value reports whether that difference did not +// fit. +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool ckd_sub(T1* result, const T2 a, const T3 b) noexcept +{ + static_assert(detail::valid_checked_type::value && + detail::valid_checked_type::value && + detail::valid_checked_type::value, + "ckd_sub operands must be integer types other than bool and plain char."); + + const auto op_a {detail::ckd_decompose(a)}; + const auto op_b {detail::ckd_decompose(b)}; + + *result = static_cast(op_a.raw - op_b.raw); + + // a - b is a + (-b): negating b flips its sign while keeping its magnitude. + const auto difference {detail::ckd_signed_sum(op_a.magnitude, op_a.negative, op_b.magnitude, !op_b.negative)}; + return detail::ckd_overflows(difference.magnitude, difference.negative, difference.carry); +} + +// Checked multiplication following the C23 ckd_mul contract. +// +// Computes a * b as if both operands had infinite range, stores the result +// wrapped to the width of *result, and returns true when the exact product did +// not fit. +BOOST_DECIMAL_DETAIL_INT128_EXPORT template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool ckd_mul(T1* result, const T2 a, const T3 b) noexcept +{ + static_assert(detail::valid_checked_type::value && + detail::valid_checked_type::value && + detail::valid_checked_type::value, + "ckd_mul operands must be integer types other than bool and plain char."); + + const auto op_a {detail::ckd_decompose(a)}; + const auto op_b {detail::ckd_decompose(b)}; + + *result = static_cast(op_a.raw * op_b.raw); + + // The product magnitude needs more than 128 bits exactly when it exceeds + // UINT128_MAX. Dividing the maximum by one magnitude tests that without + // forming a 256-bit product. + const bool exceeds_width {op_a.magnitude != 0U && + op_b.magnitude > ((std::numeric_limits::max)() / op_a.magnitude)}; + + const uint128 product_magnitude {op_a.magnitude * op_b.magnitude}; + const bool product_negative {op_a.negative != op_b.negative}; + + return detail::ckd_overflows(product_magnitude, product_negative, exceeds_width); +} + +namespace detail { + +// See: https://eel.is/c++draft/utility.intcmp +// [Note 1: These function templates cannot be used to compare byte, char, char8_t, char16_t, char32_t, wchar_t, and bool. end note] +template +struct valid_comparison_type +{ + static constexpr bool value = std::is_integral::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value && + !std::is_same::value + #if defined(__cpp_char8_t) + && !std::is_same::value + #endif + #if defined(__cpp_lib_byte) && __cpp_lib_byte >= 201603L + && !std::is_same::value + #endif + ; +}; + +template +BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_valid_comparison_type_v = valid_comparison_type::value; + +// Allow the builtins to be used when available +template +BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_int128_type_v = std::is_same::value || + std::is_same::value + #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) + || std::is_same::value + || std::is_same::value + #endif + ; + +template +BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool is_valid_comparison_operand_v = is_valid_comparison_type_v || + is_int128_type_v; + +// Maps the builtin 128-bit types onto the library equivalents +template +struct comparison_canonical +{ + using type = T; +}; + +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_INT128) || defined(BOOST_DECIMAL_DETAIL_INT128_HAS_MSVC_INT128) + +template <> +struct comparison_canonical +{ + using type = int128; +}; + +template <> +struct comparison_canonical +{ + using type = uint128; +}; + +#endif + +template +using comparison_canonical_t = typename comparison_canonical::type; + +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr comparison_canonical_t canonical_comparison_operand(const T value) noexcept +{ + return static_cast>(value); +} + +// Mathematical equality of two integers regardless of their signedness, via the +// same (sign, magnitude) decomposition. +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_equal_impl(const T lhs, const U rhs) noexcept +{ + const auto a {ckd_decompose(canonical_comparison_operand(lhs))}; + const auto b {ckd_decompose(canonical_comparison_operand(rhs))}; + + return (a.negative == b.negative) && (a.magnitude == b.magnitude); +} + +// Mathematical less-than of two integers regardless of their signedness, via the +// same (sign, magnitude) decomposition. +template +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_less_impl(const T lhs, const U rhs) noexcept +{ + const auto a {ckd_decompose(canonical_comparison_operand(lhs))}; + const auto b {ckd_decompose(canonical_comparison_operand(rhs))}; + + if (a.negative != b.negative) + { + return a.negative; + } + + return a.negative ? (a.magnitude > b.magnitude) : (a.magnitude < b.magnitude); +} + +template +BOOST_DECIMAL_DETAIL_INT128_INLINE_CONSTEXPR bool enable_comparison_v = is_valid_comparison_operand_v && + is_valid_comparison_operand_v && + (is_int128_type_v || is_int128_type_v); + +} // namespace detail + +// C++26 integer comparison functions (https://eel.is/c++draft/utility.intcmp) +// extended to the library and builtin 128-bit types and available from C++14. + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_equal(const T lhs, const U rhs) noexcept +{ + return detail::cmp_equal_impl(lhs, rhs); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_not_equal(const T lhs, const U rhs) noexcept +{ + return !detail::cmp_equal_impl(lhs, rhs); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_less(const T lhs, const U rhs) noexcept +{ + return detail::cmp_less_impl(lhs, rhs); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_greater(const T lhs, const U rhs) noexcept +{ + return detail::cmp_less_impl(rhs, lhs); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_less_equal(const T lhs, const U rhs) noexcept +{ + return !detail::cmp_less_impl(rhs, lhs); +} + +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool cmp_greater_equal(const T lhs, const U rhs) noexcept +{ + return !detail::cmp_less_impl(lhs, rhs); +} + +// Whether t is representable in the target type R. +BOOST_DECIMAL_DETAIL_INT128_EXPORT template , bool> = true> +BOOST_DECIMAL_DETAIL_INT128_HOST_DEVICE constexpr bool in_range(const T t) noexcept +{ + using limits = std::numeric_limits>; + + return !detail::cmp_less_impl(t, (limits::min)()) && + !detail::cmp_less_impl((limits::max)(), t); +} + +} // namespace int128 +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_INT128_UTILITIES_HPP diff --git a/include/boost/decimal/detail/integer_search_trees.hpp b/include/boost/decimal/detail/integer_search_trees.hpp index 13b92cf6c..249e1f23f 100644 --- a/include/boost/decimal/detail/integer_search_trees.hpp +++ b/include/boost/decimal/detail/integer_search_trees.hpp @@ -253,13 +253,13 @@ BOOST_DECIMAL_CUDA_CONSTEXPR int num_digits(const u256& x) noexcept // Approximate log10 const auto estimated_digits {(msb * 1000) / 3322 + 1}; // 1000/3322 ~= 1/log2(10) - if (estimated_digits < 78 && x >= pow10_256(estimated_digits)) + if (estimated_digits < 78 && x >= pow10_256(static_cast(estimated_digits))) { return estimated_digits + 1; } // Estimated digits will never be less than 39 (129 bits) - if (x < pow10_256(estimated_digits - 1)) + if (x < pow10_256(static_cast(estimated_digits - 1))) { return estimated_digits - 1; } diff --git a/include/boost/decimal/detail/normalize.hpp b/include/boost/decimal/detail/normalize.hpp index b7fafdca5..e7d5481d3 100644 --- a/include/boost/decimal/detail/normalize.hpp +++ b/include/boost/decimal/detail/normalize.hpp @@ -31,7 +31,7 @@ BOOST_DECIMAL_CUDA_CONSTEXPR auto normalize(T1& significand, T2& exp, bool sign { const auto zeros_needed {target_precision - digits}; BOOST_DECIMAL_ASSERT(zeros_needed >= 0); - significand *= pow10(static_cast(zeros_needed)); + significand *= pow10(static_cast(static_cast(zeros_needed))); exp -= zeros_needed; } else if (digits > target_precision) diff --git a/include/boost/decimal/detail/u256.hpp b/include/boost/decimal/detail/u256.hpp index 978a074ec..47da8e51b 100644 --- a/include/boost/decimal/detail/u256.hpp +++ b/include/boost/decimal/detail/u256.hpp @@ -16,6 +16,14 @@ #include #endif +// The vendored int128 exposes a single instruction 128/64 divide (udiv_2by1) on x86-64: divq +// under GCC/Clang and _udiv128 under MSVC. The Moller-Granlund u256/u128 fast path in this +// header is only worthwhile where that primitive exists, and never on a device compilation pass. +#if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_X86_64_DIVQ) || \ + (defined(_M_AMD64) && !defined(__GNUC__) && !defined(__clang__) && _MSC_VER >= 1920 && !defined(__CUDA_ARCH__)) +# define BOOST_DECIMAL_HAS_FAST_DIV128 +#endif + namespace boost { namespace decimal { namespace detail { @@ -868,12 +876,38 @@ BOOST_DECIMAL_CUDA_CONSTEXPR void to_words(const u256& x, std::uint32_t (&words) } } +// Word splitters for the narrower right hand side types accepted by default_mul +BOOST_DECIMAL_CUDA_CONSTEXPR void to_words(const int128::uint128_t& x, std::uint32_t (&words)[4]) noexcept +{ + #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && !BOOST_DECIMAL_ENDIAN_BIG_BYTE + if (!BOOST_DECIMAL_DETAIL_INT128_IS_CONSTANT_EVALUATED(x)) + { + std::memcpy(words, &x, sizeof(x)); + } + else + #endif + { + words[0] = static_cast(x.low & UINT32_MAX); + words[1] = static_cast(x.low >> 32U); + words[2] = static_cast(x.high & UINT32_MAX); + words[3] = static_cast(x.high >> 32U); + } +} + +BOOST_DECIMAL_CUDA_CONSTEXPR void to_words(const std::uint64_t x, std::uint32_t (&words)[2]) noexcept +{ + words[0] = static_cast(x & UINT32_MAX); + words[1] = static_cast(x >> 32U); +} + +BOOST_DECIMAL_CUDA_CONSTEXPR void to_words(const std::uint32_t x, std::uint32_t (&words)[1]) noexcept +{ + words[0] = x; +} + template BOOST_DECIMAL_FORCE_INLINE BOOST_DECIMAL_CUDA_CONSTEXPR u256 default_mul(const u256& lhs, const UnsignedInteger& rhs) noexcept { - using boost::decimal::detail::impl::to_words; - using boost::int128::detail::to_words; - constexpr std::size_t rhs_words_needed {sizeof(UnsignedInteger) / sizeof(std::uint32_t)}; std::uint32_t lhs_words[8] {}; @@ -1020,6 +1054,126 @@ BOOST_DECIMAL_CUDA_CONSTEXPR u256& u256::operator*=(const u256& rhs) noexcept namespace impl { +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable : 4127) // Pre C++17 the if constexpr remainder part will hit this +#endif + +// Knuth Algorithm D (TAOCP Vol. 2, 4.3.1) on 32-bit words, sized for 256-bit operands. +// Divides the m word dividend u by the n word divisor v (m >= n >= 2, v[n - 1] != 0) and writes the +// quotient to q. With need_remainder the remainder is left in u, otherwise u is scratch. +// Decimal owns this copy so the vendored int128 division code needs no local changes. +template +BOOST_DECIMAL_CUDA_CONSTEXPR void knuth_divide(std::uint32_t (&u)[8], const std::size_t m, + const std::uint32_t (&v)[8], const std::size_t n, + std::uint32_t (&q)[8]) noexcept +{ + // D.1: normalize so the top word of the divisor has its most significant bit set + const auto s {int128::detail::countl_zero(v[n - 1])}; + const auto complement_s {32 - s}; + const bool needs_shift {s > 0}; + + std::uint32_t un[9] {}; + std::uint32_t vn[8] {}; + + for (std::size_t i {n - 1}; i > 0; --i) + { + vn[i] = needs_shift ? ((v[i] << s) | (v[i - 1] >> complement_s)) : v[i]; + } + vn[0] = needs_shift ? (v[0] << s) : v[0]; + + un[m] = needs_shift ? (u[m - 1] >> complement_s) : 0; + for (std::size_t i {m - 1}; i > 0; --i) + { + un[i] = needs_shift ? ((u[i] << s) | (u[i - 1] >> complement_s)) : u[i]; + } + un[0] = needs_shift ? (u[0] << s) : u[0]; + + // D.2 + for (std::size_t j {m - n}; j != static_cast(-1); --j) + { + // D.3: estimate the quotient digit from the top two words + const auto dividend {(static_cast(un[j + n]) << 32) | un[j + n - 1]}; + const auto divisor {static_cast(vn[n - 1])}; + auto q_hat {dividend / divisor}; + auto r_hat {dividend % divisor}; + + while (q_hat > UINT32_MAX || + (q_hat * vn[n - 2]) > ((r_hat << 32) | un[j + n - 2])) + { + --q_hat; + r_hat += vn[n - 1]; + if (r_hat > UINT32_MAX) + { + break; + } + } + + // D.4: multiply and subtract + std::int64_t borrow {}; + for (std::size_t i {}; i < n; ++i) + { + const auto p {q_hat * vn[i]}; + const auto p_lo {static_cast(p & UINT32_MAX)}; + const auto p_hi {static_cast(p >> 32)}; + + borrow += static_cast(un[j + i]) - static_cast(p_lo); + un[j + i] = static_cast(borrow & UINT32_MAX); + borrow >>= 32; + + borrow -= p_hi; + } + borrow += un[j + n]; + un[j + n] = static_cast(borrow & UINT32_MAX); + + // D.5 + q[j] = static_cast(q_hat & UINT32_MAX); + if (BOOST_DECIMAL_UNLIKELY(borrow < 0)) + { + // D.6: the estimate was one too large, add the divisor back (probability about 4.7e-10) + --q[j]; // LCOV_EXCL_LINE + std::uint64_t carry {}; // LCOV_EXCL_LINE + for (std::size_t i {}; i < n; ++i) // LCOV_EXCL_LINE + { // LCOV_EXCL_LINE + carry += static_cast(un[j + i]) + vn[i]; // LCOV_EXCL_LINE + un[j + i] = static_cast(carry & UINT32_MAX); // LCOV_EXCL_LINE + carry >>= 32U; // LCOV_EXCL_LINE + } // LCOV_EXCL_LINE + un[j + n] += static_cast(carry & UINT32_MAX); // LCOV_EXCL_LINE + } + } + + // D.8: un-normalize the remainder into u + BOOST_DECIMAL_IF_CONSTEXPR (need_remainder) + { + if (s > 0) + { + for (std::size_t i {}; i < n - 1; ++i) + { + u[i] = (un[i] >> s) | (un[i + 1] << (32 - s)); + } + u[n - 1] = un[n - 1] >> s; + } + else + { + for (std::size_t i {}; i < n; ++i) + { + u[i] = un[i]; + } + } + + // Clear anything left in u + for (std::size_t i {n}; i < m; ++i) + { + u[i] = 0; + } + } +} + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + BOOST_DECIMAL_CUDA_CONSTEXPR std::size_t div_to_words(const u256& x, std::uint32_t (&words)[8]) noexcept { #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && !BOOST_DECIMAL_ENDIAN_BIG_BYTE @@ -1123,7 +1277,92 @@ BOOST_DECIMAL_FORCE_INLINE BOOST_DECIMAL_CUDA_CONSTEXPR u256 default_div(const u return quotient; } -#ifdef BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128 +#ifdef BOOST_DECIMAL_HAS_FAST_DIV128 + +// Precomputes the 64-bit reciprocal v of a normalized 128-bit divisor d (d.high has its MSB set), +// such that floor((2^192 - 1) / d) = 2^128 + v. +// Moller & Granlund, "Improved Division by Invariant Integers" (2010), Algorithm 6. +// Runtime only: udiv_2by1 and umul resolve to hardware instructions outside constant evaluation. +BOOST_DECIMAL_FORCE_INLINE std::uint64_t mg32_reciprocal_2by1(const int128::uint128_t d) noexcept +{ + // v = floor((2^128 - 1) / d.high) - 2^64 by one 128/64 divide. The dividend's high word + // ~d.high is below d.high because d.high >= 2^63, which is the udiv_2by1 precondition. + std::uint64_t dummy {}; + std::uint64_t v {int128::detail::udiv_2by1(~d.high, UINT64_MAX, d.high, dummy)}; + + // p = d.high * v + d.low, low 64 bits only + std::uint64_t p {d.high * v + d.low}; + + if (p < d.low) + { + --v; + if (p >= d.high) + { + --v; + p -= d.high; + } + p -= d.high; + } + + std::uint64_t t_high {}; + const std::uint64_t t_low {int128::detail::umul(v, d.low, t_high)}; + + const std::uint64_t p_new {p + t_high}; + if (p_new < p) + { + --v; + if (p_new > d.high || (p_new == d.high && t_low >= d.low)) + { + --v; + } + } + + return v; +} + +// One 3-by-2 limb division step: divides the 192-bit value (u_high:u_low) by the normalized +// 128-bit divisor d using its precomputed reciprocal v, returning the 64-bit quotient and +// writing the 128-bit remainder to r. Preconditions: d.high >= 2^63 and u_high < d. +// Moller & Granlund (2010), Algorithm 5. +BOOST_DECIMAL_FORCE_INLINE std::uint64_t mg32_div_3by2(const int128::uint128_t u_high, const std::uint64_t u_low, + const int128::uint128_t d, const std::uint64_t v, + int128::uint128_t& r) noexcept +{ + // q = u_high.high * v + u_high + std::uint64_t q_high {}; + std::uint64_t q_low {int128::detail::umul(u_high.high, v, q_high)}; + const std::uint64_t low_sum {q_low + u_high.low}; + q_high += u_high.high + static_cast(low_sum < q_low); + q_low = low_sum; + + // r = (u_high.low - q_high * d.high : u_low) - q_high * d.low - d + const std::uint64_t r1 {u_high.low - q_high * d.high}; + + std::uint64_t t_high {}; + const std::uint64_t t_low {int128::detail::umul(q_high, d.low, t_high)}; + + int128::uint128_t remainder {r1, u_low}; + remainder -= int128::uint128_t{t_high, t_low}; + remainder -= d; + + std::uint64_t quotient {q_high + 1U}; + + if (remainder.high >= q_low) + { + --quotient; + remainder += d; + } + + // Second correction is rare + if (BOOST_DECIMAL_UNLIKELY(remainder >= d)) + { + ++quotient; + remainder -= d; + } + + r = remainder; + return quotient; +} // MG 3/2 fast path for u256 divided by a uint128 divisor. // @@ -1175,13 +1414,13 @@ BOOST_DECIMAL_FORCE_INLINE bool mg32_u256_by_u128( return false; } - const std::uint64_t v {int128::detail::impl::mg32_reciprocal_2by1(d)}; + const std::uint64_t v {mg32_reciprocal_2by1(d)}; int128::uint128_t r1{}; - const std::uint64_t q_high {int128::detail::impl::mg32_div_3by2(u_top, u.bytes[1], d, v, r1)}; + const std::uint64_t q_high {mg32_div_3by2(u_top, u.bytes[1], d, v, r1)}; int128::uint128_t r2{}; - const std::uint64_t q_low {int128::detail::impl::mg32_div_3by2(r1, u.bytes[0], d, v, r2)}; + const std::uint64_t q_low {mg32_div_3by2(r1, u.bytes[0], d, v, r2)}; q = int128::uint128_t{q_high, q_low}; // Un-normalize the remainder. @@ -1189,7 +1428,7 @@ BOOST_DECIMAL_FORCE_INLINE bool mg32_u256_by_u128( return true; } -#endif // BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128 +#endif // BOOST_DECIMAL_HAS_FAST_DIV128 // True when the divisor fits in 128 bits, i.e. the MG u256-by-u128 fast path // is applicable. A u256 divisor only qualifies when its upper 128 bits are @@ -1219,7 +1458,7 @@ BOOST_DECIMAL_FORCE_INLINE BOOST_DECIMAL_CUDA_CONSTEXPR u256 default_div(const u return u256{}; } - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + #if defined(BOOST_DECIMAL_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) // MG 3/2 fast path. Replaces ~3 hardware divides in the 32-bit Knuth-D // outer loop with one reciprocal-compute plus two cheap 3/2 inner steps. // Bails out (returns false) for inputs that don't fit the algorithm's @@ -1254,7 +1493,7 @@ BOOST_DECIMAL_FORCE_INLINE BOOST_DECIMAL_CUDA_CONSTEXPR u256 default_div(const u return u256{}; } - int128::detail::impl::knuth_divide(u, m, v, n, q); + knuth_divide(u, m, v, n, q); return from_words(q); } @@ -1268,7 +1507,7 @@ struct u256_divmod_result template BOOST_DECIMAL_FORCE_INLINE BOOST_DECIMAL_CUDA_CONSTEXPR auto div_mod(const u256& lhs, const UnsignedInteger& rhs) noexcept -> u256_divmod_result { - #if defined(BOOST_DECIMAL_DETAIL_INT128_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) + #if defined(BOOST_DECIMAL_HAS_FAST_DIV128) && !defined(BOOST_DECIMAL_DETAIL_INT128_NO_CONSTEVAL_DETECTION) // MG 3/2 fast path for u256/uint128. See default_div for rationale. // Single-limb divisors fall through to the 32-bit Knuth-D path below, // which has a dedicated n==1 short-circuit. @@ -1325,7 +1564,7 @@ BOOST_DECIMAL_FORCE_INLINE BOOST_DECIMAL_CUDA_CONSTEXPR auto div_mod(const u256& } else { - int128::detail::impl::knuth_divide(u, m, v, n, q); + knuth_divide(u, m, v, n, q); } return {from_words(q), from_words(u)}; @@ -1411,7 +1650,7 @@ BOOST_DECIMAL_FORCE_INLINE BOOST_DECIMAL_CUDA_CONSTEXPR u256 default_mod(const u return lhs; } - int128::detail::impl::knuth_divide(u, m, v, n, q); + knuth_divide(u, m, v, n, q); return from_words(u); } diff --git a/include/boost/decimal/format.hpp b/include/boost/decimal/format.hpp index 6e9707023..b46423548 100644 --- a/include/boost/decimal/format.hpp +++ b/include/boost/decimal/format.hpp @@ -339,7 +339,7 @@ struct formatter # pragma warning(disable : 4244) #endif - std::transform(s.begin() + static_cast(has_sign), s.end(), s.begin() + static_cast(has_sign), + std::transform(s.begin() + static_cast(has_sign), s.end(), s.begin() + static_cast(has_sign), [](unsigned char c) { return std::toupper(c); }); diff --git a/include/boost/decimal/string.hpp b/include/boost/decimal/string.hpp index 26873c48e..6ffa59e00 100644 --- a/include/boost/decimal/string.hpp +++ b/include/boost/decimal/string.hpp @@ -81,7 +81,7 @@ auto to_string(const DecimalType value) { char buffer[64]; auto r = to_chars(buffer, buffer + sizeof(buffer), value); - return std::string(buffer, r.ptr - buffer); + return std::string(buffer, static_cast(r.ptr - buffer)); } } //namespace decimal diff --git a/module/decimal.cppm b/module/decimal.cppm index da2fade33..be33dec49 100644 --- a/module/decimal.cppm +++ b/module/decimal.cppm @@ -27,12 +27,19 @@ module; #include #include -// Platform intrinsic headers are not part of the standard library module, so -// they are always brought in here. +// Platform intrinsic headers and the MSVC built-in 128-bit header are not part of +// the standard library module, so they are always brought in here. +#if __has_include(<__msvc_int128.hpp>) && _MSVC_LANG >= 202002L +# include <__msvc_int128.hpp> +#endif + #if defined(_MSC_VER) # include #elif defined(__x86_64__) # include +# include +#elif defined(__i386__) +# include #elif defined(__ARM_NEON__) # include #endif @@ -87,7 +94,10 @@ module; // The vendored Boost.Int128 keys its inline-constexpr and export macros off its // own build-module flag; set it so its detail constants get inline (external) // linkage rather than static, otherwise extern "C++" exposes them as TU-local. +// It also declares its builtin 128-bit aliases only in the interface unit (a +// module consumer receives them through the import), so mark this unit as such. #define BOOST_DECIMAL_DETAIL_INT128_BUILD_MODULE +#define BOOST_DECIMAL_DETAIL_INT128_INTERFACE_UNIT export module boost.decimal; diff --git a/test/bench_accumulation.cpp b/test/bench_accumulation.cpp index c04f7e8ac..6a3848ea0 100644 --- a/test/bench_accumulation.cpp +++ b/test/bench_accumulation.cpp @@ -8,9 +8,6 @@ // and leaves the alignment kernel under-exercised. This benchmark generates // same-magnitude operands so the kernel actually has to align and add. -#define BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -#define BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - #include #include #include diff --git a/test/benchmarks.cpp b/test/benchmarks.cpp index 041a7e262..fde99bcb2 100644 --- a/test/benchmarks.cpp +++ b/test/benchmarks.cpp @@ -3,9 +3,6 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#define BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_CONVERSION -#define BOOST_DECIMAL_DETAIL_INT128_ALLOW_SIGN_COMPARE - #include #include #include diff --git a/test/github_issue_1329.cpp b/test/github_issue_1329.cpp index 8a50f84dc..8db0012f7 100644 --- a/test/github_issue_1329.cpp +++ b/test/github_issue_1329.cpp @@ -9,6 +9,8 @@ # pragma clang diagnostic ignored "-Wold-style-cast" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-conversion" #if (__clang_major__ > 12) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wunused-but-set-variable" diff --git a/test/test_cbrt_from_math.cpp b/test/test_cbrt_from_math.cpp index 5a3895ed5..c18035e48 100644 --- a/test/test_cbrt_from_math.cpp +++ b/test/test_cbrt_from_math.cpp @@ -52,6 +52,8 @@ # pragma clang diagnostic ignored "-Wunused-parameter" # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wdeprecated-declarations" +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wsign-conversion" #elif defined(__GNUC__) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wfloat-equal"