From b5d19635283fc4bc7d2c5f487d5891e33352f19f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 10:41:41 -0500 Subject: [PATCH 001/191] Add default constructors --- include/boost/decimal/detail/u128.hpp | 45 +++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 include/boost/decimal/detail/u128.hpp diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp new file mode 100644 index 00000000..61b08a28 --- /dev/null +++ b/include/boost/decimal/detail/u128.hpp @@ -0,0 +1,45 @@ +// Copyright 2023 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt +// +// If the architecture (e.g. 32-bit x86) does not have __int128 we need to emulate it + +#ifndef BOOST_DECIMAL_DETAIL_U128_HPP +#define BOOST_DECIMAL_DETAIL_U128_HPP + +#include + +namespace boost { +namespace decimal { +namespace detail { + +struct + #ifdef BOOST_DECIMAL_HAS_INT128 + alignas(alignof(uint128_t)) + #else + alignas(16) + #endif +u128 +{ + #if BOOST_DECIMAL_ENDIAN_LITTLE_BYTE + std::uint64_t low {}; + std::uint64_t high {}; + #else + std::uint64_t high {}; + std::uint64_t low {}; + #endif + + // Constructors + constexpr u128() noexcept = default; + constexpr u128(const u128& other) noexcept = default; + constexpr u128(u128&& other) noexcept = default; + constexpr u128& operator=(const u128& other) noexcept = default; + constexpr u128& operator=(u128&& other) noexcept = default; + +}; + +} // namespace detail +} // namespace decimal +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_U128_HPP From 4b46eb9c377f2fedd1b2c3514ee4e352d89fa0fc Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 10:57:39 -0500 Subject: [PATCH 002/191] Add signed and unsigned arithmetic constructors --- include/boost/decimal/detail/u128.hpp | 31 ++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 61b08a28..8876030d 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -15,12 +15,18 @@ namespace detail { struct #ifdef BOOST_DECIMAL_HAS_INT128 - alignas(alignof(uint128_t)) + alignas(alignof(unsigned __int128)) #else alignas(16) #endif u128 { +private: + + static constexpr std::uint64_t low_word_mask {~UINT64_C(0)}; + +public: + #if BOOST_DECIMAL_ENDIAN_LITTLE_BYTE std::uint64_t low {}; std::uint64_t high {}; @@ -36,6 +42,29 @@ u128 constexpr u128& operator=(const u128& other) noexcept = default; constexpr u128& operator=(u128&& other) noexcept = default; + // Signed arithmetic constructors + explicit constexpr u128(const std::int8_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} + explicit constexpr u128(const std::int16_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} + explicit constexpr u128(const std::int32_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} + explicit constexpr u128(const std::int64_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} + + #ifdef BOOST_DECIMAL_HAS_INT128 + explicit constexpr u128(const __int128 value) noexcept : + low {static_cast(value & low_word_mask)}, + high {static_cast(static_cast(value) >> 64U)} {} + #endif // BOOST_DECIMAL_HAS_INT128 + + // Unsigned arithmetic constructors + explicit constexpr u128(const std::uint8_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} + explicit constexpr u128(const std::uint16_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} + explicit constexpr u128(const std::uint32_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} + explicit constexpr u128(const std::uint64_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} + + #ifdef BOOST_DECIMAL_HAS_INT128 + explicit constexpr u128(const unsigned __int128 value) noexcept : + low {static_cast(value & low_word_mask)}, + high {static_cast(value >> 64U)} {} + #endif // BOOST_DECIMAL_HAS_INT128 }; } // namespace detail From 3ef7ae8d412366bae6c1f09e32a630cb2377bf74 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 11:11:52 -0500 Subject: [PATCH 003/191] Add signed and unsigned assignment operators --- include/boost/decimal/detail/u128.hpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8876030d..ad1221c9 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -65,6 +65,26 @@ u128 low {static_cast(value & low_word_mask)}, high {static_cast(value >> 64U)} {} #endif // BOOST_DECIMAL_HAS_INT128 + + // Signed assignment operators + constexpr u128& operator=(const std::int8_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } + constexpr u128& operator=(const std::int16_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } + constexpr u128& operator=(const std::int32_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } + constexpr u128& operator=(const std::int64_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator=(const __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(static_cast(value) >> 64U); return *this; } + #endif // BOOST_DECIMAL_HAS_INT128 + + // Unsigned assignment operators + constexpr u128& operator=(const std::uint8_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } + constexpr u128& operator=(const std::uint16_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } + constexpr u128& operator=(const std::uint32_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } + constexpr u128& operator=(const std::uint64_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator=(const unsigned __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(value >> 64U); return *this; } + #endif }; } // namespace detail From 508ce87f56a5fcc3f80f3dc4689fb1821ab3548c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 11:20:56 -0500 Subject: [PATCH 004/191] Add bool and integer conversion operators --- include/boost/decimal/detail/u128.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ad1221c9..a6630505 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -85,6 +85,29 @@ u128 #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& operator=(const unsigned __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(value >> 64U); return *this; } #endif + + // Bool conversion is not explicit so we can do stuff like if (num) + constexpr operator bool() const noexcept { return low || high; } + + // Conversion to signed integer types + explicit constexpr operator std::int8_t() const noexcept { return static_cast(low); } + explicit constexpr operator std::int16_t() const noexcept { return static_cast(low); } + explicit constexpr operator std::int32_t() const noexcept { return static_cast(low); } + explicit constexpr operator std::int64_t() const noexcept { return static_cast(low); } + + #ifdef BOOST_DECIMAL_HAS_INT128 + explicit constexpr operator __int128() const noexcept { return (static_cast<__int128>(high) << 64) + low; } + #endif // BOOST_DECIMAL_HAS_INT128 + + // Conversion to unsigned integer types + explicit constexpr operator std::uint8_t() const noexcept { return static_cast(low); } + explicit constexpr operator std::uint16_t() const noexcept { return static_cast(low); } + explicit constexpr operator std::uint32_t() const noexcept { return static_cast(low); } + explicit constexpr operator std::uint64_t() const noexcept { return low; } + + #ifdef BOOST_DECIMAL_HAS_INT128 + explicit constexpr operator unsigned __int128() const noexcept { return (static_cast(high) << 64U) + low; } + #endif // BOOST_DECIMAL_HAS_INT128 }; } // namespace detail From 5b50d953576d3de61490749b7cd42742c2b32ca4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 11:39:15 -0500 Subject: [PATCH 005/191] Add float conversion operators --- include/boost/decimal/detail/u128.hpp | 39 +++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index a6630505..252e06f0 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -108,8 +108,47 @@ u128 #ifdef BOOST_DECIMAL_HAS_INT128 explicit constexpr operator unsigned __int128() const noexcept { return (static_cast(high) << 64U) + low; } #endif // BOOST_DECIMAL_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 + explicit constexpr operator float() const noexcept; + explicit constexpr operator double() const noexcept; + explicit constexpr operator long double() const noexcept; + + #ifdef BOOST_DECIMAL_HAS_FLOAT128 + explicit constexpr operator __float128() const noexcept; + #endif // BOOST_DECIMAL_HAS_FLOAT128 }; +constexpr u128::operator float() const noexcept +{ + constexpr float offset {static_cast(std::numeric_limits::max())}; + return static_cast(high) * offset + static_cast(low); +} + +constexpr u128::operator double() const noexcept +{ + constexpr double offset {static_cast(std::numeric_limits::max())}; + return static_cast(high) * offset + static_cast(low); +} + +constexpr u128::operator long double() const noexcept +{ + constexpr long double offset {static_cast(std::numeric_limits::max())}; + return static_cast(high) * offset + static_cast(low); +} + +#ifdef BOOST_DECIMAL_HAS_FLOAT128 + +constexpr u128::operator __float128() const noexcept +{ + constexpr __float128 offset {static_cast<__float128>(std::numeric_limits::max())}; + return static_cast<__float128>(high) * offset + static_cast<__float128>(low); +} + +#endif // BOOST_DECIMAL_HAS_FLOAT128 + } // namespace detail } // namespace decimal } // namespace boost From cee89c1faa83f8eca652520481dcc792de3454b2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 12:01:04 -0500 Subject: [PATCH 006/191] Reduce in-class clutter --- include/boost/decimal/detail/u128.hpp | 68 +++++++++++++++++++++++---- 1 file changed, 58 insertions(+), 10 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 252e06f0..637a95b0 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -67,23 +67,23 @@ u128 #endif // BOOST_DECIMAL_HAS_INT128 // Signed assignment operators - constexpr u128& operator=(const std::int8_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } - constexpr u128& operator=(const std::int16_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } - constexpr u128& operator=(const std::int32_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } - constexpr u128& operator=(const std::int64_t value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); return *this; } + constexpr u128& operator=(const std::int8_t value) noexcept; + constexpr u128& operator=(const std::int16_t value) noexcept; + constexpr u128& operator=(const std::int32_t value) noexcept; + constexpr u128& operator=(const std::int64_t value) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 - constexpr u128& operator=(const __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(static_cast(value) >> 64U); return *this; } + constexpr u128& operator=(const __int128 value) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 // Unsigned assignment operators - constexpr u128& operator=(const std::uint8_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } - constexpr u128& operator=(const std::uint16_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } - constexpr u128& operator=(const std::uint32_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } - constexpr u128& operator=(const std::uint64_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } + constexpr u128& operator=(const std::uint8_t value) noexcept; + constexpr u128& operator=(const std::uint16_t value) noexcept; + constexpr u128& operator=(const std::uint32_t value) noexcept; + constexpr u128& operator=(const std::uint64_t value) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 - constexpr u128& operator=(const unsigned __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(value >> 64U); return *this; } + constexpr u128& operator=(const unsigned __int128 value) noexcept; #endif // Bool conversion is not explicit so we can do stuff like if (num) @@ -121,6 +121,54 @@ u128 #endif // BOOST_DECIMAL_HAS_FLOAT128 }; +// Signed assignment operators +constexpr u128& u128::operator=(const std::int8_t value) noexcept +{ + low = static_cast(value); + high = value < 0 ? UINT64_MAX : UINT64_C(0); + return *this; +} + +constexpr u128& u128::operator=(const std::int16_t value) noexcept +{ + low = static_cast(value); + high = value < 0 ? UINT64_MAX : UINT64_C(0); + return *this; +} + +constexpr u128& u128::operator=(const std::int32_t value) noexcept +{ + low = static_cast(value); + high = value < 0 ? UINT64_MAX : UINT64_C(0); + return *this; +} + +constexpr u128& u128::operator=(const std::int64_t value) noexcept +{ + low = static_cast(value); + high = value < 0 ? UINT64_MAX : UINT64_C(0); + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator=(const __int128 value) noexcept +{ + low = static_cast(value & low_word_mask); high = static_cast(static_cast(value) >> 64U); return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + +// Unsigned assignment operators +constexpr u128& u128::operator=(const std::uint8_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } +constexpr u128& u128::operator=(const std::uint16_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } +constexpr u128& u128::operator=(const std::uint32_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } +constexpr u128& u128::operator=(const std::uint64_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } + +#ifdef BOOST_DECIMAL_HAS_INT128 +constexpr u128& u128::operator=(const unsigned __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(value >> 64U); return *this; } +#endif + constexpr u128::operator float() const noexcept { constexpr float offset {static_cast(std::numeric_limits::max())}; From 46a48f20738cd73c425eca5c69e89154b2e07b92 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 12:07:46 -0500 Subject: [PATCH 007/191] Add unary arithmetic operators --- include/boost/decimal/detail/u128.hpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 637a95b0..85ed0803 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -42,6 +42,9 @@ u128 constexpr u128& operator=(const u128& other) noexcept = default; constexpr u128& operator=(u128&& other) noexcept = default; + // Direct construction of the number + constexpr u128(const std::uint64_t hi, const std::uint64_t lo) noexcept : low{lo}, high{hi} {} + // Signed arithmetic constructors explicit constexpr u128(const std::int8_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} explicit constexpr u128(const std::int16_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} @@ -119,6 +122,10 @@ u128 #ifdef BOOST_DECIMAL_HAS_FLOAT128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 + + // Unary operators + friend constexpr u128 operator+(const u128 value) noexcept; + friend constexpr u128 operator-(const u128 value) noexcept; }; // Signed assignment operators @@ -197,6 +204,16 @@ constexpr u128::operator __float128() const noexcept #endif // BOOST_DECIMAL_HAS_FLOAT128 +constexpr u128 operator+(const u128 value) noexcept +{ + return value; +} + +constexpr u128 operator-(const u128 value) noexcept +{ + return u128{~value.high + static_cast(value.low == UINT64_C(0)), ~value.low + UINT64_C(1)}; +} + } // namespace detail } // namespace decimal } // namespace boost From a4879d1d60f9e00e467ba574fe133408a51dc7b0 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 12:14:00 -0500 Subject: [PATCH 008/191] Fix clang tidy warnings --- include/boost/decimal/detail/u128.hpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 85ed0803..b6de8f89 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -70,23 +70,23 @@ u128 #endif // BOOST_DECIMAL_HAS_INT128 // Signed assignment operators - constexpr u128& operator=(const std::int8_t value) noexcept; - constexpr u128& operator=(const std::int16_t value) noexcept; - constexpr u128& operator=(const std::int32_t value) noexcept; - constexpr u128& operator=(const std::int64_t value) noexcept; + constexpr u128& operator=(std::int8_t value) noexcept; + constexpr u128& operator=(std::int16_t value) noexcept; + constexpr u128& operator=(std::int32_t value) noexcept; + constexpr u128& operator=(std::int64_t value) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 - constexpr u128& operator=(const __int128 value) noexcept; + constexpr u128& operator=(__int128 value) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 // Unsigned assignment operators - constexpr u128& operator=(const std::uint8_t value) noexcept; - constexpr u128& operator=(const std::uint16_t value) noexcept; - constexpr u128& operator=(const std::uint32_t value) noexcept; - constexpr u128& operator=(const std::uint64_t value) noexcept; + constexpr u128& operator=(std::uint8_t value) noexcept; + constexpr u128& operator=(std::uint16_t value) noexcept; + constexpr u128& operator=(std::uint32_t value) noexcept; + constexpr u128& operator=(std::uint64_t value) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 - constexpr u128& operator=(const unsigned __int128 value) noexcept; + constexpr u128& operator=(unsigned __int128 value) noexcept; #endif // Bool conversion is not explicit so we can do stuff like if (num) @@ -124,8 +124,8 @@ u128 #endif // BOOST_DECIMAL_HAS_FLOAT128 // Unary operators - friend constexpr u128 operator+(const u128 value) noexcept; - friend constexpr u128 operator-(const u128 value) noexcept; + friend constexpr u128 operator+(u128 value) noexcept; + friend constexpr u128 operator-(u128 value) noexcept; }; // Signed assignment operators From 1e7ef1324109bf3fd2966dd1d736a11f97e39a5f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 12:22:33 -0500 Subject: [PATCH 009/191] Add arithmetic constructor tests --- test/Jamfile | 1 + test/test_u128.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 test/test_u128.cpp diff --git a/test/Jamfile b/test/Jamfile index bfcac416..07008b82 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -156,6 +156,7 @@ run test_tanh.cpp ; run test_tgamma.cpp ; run test_to_chars.cpp ; run test_to_string.cpp ; +run test_u128.cpp ; run test_zeta.cpp ; # Run the examples too diff --git a/test/test_u128.cpp b/test/test_u128.cpp new file mode 100644 index 00000000..783357bd --- /dev/null +++ b/test/test_u128.cpp @@ -0,0 +1,62 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include + +#ifdef BOOST_DECIMAL_HAS_INT128 + +#include +#include +#include +#include +#include +#include + +// Used defined seed for repeatability +static std::mt19937_64 rng(42); + +constexpr std::size_t N = 1024; + +template +void test_arithmetic_constructor() +{ + std::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value = value; + boost::decimal::detail::u128 emulated_value {value}; + + unsigned __int128 emulated_bits; + std::memcpy(&emulated_bits, &emulated_value, sizeof(unsigned __int128)); + + BOOST_TEST(emulated_bits == builtin_value); + } +} + +int main() +{ + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + + return boost::report_errors(); +} + +#else + +int main() +{ + return 0; +} + +#endif From 27be82a825d70659be0e2b27123d2d6e42dc374c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 12:25:59 -0500 Subject: [PATCH 010/191] Add missing STL headers --- include/boost/decimal/detail/u128.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index b6de8f89..0b76ffef 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -9,6 +9,21 @@ #include +#ifndef BOOST_DECIMAL_BUILD_MODULE + +#include +#include +#include +#include + +#if !defined(BOOST_DECIMAL_DISABLE_IOSTREAM) +#include +#include +#include +#endif + +#endif // BOOST_DECIMAL_BUILD_MODULE + namespace boost { namespace decimal { namespace detail { From d8ba8485cf32c4a909ef33441be9662090183607 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 12:26:16 -0500 Subject: [PATCH 011/191] Test assignment operators --- test/test_u128.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 783357bd..ca4d6b41 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -37,6 +37,28 @@ void test_arithmetic_constructor() } } +template +void test_assignment_operators() +{ + std::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value; + builtin_value = value; + boost::decimal::detail::u128 emulated_value {}; + emulated_value = value; + + unsigned __int128 emulated_bits; + std::memcpy(&emulated_bits, &emulated_value, sizeof(unsigned __int128)); + + BOOST_TEST(emulated_bits == builtin_value); + } +} + + int main() { test_arithmetic_constructor(); @@ -49,6 +71,16 @@ int main() test_arithmetic_constructor(); test_arithmetic_constructor(); + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + return boost::report_errors(); } From be8699503c1012503a08a59be5a1e0ad1605344e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 12:27:12 -0500 Subject: [PATCH 012/191] Test integer conversion operators --- test/test_u128.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index ca4d6b41..b1033f50 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -58,6 +58,26 @@ void test_assignment_operators() } } +template +void test_integer_conversion_operators() +{ + std::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value; + builtin_value = value; + boost::decimal::detail::u128 emulated_value {}; + emulated_value = value; + + const auto builtin_value_return = static_cast(builtin_value); + const auto emulated_value_return = static_cast(emulated_value); + + BOOST_TEST_EQ(builtin_value_return, emulated_value_return); + } +} int main() { @@ -81,6 +101,16 @@ int main() test_assignment_operators(); test_assignment_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + return boost::report_errors(); } From 9bf4219a28fa5c1634c4345345987c609a09ba2f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 13:36:21 -0500 Subject: [PATCH 013/191] Add float conversion operators testing --- test/test_u128.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index b1033f50..9d016639 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -12,6 +12,7 @@ #include #include #include +#include // Used defined seed for repeatability static std::mt19937_64 rng(42); @@ -79,6 +80,27 @@ void test_integer_conversion_operators() } } +template +void test_float_conversion_operators() +{ + std::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const auto value {dist(rng)}; + unsigned __int128 builtin_value; + builtin_value = value; + boost::decimal::detail::u128 emulated_value {}; + emulated_value = value; + + const auto builtin_value_return = static_cast(builtin_value); + const auto emulated_value_return = static_cast(emulated_value); + + BOOST_TEST(std::abs(builtin_value_return - emulated_value_return) < std::numeric_limits::epsilon()); + } +} + int main() { test_arithmetic_constructor(); @@ -111,6 +133,14 @@ int main() test_integer_conversion_operators(); test_integer_conversion_operators(); + test_float_conversion_operators(); + test_float_conversion_operators(); + test_float_conversion_operators(); + + #ifdef BOOST_DECIMAL_HAS_FLOAT128 + test_float_conversion_operators<__float128>(); + #endif + return boost::report_errors(); } From 4f4a9a5475c2da23ee70f15e4c39b95a664091e5 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 13:44:24 -0500 Subject: [PATCH 014/191] Test 128-bit ints and fix sign conversion warning --- test/test_u128.cpp | 49 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 9d016639..4614a126 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -14,6 +14,35 @@ #include #include +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wold-style-cast" +# pragma clang diagnostic ignored "-Wundef" +# pragma clang diagnostic ignored "-Wconversion" +# pragma clang diagnostic ignored "-Wsign-conversion" +# pragma clang diagnostic ignored "-Wfloat-equal" + +# if (__clang_major__ >= 10 && !defined(__APPLE__)) || __clang_major__ >= 13 +# pragma clang diagnostic ignored "-Wdeprecated-copy" +# endif + +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wold-style-cast" +# pragma GCC diagnostic ignored "-Wundef" +# pragma GCC diagnostic ignored "-Wconversion" +# pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + // Used defined seed for repeatability static std::mt19937_64 rng(42); @@ -22,13 +51,13 @@ constexpr std::size_t N = 1024; template void test_arithmetic_constructor() { - std::uniform_int_distribution dist(std::numeric_limits::min(), + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) { const IntType value {dist(rng)}; - unsigned __int128 builtin_value = value; + unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; unsigned __int128 emulated_bits; @@ -41,14 +70,14 @@ void test_arithmetic_constructor() template void test_assignment_operators() { - std::uniform_int_distribution dist(std::numeric_limits::min(), + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) { const IntType value {dist(rng)}; unsigned __int128 builtin_value; - builtin_value = value; + builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {}; emulated_value = value; @@ -62,14 +91,14 @@ void test_assignment_operators() template void test_integer_conversion_operators() { - std::uniform_int_distribution dist(std::numeric_limits::min(), + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) { const IntType value {dist(rng)}; unsigned __int128 builtin_value; - builtin_value = value; + builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {}; emulated_value = value; @@ -83,7 +112,7 @@ void test_integer_conversion_operators() template void test_float_conversion_operators() { - std::uniform_int_distribution dist(std::numeric_limits::min(), + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) @@ -107,31 +136,37 @@ int main() test_arithmetic_constructor(); test_arithmetic_constructor(); test_arithmetic_constructor(); + test_arithmetic_constructor<__int128>(); test_arithmetic_constructor(); test_arithmetic_constructor(); test_arithmetic_constructor(); test_arithmetic_constructor(); + test_arithmetic_constructor(); test_assignment_operators(); test_assignment_operators(); test_assignment_operators(); test_assignment_operators(); + test_arithmetic_constructor<__int128>(); test_assignment_operators(); test_assignment_operators(); test_assignment_operators(); test_assignment_operators(); + test_arithmetic_constructor(); test_integer_conversion_operators(); test_integer_conversion_operators(); test_integer_conversion_operators(); test_integer_conversion_operators(); + test_arithmetic_constructor<__int128>(); test_integer_conversion_operators(); test_integer_conversion_operators(); test_integer_conversion_operators(); test_integer_conversion_operators(); + test_arithmetic_constructor(); test_float_conversion_operators(); test_float_conversion_operators(); From 5f113ff11ab193cf68f10835bea28f9e367bd166 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 13:52:13 -0500 Subject: [PATCH 015/191] Test bool conversion --- test/test_u128.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 4614a126..f36fdbc6 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -52,7 +52,7 @@ template void test_arithmetic_constructor() { boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) { @@ -71,7 +71,7 @@ template void test_assignment_operators() { boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) { @@ -92,7 +92,7 @@ template void test_integer_conversion_operators() { boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) { @@ -106,6 +106,12 @@ void test_integer_conversion_operators() const auto emulated_value_return = static_cast(emulated_value); BOOST_TEST_EQ(builtin_value_return, emulated_value_return); + + // Hits the implicit bool conversion + if (builtin_value) + { + BOOST_TEST(emulated_value); + } } } @@ -113,7 +119,7 @@ template void test_float_conversion_operators() { boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + std::numeric_limits::max()); for (std::size_t i {}; i < N; ++i) { From 6c6df063c23d6c856e371822f34038f67afea60c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 13:54:37 -0500 Subject: [PATCH 016/191] test unary plus and minus --- test/test_u128.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index f36fdbc6..baf1870c 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -136,6 +136,47 @@ void test_float_conversion_operators() } } +template +void test_unary_plus() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + emulated_value = +emulated_value; + + unsigned __int128 emulated_bits; + std::memcpy(&emulated_bits, &emulated_value, sizeof(unsigned __int128)); + + BOOST_TEST(emulated_bits == builtin_value); + } +} + +template +void test_unary_minus() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + builtin_value = -builtin_value; + boost::decimal::detail::u128 emulated_value {value}; + emulated_value = -emulated_value; + + unsigned __int128 emulated_bits; + std::memcpy(&emulated_bits, &emulated_value, sizeof(unsigned __int128)); + + BOOST_TEST(emulated_bits == builtin_value); + } +} + int main() { test_arithmetic_constructor(); @@ -182,6 +223,9 @@ int main() test_float_conversion_operators<__float128>(); #endif + test_unary_plus(); + test_unary_minus(); + return boost::report_errors(); } From 62071051356fb3e6f9c9e7482b6ca5b86601c807 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 14:22:52 -0500 Subject: [PATCH 017/191] Add equality operators --- include/boost/decimal/detail/u128.hpp | 114 ++++++++++++++++++++++++++ test/test_u128.cpp | 43 ++++++++++ 2 files changed, 157 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 0b76ffef..ba64318a 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -141,6 +141,30 @@ u128 // Unary operators friend constexpr u128 operator+(u128 value) noexcept; friend constexpr u128 operator-(u128 value) noexcept; + + // Equality to signed integers + friend constexpr bool operator==(u128 lhs, bool rhs) noexcept; + friend constexpr bool operator==(u128 lhs, std::int8_t rhs) noexcept; + friend constexpr bool operator==(u128 lhs, std::int16_t rhs) noexcept; + friend constexpr bool operator==(u128 lhs, std::int32_t rhs) noexcept; + friend constexpr bool operator==(u128 lhs, std::int64_t rhs) noexcept; + + friend constexpr bool operator==(bool lhs, u128 rhs) noexcept; + friend constexpr bool operator==(std::int8_t lhs, u128 rhs) noexcept; + friend constexpr bool operator==(std::int16_t lhs, u128 rhs) noexcept; + friend constexpr bool operator==(std::int32_t lhs, u128 rhs) noexcept; + friend constexpr bool operator==(std::int64_t lhs, u128 rhs) noexcept; + + // Equality to unsigned integers + friend constexpr bool operator==(u128 lhs, std::uint8_t rhs) noexcept; + friend constexpr bool operator==(u128 lhs, std::uint16_t rhs) noexcept; + friend constexpr bool operator==(u128 lhs, std::uint32_t rhs) noexcept; + friend constexpr bool operator==(u128 lhs, std::uint64_t rhs) noexcept; + + friend constexpr bool operator==(std::uint8_t lhs, u128 rhs) noexcept; + friend constexpr bool operator==(std::uint16_t lhs, u128 rhs) noexcept; + friend constexpr bool operator==(std::uint32_t lhs, u128 rhs) noexcept; + friend constexpr bool operator==(std::uint64_t lhs, u128 rhs) noexcept; }; // Signed assignment operators @@ -229,6 +253,96 @@ constexpr u128 operator-(const u128 value) noexcept return u128{~value.high + static_cast(value.low == UINT64_C(0)), ~value.low + UINT64_C(1)}; } +constexpr bool operator==(const u128 lhs, const bool rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const u128 lhs, const std::int8_t rhs) noexcept +{ + return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const u128 lhs, const std::int16_t rhs) noexcept +{ + return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const u128 lhs, const std::int32_t rhs) noexcept +{ + return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const u128 lhs, const std::int64_t rhs) noexcept +{ + return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const bool lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const std::int8_t lhs, const u128 rhs) noexcept +{ + return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const std::int16_t lhs, const u128 rhs) noexcept +{ + return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const std::int32_t lhs, const u128 rhs) noexcept +{ + return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const std::int64_t lhs, const u128 rhs) noexcept +{ + return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const u128 lhs, const std::uint8_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const u128 lhs, const std::uint16_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const u128 lhs, const std::uint32_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const u128 lhs, const std::uint64_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); +} + +constexpr bool operator==(const std::uint8_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const std::uint16_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const std::uint32_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + +constexpr bool operator==(const std::uint64_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); +} + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index baf1870c..a8745999 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -177,6 +177,39 @@ void test_unary_minus() } } +template +void test_operator_equality() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + // Always equal + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST(((value == emulated_value) == (emulated_value == value)) == + ((value == builtin_value) == (builtin_value == value))); + } + + // Potentially equal + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST(((value2 == emulated_value) == (emulated_value == value2)) == + ((value2 == builtin_value) == (builtin_value == value2))); + } + + boost::decimal::detail::u128 bool_val {dist(rng)}; + BOOST_TEST((true == bool_val) == (bool_val == true)); +} + int main() { test_arithmetic_constructor(); @@ -226,6 +259,16 @@ int main() test_unary_plus(); test_unary_minus(); + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + return boost::report_errors(); } From a96199e540be140bdc842992af168e5c5a3464a2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 15:00:58 -0500 Subject: [PATCH 018/191] Add and test inequality operator --- include/boost/decimal/detail/u128.hpp | 115 ++++++++++++++++++++++++++ test/test_u128.cpp | 43 ++++++++++ 2 files changed, 158 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ba64318a..83692209 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -165,6 +165,31 @@ u128 friend constexpr bool operator==(std::uint16_t lhs, u128 rhs) noexcept; friend constexpr bool operator==(std::uint32_t lhs, u128 rhs) noexcept; friend constexpr bool operator==(std::uint64_t lhs, u128 rhs) noexcept; + + // Inequality to signed integers + friend constexpr bool operator!=(u128 lhs, bool rhs) noexcept; + friend constexpr bool operator!=(u128 lhs, std::int8_t rhs) noexcept; + friend constexpr bool operator!=(u128 lhs, std::int16_t rhs) noexcept; + friend constexpr bool operator!=(u128 lhs, std::int32_t rhs) noexcept; + friend constexpr bool operator!=(u128 lhs, std::int64_t rhs) noexcept; + + friend constexpr bool operator!=(bool lhs, u128 rhs) noexcept; + friend constexpr bool operator!=(std::int8_t lhs, u128 rhs) noexcept; + friend constexpr bool operator!=(std::int16_t lhs, u128 rhs) noexcept; + friend constexpr bool operator!=(std::int32_t lhs, u128 rhs) noexcept; + friend constexpr bool operator!=(std::int64_t lhs, u128 rhs) noexcept; + + // Inequality to unsigned integers + friend constexpr bool operator!=(u128 lhs, std::uint8_t rhs) noexcept; + friend constexpr bool operator!=(u128 lhs, std::uint16_t rhs) noexcept; + friend constexpr bool operator!=(u128 lhs, std::uint32_t rhs) noexcept; + friend constexpr bool operator!=(u128 lhs, std::uint64_t rhs) noexcept; + + friend constexpr bool operator!=(std::uint8_t lhs, u128 rhs) noexcept; + friend constexpr bool operator!=(std::uint16_t lhs, u128 rhs) noexcept; + friend constexpr bool operator!=(std::uint32_t lhs, u128 rhs) noexcept; + friend constexpr bool operator!=(std::uint64_t lhs, u128 rhs) noexcept; + }; // Signed assignment operators @@ -343,6 +368,96 @@ constexpr bool operator==(const std::uint64_t lhs, const u128 rhs) noexcept return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } +constexpr bool operator!=(const u128 lhs, const bool rhs) noexcept +{ + return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::int8_t rhs) noexcept +{ + return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::int16_t rhs) noexcept +{ + return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::int32_t rhs) noexcept +{ + return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::int64_t rhs) noexcept +{ + return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::uint8_t rhs) noexcept +{ + return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::uint16_t rhs) noexcept +{ + return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::uint32_t rhs) noexcept +{ + return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const u128 lhs, const std::uint64_t rhs) noexcept +{ + return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); +} + +constexpr bool operator!=(const bool lhs, const u128 rhs) noexcept +{ + return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::int8_t lhs, const u128 rhs) noexcept +{ + return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::int16_t lhs, const u128 rhs) noexcept +{ + return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::int32_t lhs, const u128 rhs) noexcept +{ + return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::int64_t lhs, const u128 rhs) noexcept +{ + return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::uint8_t lhs, const u128 rhs) noexcept +{ + return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::uint16_t lhs, const u128 rhs) noexcept +{ + return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::uint32_t lhs, const u128 rhs) noexcept +{ + return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + +constexpr bool operator!=(const std::uint64_t lhs, const u128 rhs) noexcept +{ + return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); +} + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index a8745999..6cfb4b34 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -210,6 +210,39 @@ void test_operator_equality() BOOST_TEST((true == bool_val) == (bool_val == true)); } +template +void test_operator_inequality() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + // Always equal + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST(((value != emulated_value) == (emulated_value != value)) == + ((value != builtin_value) == (builtin_value != value))); + } + + // Potentially equal + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST(((value2 != emulated_value) == (emulated_value != value2)) == + ((value2 != builtin_value) == (builtin_value != value2))); + } + + boost::decimal::detail::u128 bool_val {dist(rng)}; + BOOST_TEST((true != bool_val) == (bool_val != true)); +} + int main() { test_arithmetic_constructor(); @@ -269,6 +302,16 @@ int main() test_operator_equality(); test_operator_equality(); + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + return boost::report_errors(); } From 81a92336118ee2ef20cef4fc634d1ea609bdb901 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 15:22:53 -0500 Subject: [PATCH 019/191] Add 128 bit equality and inequality --- include/boost/decimal/detail/u128.hpp | 83 +++++++++++++++++++++++++++ test/test_u128.cpp | 4 ++ 2 files changed, 87 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 83692209..3f897941 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -166,6 +166,19 @@ u128 friend constexpr bool operator==(std::uint32_t lhs, u128 rhs) noexcept; friend constexpr bool operator==(std::uint64_t lhs, u128 rhs) noexcept; + // 128-bit equality + friend constexpr bool operator==(u128 lhs, u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + + friend constexpr bool operator==(u128 lhs, __int128 rhs) noexcept; + friend constexpr bool operator==(__int128 lhs, u128 rhs) noexcept; + + friend constexpr bool operator==(u128 lhs, unsigned __int128 rhs) noexcept; + friend constexpr bool operator==(unsigned __int128 lhs, u128 rhs) noexcept; + + #endif // BOOST_DECIMAL_HAS_INT128 + // Inequality to signed integers friend constexpr bool operator!=(u128 lhs, bool rhs) noexcept; friend constexpr bool operator!=(u128 lhs, std::int8_t rhs) noexcept; @@ -190,6 +203,18 @@ u128 friend constexpr bool operator!=(std::uint32_t lhs, u128 rhs) noexcept; friend constexpr bool operator!=(std::uint64_t lhs, u128 rhs) noexcept; + // 128-bit inequality + friend constexpr bool operator!=(u128 lhs, u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + + friend constexpr bool operator!=(u128 lhs, __int128 rhs) noexcept; + friend constexpr bool operator!=(__int128 lhs, u128 rhs) noexcept; + + friend constexpr bool operator!=(u128 lhs, unsigned __int128 rhs) noexcept; + friend constexpr bool operator!=(unsigned __int128 lhs, u128 rhs) noexcept; + + #endif // BOOST_DECIMAL_HAS_INT128 }; // Signed assignment operators @@ -368,6 +393,35 @@ constexpr bool operator==(const std::uint64_t lhs, const u128 rhs) noexcept return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } +constexpr bool operator==(const u128 lhs, const u128 rhs) noexcept +{ + return lhs.low == rhs.low && lhs.high == rhs.high; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr bool operator==(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs == static_cast(rhs); +} + +constexpr bool operator==(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) == rhs; +} + +constexpr bool operator==(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs == static_cast(rhs); +} + +constexpr bool operator==(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) == rhs; +} + +#endif + constexpr bool operator!=(const u128 lhs, const bool rhs) noexcept { return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); @@ -458,6 +512,35 @@ constexpr bool operator!=(const std::uint64_t lhs, const u128 rhs) noexcept return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } +constexpr bool operator!=(const u128 lhs, const u128 rhs) noexcept +{ + return lhs.low != rhs.low || lhs.high != rhs.high; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr bool operator!=(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs != static_cast(rhs); +} + +constexpr bool operator!=(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) != rhs; +} + +constexpr bool operator!=(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs != static_cast(rhs); +} + +constexpr bool operator!=(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) != rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 6cfb4b34..2b5cec1c 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -296,21 +296,25 @@ int main() test_operator_equality(); test_operator_equality(); test_operator_equality(); + test_operator_equality<__int128>(); test_operator_equality(); test_operator_equality(); test_operator_equality(); test_operator_equality(); + test_operator_equality(); test_operator_inequality(); test_operator_inequality(); test_operator_inequality(); test_operator_inequality(); + test_operator_inequality<__int128>(); test_operator_inequality(); test_operator_inequality(); test_operator_inequality(); test_operator_inequality(); + test_operator_inequality(); return boost::report_errors(); } From fd4f7d0cdafa905cda720ffb8e85a8b5d636bc22 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 16:29:06 -0500 Subject: [PATCH 020/191] Add operator< --- include/boost/decimal/detail/u128.hpp | 138 ++++++++++++++++++++++++++ test/test_u128.cpp | 30 ++++++ 2 files changed, 168 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 3f897941..68352441 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -215,6 +215,39 @@ u128 friend constexpr bool operator!=(unsigned __int128 lhs, u128 rhs) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 + + // Less than for signed integers + friend constexpr bool operator<(u128 lhs, std::int8_t rhs) noexcept; + friend constexpr bool operator<(u128 lhs, std::int16_t rhs) noexcept; + friend constexpr bool operator<(u128 lhs, std::int32_t rhs) noexcept; + friend constexpr bool operator<(u128 lhs, std::int64_t rhs) noexcept; + + friend constexpr bool operator<(std::int8_t lhs, u128 rhs) noexcept; + friend constexpr bool operator<(std::int16_t lhs, u128 rhs) noexcept; + friend constexpr bool operator<(std::int32_t lhs, u128 rhs) noexcept; + friend constexpr bool operator<(std::int64_t lhs, u128 rhs) noexcept; + + // Less than for unsigned integers + friend constexpr bool operator<(u128 lhs, std::uint8_t rhs) noexcept; + friend constexpr bool operator<(u128 lhs, std::uint16_t rhs) noexcept; + friend constexpr bool operator<(u128 lhs, std::uint32_t rhs) noexcept; + friend constexpr bool operator<(u128 lhs, std::uint64_t rhs) noexcept; + + friend constexpr bool operator<(std::uint8_t lhs, u128 rhs) noexcept; + friend constexpr bool operator<(std::uint16_t lhs, u128 rhs) noexcept; + friend constexpr bool operator<(std::uint32_t lhs, u128 rhs) noexcept; + friend constexpr bool operator<(std::uint64_t lhs, u128 rhs) noexcept; + + // Less than for 128-bit types + friend constexpr bool operator<(u128 lhs, u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + friend constexpr bool operator<(u128 lhs, __int128 rhs) noexcept; + friend constexpr bool operator<(__int128 lhs, u128 rhs) noexcept; + + friend constexpr bool operator<(u128 lhs, unsigned __int128 rhs) noexcept; + friend constexpr bool operator<(unsigned __int128 lhs, u128 rhs) noexcept; + #endif // BOOST_DECIMAL_HAS_INT128 }; // Signed assignment operators @@ -541,6 +574,111 @@ constexpr bool operator!=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +constexpr bool operator<(const u128 lhs, const std::int8_t rhs) noexcept +{ + return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const u128 lhs, const std::int16_t rhs) noexcept +{ + return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const u128 lhs, const std::int32_t rhs) noexcept +{ + return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const u128 lhs, const std::int64_t rhs) noexcept +{ + return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const u128 lhs, const std::uint8_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const u128 lhs, const std::uint16_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const u128 lhs, const std::uint32_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const u128 lhs, const std::uint64_t rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const std::int8_t lhs, const u128 rhs) noexcept +{ + return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const std::int16_t lhs, const u128 rhs) noexcept +{ + return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const std::int32_t lhs, const u128 rhs) noexcept +{ + return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const std::int64_t lhs, const u128 rhs) noexcept +{ + return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const std::uint8_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const std::uint16_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const std::uint32_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const std::uint64_t lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; +} + +constexpr bool operator<(const u128 lhs, const u128 rhs) noexcept +{ + return lhs.high == rhs.high ? lhs.low < rhs.low : lhs.high < rhs.high; +} + +constexpr bool operator<(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs < static_cast(rhs); +} + +constexpr bool operator<(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) < rhs; +} + +constexpr bool operator<(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs < static_cast(rhs); +} + +constexpr bool operator<(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) < rhs; +} + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 2b5cec1c..79ac0e78 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -243,6 +243,24 @@ void test_operator_inequality() BOOST_TEST((true != bool_val) == (bool_val != true)); } +template +void test_operator_less() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST(((value2 < emulated_value) == (emulated_value < value2)) == + ((value2 < builtin_value) == (builtin_value < value2))); + } +} + int main() { test_arithmetic_constructor(); @@ -316,6 +334,18 @@ int main() test_operator_inequality(); test_operator_inequality(); + test_operator_less(); + test_operator_less(); + test_operator_less(); + test_operator_less(); + test_operator_less<__int128>(); + + test_operator_less(); + test_operator_less(); + test_operator_less(); + test_operator_less(); + test_operator_less(); + return boost::report_errors(); } From 8206317ef84947459392939f45cc7ff2ee2252ef Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 16:31:12 -0500 Subject: [PATCH 021/191] Remove unary operators from being friends --- include/boost/decimal/detail/u128.hpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 68352441..fc20f7b1 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -138,10 +138,6 @@ u128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 - // Unary operators - friend constexpr u128 operator+(u128 value) noexcept; - friend constexpr u128 operator-(u128 value) noexcept; - // Equality to signed integers friend constexpr bool operator==(u128 lhs, bool rhs) noexcept; friend constexpr bool operator==(u128 lhs, std::int8_t rhs) noexcept; From 8010ec2f8364a4629cc6f3d4075325968f5a5151 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 16:38:36 -0500 Subject: [PATCH 022/191] Make equality a non-friend template --- include/boost/decimal/detail/u128.hpp | 111 +++----------------------- test/test_u128.cpp | 7 +- 2 files changed, 10 insertions(+), 108 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index fc20f7b1..81cee229 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -138,43 +138,6 @@ u128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 - // Equality to signed integers - friend constexpr bool operator==(u128 lhs, bool rhs) noexcept; - friend constexpr bool operator==(u128 lhs, std::int8_t rhs) noexcept; - friend constexpr bool operator==(u128 lhs, std::int16_t rhs) noexcept; - friend constexpr bool operator==(u128 lhs, std::int32_t rhs) noexcept; - friend constexpr bool operator==(u128 lhs, std::int64_t rhs) noexcept; - - friend constexpr bool operator==(bool lhs, u128 rhs) noexcept; - friend constexpr bool operator==(std::int8_t lhs, u128 rhs) noexcept; - friend constexpr bool operator==(std::int16_t lhs, u128 rhs) noexcept; - friend constexpr bool operator==(std::int32_t lhs, u128 rhs) noexcept; - friend constexpr bool operator==(std::int64_t lhs, u128 rhs) noexcept; - - // Equality to unsigned integers - friend constexpr bool operator==(u128 lhs, std::uint8_t rhs) noexcept; - friend constexpr bool operator==(u128 lhs, std::uint16_t rhs) noexcept; - friend constexpr bool operator==(u128 lhs, std::uint32_t rhs) noexcept; - friend constexpr bool operator==(u128 lhs, std::uint64_t rhs) noexcept; - - friend constexpr bool operator==(std::uint8_t lhs, u128 rhs) noexcept; - friend constexpr bool operator==(std::uint16_t lhs, u128 rhs) noexcept; - friend constexpr bool operator==(std::uint32_t lhs, u128 rhs) noexcept; - friend constexpr bool operator==(std::uint64_t lhs, u128 rhs) noexcept; - - // 128-bit equality - friend constexpr bool operator==(u128 lhs, u128 rhs) noexcept; - - #ifdef BOOST_DECIMAL_HAS_INT128 - - friend constexpr bool operator==(u128 lhs, __int128 rhs) noexcept; - friend constexpr bool operator==(__int128 lhs, u128 rhs) noexcept; - - friend constexpr bool operator==(u128 lhs, unsigned __int128 rhs) noexcept; - friend constexpr bool operator==(unsigned __int128 lhs, u128 rhs) noexcept; - - #endif // BOOST_DECIMAL_HAS_INT128 - // Inequality to signed integers friend constexpr bool operator!=(u128 lhs, bool rhs) noexcept; friend constexpr bool operator!=(u128 lhs, std::int8_t rhs) noexcept; @@ -337,87 +300,31 @@ constexpr bool operator==(const u128 lhs, const bool rhs) noexcept return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); } -constexpr bool operator==(const u128 lhs, const std::int8_t rhs) noexcept -{ - return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); -} - -constexpr bool operator==(const u128 lhs, const std::int16_t rhs) noexcept -{ - return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); -} - -constexpr bool operator==(const u128 lhs, const std::int32_t rhs) noexcept -{ - return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); -} - -constexpr bool operator==(const u128 lhs, const std::int64_t rhs) noexcept -{ - return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); -} - constexpr bool operator==(const bool lhs, const u128 rhs) noexcept { return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } -constexpr bool operator==(const std::int8_t lhs, const u128 rhs) noexcept +template ::value, bool> = true> +constexpr bool operator==(const u128 lhs, const SignedInteger rhs) noexcept { - return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); -} - -constexpr bool operator==(const std::int16_t lhs, const u128 rhs) noexcept -{ - return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); -} - -constexpr bool operator==(const std::int32_t lhs, const u128 rhs) noexcept -{ - return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); + return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); } -constexpr bool operator==(const std::int64_t lhs, const u128 rhs) noexcept +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator==(const SignedInteger lhs, const u128 rhs) noexcept { return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } -constexpr bool operator==(const u128 lhs, const std::uint8_t rhs) noexcept -{ - return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); -} - -constexpr bool operator==(const u128 lhs, const std::uint16_t rhs) noexcept -{ - return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); -} - -constexpr bool operator==(const u128 lhs, const std::uint32_t rhs) noexcept -{ - return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); -} - -constexpr bool operator==(const u128 lhs, const std::uint64_t rhs) noexcept +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator==(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); } -constexpr bool operator==(const std::uint8_t lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); -} - -constexpr bool operator==(const std::uint16_t lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); -} - -constexpr bool operator==(const std::uint32_t lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); -} - -constexpr bool operator==(const std::uint64_t lhs, const u128 rhs) noexcept +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator==(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 79ac0e78..f665792e 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -32,17 +32,12 @@ # pragma GCC diagnostic ignored "-Wundef" # pragma GCC diagnostic ignored "-Wconversion" # pragma GCC diagnostic ignored "-Wsign-conversion" +# pragma GCC diagnostic ignored "-Wsign-compare" # pragma GCC diagnostic ignored "-Wfloat-equal" #endif #include -#if defined(__clang__) -# pragma clang diagnostic pop -#elif defined(__GNUC__) -# pragma GCC diagnostic pop -#endif - // Used defined seed for repeatability static std::mt19937_64 rng(42); From bc5bdf18c0aa4d52707b8557e0b1a038635de492 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 16:41:51 -0500 Subject: [PATCH 023/191] Simplify operator!= to a non-friend template --- include/boost/decimal/detail/u128.hpp | 113 +++----------------------- 1 file changed, 10 insertions(+), 103 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 81cee229..f57526c0 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -138,43 +138,6 @@ u128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 - // Inequality to signed integers - friend constexpr bool operator!=(u128 lhs, bool rhs) noexcept; - friend constexpr bool operator!=(u128 lhs, std::int8_t rhs) noexcept; - friend constexpr bool operator!=(u128 lhs, std::int16_t rhs) noexcept; - friend constexpr bool operator!=(u128 lhs, std::int32_t rhs) noexcept; - friend constexpr bool operator!=(u128 lhs, std::int64_t rhs) noexcept; - - friend constexpr bool operator!=(bool lhs, u128 rhs) noexcept; - friend constexpr bool operator!=(std::int8_t lhs, u128 rhs) noexcept; - friend constexpr bool operator!=(std::int16_t lhs, u128 rhs) noexcept; - friend constexpr bool operator!=(std::int32_t lhs, u128 rhs) noexcept; - friend constexpr bool operator!=(std::int64_t lhs, u128 rhs) noexcept; - - // Inequality to unsigned integers - friend constexpr bool operator!=(u128 lhs, std::uint8_t rhs) noexcept; - friend constexpr bool operator!=(u128 lhs, std::uint16_t rhs) noexcept; - friend constexpr bool operator!=(u128 lhs, std::uint32_t rhs) noexcept; - friend constexpr bool operator!=(u128 lhs, std::uint64_t rhs) noexcept; - - friend constexpr bool operator!=(std::uint8_t lhs, u128 rhs) noexcept; - friend constexpr bool operator!=(std::uint16_t lhs, u128 rhs) noexcept; - friend constexpr bool operator!=(std::uint32_t lhs, u128 rhs) noexcept; - friend constexpr bool operator!=(std::uint64_t lhs, u128 rhs) noexcept; - - // 128-bit inequality - friend constexpr bool operator!=(u128 lhs, u128 rhs) noexcept; - - #ifdef BOOST_DECIMAL_HAS_INT128 - - friend constexpr bool operator!=(u128 lhs, __int128 rhs) noexcept; - friend constexpr bool operator!=(__int128 lhs, u128 rhs) noexcept; - - friend constexpr bool operator!=(u128 lhs, unsigned __int128 rhs) noexcept; - friend constexpr bool operator!=(unsigned __int128 lhs, u128 rhs) noexcept; - - #endif // BOOST_DECIMAL_HAS_INT128 - // Less than for signed integers friend constexpr bool operator<(u128 lhs, std::int8_t rhs) noexcept; friend constexpr bool operator<(u128 lhs, std::int16_t rhs) noexcept; @@ -363,87 +326,31 @@ constexpr bool operator!=(const u128 lhs, const bool rhs) noexcept return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); } -constexpr bool operator!=(const u128 lhs, const std::int8_t rhs) noexcept -{ - return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - -constexpr bool operator!=(const u128 lhs, const std::int16_t rhs) noexcept -{ - return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - -constexpr bool operator!=(const u128 lhs, const std::int32_t rhs) noexcept -{ - return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - -constexpr bool operator!=(const u128 lhs, const std::int64_t rhs) noexcept -{ - return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - -constexpr bool operator!=(const u128 lhs, const std::uint8_t rhs) noexcept -{ - return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - -constexpr bool operator!=(const u128 lhs, const std::uint16_t rhs) noexcept -{ - return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - -constexpr bool operator!=(const u128 lhs, const std::uint32_t rhs) noexcept -{ - return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - -constexpr bool operator!=(const u128 lhs, const std::uint64_t rhs) noexcept -{ - return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); -} - constexpr bool operator!=(const bool lhs, const u128 rhs) noexcept { return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } -constexpr bool operator!=(const std::int8_t lhs, const u128 rhs) noexcept -{ - return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); -} - -constexpr bool operator!=(const std::int16_t lhs, const u128 rhs) noexcept -{ - return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); -} - -constexpr bool operator!=(const std::int32_t lhs, const u128 rhs) noexcept +template ::value, bool> = true> +constexpr bool operator!=(const u128 lhs, const SignedInteger rhs) noexcept { - return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); + return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); } -constexpr bool operator!=(const std::int64_t lhs, const u128 rhs) noexcept +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator!=(const SignedInteger lhs, const u128 rhs) noexcept { return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } -constexpr bool operator!=(const std::uint8_t lhs, const u128 rhs) noexcept -{ - return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); -} - -constexpr bool operator!=(const std::uint16_t lhs, const u128 rhs) noexcept -{ - return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); -} - -constexpr bool operator!=(const std::uint32_t lhs, const u128 rhs) noexcept +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator!=(const u128 lhs, const UnsignedInteger rhs) noexcept { - return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); + return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); } -constexpr bool operator!=(const std::uint64_t lhs, const u128 rhs) noexcept +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator!=(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } From 9889e11e466c8b5cba90cd1abcae13df0508d02b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 16:46:35 -0500 Subject: [PATCH 024/191] Simplify operator< to a non-friend template --- include/boost/decimal/detail/u128.hpp | 113 ++++---------------------- 1 file changed, 14 insertions(+), 99 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index f57526c0..2ae62ab6 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -137,39 +137,6 @@ u128 #ifdef BOOST_DECIMAL_HAS_FLOAT128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 - - // Less than for signed integers - friend constexpr bool operator<(u128 lhs, std::int8_t rhs) noexcept; - friend constexpr bool operator<(u128 lhs, std::int16_t rhs) noexcept; - friend constexpr bool operator<(u128 lhs, std::int32_t rhs) noexcept; - friend constexpr bool operator<(u128 lhs, std::int64_t rhs) noexcept; - - friend constexpr bool operator<(std::int8_t lhs, u128 rhs) noexcept; - friend constexpr bool operator<(std::int16_t lhs, u128 rhs) noexcept; - friend constexpr bool operator<(std::int32_t lhs, u128 rhs) noexcept; - friend constexpr bool operator<(std::int64_t lhs, u128 rhs) noexcept; - - // Less than for unsigned integers - friend constexpr bool operator<(u128 lhs, std::uint8_t rhs) noexcept; - friend constexpr bool operator<(u128 lhs, std::uint16_t rhs) noexcept; - friend constexpr bool operator<(u128 lhs, std::uint32_t rhs) noexcept; - friend constexpr bool operator<(u128 lhs, std::uint64_t rhs) noexcept; - - friend constexpr bool operator<(std::uint8_t lhs, u128 rhs) noexcept; - friend constexpr bool operator<(std::uint16_t lhs, u128 rhs) noexcept; - friend constexpr bool operator<(std::uint32_t lhs, u128 rhs) noexcept; - friend constexpr bool operator<(std::uint64_t lhs, u128 rhs) noexcept; - - // Less than for 128-bit types - friend constexpr bool operator<(u128 lhs, u128 rhs) noexcept; - - #ifdef BOOST_DECIMAL_HAS_INT128 - friend constexpr bool operator<(u128 lhs, __int128 rhs) noexcept; - friend constexpr bool operator<(__int128 lhs, u128 rhs) noexcept; - - friend constexpr bool operator<(u128 lhs, unsigned __int128 rhs) noexcept; - friend constexpr bool operator<(unsigned __int128 lhs, u128 rhs) noexcept; - #endif // BOOST_DECIMAL_HAS_INT128 }; // Signed assignment operators @@ -384,84 +351,28 @@ constexpr bool operator!=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 -constexpr bool operator<(const u128 lhs, const std::int8_t rhs) noexcept -{ - return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); -} - -constexpr bool operator<(const u128 lhs, const std::int16_t rhs) noexcept -{ - return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); -} - -constexpr bool operator<(const u128 lhs, const std::int32_t rhs) noexcept -{ - return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); -} - -constexpr bool operator<(const u128 lhs, const std::int64_t rhs) noexcept +template ::value, bool> = true> +constexpr bool operator<(const u128 lhs, const SignedInteger rhs) noexcept { return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); } -constexpr bool operator<(const u128 lhs, const std::uint8_t rhs) noexcept -{ - return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); -} - -constexpr bool operator<(const u128 lhs, const std::uint16_t rhs) noexcept -{ - return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); -} - -constexpr bool operator<(const u128 lhs, const std::uint32_t rhs) noexcept +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator<(const SignedInteger lhs, const u128 rhs) noexcept { - return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); + return lhs < 0 || rhs.high > UINT64_C(0) || static_cast(lhs) < rhs.low; } -constexpr bool operator<(const u128 lhs, const std::uint64_t rhs) noexcept +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator<(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); } -constexpr bool operator<(const std::int8_t lhs, const u128 rhs) noexcept -{ - return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; -} - -constexpr bool operator<(const std::int16_t lhs, const u128 rhs) noexcept -{ - return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; -} - -constexpr bool operator<(const std::int32_t lhs, const u128 rhs) noexcept -{ - return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; -} - -constexpr bool operator<(const std::int64_t lhs, const u128 rhs) noexcept -{ - return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; -} - -constexpr bool operator<(const std::uint8_t lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; -} - -constexpr bool operator<(const std::uint16_t lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; -} - -constexpr bool operator<(const std::uint32_t lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; -} - -constexpr bool operator<(const std::uint64_t lhs, const u128 rhs) noexcept +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator<(const UnsignedInteger lhs, const u128 rhs) noexcept { - return rhs.high == UINT64_C(0) && static_cast(lhs) < rhs.low; + return rhs.high > UINT64_C(0) || static_cast(lhs) < rhs.low; } constexpr bool operator<(const u128 lhs, const u128 rhs) noexcept @@ -469,6 +380,8 @@ constexpr bool operator<(const u128 lhs, const u128 rhs) noexcept return lhs.high == rhs.high ? lhs.low < rhs.low : lhs.high < rhs.high; } +#ifdef BOOST_DECIMAL_HAS_INT128 + constexpr bool operator<(const u128 lhs, const __int128 rhs) noexcept { return lhs < static_cast(rhs); @@ -489,6 +402,8 @@ constexpr bool operator<(const unsigned __int128 lhs, const u128 rhs) noexcept return static_cast(lhs) < rhs; } +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost From 19befdefc2a49247047f1c9f1d2bf1448aae3704 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 28 Feb 2025 17:45:34 -0500 Subject: [PATCH 025/191] Add hardware bug workaround --- test/test_u128.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index f665792e..ba89bf68 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -251,6 +251,24 @@ void test_operator_less() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; + // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) + #endif + + BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) + { + if (value == value2 && value < 0) + { + continue; + } + } + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + BOOST_TEST(((value2 < emulated_value) == (emulated_value < value2)) == ((value2 < builtin_value) == (builtin_value < value2))); } From e2eb2f32d449812715e1b4dc93eefcc522db8f70 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 10:16:34 -0500 Subject: [PATCH 026/191] Fix SFINAE --- include/boost/decimal/detail/u128.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 2ae62ab6..aafb6f53 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -235,7 +235,7 @@ constexpr bool operator==(const bool lhs, const u128 rhs) noexcept return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } -template ::value, bool> = true> +template ::value && std::is_signed::value, bool> = true> constexpr bool operator==(const u128 lhs, const SignedInteger rhs) noexcept { return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); @@ -298,7 +298,7 @@ constexpr bool operator!=(const bool lhs, const u128 rhs) noexcept return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } -template ::value, bool> = true> +template ::value && std::is_signed::value, bool> = true> constexpr bool operator!=(const u128 lhs, const SignedInteger rhs) noexcept { return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); From 5d0531c46909320d55bdfe89e3a1907ba4b88289 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 10:16:54 -0500 Subject: [PATCH 027/191] Add operator< for bool test --- include/boost/decimal/detail/u128.hpp | 12 +++++++++++- test/test_u128.cpp | 4 ++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index aafb6f53..98b43c2b 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -351,7 +351,17 @@ constexpr bool operator!=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 -template ::value, bool> = true> +constexpr bool operator<(const u128 lhs, const bool rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); +} + +constexpr bool operator<(const bool lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && rhs.low < static_cast(lhs); +} + +template ::value && std::is_signed::value, bool> = true> constexpr bool operator<(const u128 lhs, const SignedInteger rhs) noexcept { return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); diff --git a/test/test_u128.cpp b/test/test_u128.cpp index ba89bf68..df055659 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -272,8 +272,12 @@ void test_operator_less() BOOST_TEST(((value2 < emulated_value) == (emulated_value < value2)) == ((value2 < builtin_value) == (builtin_value < value2))); } + + boost::decimal::detail::u128 bool_val {dist(rng)}; + BOOST_TEST((true < bool_val) == (bool_val < true)); } + int main() { test_arithmetic_constructor(); From e238bb6d1dd6db1cdccab0c2890201f0fbbf9b3f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 10:17:02 -0500 Subject: [PATCH 028/191] Add operator LE --- include/boost/decimal/detail/u128.hpp | 63 +++++++++++++++++++++++++++ test/test_u128.cpp | 50 +++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 98b43c2b..c6dbd3e3 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -414,6 +414,69 @@ constexpr bool operator<(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +constexpr bool operator<=(const u128 lhs, const bool rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); +} + +constexpr bool operator<=(const bool lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && rhs.low <= static_cast(lhs); +} + +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator<=(const u128 lhs, const SignedInteger rhs) noexcept +{ + return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); +} + +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator<=(const SignedInteger lhs, const u128 rhs) noexcept +{ + return lhs < 0 || rhs.high > UINT64_C(0) || static_cast(lhs) <= rhs.low; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator<=(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator<=(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + return rhs.high > UINT64_C(0) || static_cast(lhs) <= rhs.low; +} + +constexpr bool operator<=(const u128 lhs, const u128 rhs) noexcept +{ + return lhs.high == rhs.high ? lhs.low < rhs.low : lhs.high < rhs.high; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr bool operator<=(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs <= static_cast(rhs); +} + +constexpr bool operator<=(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) < rhs; +} + +constexpr bool operator<=(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs < static_cast(rhs); +} + +constexpr bool operator<=(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) < rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index df055659..3d9dd636 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -277,6 +277,44 @@ void test_operator_less() BOOST_TEST((true < bool_val) == (bool_val < true)); } +template +void test_operator_le() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) + #endif + + BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) + { + if (value == value2 && value < 0) + { + continue; + } + } + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + BOOST_TEST(((value2 <= emulated_value) == (emulated_value <= value2)) == + ((value2 <= builtin_value) == (builtin_value <= value2))); + } + + boost::decimal::detail::u128 bool_val {dist(rng)}; + BOOST_TEST((true <= bool_val) == (bool_val <= true)); +} int main() { @@ -363,6 +401,18 @@ int main() test_operator_less(); test_operator_less(); + test_operator_le(); + test_operator_le(); + test_operator_le(); + test_operator_le(); + test_operator_le<__int128>(); + + test_operator_le(); + test_operator_le(); + test_operator_le(); + test_operator_le(); + test_operator_le(); + return boost::report_errors(); } From eac2575293ef3b415df9d9c9261f0710ad258cdf Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 11:01:05 -0500 Subject: [PATCH 029/191] Add operator> --- include/boost/decimal/detail/u128.hpp | 44 ++++++++++++----------- test/test_u128.cpp | 52 ++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index c6dbd3e3..8b805dc6 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -351,16 +351,6 @@ constexpr bool operator!=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 -constexpr bool operator<(const u128 lhs, const bool rhs) noexcept -{ - return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); -} - -constexpr bool operator<(const bool lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && rhs.low < static_cast(lhs); -} - template ::value && std::is_signed::value, bool> = true> constexpr bool operator<(const u128 lhs, const SignedInteger rhs) noexcept { @@ -414,16 +404,6 @@ constexpr bool operator<(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 -constexpr bool operator<=(const u128 lhs, const bool rhs) noexcept -{ - return lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); -} - -constexpr bool operator<=(const bool lhs, const u128 rhs) noexcept -{ - return rhs.high == UINT64_C(0) && rhs.low <= static_cast(lhs); -} - template ::value && std::is_signed::value, bool> = true> constexpr bool operator<=(const u128 lhs, const SignedInteger rhs) noexcept { @@ -477,6 +457,30 @@ constexpr bool operator<=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator>(const u128 lhs, const SignedInteger rhs) noexcept +{ + return rhs < 0 || lhs.high > UINT64_C(0) || lhs.low > static_cast(rhs); +} + +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator>(const SignedInteger lhs, const u128 rhs) noexcept +{ + return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) > rhs.low; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator>(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return lhs.high > UINT64_C(0) || lhs.low > static_cast(rhs); +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator>(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && static_cast(lhs) > rhs.low; +} + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 3d9dd636..70c2f1bb 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -272,9 +272,6 @@ void test_operator_less() BOOST_TEST(((value2 < emulated_value) == (emulated_value < value2)) == ((value2 < builtin_value) == (builtin_value < value2))); } - - boost::decimal::detail::u128 bool_val {dist(rng)}; - BOOST_TEST((true < bool_val) == (bool_val < true)); } template @@ -311,9 +308,42 @@ void test_operator_le() BOOST_TEST(((value2 <= emulated_value) == (emulated_value <= value2)) == ((value2 <= builtin_value) == (builtin_value <= value2))); } +} - boost::decimal::detail::u128 bool_val {dist(rng)}; - BOOST_TEST((true <= bool_val) == (bool_val <= true)); +template +void test_operator_greater() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) + #endif + + BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) + { + if (value == value2 && value < 0) + { + continue; + } + } + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + BOOST_TEST(((value2 > emulated_value) == (emulated_value > value2)) == + ((value2 > builtin_value) == (builtin_value > value2))); + } } int main() @@ -413,6 +443,18 @@ int main() test_operator_le(); test_operator_le(); + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + test_operator_greater<__int128>(); + + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + return boost::report_errors(); } From 87958d44542bfb1a0c541f72d5587f7ca3ba80f0 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 11:29:29 -0500 Subject: [PATCH 030/191] Improve testing and 128-bit operators --- include/boost/decimal/detail/u128.hpp | 35 ++++++++++++++++++++++++--- test/test_u128.cpp | 12 ++++----- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8b805dc6..dd810514 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -442,17 +442,17 @@ constexpr bool operator<=(const u128 lhs, const __int128 rhs) noexcept constexpr bool operator<=(const __int128 lhs, const u128 rhs) noexcept { - return static_cast(lhs) < rhs; + return static_cast(lhs) <= rhs; } constexpr bool operator<=(const u128 lhs, const unsigned __int128 rhs) noexcept { - return lhs < static_cast(rhs); + return lhs <= static_cast(rhs); } constexpr bool operator<=(const unsigned __int128 lhs, const u128 rhs) noexcept { - return static_cast(lhs) < rhs; + return static_cast(lhs) <= rhs; } #endif // BOOST_DECIMAL_HAS_INT128 @@ -481,6 +481,35 @@ constexpr bool operator>(const UnsignedInteger lhs, const u128 rhs) noexcept return rhs.high == UINT64_C(0) && static_cast(lhs) > rhs.low; } +constexpr bool operator>(const u128 lhs, const u128 rhs) noexcept +{ + return lhs.high > rhs.high || (lhs.high == rhs.high && lhs.low > rhs.low); +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr bool operator>(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs > static_cast(rhs); +} + +constexpr bool operator>(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) > rhs; +} + +constexpr bool operator>(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs > static_cast(rhs); +} + +constexpr bool operator>(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) > rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 70c2f1bb..1ea59455 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -269,8 +269,8 @@ void test_operator_less() #pragma warning(pop) #endif - BOOST_TEST(((value2 < emulated_value) == (emulated_value < value2)) == - ((value2 < builtin_value) == (builtin_value < value2))); + BOOST_TEST(((value2 < emulated_value) == (value2 < builtin_value)) == + ((emulated_value < value2) == (builtin_value < value2))); } } @@ -305,8 +305,8 @@ void test_operator_le() #pragma warning(pop) #endif - BOOST_TEST(((value2 <= emulated_value) == (emulated_value <= value2)) == - ((value2 <= builtin_value) == (builtin_value <= value2))); + BOOST_TEST(((value2 <= emulated_value) == (value2 <= builtin_value)) == + ((emulated_value <= value2) == (builtin_value <= value2))); } } @@ -341,8 +341,8 @@ void test_operator_greater() #pragma warning(pop) #endif - BOOST_TEST(((value2 > emulated_value) == (emulated_value > value2)) == - ((value2 > builtin_value) == (builtin_value > value2))); + BOOST_TEST(((value2 > emulated_value) == (value2 > builtin_value)) == + ((emulated_value > value2) == (builtin_value > value2))); } } From 397396ead3b5376d5cd8cb650b1104bdf0b5ed14 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 11:33:21 -0500 Subject: [PATCH 031/191] Add and test operator ge --- include/boost/decimal/detail/u128.hpp | 53 +++++++++++++++++++++++++++ test/test_u128.cpp | 48 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index dd810514..74a9529f 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -510,6 +510,59 @@ constexpr bool operator>(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator>=(const u128 lhs, const SignedInteger rhs) noexcept +{ + return rhs < 0 || lhs.high > UINT64_C(0) || lhs.low >= static_cast(rhs); +} + +template ::value && std::is_signed::value, bool> = true> +constexpr bool operator>=(const SignedInteger lhs, const u128 rhs) noexcept +{ + return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) >= rhs.low; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator>=(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return lhs.high > UINT64_C(0) || lhs.low >= static_cast(rhs); +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr bool operator>=(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + return rhs.high == UINT64_C(0) && static_cast(lhs) >= rhs.low; +} + +constexpr bool operator>=(const u128 lhs, const u128 rhs) noexcept +{ + return lhs.high > rhs.high || (lhs.high == rhs.high && lhs.low >= rhs.low); +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr bool operator>=(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs >= static_cast(rhs); +} + +constexpr bool operator>=(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) >= rhs; +} + +constexpr bool operator>=(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs >= static_cast(rhs); +} + +constexpr bool operator>=(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) >= rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 1ea59455..4024fecf 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -346,6 +346,42 @@ void test_operator_greater() } } +template +void test_operator_ge() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) + #endif + + BOOST_DECIMAL_IF_CONSTEXPR (std::is_signed::value) + { + if (value == value2 && value < 0) + { + continue; + } + } + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + BOOST_TEST(((value2 >= emulated_value) == (value2 >= builtin_value)) == + ((emulated_value >= value2) == (builtin_value >= value2))); + } +} + int main() { test_arithmetic_constructor(); @@ -455,6 +491,18 @@ int main() test_operator_greater(); test_operator_greater(); + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + test_operator_ge<__int128>(); + + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + return boost::report_errors(); } From 28d71c7cdf3a964b11fe01413104fd61c08b1318 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 11:52:35 -0500 Subject: [PATCH 032/191] Operator not --- include/boost/decimal/detail/u128.hpp | 37 +++++++++++++++++++++++++++ test/test_u128.cpp | 28 ++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 74a9529f..60a57024 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -215,6 +215,10 @@ constexpr u128::operator __float128() const noexcept #endif // BOOST_DECIMAL_HAS_FLOAT128 +//===================================== +// Unary Operators +//===================================== + constexpr u128 operator+(const u128 value) noexcept { return value; @@ -225,6 +229,10 @@ constexpr u128 operator-(const u128 value) noexcept return u128{~value.high + static_cast(value.low == UINT64_C(0)), ~value.low + UINT64_C(1)}; } +//===================================== +// Equality Operators +//===================================== + constexpr bool operator==(const u128 lhs, const bool rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); @@ -288,6 +296,10 @@ constexpr bool operator==(const unsigned __int128 lhs, const u128 rhs) noexcept #endif +//===================================== +// Inequality Operators +//===================================== + constexpr bool operator!=(const u128 lhs, const bool rhs) noexcept { return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); @@ -351,6 +363,10 @@ constexpr bool operator!=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Less than Operators +//===================================== + template ::value && std::is_signed::value, bool> = true> constexpr bool operator<(const u128 lhs, const SignedInteger rhs) noexcept { @@ -404,6 +420,10 @@ constexpr bool operator<(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Less-equal Operators +//===================================== + template ::value && std::is_signed::value, bool> = true> constexpr bool operator<=(const u128 lhs, const SignedInteger rhs) noexcept { @@ -457,6 +477,10 @@ constexpr bool operator<=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Greater Than Operators +//===================================== + template ::value && std::is_signed::value, bool> = true> constexpr bool operator>(const u128 lhs, const SignedInteger rhs) noexcept { @@ -510,6 +534,10 @@ constexpr bool operator>(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Greater-equal Operators +//===================================== + template ::value && std::is_signed::value, bool> = true> constexpr bool operator>=(const u128 lhs, const SignedInteger rhs) noexcept { @@ -563,6 +591,15 @@ constexpr bool operator>=(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Not Operator +//===================================== + +constexpr u128 operator~(const u128 rhs) noexcept +{ + return {~rhs.high, ~rhs.low}; +} + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 4024fecf..d0aba5d3 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -382,6 +382,32 @@ void test_operator_ge() } } +template +void test_operator_not() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) + #endif + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + BOOST_TEST(~emulated_value == ~builtin_value); + } +} + int main() { test_arithmetic_constructor(); @@ -503,6 +529,8 @@ int main() test_operator_ge(); test_operator_ge(); + test_operator_not(); + return boost::report_errors(); } From 40bc160eb0fc3a4a8a342068f13acfc4bea3091b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 12:16:52 -0500 Subject: [PATCH 033/191] Operator or --- include/boost/decimal/detail/u128.hpp | 57 +++++++++++++++++++++++++++ test/test_u128.cpp | 40 +++++++++++++++++++ 2 files changed, 97 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 60a57024..69746243 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -600,6 +600,63 @@ constexpr u128 operator~(const u128 rhs) noexcept return {~rhs.high, ~rhs.low}; } +//===================================== +// Or Operator +//===================================== + +template ::value && std::is_signed::value, bool> = true> +constexpr u128 operator|(const u128 lhs, const SignedInteger rhs) noexcept +{ + return {lhs.high | (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low | static_cast(rhs)}; +} + +template ::value && std::is_signed::value, bool> = true> +constexpr u128 operator|(const SignedInteger lhs, const u128 rhs) noexcept +{ + return {rhs.high | (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low | static_cast(lhs)}; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr u128 operator|(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return {lhs.high, lhs.low | static_cast(rhs)}; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr u128 operator|(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + return {rhs.high, rhs.low | static_cast(lhs)}; +} + +constexpr u128 operator|(const u128 lhs, const u128 rhs) noexcept +{ + return {lhs.high | rhs.high, lhs.low | rhs.low}; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator|(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs | static_cast(rhs); +} + +constexpr u128 operator|(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) | rhs; +} + +constexpr u128 operator|(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs | static_cast(rhs); +} + +constexpr u128 operator|(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) | rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index d0aba5d3..b593ac87 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -408,6 +408,34 @@ void test_operator_not() } } +template +void test_operator_or() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) + #endif + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + BOOST_TEST((emulated_value | value2) == (builtin_value | value2)); + BOOST_TEST((value2 | emulated_value) == (value2 | builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -531,6 +559,18 @@ int main() test_operator_not(); + test_operator_or(); + test_operator_or(); + test_operator_or(); + test_operator_or(); + test_operator_or<__int128>(); + + test_operator_or(); + test_operator_or(); + test_operator_or(); + test_operator_or(); + test_operator_or(); + return boost::report_errors(); } From 34b5e413bf5cbcd5445ca0909cdb0117a55979c3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 12:19:43 -0500 Subject: [PATCH 034/191] Operator and --- include/boost/decimal/detail/u128.hpp | 57 +++++++++++++++++++++++++++ test/test_u128.cpp | 42 +++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 69746243..2b9354ac 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -657,6 +657,63 @@ constexpr u128 operator|(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// And Operator +//===================================== + +template ::value && std::is_signed::value, bool> = true> +constexpr u128 operator&(const u128 lhs, const SignedInteger rhs) noexcept +{ + return {lhs.high & (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low & static_cast(rhs)}; +} + +template ::value && std::is_signed::value, bool> = true> +constexpr u128 operator&(const SignedInteger lhs, const u128 rhs) noexcept +{ + return {rhs.high & (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low & static_cast(lhs)}; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr u128 operator&(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return {lhs.high, lhs.low & static_cast(rhs)}; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr u128 operator&(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + return {rhs.high, rhs.low & static_cast(lhs)}; +} + +constexpr u128 operator&(const u128 lhs, const u128 rhs) noexcept +{ + return {lhs.high & rhs.high, lhs.low & rhs.low}; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator&(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs & static_cast(rhs); +} + +constexpr u128 operator&(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) & rhs; +} + +constexpr u128 operator&(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs & static_cast(rhs); +} + +constexpr u128 operator&(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) & rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index b593ac87..914b78a5 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -408,7 +408,7 @@ void test_operator_not() } } -template +template void test_operator_or() { boost::random::uniform_int_distribution dist(std::numeric_limits::min(), @@ -436,6 +436,34 @@ void test_operator_or() } } +template +void test_operator_and() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false + #ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable:4127) + #endif + + #ifdef _MSC_VER + #pragma warning(pop) + #endif + + BOOST_TEST((emulated_value & value2) == (builtin_value & value2)); + BOOST_TEST((value2 & emulated_value) == (value2 & builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -571,6 +599,18 @@ int main() test_operator_or(); test_operator_or(); + test_operator_and(); + test_operator_and(); + test_operator_and(); + test_operator_and(); + test_operator_and<__int128>(); + + test_operator_and(); + test_operator_and(); + test_operator_and(); + test_operator_and(); + test_operator_and(); + return boost::report_errors(); } From cd12a4370e424f778763bd4f2baec25f41dcee29 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 12:23:14 -0500 Subject: [PATCH 035/191] Operator xor --- include/boost/decimal/detail/u128.hpp | 57 +++++++++++++++++++++++++ test/test_u128.cpp | 60 +++++++++++++-------------- 2 files changed, 87 insertions(+), 30 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 2b9354ac..c7ee22ae 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -714,6 +714,63 @@ constexpr u128 operator&(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Xor Operator +//===================================== + +template ::value && std::is_signed::value, bool> = true> +constexpr u128 operator^(const u128 lhs, const SignedInteger rhs) noexcept +{ + return {lhs.high ^ (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low ^ static_cast(rhs)}; +} + +template ::value && std::is_signed::value, bool> = true> +constexpr u128 operator^(const SignedInteger lhs, const u128 rhs) noexcept +{ + return {rhs.high ^ (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low ^ static_cast(lhs)}; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr u128 operator^(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return {lhs.high, lhs.low ^ static_cast(rhs)}; +} + +template ::value && std::is_unsigned::value, bool> = true> +constexpr u128 operator^(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + return {rhs.high, rhs.low ^ static_cast(lhs)}; +} + +constexpr u128 operator^(const u128 lhs, const u128 rhs) noexcept +{ + return {lhs.high ^ rhs.high, lhs.low ^ rhs.low}; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator^(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs ^ static_cast(rhs); +} + +constexpr u128 operator^(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) ^ rhs; +} + +constexpr u128 operator^(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs ^ static_cast(rhs); +} + +constexpr u128 operator^(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) ^ rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 914b78a5..32b001e2 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -394,16 +394,6 @@ void test_operator_not() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4127) - #endif - - #ifdef _MSC_VER - #pragma warning(pop) - #endif - BOOST_TEST(~emulated_value == ~builtin_value); } } @@ -421,16 +411,6 @@ void test_operator_or() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4127) - #endif - - #ifdef _MSC_VER - #pragma warning(pop) - #endif - BOOST_TEST((emulated_value | value2) == (builtin_value | value2)); BOOST_TEST((value2 | emulated_value) == (value2 | builtin_value)); } @@ -449,21 +429,29 @@ void test_operator_and() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - // Some platforms get this wrong where for example -99 < 340282366920938463463374607431768211408 evaluates to false - #ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable:4127) - #endif - - #ifdef _MSC_VER - #pragma warning(pop) - #endif - BOOST_TEST((emulated_value & value2) == (builtin_value & value2)); BOOST_TEST((value2 & emulated_value) == (value2 & builtin_value)); } } +template +void test_operator_xor() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value ^ value2) == (builtin_value ^ value2)); + BOOST_TEST((value2 ^ emulated_value) == (value2 ^ builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -611,6 +599,18 @@ int main() test_operator_and(); test_operator_and(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor<__int128>(); + + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + return boost::report_errors(); } From 42f3d274025b59f4f6cd622bbd5ef4324a3add1e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 15:07:46 -0500 Subject: [PATCH 036/191] Left shift operator --- include/boost/decimal/detail/u128.hpp | 124 ++++++++++++++++++++++++++ test/test_u128.cpp | 36 ++++++++ 2 files changed, 160 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index c7ee22ae..41ea5969 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -771,6 +771,130 @@ constexpr u128 operator^(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Left Shift Operator +//===================================== + +template ::value, bool> = true> +constexpr u128 operator<<(const u128 lhs, const Integer rhs) noexcept +{ + if (rhs < 0 || rhs >= 128) + { + return {0, 0}; + } + + if (rhs == 0) + { + return lhs; + } + + if (rhs == 64) + { + return {lhs.low, 0}; + } + + if (rhs > 64) + { + return {lhs.low << (rhs - 64), 0}; + } + + return { + (lhs.high << rhs) | (lhs.low >> (64 - rhs)), + lhs.low << rhs + }; +} + +template ::value && (sizeof(Integer) * 8 > 16), bool> = true> +constexpr Integer operator<<(const Integer lhs, const u128 rhs) noexcept +{ + constexpr auto bit_width = sizeof(Integer) * 8; + + if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) + { + return 0; + } + + return lhs << rhs.low; +} + +template ::value && (sizeof(Integer) * 8 <= 16) && std::is_signed::value, bool> = true> +constexpr int operator<<(const Integer lhs, const u128 rhs) noexcept +{ + constexpr auto bit_width = sizeof(Integer) * 8; + + if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) + { + return 0; + } + + return static_cast(lhs) << rhs.low; +} + +template ::value && (sizeof(Integer) * 8 <= 16) && std::is_unsigned::value, bool> = true> +constexpr unsigned operator<<(const Integer lhs, const u128 rhs) noexcept +{ + constexpr auto bit_width = sizeof(Integer) * 8; + + if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) + { + return 0; + } + + return static_cast(lhs) << rhs.low; +} + +constexpr u128 operator<<(const u128 lhs, const u128 rhs) noexcept +{ + if (rhs >= 128) + { + return {0, 0}; + } + + if (rhs.low == 0) + { + return lhs; + } + + if (rhs.low == 64) + { + return {lhs.low, 0}; + } + + if (rhs.low > 64) + { + return {lhs.low << (rhs.low - 64), 0}; + } + + return { + (lhs.high << rhs.low) | (lhs.low >> (64 - rhs.low)), + lhs.low << rhs.low + }; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator<<(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs << static_cast(rhs); +} + +constexpr __int128 operator<<(const __int128 lhs, const u128 rhs) noexcept +{ + return lhs << static_cast<__int128>(rhs); +} + +constexpr u128 operator<<(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs << static_cast(rhs); +} + +constexpr unsigned __int128 operator<<(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return lhs << static_cast(rhs); +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 32b001e2..ed232a9c 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -2,6 +2,7 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt +#include #include #ifdef BOOST_DECIMAL_HAS_INT128 @@ -452,6 +453,29 @@ void test_operator_xor() } } +template +void test_operator_left_shift() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + boost::random::uniform_int_distribution shift_dist(0, sizeof(IntType) * CHAR_BIT - 1); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const unsigned shift_value {shift_dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value << shift_value) == (builtin_value << shift_value)); + + emulated_value = shift_value; + builtin_value = shift_value; + BOOST_TEST((value << emulated_value) == (value << builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -611,6 +635,18 @@ int main() test_operator_xor(); test_operator_xor(); + test_operator_left_shift(); + test_operator_left_shift(); + test_operator_left_shift(); + test_operator_left_shift(); + test_operator_left_shift<__int128>(); + + test_operator_left_shift(); + test_operator_left_shift(); + test_operator_left_shift(); + test_operator_left_shift(); + test_operator_left_shift(); + return boost::report_errors(); } From 09788791072281a0f85954ad049944da9a3d725e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 15:47:04 -0500 Subject: [PATCH 037/191] Right shift operator --- include/boost/decimal/detail/u128.hpp | 124 ++++++++++++++++++++++++++ test/test_u128.cpp | 35 ++++++++ 2 files changed, 159 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 41ea5969..4c7fe147 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -895,6 +895,130 @@ constexpr unsigned __int128 operator<<(const unsigned __int128 lhs, const u128 r #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Right Shift Operator +//===================================== + +template ::value, bool> = true> +constexpr u128 operator>>(const u128 lhs, const Integer rhs) noexcept +{ + if (rhs < 0 || rhs >= 128) + { + return {0, 0}; + } + + if (rhs == 0) + { + return lhs; + } + + if (rhs == 64) + { + return {0, lhs.high}; + } + + if (rhs > 64) + { + return {0, lhs.high >> (rhs - 64)}; + } + + return { + lhs.high >> rhs, + (lhs.low >> rhs) | (lhs.high << (64 - rhs)) + }; +} + +template ::value && (sizeof(Integer) * 8 > 16), bool> = true> +constexpr Integer operator>>(const Integer lhs, const u128 rhs) noexcept +{ + constexpr auto bit_width = sizeof(Integer) * 8; + + if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) + { + return 0; + } + + return lhs >> rhs.low; +} + +template ::value && (sizeof(Integer) * 8 <= 16) && std::is_signed::value, bool> = true> +constexpr int operator>>(const Integer lhs, const u128 rhs) noexcept +{ + constexpr auto bit_width = sizeof(Integer) * 8; + + if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) + { + return 0; + } + + return static_cast(lhs) >> rhs.low; +} + +template ::value && (sizeof(Integer) * 8 <= 16) && std::is_unsigned::value, bool> = true> +constexpr unsigned operator>>(const Integer lhs, const u128 rhs) noexcept +{ + constexpr auto bit_width = sizeof(Integer) * 8; + + if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) + { + return 0; + } + + return static_cast(lhs) >> rhs.low; +} + +constexpr u128 operator>>(const u128 lhs, const u128 rhs) noexcept +{ + if (rhs >= 128) + { + return {0, 0}; + } + + if (rhs.low == 0) + { + return lhs; + } + + if (rhs.low == 64) + { + return {0, lhs.high}; + } + + if (rhs.low > 64) + { + return {0, lhs.high >> (rhs.low - UINT64_C(64))}; + } + + return { + lhs.high >> rhs.low, + (lhs.low >> rhs.low) | (lhs.high << (UINT64_C(64) - rhs.low)) + }; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator>>(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs >> static_cast(rhs); +} + +constexpr __int128 operator>>(const __int128 lhs, const u128 rhs) noexcept +{ + return lhs >> static_cast<__int128>(rhs); +} + +constexpr u128 operator>>(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs >> static_cast(rhs); +} + +constexpr unsigned __int128 operator>>(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return lhs >> static_cast(rhs); +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index ed232a9c..44f9d24c 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -476,6 +476,29 @@ void test_operator_left_shift() } } +template +void test_operator_right_shift() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + boost::random::uniform_int_distribution shift_dist(0, sizeof(IntType) * CHAR_BIT - 1); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const unsigned shift_value {shift_dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value >> shift_value) == (builtin_value >> shift_value)); + + emulated_value = shift_value; + builtin_value = shift_value; + BOOST_TEST((value >> emulated_value) == (value >> builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -647,6 +670,18 @@ int main() test_operator_left_shift(); test_operator_left_shift(); + test_operator_right_shift(); + test_operator_right_shift(); + test_operator_right_shift(); + test_operator_right_shift(); + test_operator_right_shift<__int128>(); + + test_operator_right_shift(); + test_operator_right_shift(); + test_operator_right_shift(); + test_operator_right_shift(); + test_operator_right_shift(); + return boost::report_errors(); } From 706bc688dcd0fde3eff8b6c07e593092f85f7c2f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 3 Mar 2025 17:06:07 -0500 Subject: [PATCH 038/191] Add optimized 128-bit addition --- include/boost/decimal/detail/u128.hpp | 109 ++++++++++++++++++++++++++ test/test_u128.cpp | 30 +++++++ 2 files changed, 139 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 4c7fe147..d73f45fd 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1019,6 +1019,115 @@ constexpr unsigned __int128 operator>>(const unsigned __int128 lhs, const u128 r #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Addition Operator +//===================================== + +namespace impl { + +template ::value, bool> = true> +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const T rhs) noexcept +{ + u128 temp {lhs.high, lhs.low + rhs}; + + if (temp.low < lhs.low) + { + ++temp.high; + } + + return temp; +} + +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const u128 rhs) noexcept +{ + u128 temp {lhs.high + rhs.high, lhs.low + rhs.low}; + + if (temp.low < lhs.low) + { + ++temp.high; + } + + return temp; +} + +} // namespace impl + +constexpr u128 operator+(const u128 lhs, const u128 rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_add(lhs, rhs); + } + else + { + #if (defined(__aarch64__) && __cplusplus >= 202002L) || (defined(_M_ARM64) && _MSVC_LANG >= 202002L) + + std::uint64_t result_low {}; + std::uint64_t result_high {}; + + // Use inline assembly to access the carry flag directly + // Roughly equivalent to the ADX instructions below for x64 platforms + __asm__ volatile( + "adds %0, %2, %3\n" // adds sets carry flag if overflow occurs + "adc %1, %4, %5\n" // adc adds with carry from previous operation + : "=r" (result_low), "=r" (result_high) + : "r" (lhs.low), "r" (rhs.low), "r" (lhs.high), "r" (rhs.high) + : "cc" // clobbering condition codes (flags) + ); + + return {result_high, result_low}; + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + // Intel ADX instructions are specifically for Multi-Precision Arithmetic + unsigned long long int res_low {}; + unsigned long long int res_high {}; + + const unsigned char carry {BOOST_DECIMAL_ADD_CARRY(0, lhs.low, rhs.low, &res_low)}; + BOOST_DECIMAL_ADD_CARRY(carry, lhs.high, rhs.high, &res_high); + + return {res_high, res_low}; + + #else + + return impl::default_add(lhs, rhs); + + #endif + } + + #else + + return impl::default_add(lhs, rhs); + + #endif +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator+(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs + static_cast(rhs); +} + +constexpr u128 operator+(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) + rhs; +} + +constexpr u128 operator+(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs + static_cast(rhs); +} + +constexpr u128 operator+(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) + rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 44f9d24c..21292c5b 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -499,6 +499,24 @@ void test_operator_right_shift() } } +template +void test_operator_add() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value + value2) == (builtin_value + value2)); + BOOST_TEST((value2 + emulated_value) == (value2 + builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -682,6 +700,18 @@ int main() test_operator_right_shift(); test_operator_right_shift(); + //test_operator_add(); + //test_operator_add(); + //test_operator_add(); + //test_operator_add(); + test_operator_add<__int128>(); + + //test_operator_add(); + //test_operator_add(); + //test_operator_add(); + //test_operator_add(); + test_operator_add(); + return boost::report_errors(); } From a224d4723e42ed24376d0d2f6710e5131cbbad39 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 08:53:07 -0500 Subject: [PATCH 039/191] Re-work ARM ASM so it can be used with C++14 instead of 20 --- include/boost/decimal/detail/u128.hpp | 43 +++++++++++++++++---------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index d73f45fd..b918ebd7 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1050,6 +1050,30 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const u128 return temp; } +#if defined(__aarch64__) || defined(_M_ARM64) + +// Inline ASM is not constexpr until C++20 so we offload into this function +// in the non-constant evaluated case. +BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_add(const u128 lhs, const u128 rhs) noexcept +{ + std::uint64_t result_low {}; + std::uint64_t result_high {}; + + // Use inline assembly to access the carry flag directly + // Roughly equivalent to the ADX instructions below for x64 platforms + __asm__ volatile( + "adds %0, %2, %3\n" // adds sets carry flag if overflow occurs + "adc %1, %4, %5\n" // adc adds with carry from previous operation + : "=r" (result_low), "=r" (result_high) + : "r" (lhs.low), "r" (rhs.low), "r" (lhs.high), "r" (rhs.high) + : "cc" // clobbering condition codes (flags) + ); + + return {result_high, result_low}; +} + +#endif + } // namespace impl constexpr u128 operator+(const u128 lhs, const u128 rhs) noexcept @@ -1062,22 +1086,9 @@ constexpr u128 operator+(const u128 lhs, const u128 rhs) noexcept } else { - #if (defined(__aarch64__) && __cplusplus >= 202002L) || (defined(_M_ARM64) && _MSVC_LANG >= 202002L) - - std::uint64_t result_low {}; - std::uint64_t result_high {}; - - // Use inline assembly to access the carry flag directly - // Roughly equivalent to the ADX instructions below for x64 platforms - __asm__ volatile( - "adds %0, %2, %3\n" // adds sets carry flag if overflow occurs - "adc %1, %4, %5\n" // adc adds with carry from previous operation - : "=r" (result_low), "=r" (result_high) - : "r" (lhs.low), "r" (rhs.low), "r" (lhs.high), "r" (rhs.high) - : "cc" // clobbering condition codes (flags) - ); - - return {result_high, result_low}; + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_add(lhs, rhs); #elif defined(BOOST_DECIMAL_ADD_CARRY) From c8e1d6a0c3e2d7cf0204df3ac16865f6dd9e4871 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 11:44:39 -0500 Subject: [PATCH 040/191] Implement and test unsigned addition --- include/boost/decimal/detail/u128.hpp | 113 +++++++++++++++++++++++--- test/test_u128.cpp | 8 +- 2 files changed, 105 insertions(+), 16 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index b918ebd7..e7a6230f 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1025,8 +1025,7 @@ constexpr unsigned __int128 operator>>(const unsigned __int128 lhs, const u128 r namespace impl { -template ::value, bool> = true> -BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const T rhs) noexcept +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const std::uint64_t rhs) noexcept { u128 temp {lhs.high, lhs.low + rhs}; @@ -1072,11 +1071,74 @@ BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_add(const u128 lhs, const u128 rhs) noex return {result_high, result_low}; } -#endif +BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_add(const u128 lhs, const std::uint64_t rhs) noexcept +{ + std::uint64_t result_low {}; + std::uint64_t result_high {}; + + // Use inline assembly to access the carry flag directly + // Roughly equivalent to the ADX instructions below for x64 platforms + __asm__ volatile( + "adds %0, %2, %3\n" // adds sets carry flag if overflow occurs + "adc %1, %4, xzr\n" // add carry to high + : "=r" (result_low), "=r" (result_high) + : "r" (lhs.low), "r" (rhs), "r" (lhs.high) + : "cc" // clobbering condition codes (flags) + ); + + return {result_high, result_low}; +} + +BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_sub(const u128 lhs, const std::uint64_t rhs) noexcept +{ + std::uint64_t result_low {}; + std::uint64_t result_high {}; + + // Use inline assembly to access the carry flag directly + // Roughly equivalent to the ADX instructions below for x64 platforms + __asm__ volatile( + "subs %0, %2, %3\n" // subs sets carry flag if underflow occurs + "sbc %1, %4, xzr\n" // subtract carry from high value + : "=r" (result_low), "=r" (result_high) + : "r" (lhs.low), "r" (rhs), "r" (lhs.high) + : "cc" // clobbering condition codes (flags) + ); + + return {result_high, result_low}; +} + +#endif // defined(__aarch64__) || defined(_M_ARM64) + +#ifdef BOOST_DECIMAL_ADD_CARRY + +BOOST_DECIMAL_FORCE_INLINE u128 adx_add(const u128 lhs, const u128 rhs) noexcept +{ + // Intel ADX instructions are specifically for Multi-Precision Arithmetic + unsigned long long int res_low {}; + unsigned long long int res_high {}; + + const unsigned char carry {BOOST_DECIMAL_ADD_CARRY(0, lhs.low, rhs.low, &res_low)}; + BOOST_DECIMAL_ADD_CARRY(carry, lhs.high, rhs.high, &res_high); + + return {res_high, res_low}; +} + +BOOST_DECIMAL_FORCE_INLINE u128 adx_add(const u128 lhs, const std::uint64_t rhs) noexcept +{ + // Intel ADX instructions are specifically for Multi-Precision Arithmetic + unsigned long long int res_low {}; + const unsigned char carry {BOOST_DECIMAL_ADD_CARRY(0, lhs.low, rhs, &res_low)}; + + // Unconditionally add the carry to lhs.high since we don't have multiple additions here + return {lhs.high + static_cast(carry), res_low}; +} + +#endif // BOOST_DECIMAL_ADD_CARRY } // namespace impl -constexpr u128 operator+(const u128 lhs, const u128 rhs) noexcept +template ::value || std::is_same::value, bool> = true> +constexpr u128 operator+(const u128 lhs, const UnsignedInteger rhs) noexcept { #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1092,14 +1154,7 @@ constexpr u128 operator+(const u128 lhs, const u128 rhs) noexcept #elif defined(BOOST_DECIMAL_ADD_CARRY) - // Intel ADX instructions are specifically for Multi-Precision Arithmetic - unsigned long long int res_low {}; - unsigned long long int res_high {}; - - const unsigned char carry {BOOST_DECIMAL_ADD_CARRY(0, lhs.low, rhs.low, &res_low)}; - BOOST_DECIMAL_ADD_CARRY(carry, lhs.high, rhs.high, &res_high); - - return {res_high, res_low}; + return impl::adx_add(lhs, rhs); #else @@ -1115,6 +1170,40 @@ constexpr u128 operator+(const u128 lhs, const u128 rhs) noexcept #endif } +// Since unsigned addition is trivially commutative we just reverse the order of the operands into the impl functions +template ::value, bool> = true> +constexpr u128 operator+(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_add(rhs, lhs); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_add(rhs, lhs); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_add(rhs, lhs); + + #else + + return impl::default_add(rhs, lhs); + + #endif + } + + #else + + return impl::default_add(rhs, lhs); + + #endif +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator+(const u128 lhs, const __int128 rhs) noexcept diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 21292c5b..8a250c10 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -706,10 +706,10 @@ int main() //test_operator_add(); test_operator_add<__int128>(); - //test_operator_add(); - //test_operator_add(); - //test_operator_add(); - //test_operator_add(); + test_operator_add(); + test_operator_add(); + test_operator_add(); + test_operator_add(); test_operator_add(); return boost::report_errors(); From 18b3acf1d3acc24e5812d0870aadae51f035f48e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 14:52:30 -0500 Subject: [PATCH 041/191] Add macro for subborrow --- include/boost/decimal/detail/config.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index 02ad865c..e812f6c4 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -98,6 +98,7 @@ # elif defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) # define BOOST_DECIMAL_ADD_CARRY _addcarry_u64 # endif +# define BOOST_DECIMAL_SUB_BORROW _subborrow_u64 #elif defined(__x86_64__) # ifndef BOOST_DECIMAL_BUILD_MODULE # include @@ -108,6 +109,7 @@ # else # define BOOST_DECIMAL_ADD_CARRY _addcarry_u64 # endif +# define BOOST_DECIMAL_SUB_BORROW _subborrow_u64 #elif defined(__ARM_NEON__) # ifndef BOOST_DECIMAL_BUILD_MODULE # include From de0c75c7fd9d6d86b9885156f0637c69a34064af Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 14:52:43 -0500 Subject: [PATCH 042/191] Implement and test addition --- include/boost/decimal/detail/u128.hpp | 202 ++++++++++++++++++++++++++ test/test_u128.cpp | 8 +- 2 files changed, 206 insertions(+), 4 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index e7a6230f..d5bdb095 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -8,6 +8,7 @@ #define BOOST_DECIMAL_DETAIL_U128_HPP #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -1049,6 +1050,32 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const u128 return temp; } +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const u128 rhs) noexcept +{ + u128 temp {lhs.high - rhs.high, lhs.low - rhs.low}; + + // Check for carry + if (lhs.low < rhs.low) + { + --temp.high; + } + + return temp; +} + +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const std::uint64_t rhs) noexcept +{ + u128 temp {lhs.high, lhs.low - rhs}; + + // Check for carry + if (lhs.low < rhs) + { + --temp.high; + } + + return temp; +} + #if defined(__aarch64__) || defined(_M_ARM64) // Inline ASM is not constexpr until C++20 so we offload into this function @@ -1089,6 +1116,24 @@ BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_add(const u128 lhs, const std::uint64_t return {result_high, result_low}; } +BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_sub(const u128 lhs, const u128 rhs) noexcept +{ + std::uint64_t result_low {}; + std::uint64_t result_high {}; + + // Use inline assembly to access the carry flag directly + // Roughly equivalent to the ADX instructions below for x64 platforms + __asm__ volatile( + "subs %0, %2, %3\n" // adds sets carry flag if overflow occurs + "sbc %1, %4, %5\n" // adc adds with carry from previous operation + : "=r" (result_low), "=r" (result_high) + : "r" (lhs.low), "r" (rhs.low), "r" (lhs.high), "r" (rhs.high) + : "cc" // clobbering condition codes (flags) + ); + + return {result_high, result_low}; +} + BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_sub(const u128 lhs, const std::uint64_t rhs) noexcept { std::uint64_t result_low {}; @@ -1133,6 +1178,25 @@ BOOST_DECIMAL_FORCE_INLINE u128 adx_add(const u128 lhs, const std::uint64_t rhs) return {lhs.high + static_cast(carry), res_low}; } +BOOST_DECIMAL_FORCE_INLINE u128 adx_sub(const u128 lhs, const u128 rhs) noexcept +{ + unsigned long long int res_low {}; + unsigned long long int res_high {}; + + const unsigned char carry {BOOST_DECIMAL_SUB_BORROW(0, lhs.low, rhs.low, &res_low)}; + BOOST_DECIMAL_SUB_BORROW(carry, lhs.high, rhs.high, &res_high); + + return {res_high, res_low}; +} + +BOOST_DECIMAL_FORCE_INLINE u128 adx_sub(const u128 lhs, const std::uint64_t rhs) noexcept +{ + unsigned long long int res_low {}; + const unsigned char carry {BOOST_DECIMAL_SUB_BORROW(0, lhs.low, rhs, &res_low)}; + + return {lhs.high - static_cast(carry), res_low}; +} + #endif // BOOST_DECIMAL_ADD_CARRY } // namespace impl @@ -1204,6 +1268,144 @@ constexpr u128 operator+(const UnsignedInteger lhs, const u128 rhs) noexcept #endif } +template ::value, bool> = true> +constexpr u128 operator+(const u128 lhs, const SignedInteger rhs) noexcept +{ + if (rhs > 0) + { + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_add(lhs, static_cast(rhs)); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_add(lhs, static_cast(rhs)); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_add(lhs, static_cast(rhs)); + + #else + + return impl::default_add(lhs, static_cast(rhs)); + + #endif + } + + #else + + return impl::default_add(lhs, static_cast(rhs)); + + #endif + } + else + { + const auto unsigned_rhs {detail::make_positive_unsigned(rhs)}; + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_sub(lhs, unsigned_rhs); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_sub(lhs, unsigned_rhs); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_sub(lhs, unsigned_rhs); + + #else + + return impl::default_sub(lhs, unsigned_rhs); + + #endif + } + + #else + + return impl::default_sub(lhs, unsigned_rhs); + + #endif + } +} + +// -a + b == b - a +// a + b == b + a +template ::value, bool> = true> +constexpr u128 operator+(const SignedInteger lhs, const u128 rhs) noexcept +{ + if (lhs > 0) + { + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_add(rhs, static_cast(lhs)); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_add(rhs, static_cast(lhs)); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_add(rhs, static_cast(lhs)); + + #else + + return impl::default_add(rhs, static_cast(lhs)); + + #endif + } + + #else + + return impl::default_add(rhs, static_cast(lhs)); + + #endif + } + else + { + const auto unsigned_lhs {detail::make_positive_unsigned(lhs)}; + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_sub(rhs, unsigned_lhs); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_sub(rhs, unsigned_lhs); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_sub(rhs, unsigned_lhs); + + #else + + return impl::default_sub(rhs, unsigned_lhs); + + #endif + } + + #else + + return impl::default_sub(rhs, unsigned_lhs); + + #endif + } +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator+(const u128 lhs, const __int128 rhs) noexcept diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 8a250c10..5c853647 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -700,10 +700,10 @@ int main() test_operator_right_shift(); test_operator_right_shift(); - //test_operator_add(); - //test_operator_add(); - //test_operator_add(); - //test_operator_add(); + test_operator_add(); + test_operator_add(); + test_operator_add(); + test_operator_add(); test_operator_add<__int128>(); test_operator_add(); From 17298f089ebefd66d96093527ad2571e06ed9745 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 14:55:14 -0500 Subject: [PATCH 043/191] Add subtraction test set --- test/test_u128.cpp | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 5c853647..1216f266 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -517,6 +517,24 @@ void test_operator_add() } } +template +void test_operator_sub() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value + value2) == (builtin_value + value2)); + BOOST_TEST((value2 + emulated_value) == (value2 + builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -712,6 +730,18 @@ int main() test_operator_add(); test_operator_add(); + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + test_operator_sub<__int128>(); + + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + return boost::report_errors(); } From f697e83bc397079039fa968570fc741af2d34406 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 15:35:17 -0500 Subject: [PATCH 044/191] Add unsigned subtraction --- include/boost/decimal/detail/u128.hpp | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index d5bdb095..067020b8 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1430,6 +1430,70 @@ constexpr u128 operator+(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +template ::value || std::is_same::value, bool> = true> +constexpr u128 operator-(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_sub(lhs, rhs); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_sub(lhs, rhs); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_sub(lhs, rhs); + + #else + + return impl::default_sub(lhs, rhs); + + #endif + } + + #else + + return impl::default_sub(lhs, rhs); + + #endif +} + +template ::value, bool> = true> +constexpr u128 operator-(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + // The only real viable way to do this is to promote lhs and perform 128-bit sub + return u128{UINT64_C(0), lhs} - rhs; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator-(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs - static_cast(rhs); +} + +constexpr u128 operator-(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) - rhs; +} + +constexpr u128 operator-(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs - static_cast(rhs); +} + +constexpr u128 operator-(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) - rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost From 26eca65a6f78644c25dfb052d3238e23779a772b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 15:37:41 -0500 Subject: [PATCH 045/191] Add signed subtraction --- include/boost/decimal/detail/u128.hpp | 74 +++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 067020b8..10b16176 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1470,6 +1470,80 @@ constexpr u128 operator-(const UnsignedInteger lhs, const u128 rhs) noexcept return u128{UINT64_C(0), lhs} - rhs; } +template ::value, bool> = true> +constexpr u128 operator-(const u128 lhs, const SignedInteger rhs) noexcept +{ + if (rhs < 0) + { + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_add(lhs, static_cast(rhs)); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_add(lhs, static_cast(rhs)); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_add(lhs, static_cast(rhs)); + + #else + + return impl::default_add(lhs, static_cast(rhs)); + + #endif + } + + #else + + return impl::default_add(lhs, static_cast(rhs)); + + #endif + } + else + { + const auto unsigned_rhs {detail::make_positive_unsigned(rhs)}; + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_sub(lhs, unsigned_rhs); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_sub(lhs, unsigned_rhs); + + #elif defined(BOOST_DECIMAL_ADD_CARRY) + + return impl::adx_sub(lhs, unsigned_rhs); + + #else + + return impl::default_sub(lhs, unsigned_rhs); + + #endif + } + + #else + + return impl::default_sub(lhs, unsigned_rhs); + + #endif + } +} + +template ::value, bool> = true> +constexpr u128 operator-(const SignedInteger lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) - rhs; +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator-(const u128 lhs, const __int128 rhs) noexcept From 82c245017ff388d3aba908276d5ea453d0e5d798 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 15:54:15 -0500 Subject: [PATCH 046/191] Add compound addition --- include/boost/decimal/detail/u128.hpp | 88 +++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 10b16176..681b81de 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -138,6 +138,22 @@ u128 #ifdef BOOST_DECIMAL_HAS_FLOAT128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 + + // Compound operators + constexpr u128& operator+=(std::uint8_t rhs) noexcept; + constexpr u128& operator+=(std::uint16_t rhs) noexcept; + constexpr u128& operator+=(std::uint32_t rhs) noexcept; + constexpr u128& operator+=(std::uint64_t rhs) noexcept; + + constexpr u128& operator+=(std::int8_t rhs) noexcept; + constexpr u128& operator+=(std::int16_t rhs) noexcept; + constexpr u128& operator+=(std::int32_t rhs) noexcept; + constexpr u128& operator+=(std::int64_t rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator+=(__int128 rhs) noexcept; + constexpr u128& operator+=(unsigned __int128 rhs) noexcept; + #endif // BOOST_DECIMAL_HAS_INT128 }; // Signed assignment operators @@ -1430,6 +1446,78 @@ constexpr u128 operator+(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound Addition Operator +//===================================== + +constexpr u128& u128::operator+=(const std::uint8_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const std::uint16_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const std::uint32_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const std::uint64_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const std::int8_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const std::int16_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const std::int32_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const std::int64_t rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator+=(const __int128 rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +constexpr u128& u128::operator+=(const unsigned __int128 rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + +#endif + +//===================================== +// Subtraction Operator +//===================================== + template ::value || std::is_same::value, bool> = true> constexpr u128 operator-(const u128 lhs, const UnsignedInteger rhs) noexcept { From 007c217921c70a9b1f42e93ef7b2a79a3e8ef392 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 15:57:00 -0500 Subject: [PATCH 047/191] Add compound subtraction --- include/boost/decimal/detail/u128.hpp | 87 ++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 681b81de..f049a3f9 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -139,7 +139,7 @@ u128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 - // Compound operators + // Compound Addition Operators constexpr u128& operator+=(std::uint8_t rhs) noexcept; constexpr u128& operator+=(std::uint16_t rhs) noexcept; constexpr u128& operator+=(std::uint32_t rhs) noexcept; @@ -154,6 +154,22 @@ u128 constexpr u128& operator+=(__int128 rhs) noexcept; constexpr u128& operator+=(unsigned __int128 rhs) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 + + // Compound Subtraction Operators + constexpr u128& operator-=(std::uint8_t rhs) noexcept; + constexpr u128& operator-=(std::uint16_t rhs) noexcept; + constexpr u128& operator-=(std::uint32_t rhs) noexcept; + constexpr u128& operator-=(std::uint64_t rhs) noexcept; + + constexpr u128& operator-=(std::int8_t rhs) noexcept; + constexpr u128& operator-=(std::int16_t rhs) noexcept; + constexpr u128& operator-=(std::int32_t rhs) noexcept; + constexpr u128& operator-=(std::int64_t rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator-=(__int128 rhs) noexcept; + constexpr u128& operator-=(unsigned __int128 rhs) noexcept; + #endif // BOOST_DECIMAL_HAS_INT128 }; // Signed assignment operators @@ -1656,6 +1672,75 @@ constexpr u128 operator-(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound Subtraction Operator +//===================================== + +constexpr u128& u128::operator-=(const std::uint8_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const std::uint16_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const std::uint32_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const std::uint64_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const std::int8_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const std::int16_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const std::int32_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const std::int64_t rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator-=(const __int128 rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +constexpr u128& u128::operator-=(const unsigned __int128 rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + +#endif + + } // namespace detail } // namespace decimal } // namespace boost From 79938c930663663f66fb5f9e26b68f7e6c8d7970 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 16:03:38 -0500 Subject: [PATCH 048/191] Add increment operators --- include/boost/decimal/detail/u128.hpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index f049a3f9..83340a60 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -139,6 +139,10 @@ u128 explicit constexpr operator __float128() const noexcept; #endif // BOOST_DECIMAL_HAS_FLOAT128 + // Prefix and postfix increment + constexpr u128& operator++() noexcept; + constexpr u128& operator++(int) noexcept; + // Compound Addition Operators constexpr u128& operator+=(std::uint8_t rhs) noexcept; constexpr u128& operator+=(std::uint16_t rhs) noexcept; @@ -248,6 +252,25 @@ constexpr u128::operator __float128() const noexcept #endif // BOOST_DECIMAL_HAS_FLOAT128 +//===================================== +// Increment Operators +//===================================== + +constexpr u128& u128::operator++() noexcept +{ + if (++low == UINT64_C(0)) + { + ++high; + } + + return *this; +} + +constexpr u128& u128::operator++(int) noexcept +{ + return ++(*this); +} + //===================================== // Unary Operators //===================================== From d879751070c782c85284f4882ac71e5b5c308589 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 17:06:05 -0500 Subject: [PATCH 049/191] Add test set --- test/test_u128.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 1216f266..5568e939 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -535,6 +535,26 @@ void test_operator_sub() } } +template +void test_operator_mul() +{ + const auto root_max {static_cast(std::sqrt(std::numeric_limits::max()))}; + const auto root_min {std::is_unsigned::value ? 0 : -root_max}; + + boost::random::uniform_int_distribution dist(root_min, root_max); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value * value2) == (builtin_value * value2)); + BOOST_TEST((value2 * emulated_value) == (value2 * builtin_value)); + } +} + int main() { test_arithmetic_constructor(); @@ -742,6 +762,18 @@ int main() test_operator_sub(); test_operator_sub(); + //test_operator_mul(); + //test_operator_mul(); + //test_operator_mul(); + //test_operator_mul(); + test_operator_mul<__int128>(); + + //test_operator_mul(); + //test_operator_mul(); + //test_operator_mul(); + //test_operator_mul(); + test_operator_mul(); + return boost::report_errors(); } From f613d45a025c8d860ebab60c01f7dde48a11f6f8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 17:06:19 -0500 Subject: [PATCH 050/191] Add 128 bit impls --- include/boost/decimal/detail/u128.hpp | 163 ++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 83340a60..e4ac4cbf 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1763,6 +1763,169 @@ constexpr u128& u128::operator-=(const unsigned __int128 rhs) noexcept #endif +//===================================== +// Multiplication Operator +//===================================== + +namespace impl { + +constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept +{ + const auto a = static_cast(lhs.low >> 32); + const auto b = static_cast(lhs.low & UINT32_MAX); + const auto c = static_cast(rhs.low >> 32); + const auto d = static_cast(rhs.low & UINT32_MAX); + + u128 result { lhs.high * rhs.low + lhs.low * rhs.high + a * c, b * d }; + result += u128{0, a * d} << 32; + result += u128{0, b * c} << 32; + + return result; +} + +#if defined(__aarch64__) || defined(_M_ARM64) + +BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_mul(const u128 lhs, const u128 rhs) noexcept +{ + u128 result; + + // Does not compensate or track overflow of the high word + __asm__ volatile( + // Multiply lhs.low * rhs.low + "mul %[result_low], %[lhs_low], %[rhs_low]\n" // result.low = lhs.low * rhs.low (low 64 bits) + "umulh %[result_high], %[lhs_low], %[rhs_low]\n" // result.high = lhs.low * rhs.low (high 64 bits) + + // Multiply lhs.high * rhs.low and add to result.high + "mul x8, %[lhs_high], %[rhs_low]\n" // x8 = lhs.high * rhs.low + "adds %[result_high], %[result_high], x8\n" // result.high += x8 (with carry) + + // Multiply lhs.low * rhs.high and add to result.high + "mul x8, %[lhs_low], %[rhs_high]\n" // x8 = lhs.low * rhs.high + "adds %[result_high], %[result_high], x8\n" // result.high += x8 (with carry) + + // Output operands + : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) + // Input operands + : [lhs_low] "r" (lhs.low), [lhs_high] "r" (lhs.high), + [rhs_low] "r" (rhs.low), [rhs_high] "r" (rhs.high) + // Clobbered registers + : "x8", "cc" + ); + + return result; +} + +#elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) + +BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept +{ + u128 result; + + __asm__ volatile( + // Multiply lhs.low * rhs.low + "movq %[lhs_low], %%rax\n" + "mulq %[rhs_low]\n" // rdx:rax = lhs.low * rhs.low + "movq %%rax, %[result_low]\n" + "movq %%rdx, %%r8\n" // Save high part in r8 + + // Multiply lhs.high * rhs.low and add to r8 + "movq %[lhs_high], %%rax\n" + "mulq %[rhs_low]\n" // rdx:rax = lhs.high * rhs.low + "addq %%rax, %%r8\n" + "adcq $0, %%rdx\n" // Add with carry + + // Multiply lhs.low * rhs.high and add to r8:rdx + "movq %[lhs_low], %%rax\n" + "mulq %[rhs_high]\n" // rdx:rax = lhs.low * rhs.high + "addq %%rax, %%r8\n" + "movq %%r8, %[result_high]\n" + + : [result_low] "=m" (result.low), [result_high] "=m" (result.high) + : [lhs_low] "m" (lhs.low), [lhs_high] "m" (lhs.high), + [rhs_low] "m" (rhs.low), [rhs_high] "m" (rhs.high) + : "rax", "rdx", "r8", "cc" + ); + + return result; +} + +#elif defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) + +BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept +{ + u128 result; + + // Multiply lhs.low * rhs.low (full 128-bit result) + result.low = _umul128(lhs.low, rhs.low, &result.high); + + // Add lhs.high * rhs.low to result.high + unsigned char carry = BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * rhs.low, &result.high); + + // Add lhs.low * rhs.high to result.high + BOOST_DECIMAL_ADD_CARRY(carry, result.high, lhs.low * rhs.high, &result.high); + + return result; +} + +#endif + +} // namespace impl + +constexpr u128 operator*(const u128 lhs, const u128 rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_mul(lhs, rhs); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_mul(lhs, rhs); + + #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) + + return impl::x64_mul(lhs, rhs); + + #else + + return impl::default_mul(lhs, rhs); + + #endif + } + + #else + + return impl::default_mul(lhs, rhs); + + #endif +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator*(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs * static_cast(rhs); +} + +constexpr u128 operator*(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) * rhs; +} + +constexpr u128 operator*(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs * static_cast(rhs); +} + +constexpr u128 operator*(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) * rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 } // namespace detail } // namespace decimal From dfb7313120ba1f5a836d6182b53e35f700f71ea7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 4 Mar 2025 17:16:56 -0500 Subject: [PATCH 051/191] Add mul for all unsigned integers --- include/boost/decimal/detail/u128.hpp | 116 +++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index e4ac4cbf..cdf3cc7e 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1783,6 +1783,21 @@ constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept return result; } +constexpr u128 default_mul(const u128 lhs, const std::uint64_t rhs) noexcept +{ + const auto c = static_cast(rhs >> 32); + const auto d = static_cast(rhs & UINT32_MAX); + const auto a = static_cast(lhs.low >> 32); + const auto b = static_cast(lhs.low & UINT32_MAX); + + u128 result{lhs.high * rhs, b * d}; + result += u128{0, a * d} << 32; + result += u128{0, b * c} << 32; + + return result; +} + + #if defined(__aarch64__) || defined(_M_ARM64) BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_mul(const u128 lhs, const u128 rhs) noexcept @@ -1815,6 +1830,32 @@ BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_mul(const u128 lhs, const u128 rhs) noex return result; } +BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_mul(const u128 lhs, const std::uint64_t rhs) noexcept +{ + u128 result; + + // Does not compensate or track overflow of the high word + __asm__ volatile( + // Multiply lhs.low * rhs + "mul %[result_low], %[lhs_low], %[rhs]\n" // result.low = lhs.low * rhs (low 64 bits) + "umulh %[result_high], %[lhs_low], %[rhs]\n" // result.high = lhs.low * rhs (high 64 bits) + + // Multiply lhs.high * rhs and add to result.high + "mul x8, %[lhs_high], %[rhs]\n" // x8 = lhs.high * rhs + "adds %[result_high], %[result_high], x8\n" // result.high += x8 (with carry) + + // Output operands + : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) + // Input operands + : [lhs_low] "r" (lhs.low), [lhs_high] "r" (lhs.high), + [rhs] "r" (rhs) + // Clobbered registers + : "x8", "cc" + ); + + return result; +} + #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept @@ -1849,6 +1890,32 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept return result; } +BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) noexcept +{ + u128 result; + + __asm__ volatile( + // Multiply lhs.low * rhs + "movq %[lhs_low], %%rax\n" + "mulq %[rhs]\n" // rdx:rax = lhs.low * rhs + "movq %%rax, %[result_low]\n" + "movq %%rdx, %%r8\n" // Save high part in r8 + + // Multiply lhs.high * rhs and add to r8 + "movq %[lhs_high], %%rax\n" + "mulq %[rhs]\n" // rdx:rax = lhs.high * rhs + "addq %%rax, %%r8\n" + "movq %%r8, %[result_high]\n" + + : [result_low] "=m" (result.low), [result_high] "=m" (result.high) + : [lhs_low] "m" (lhs.low), [lhs_high] "m" (lhs.high), + [rhs] "m" (rhs) + : "rax", "rdx", "r8", "cc" + ); + + return result; +} + #elif defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept @@ -1867,11 +1934,25 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept return result; } +BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) noexcept +{ + u128 result; + + // Multiply lhs.low * rhs (full 128-bit result) + result.low = _umul128(lhs.low, rhs, &result.high); + + // Add lhs.high * rhs to result.high + BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * rhs, &result.high); + + return result; +} + #endif } // namespace impl -constexpr u128 operator*(const u128 lhs, const u128 rhs) noexcept +template ::value || std::is_same::value, bool> = true> +constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept { #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1903,6 +1984,39 @@ constexpr u128 operator*(const u128 lhs, const u128 rhs) noexcept #endif } +template ::value, bool> = true> +constexpr u128 operator*(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_mul(rhs, static_cast(lhs)); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_mul(rhs, static_cast(lhs)); + + #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) + + return impl::x64_mul(rhs, static_cast(lhs)); + + #else + + return impl::default_mul(rhs, static_cast(lhs)); + + #endif + } + + #else + + return impl::default_mul(rhs, static_cast(lhs)); + + #endif +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator*(const u128 lhs, const __int128 rhs) noexcept From 84b117570b9c8e84dd5fda63e42fb54f50277631 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 09:38:57 -0500 Subject: [PATCH 052/191] Add better traits --- include/boost/decimal/detail/u128.hpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index cdf3cc7e..1ad253e5 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -29,6 +29,28 @@ namespace boost { namespace decimal { namespace detail { +namespace impl { + +template +struct signed_integer +{ + constexpr bool value = std::is_signed::value && std::is_integral::value; +}; + +template +static constexpr bool is_signed_integer_v = signed_integer::value; + +template +struct unsigned_integer +{ + constexpr bool value = std::is_unsigned::value && std::is_integral::value; +}; + +template +static constexpr bool is_unsigned_integer_v = unsigned_integer::value; + +} + struct #ifdef BOOST_DECIMAL_HAS_INT128 alignas(alignof(unsigned __int128)) From 0684d268f601637ff2dfe2c2f83339d5481cf77d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 10:46:59 -0500 Subject: [PATCH 053/191] Simplify member operators --- include/boost/decimal/detail/u128.hpp | 178 +++++--------------------- 1 file changed, 34 insertions(+), 144 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 1ad253e5..9881e9a4 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -34,7 +34,7 @@ namespace impl { template struct signed_integer { - constexpr bool value = std::is_signed::value && std::is_integral::value; + static constexpr bool value = std::is_signed::value && std::is_integral::value; }; template @@ -43,7 +43,7 @@ static constexpr bool is_signed_integer_v = signed_integer::value; template struct unsigned_integer { - constexpr bool value = std::is_unsigned::value && std::is_integral::value; + static constexpr bool value = std::is_unsigned::value && std::is_integral::value; }; template @@ -84,10 +84,8 @@ u128 constexpr u128(const std::uint64_t hi, const std::uint64_t lo) noexcept : low{lo}, high{hi} {} // Signed arithmetic constructors - explicit constexpr u128(const std::int8_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} - explicit constexpr u128(const std::int16_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} - explicit constexpr u128(const std::int32_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} - explicit constexpr u128(const std::int64_t value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} + template , bool> = true> + explicit constexpr u128(const SignedInteger value) noexcept : low {static_cast(value)}, high {value < 0 ? UINT64_MAX : UINT64_C(0)} {} #ifdef BOOST_DECIMAL_HAS_INT128 explicit constexpr u128(const __int128 value) noexcept : @@ -96,10 +94,8 @@ u128 #endif // BOOST_DECIMAL_HAS_INT128 // Unsigned arithmetic constructors - explicit constexpr u128(const std::uint8_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} - explicit constexpr u128(const std::uint16_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} - explicit constexpr u128(const std::uint32_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} - explicit constexpr u128(const std::uint64_t value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} + template , bool> = true> + explicit constexpr u128(const UnsignedInteger value) noexcept : low {static_cast(value)}, high {UINT64_C(0)} {} #ifdef BOOST_DECIMAL_HAS_INT128 explicit constexpr u128(const unsigned __int128 value) noexcept : @@ -108,20 +104,16 @@ u128 #endif // BOOST_DECIMAL_HAS_INT128 // Signed assignment operators - constexpr u128& operator=(std::int8_t value) noexcept; - constexpr u128& operator=(std::int16_t value) noexcept; - constexpr u128& operator=(std::int32_t value) noexcept; - constexpr u128& operator=(std::int64_t value) noexcept; + template , bool> = true> + constexpr u128& operator=(SignedInteger value) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& operator=(__int128 value) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 // Unsigned assignment operators - constexpr u128& operator=(std::uint8_t value) noexcept; - constexpr u128& operator=(std::uint16_t value) noexcept; - constexpr u128& operator=(std::uint32_t value) noexcept; - constexpr u128& operator=(std::uint64_t value) noexcept; + template , bool> = true> + constexpr u128& operator=(UnsignedInteger value) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& operator=(unsigned __int128 value) noexcept; @@ -131,20 +123,16 @@ u128 constexpr operator bool() const noexcept { return low || high; } // Conversion to signed integer types - explicit constexpr operator std::int8_t() const noexcept { return static_cast(low); } - explicit constexpr operator std::int16_t() const noexcept { return static_cast(low); } - explicit constexpr operator std::int32_t() const noexcept { return static_cast(low); } - explicit constexpr operator std::int64_t() const noexcept { return static_cast(low); } + template , bool> = true> + explicit constexpr operator SignedInteger() const noexcept { return static_cast(low); } #ifdef BOOST_DECIMAL_HAS_INT128 explicit constexpr operator __int128() const noexcept { return (static_cast<__int128>(high) << 64) + low; } #endif // BOOST_DECIMAL_HAS_INT128 // Conversion to unsigned integer types - explicit constexpr operator std::uint8_t() const noexcept { return static_cast(low); } - explicit constexpr operator std::uint16_t() const noexcept { return static_cast(low); } - explicit constexpr operator std::uint32_t() const noexcept { return static_cast(low); } - explicit constexpr operator std::uint64_t() const noexcept { return low; } + template , bool> = true> + explicit constexpr operator UnsignedInteger() const noexcept { return static_cast(low); } #ifdef BOOST_DECIMAL_HAS_INT128 explicit constexpr operator unsigned __int128() const noexcept { return (static_cast(high) << 64U) + low; } @@ -166,15 +154,11 @@ u128 constexpr u128& operator++(int) noexcept; // Compound Addition Operators - constexpr u128& operator+=(std::uint8_t rhs) noexcept; - constexpr u128& operator+=(std::uint16_t rhs) noexcept; - constexpr u128& operator+=(std::uint32_t rhs) noexcept; - constexpr u128& operator+=(std::uint64_t rhs) noexcept; + template , bool> = true> + constexpr u128& operator+=(SignedInteger rhs) noexcept; - constexpr u128& operator+=(std::int8_t rhs) noexcept; - constexpr u128& operator+=(std::int16_t rhs) noexcept; - constexpr u128& operator+=(std::int32_t rhs) noexcept; - constexpr u128& operator+=(std::int64_t rhs) noexcept; + template , bool> = true> + constexpr u128& operator+=(UnsignedInteger rhs) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& operator+=(__int128 rhs) noexcept; @@ -182,15 +166,11 @@ u128 #endif // BOOST_DECIMAL_HAS_INT128 // Compound Subtraction Operators - constexpr u128& operator-=(std::uint8_t rhs) noexcept; - constexpr u128& operator-=(std::uint16_t rhs) noexcept; - constexpr u128& operator-=(std::uint32_t rhs) noexcept; - constexpr u128& operator-=(std::uint64_t rhs) noexcept; + template , bool> = true> + constexpr u128& operator-=(SignedInteger rhs) noexcept; - constexpr u128& operator-=(std::int8_t rhs) noexcept; - constexpr u128& operator-=(std::int16_t rhs) noexcept; - constexpr u128& operator-=(std::int32_t rhs) noexcept; - constexpr u128& operator-=(std::int64_t rhs) noexcept; + template , bool> = true> + constexpr u128& operator-=(UnsignedInteger rhs) noexcept; #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& operator-=(__int128 rhs) noexcept; @@ -199,28 +179,8 @@ u128 }; // Signed assignment operators -constexpr u128& u128::operator=(const std::int8_t value) noexcept -{ - low = static_cast(value); - high = value < 0 ? UINT64_MAX : UINT64_C(0); - return *this; -} - -constexpr u128& u128::operator=(const std::int16_t value) noexcept -{ - low = static_cast(value); - high = value < 0 ? UINT64_MAX : UINT64_C(0); - return *this; -} - -constexpr u128& u128::operator=(const std::int32_t value) noexcept -{ - low = static_cast(value); - high = value < 0 ? UINT64_MAX : UINT64_C(0); - return *this; -} - -constexpr u128& u128::operator=(const std::int64_t value) noexcept +template , bool>> +constexpr u128& u128::operator=(const SignedInteger value) noexcept { low = static_cast(value); high = value < 0 ? UINT64_MAX : UINT64_C(0); @@ -237,10 +197,8 @@ constexpr u128& u128::operator=(const __int128 value) noexcept #endif // BOOST_DECIMAL_HAS_INT128 // Unsigned assignment operators -constexpr u128& u128::operator=(const std::uint8_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } -constexpr u128& u128::operator=(const std::uint16_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } -constexpr u128& u128::operator=(const std::uint32_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } -constexpr u128& u128::operator=(const std::uint64_t value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } +template , bool>> +constexpr u128& u128::operator=(const UnsignedInteger value) noexcept { low = static_cast(value); high = UINT64_C(0); return *this; } #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& u128::operator=(const unsigned __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(value >> 64U); return *this; } @@ -1511,49 +1469,15 @@ constexpr u128 operator+(const unsigned __int128 lhs, const u128 rhs) noexcept // Compound Addition Operator //===================================== -constexpr u128& u128::operator+=(const std::uint8_t rhs) noexcept -{ - *this = *this + rhs; - return *this; -} - -constexpr u128& u128::operator+=(const std::uint16_t rhs) noexcept -{ - *this = *this + rhs; - return *this; -} - -constexpr u128& u128::operator+=(const std::uint32_t rhs) noexcept -{ - *this = *this + rhs; - return *this; -} - -constexpr u128& u128::operator+=(const std::uint64_t rhs) noexcept -{ - *this = *this + rhs; - return *this; -} - -constexpr u128& u128::operator+=(const std::int8_t rhs) noexcept -{ - *this = *this + rhs; - return *this; -} - -constexpr u128& u128::operator+=(const std::int16_t rhs) noexcept -{ - *this = *this + rhs; - return *this; -} - -constexpr u128& u128::operator+=(const std::int32_t rhs) noexcept +template , bool>> +constexpr u128& u128::operator+=(const UnsignedInteger rhs) noexcept { *this = *this + rhs; return *this; } -constexpr u128& u128::operator+=(const std::int64_t rhs) noexcept +template , bool>> +constexpr u128& u128::operator+=(const SignedInteger rhs) noexcept { *this = *this + rhs; return *this; @@ -1721,49 +1645,15 @@ constexpr u128 operator-(const unsigned __int128 lhs, const u128 rhs) noexcept // Compound Subtraction Operator //===================================== -constexpr u128& u128::operator-=(const std::uint8_t rhs) noexcept -{ - *this = *this - rhs; - return *this; -} - -constexpr u128& u128::operator-=(const std::uint16_t rhs) noexcept -{ - *this = *this - rhs; - return *this; -} - -constexpr u128& u128::operator-=(const std::uint32_t rhs) noexcept -{ - *this = *this - rhs; - return *this; -} - -constexpr u128& u128::operator-=(const std::uint64_t rhs) noexcept -{ - *this = *this - rhs; - return *this; -} - -constexpr u128& u128::operator-=(const std::int8_t rhs) noexcept -{ - *this = *this - rhs; - return *this; -} - -constexpr u128& u128::operator-=(const std::int16_t rhs) noexcept -{ - *this = *this - rhs; - return *this; -} - -constexpr u128& u128::operator-=(const std::int32_t rhs) noexcept +template , bool>> +constexpr u128& u128::operator-=(const SignedInteger rhs) noexcept { *this = *this - rhs; return *this; } -constexpr u128& u128::operator-=(const std::int64_t rhs) noexcept +template , bool>> +constexpr u128& u128::operator-=(const UnsignedInteger rhs) noexcept { *this = *this - rhs; return *this; From c3d22195963bb4d67b7dd6b5c2bc1e5bd511fc7b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 10:56:14 -0500 Subject: [PATCH 054/191] Improve correctness with new traits --- include/boost/decimal/detail/u128.hpp | 144 +++++++++++++++----------- 1 file changed, 86 insertions(+), 58 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 9881e9a4..59a74b0e 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -160,6 +160,8 @@ u128 template , bool> = true> constexpr u128& operator+=(UnsignedInteger rhs) noexcept; + constexpr u128& operator+=(u128 rhs) noexcept; + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& operator+=(__int128 rhs) noexcept; constexpr u128& operator+=(unsigned __int128 rhs) noexcept; @@ -172,6 +174,8 @@ u128 template , bool> = true> constexpr u128& operator-=(UnsignedInteger rhs) noexcept; + constexpr u128& operator-=(u128 rhs) noexcept; + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& operator-=(__int128 rhs) noexcept; constexpr u128& operator-=(unsigned __int128 rhs) noexcept; @@ -279,25 +283,25 @@ constexpr bool operator==(const bool lhs, const u128 rhs) noexcept return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator==(const u128 lhs, const SignedInteger rhs) noexcept { return rhs >= 0 && lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator==(const SignedInteger lhs, const u128 rhs) noexcept { return lhs >= 0 && rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator==(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low == static_cast(rhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator==(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); @@ -346,25 +350,25 @@ constexpr bool operator!=(const bool lhs, const u128 rhs) noexcept return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator!=(const u128 lhs, const SignedInteger rhs) noexcept { return rhs < 0 || lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator!=(const SignedInteger lhs, const u128 rhs) noexcept { return lhs < 0 || rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator!=(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high != UINT64_C(0) || lhs.low != static_cast(rhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator!=(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high != UINT64_C(0) || rhs.low != static_cast(lhs); @@ -403,25 +407,25 @@ constexpr bool operator!=(const unsigned __int128 lhs, const u128 rhs) noexcept // Less than Operators //===================================== -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator<(const u128 lhs, const SignedInteger rhs) noexcept { return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator<(const SignedInteger lhs, const u128 rhs) noexcept { return lhs < 0 || rhs.high > UINT64_C(0) || static_cast(lhs) < rhs.low; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator<(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low < static_cast(rhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator<(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high > UINT64_C(0) || static_cast(lhs) < rhs.low; @@ -460,25 +464,25 @@ constexpr bool operator<(const unsigned __int128 lhs, const u128 rhs) noexcept // Less-equal Operators //===================================== -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator<=(const u128 lhs, const SignedInteger rhs) noexcept { return rhs > 0 && lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator<=(const SignedInteger lhs, const u128 rhs) noexcept { return lhs < 0 || rhs.high > UINT64_C(0) || static_cast(lhs) <= rhs.low; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator<=(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high == UINT64_C(0) && lhs.low <= static_cast(rhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator<=(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high > UINT64_C(0) || static_cast(lhs) <= rhs.low; @@ -517,25 +521,25 @@ constexpr bool operator<=(const unsigned __int128 lhs, const u128 rhs) noexcept // Greater Than Operators //===================================== -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator>(const u128 lhs, const SignedInteger rhs) noexcept { return rhs < 0 || lhs.high > UINT64_C(0) || lhs.low > static_cast(rhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator>(const SignedInteger lhs, const u128 rhs) noexcept { return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) > rhs.low; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator>(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high > UINT64_C(0) || lhs.low > static_cast(rhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator>(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high == UINT64_C(0) && static_cast(lhs) > rhs.low; @@ -574,25 +578,25 @@ constexpr bool operator>(const unsigned __int128 lhs, const u128 rhs) noexcept // Greater-equal Operators //===================================== -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator>=(const u128 lhs, const SignedInteger rhs) noexcept { return rhs < 0 || lhs.high > UINT64_C(0) || lhs.low >= static_cast(rhs); } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr bool operator>=(const SignedInteger lhs, const u128 rhs) noexcept { return lhs > 0 && rhs.high == UINT64_C(0) && static_cast(lhs) >= rhs.low; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator>=(const u128 lhs, const UnsignedInteger rhs) noexcept { return lhs.high > UINT64_C(0) || lhs.low >= static_cast(rhs); } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr bool operator>=(const UnsignedInteger lhs, const u128 rhs) noexcept { return rhs.high == UINT64_C(0) && static_cast(lhs) >= rhs.low; @@ -640,25 +644,25 @@ constexpr u128 operator~(const u128 rhs) noexcept // Or Operator //===================================== -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr u128 operator|(const u128 lhs, const SignedInteger rhs) noexcept { return {lhs.high | (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low | static_cast(rhs)}; } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr u128 operator|(const SignedInteger lhs, const u128 rhs) noexcept { return {rhs.high | (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low | static_cast(lhs)}; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr u128 operator|(const u128 lhs, const UnsignedInteger rhs) noexcept { return {lhs.high, lhs.low | static_cast(rhs)}; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr u128 operator|(const UnsignedInteger lhs, const u128 rhs) noexcept { return {rhs.high, rhs.low | static_cast(lhs)}; @@ -697,25 +701,25 @@ constexpr u128 operator|(const unsigned __int128 lhs, const u128 rhs) noexcept // And Operator //===================================== -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr u128 operator&(const u128 lhs, const SignedInteger rhs) noexcept { return {lhs.high & (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low & static_cast(rhs)}; } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr u128 operator&(const SignedInteger lhs, const u128 rhs) noexcept { return {rhs.high & (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low & static_cast(lhs)}; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr u128 operator&(const u128 lhs, const UnsignedInteger rhs) noexcept { return {lhs.high, lhs.low & static_cast(rhs)}; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr u128 operator&(const UnsignedInteger lhs, const u128 rhs) noexcept { return {rhs.high, rhs.low & static_cast(lhs)}; @@ -754,25 +758,25 @@ constexpr u128 operator&(const unsigned __int128 lhs, const u128 rhs) noexcept // Xor Operator //===================================== -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr u128 operator^(const u128 lhs, const SignedInteger rhs) noexcept { return {lhs.high ^ (rhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), lhs.low ^ static_cast(rhs)}; } -template ::value && std::is_signed::value, bool> = true> +template , bool> = true> constexpr u128 operator^(const SignedInteger lhs, const u128 rhs) noexcept { return {rhs.high ^ (lhs < 0 ? ~UINT64_C(0) : UINT64_C(0)), rhs.low ^ static_cast(lhs)}; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr u128 operator^(const u128 lhs, const UnsignedInteger rhs) noexcept { return {lhs.high, lhs.low ^ static_cast(rhs)}; } -template ::value && std::is_unsigned::value, bool> = true> +template , bool> = true> constexpr u128 operator^(const UnsignedInteger lhs, const u128 rhs) noexcept { return {rhs.high, rhs.low ^ static_cast(lhs)}; @@ -853,10 +857,10 @@ constexpr Integer operator<<(const Integer lhs, const u128 rhs) noexcept return lhs << rhs.low; } -template ::value && (sizeof(Integer) * 8 <= 16) && std::is_signed::value, bool> = true> -constexpr int operator<<(const Integer lhs, const u128 rhs) noexcept +template && (sizeof(SignedInteger) * 8 <= 16), bool> = true> +constexpr int operator<<(const SignedInteger lhs, const u128 rhs) noexcept { - constexpr auto bit_width = sizeof(Integer) * 8; + constexpr auto bit_width = sizeof(SignedInteger) * 8; if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) { @@ -866,10 +870,10 @@ constexpr int operator<<(const Integer lhs, const u128 rhs) noexcept return static_cast(lhs) << rhs.low; } -template ::value && (sizeof(Integer) * 8 <= 16) && std::is_unsigned::value, bool> = true> -constexpr unsigned operator<<(const Integer lhs, const u128 rhs) noexcept +template && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true> +constexpr unsigned operator<<(const UnsignedInteger lhs, const u128 rhs) noexcept { - constexpr auto bit_width = sizeof(Integer) * 8; + constexpr auto bit_width = sizeof(UnsignedInteger) * 8; if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) { @@ -977,10 +981,10 @@ constexpr Integer operator>>(const Integer lhs, const u128 rhs) noexcept return lhs >> rhs.low; } -template ::value && (sizeof(Integer) * 8 <= 16) && std::is_signed::value, bool> = true> -constexpr int operator>>(const Integer lhs, const u128 rhs) noexcept +template && (sizeof(SignedInteger) * 8 <= 16), bool> = true> +constexpr int operator>>(const SignedInteger lhs, const u128 rhs) noexcept { - constexpr auto bit_width = sizeof(Integer) * 8; + constexpr auto bit_width = sizeof(SignedInteger) * 8; if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) { @@ -990,10 +994,10 @@ constexpr int operator>>(const Integer lhs, const u128 rhs) noexcept return static_cast(lhs) >> rhs.low; } -template ::value && (sizeof(Integer) * 8 <= 16) && std::is_unsigned::value, bool> = true> -constexpr unsigned operator>>(const Integer lhs, const u128 rhs) noexcept +template && (sizeof(UnsignedInteger) * 8 <= 16), bool> = true> +constexpr unsigned operator>>(UnsignedInteger lhs, const u128 rhs) noexcept { - constexpr auto bit_width = sizeof(Integer) * 8; + constexpr auto bit_width = sizeof(UnsignedInteger) * 8; if (rhs.high > UINT64_C(0) || rhs.low >= bit_width) { @@ -1236,7 +1240,7 @@ BOOST_DECIMAL_FORCE_INLINE u128 adx_sub(const u128 lhs, const std::uint64_t rhs) } // namespace impl -template ::value || std::is_same::value, bool> = true> +template || std::is_same::value, bool> = true> constexpr u128 operator+(const u128 lhs, const UnsignedInteger rhs) noexcept { #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1270,7 +1274,7 @@ constexpr u128 operator+(const u128 lhs, const UnsignedInteger rhs) noexcept } // Since unsigned addition is trivially commutative we just reverse the order of the operands into the impl functions -template ::value, bool> = true> +template , bool> = true> constexpr u128 operator+(const UnsignedInteger lhs, const u128 rhs) noexcept { #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1303,7 +1307,7 @@ constexpr u128 operator+(const UnsignedInteger lhs, const u128 rhs) noexcept #endif } -template ::value, bool> = true> +template , bool> = true> constexpr u128 operator+(const u128 lhs, const SignedInteger rhs) noexcept { if (rhs > 0) @@ -1373,7 +1377,7 @@ constexpr u128 operator+(const u128 lhs, const SignedInteger rhs) noexcept // -a + b == b - a // a + b == b + a -template ::value, bool> = true> +template , bool> = true> constexpr u128 operator+(const SignedInteger lhs, const u128 rhs) noexcept { if (lhs > 0) @@ -1483,6 +1487,12 @@ constexpr u128& u128::operator+=(const SignedInteger rhs) noexcept return *this; } +constexpr u128& u128::operator+=(const u128 rhs) noexcept +{ + *this = *this + rhs; + return *this; +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& u128::operator+=(const __int128 rhs) noexcept @@ -1503,7 +1513,7 @@ constexpr u128& u128::operator+=(const unsigned __int128 rhs) noexcept // Subtraction Operator //===================================== -template ::value || std::is_same::value, bool> = true> +template || std::is_same::value, bool> = true> constexpr u128 operator-(const u128 lhs, const UnsignedInteger rhs) noexcept { #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1536,14 +1546,14 @@ constexpr u128 operator-(const u128 lhs, const UnsignedInteger rhs) noexcept #endif } -template ::value, bool> = true> +template , bool> = true> constexpr u128 operator-(const UnsignedInteger lhs, const u128 rhs) noexcept { // The only real viable way to do this is to promote lhs and perform 128-bit sub return u128{UINT64_C(0), lhs} - rhs; } -template ::value, bool> = true> +template , bool> = true> constexpr u128 operator-(const u128 lhs, const SignedInteger rhs) noexcept { if (rhs < 0) @@ -1611,7 +1621,7 @@ constexpr u128 operator-(const u128 lhs, const SignedInteger rhs) noexcept } } -template ::value, bool> = true> +template , bool> = true> constexpr u128 operator-(const SignedInteger lhs, const u128 rhs) noexcept { return static_cast(lhs) - rhs; @@ -1659,6 +1669,12 @@ constexpr u128& u128::operator-=(const UnsignedInteger rhs) noexcept return *this; } +constexpr u128& u128::operator-=(const u128 rhs) noexcept +{ + *this = *this - rhs; + return *this; +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128& u128::operator-=(const __int128 rhs) noexcept @@ -1863,7 +1879,7 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) } // namespace impl -template ::value || std::is_same::value, bool> = true> +template || std::is_same::value, bool> = true> constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept { #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1896,7 +1912,7 @@ constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept #endif } -template ::value, bool> = true> +template , bool> = true> constexpr u128 operator*(const UnsignedInteger lhs, const u128 rhs) noexcept { #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1929,6 +1945,18 @@ constexpr u128 operator*(const UnsignedInteger lhs, const u128 rhs) noexcept #endif } +template , bool> = true> +constexpr u128 operator*(const u128 lhs, const SignedInteger rhs) noexcept +{ + return lhs * static_cast(rhs); +} + +template , bool> = true> +constexpr u128 operator*(const SignedInteger lhs, const u128 rhs) noexcept +{ + return rhs * static_cast(lhs); +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator*(const u128 lhs, const __int128 rhs) noexcept From 7e02ea348acd63d21d03352013cfd9418840780d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 10:59:55 -0500 Subject: [PATCH 055/191] Add traits testing --- test/test_u128.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 5568e939..e38505f9 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -44,6 +44,25 @@ static std::mt19937_64 rng(42); constexpr std::size_t N = 1024; +void test_traits() +{ + using namespace boost::decimal::detail::impl; + + static_assert(is_signed_integer_v && !is_unsigned_integer_v); + static_assert(is_signed_integer_v && !is_unsigned_integer_v); + static_assert(is_signed_integer_v && !is_unsigned_integer_v); + static_assert(is_signed_integer_v && !is_unsigned_integer_v); + + static_assert(!is_signed_integer_v && is_unsigned_integer_v); + static_assert(!is_signed_integer_v && is_unsigned_integer_v); + static_assert(!is_signed_integer_v && is_unsigned_integer_v); + static_assert(!is_signed_integer_v && is_unsigned_integer_v); + + static_assert(!is_signed_integer_v && !is_unsigned_integer_v); + static_assert(!is_signed_integer_v && !is_unsigned_integer_v); + static_assert(!is_signed_integer_v && !is_unsigned_integer_v); +} + template void test_arithmetic_constructor() { @@ -557,6 +576,8 @@ void test_operator_mul() int main() { + test_traits(); + test_arithmetic_constructor(); test_arithmetic_constructor(); test_arithmetic_constructor(); @@ -768,10 +789,10 @@ int main() //test_operator_mul(); test_operator_mul<__int128>(); - //test_operator_mul(); - //test_operator_mul(); - //test_operator_mul(); - //test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); test_operator_mul(); return boost::report_errors(); From 93be0b5750e649ff36c2848a6e0caa443644e2c2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 11:40:26 -0500 Subject: [PATCH 056/191] Add optimized signed mul --- include/boost/decimal/detail/u128.hpp | 180 +++++++++++++++++++++++++- test/test_u128.cpp | 34 ++--- 2 files changed, 195 insertions(+), 19 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 59a74b0e..a028b9f9 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1725,6 +1725,27 @@ constexpr u128 default_mul(const u128 lhs, const std::uint64_t rhs) noexcept return result; } +constexpr u128 default_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept +{ + const bool isneg {rhs < 0}; + const auto abs_rhs {detail::make_positive_unsigned(rhs)}; + + auto res {default_mul(lhs, abs_rhs)}; + + if (isneg) + { + res.high = ~res.high; + res.low = ~res.low; + + ++res.low; + if (res.low == UINT64_C(0)) + { + ++res.high; + } + } + + return res; +} #if defined(__aarch64__) || defined(_M_ARM64) @@ -1784,6 +1805,41 @@ BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_mul(const u128 lhs, const std::uint64_t return result; } +BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept +{ + u128 result; + + __asm__ volatile( + // Compute abs(rhs) and save sign bit + "mov x9, %[rhs]\n" // x9 = rhs + "asr x10, x9, #63\n" // x10 = sign extension (0 if rhs >= 0, -1 if rhs < 0) + "eor x9, x9, x10\n" // Flip bits if negative + "sub x9, x9, x10\n" // Add 1 if negative (complete two's complement) + + // Perform unsigned multiplication + "mul %[result_low], %[lhs_low], x9\n" // result.low = lhs.low * abs(rhs) + "umulh %[result_high], %[lhs_low], x9\n" // result.high = high part of lhs.low * abs(rhs) + "mul x8, %[lhs_high], x9\n" // x8 = lhs.high * abs(rhs) + "adds %[result_high], %[result_high], x8\n" // result.high += x8 + + // Conditionally negate the result based on sign bit + "eor %[result_low], %[result_low], x10\n" // Flip bits if rhs < 0 + "eor %[result_high], %[result_high], x10\n" // Flip bits if rhs < 0 + + // Extract just 1 or 0 from the sign mask for addition + "and x8, x10, #1\n" // x8 = 1 if rhs < 0, 0 otherwise + "adds %[result_low], %[result_low], x8\n" // Add 1 if rhs < 0 + "adc %[result_high], %[result_high], xzr\n" // Add carry + + : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) + : [lhs_low] "r" (lhs.low), [lhs_high] "r" (lhs.high), + [rhs] "r" (rhs) + : "x8", "x9", "x10", "cc" + ); + + return result; +} + #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept @@ -1844,6 +1900,48 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) return result; } +BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept +{ + u128 result; + + __asm__ volatile( + // Prepare abs(rhs) and save sign + "movq %[rhs], %%r10\n" // r10 = rhs + "movq %%r10, %%r11\n" // r11 = rhs (save original) + "sarq $63, %%r11\n" // r11 = 0 if rhs >= 0, r11 = -1 if rhs < 0 + "xorq %%r11, %%r10\n" // Conditionally invert bits + "subq %%r11, %%r10\n" // Add 1 if negative (two's complement) + + // Multiply lhs.low * abs(rhs) + "movq %[lhs_low], %%rax\n" + "mulq %%r10\n" // rdx:rax = lhs.low * abs(rhs) + "movq %%rax, %%r8\n" // r8 = low result + "movq %%rdx, %%r9\n" // r9 = high part + + // Multiply lhs.high * abs(rhs) + "movq %[lhs_high], %%rax\n" + "mulq %%r10\n" // rdx:rax = lhs.high * abs(rhs) + "addq %%rax, %%r9\n" // r9 += rax + + // Conditionally negate the result + "xorq %%r11, %%r8\n" // Conditionally invert bits of low part + "xorq %%r11, %%r9\n" // Conditionally invert bits of high part + "subq %%r11, %%r8\n" // Add 1 if needed (low part) + "sbbq %%r11, %%r9\n" // Subtract borrow (high part) + + // Store the result + "movq %%r8, %[result_low]\n" + "movq %%r9, %[result_high]\n" + + : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) + : [lhs_low] "rm" (lhs.low), [lhs_high] "rm" (lhs.high), + [rhs] "rm" (rhs) + : "rax", "rdx", "r8", "r9", "r10", "r11", "cc" + ); + + return result; +} + #elif defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept @@ -1875,6 +1973,30 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) return result; } +BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept +{ + const bool is_negative = rhs < 0; + + const std::uint64_t abs_rhs = is_negative ? static_cast(-rhs) : static_cast(rhs); + + u128 result; + result.low = _umul128(lhs.low, abs_rhs, &result.high); + BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * abs_rhs, &result.high); + + if (is_negative) + { + // Two's complement negation + result.high = ~result.high; + result.low = ~result.low; + + // Add 1 with carry handling + unsigned char carry = BOOST_DECIMAL_ADD_CARRY(0, result.low, 1ULL, &result.low); + BOOST_DECIMAL_ADD_CARRY(carry, result.high, 0ULL, &result.high); + } + + return result; +} + #endif } // namespace impl @@ -1948,13 +2070,67 @@ constexpr u128 operator*(const UnsignedInteger lhs, const u128 rhs) noexcept template , bool> = true> constexpr u128 operator*(const u128 lhs, const SignedInteger rhs) noexcept { - return lhs * static_cast(rhs); + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_signed_mul(lhs, static_cast(rhs)); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_signed_mul(lhs, static_cast(rhs)); + + #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) + + return impl::x64_signed_mul(lhs, static_cast(rhs)); + + #else + + return impl::default_signed_mul(lhs, static_cast(rhs)); + + #endif + } + + #else + + return impl::default_signed_mul(lhs, static_cast(rhs)); + + #endif } template , bool> = true> constexpr u128 operator*(const SignedInteger lhs, const u128 rhs) noexcept { - return rhs * static_cast(lhs); + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_signed_mul(rhs, static_cast(lhs)); + } + else + { + #if defined(__aarch64__) || defined(_M_ARM64) + + return impl::arm_asm_signed_mul(rhs, static_cast(lhs)); + + #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) + + return impl::x64_signed_mul(rhs, static_cast(lhs)); + + #else + + return impl::default_signed_mul(rhs, static_cast(lhs)); + + #endif + } + + #else + + return impl::default_signed_mul(rhs, static_cast(lhs)); + + #endif } #ifdef BOOST_DECIMAL_HAS_INT128 diff --git a/test/test_u128.cpp b/test/test_u128.cpp index e38505f9..691a0118 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -48,19 +48,19 @@ void test_traits() { using namespace boost::decimal::detail::impl; - static_assert(is_signed_integer_v && !is_unsigned_integer_v); - static_assert(is_signed_integer_v && !is_unsigned_integer_v); - static_assert(is_signed_integer_v && !is_unsigned_integer_v); - static_assert(is_signed_integer_v && !is_unsigned_integer_v); - - static_assert(!is_signed_integer_v && is_unsigned_integer_v); - static_assert(!is_signed_integer_v && is_unsigned_integer_v); - static_assert(!is_signed_integer_v && is_unsigned_integer_v); - static_assert(!is_signed_integer_v && is_unsigned_integer_v); - - static_assert(!is_signed_integer_v && !is_unsigned_integer_v); - static_assert(!is_signed_integer_v && !is_unsigned_integer_v); - static_assert(!is_signed_integer_v && !is_unsigned_integer_v); + static_assert(is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); + static_assert(is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); + static_assert(is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); + static_assert(is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); + + static_assert(!is_signed_integer_v && is_unsigned_integer_v, "Wrong answer"); + static_assert(!is_signed_integer_v && is_unsigned_integer_v, "Wrong answer"); + static_assert(!is_signed_integer_v && is_unsigned_integer_v, "Wrong answer"); + static_assert(!is_signed_integer_v && is_unsigned_integer_v, "Wrong answer"); + + static_assert(!is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); + static_assert(!is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); + static_assert(!is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); } template @@ -783,10 +783,10 @@ int main() test_operator_sub(); test_operator_sub(); - //test_operator_mul(); - //test_operator_mul(); - //test_operator_mul(); - //test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); test_operator_mul<__int128>(); test_operator_mul(); From b932e79b269db31c66ed19bcafa2b4b8a2d09e9f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 12:17:37 -0500 Subject: [PATCH 057/191] Add benchmarks of u128 types --- include/boost/decimal/detail/u128.hpp | 6 +- test/Jamfile | 1 + test/benchmark_uints.cpp | 175 ++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 test/benchmark_uints.cpp diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index a028b9f9..756fe662 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1817,9 +1817,9 @@ BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_signed_mul(const u128 lhs, const std::in "sub x9, x9, x10\n" // Add 1 if negative (complete two's complement) // Perform unsigned multiplication - "mul %[result_low], %[lhs_low], x9\n" // result.low = lhs.low * abs(rhs) - "umulh %[result_high], %[lhs_low], x9\n" // result.high = high part of lhs.low * abs(rhs) - "mul x8, %[lhs_high], x9\n" // x8 = lhs.high * abs(rhs) + "mul %[result_low], %[lhs_low], x9\n" // result.low = lhs.low * abs(rhs) + "umulh %[result_high], %[lhs_low], x9\n" // result.high = high part of lhs.low * abs(rhs) + "mul x8, %[lhs_high], x9\n" // x8 = lhs.high * abs(rhs) "adds %[result_high], %[result_high], x8\n" // result.high += x8 // Conditionally negate the result based on sign bit diff --git a/test/Jamfile b/test/Jamfile index 07008b82..ec5a50f9 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -42,6 +42,7 @@ project : requirements [ requires cxx14_decltype_auto cxx14_generic_lambdas cxx14_return_type_deduction cxx14_variable_templates cxx14_constexpr ] ; +run-fail benchmark_uints.cpp ; run-fail benchmarks.cpp ; run compare_dec128_and_fast.cpp ; compile-fail concepts_test.cpp ; diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp new file mode 100644 index 00000000..9110ee70 --- /dev/null +++ b/test/benchmark_uints.cpp @@ -0,0 +1,175 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define BOOST_DECIMAL_BENCHMARK_U128 + +#include + +#ifdef BOOST_DECIMAL_BENCHMARK_U128 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +constexpr unsigned N = 20'000'000; +constexpr unsigned K = 5; + +using namespace boost::decimal; +using namespace std::chrono_literals; + +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +# pragma clang diagnostic ignored "-Wold-style-cast" +# define BOOST_DECIMAL_NO_INLINE __attribute__ ((__noinline__)) +#elif defined(_MSC_VER) +# define BOOST_DECIMAL_NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Wold-style-cast" +# pragma GCC diagnostic ignored "-Wstringop-overread" +# define BOOST_DECIMAL_NO_INLINE __attribute__ ((__noinline__)) +#endif + +template +std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) +{ + if (seed == 0) + { + std::random_device rd; + seed = rd(); + } + + std::mt19937_64 gen(seed); + std::uniform_int_distribution dist(UINT64_C(0), UINT64_MAX); + + std::vector result(size); + for (std::size_t i = 0; i < size; ++i) + { + BOOST_DECIMAL_IF_CONSTEXPR (mul) + { + result[i] = T{dist(gen)}; + } + else + { + BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value) + { + result[i] = T{boost::decimal::detail::u128{dist(gen), dist(gen)}}; + } + else + { + result[i] = T{dist(gen), dist(gen)}; + } + } + } + + return result; +} + +template +BOOST_DECIMAL_NO_INLINE void test_comparisons(const std::vector& data_vec, const char* label) +{ + const auto t1 = std::chrono::steady_clock::now(); + std::size_t s = 0; // discard variable + + for (std::size_t k {}; k < K; ++k) + { + for (std::size_t i {}; i < data_vec.size() - 1U; ++i) + { + const auto val1 = data_vec[i]; + const auto val2 = data_vec[i + 1]; + s += static_cast(val1 > val2); + s += static_cast(val1 >= val2); + s += static_cast(val1 < val2); + s += static_cast(val1 <= val2); + s += static_cast(val1 == val2); + s += static_cast(val1 != val2); + } + } + + const auto t2 = std::chrono::steady_clock::now(); + + std::cout << "comparisons<" << std::left << std::setw(11) << label << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; +} + +template +BOOST_DECIMAL_NO_INLINE void test_two_element_operation(const std::vector& data_vec, Func op, const char* operation, const char* type) +{ + const auto t1 = std::chrono::steady_clock::now(); + std::size_t s = 0; // discard variable + + for (std::size_t k {}; k < K; ++k) + { + for (std::size_t i {}; i < data_vec.size() - 1U; ++i) + { + const auto val1 = data_vec[i]; + const auto val2 = data_vec[i + 1]; + s += static_cast(op(val1, val2)); + } + } + + const auto t2 = std::chrono::steady_clock::now(); + + std::cout << operation << "<" << std::left << std::setw(11) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; +} + + +int main() +{ + using namespace boost::decimal::detail; + + // Comp, add, sub + { + const auto old_vector = generate_random_vector(); + const auto new_vector = generate_random_vector(); + const auto builtin_vector = generate_random_vector(); + + test_comparisons(builtin_vector, "builtin"); + test_comparisons(old_vector, "old"); + test_comparisons(new_vector, "new"); + + test_two_element_operation(builtin_vector, std::plus<>(), "Addition", "Builtin"); + test_two_element_operation(old_vector, std::plus<>(), "Addition", "Old"); + test_two_element_operation(new_vector, std::plus<>(), "Addition", "New"); + + test_two_element_operation(builtin_vector, std::minus<>(), "Subtraction", "Builtin"); + test_two_element_operation(old_vector, std::minus<>(), "Subtraction", "Old"); + test_two_element_operation(new_vector, std::minus<>(), "Subtraction", "New"); + + test_two_element_operation(builtin_vector, std::multiplies<>(), "Two word mul", "Builtin"); + test_two_element_operation(old_vector, std::multiplies<>(), "Two word mul", "Old"); + test_two_element_operation(new_vector, std::multiplies<>(), "Two word mul", "New"); + + } + // Mul + { + const auto old_vector = generate_random_vector(); + const auto new_vector = generate_random_vector(); + const auto builtin_vector = generate_random_vector(); + + test_two_element_operation(builtin_vector, std::multiplies<>(), "One word mul", "Builtin"); + test_two_element_operation(old_vector, std::multiplies<>(), "One word mul", "Old"); + test_two_element_operation(new_vector, std::multiplies<>(), "One word mul", "New"); + } + + return 1; +} + +#else + +int main() +{ + std::cerr << "Benchmarks not run" << std::endl; + return 1; +} + +#endif // BOOST_DECIMAL_BENCHMARK_U128 From b412e632d13b3be9f44b637a5a9e159756e0194d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 13:07:05 -0500 Subject: [PATCH 058/191] Add single and double word ops --- test/benchmark_uints.cpp | 47 +++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 9110ee70..2df68fff 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -40,7 +40,7 @@ using namespace std::chrono_literals; # define BOOST_DECIMAL_NO_INLINE __attribute__ ((__noinline__)) #endif -template +template std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) { if (seed == 0) @@ -55,7 +55,7 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) std::vector result(size); for (std::size_t i = 0; i < size; ++i) { - BOOST_DECIMAL_IF_CONSTEXPR (mul) + BOOST_DECIMAL_IF_CONSTEXPR (one_word) { result[i] = T{dist(gen)}; } @@ -127,7 +127,7 @@ int main() { using namespace boost::decimal::detail; - // Comp, add, sub + // Two word operations { const auto old_vector = generate_random_vector(); const auto new_vector = generate_random_vector(); @@ -137,28 +137,49 @@ int main() test_comparisons(old_vector, "old"); test_comparisons(new_vector, "new"); - test_two_element_operation(builtin_vector, std::plus<>(), "Addition", "Builtin"); - test_two_element_operation(old_vector, std::plus<>(), "Addition", "Old"); - test_two_element_operation(new_vector, std::plus<>(), "Addition", "New"); + std::cout << std::endl; - test_two_element_operation(builtin_vector, std::minus<>(), "Subtraction", "Builtin"); - test_two_element_operation(old_vector, std::minus<>(), "Subtraction", "Old"); - test_two_element_operation(new_vector, std::minus<>(), "Subtraction", "New"); + test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); + test_two_element_operation(old_vector, std::plus<>(), "add", "Old"); + test_two_element_operation(new_vector, std::plus<>(), "add", "New"); - test_two_element_operation(builtin_vector, std::multiplies<>(), "Two word mul", "Builtin"); - test_two_element_operation(old_vector, std::multiplies<>(), "Two word mul", "Old"); - test_two_element_operation(new_vector, std::multiplies<>(), "Two word mul", "New"); + std::cout << std::endl; + test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); + test_two_element_operation(old_vector, std::minus<>(), "sub", "Old"); + test_two_element_operation(new_vector, std::minus<>(), "sub", "New"); + + std::cout << std::endl; + + test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); + test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); + test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); } - // Mul + // Single word operations { const auto old_vector = generate_random_vector(); const auto new_vector = generate_random_vector(); const auto builtin_vector = generate_random_vector(); + std::cout << std::endl; + + test_two_element_operation(builtin_vector, std::plus<>(), "One word add", "Builtin"); + test_two_element_operation(old_vector, std::plus<>(), "One word add", "Old"); + test_two_element_operation(new_vector, std::plus<>(), "One word add", "New"); + + std::cout << std::endl; + + test_two_element_operation(builtin_vector, std::minus<>(), "One word sub", "Builtin"); + test_two_element_operation(old_vector, std::minus<>(), "One word sub", "Old"); + test_two_element_operation(new_vector, std::minus<>(), "One word sub", "New"); + + std::cout << std::endl; + test_two_element_operation(builtin_vector, std::multiplies<>(), "One word mul", "Builtin"); test_two_element_operation(old_vector, std::multiplies<>(), "One word mul", "Old"); test_two_element_operation(new_vector, std::multiplies<>(), "One word mul", "New"); + + std::cout << std::endl; } return 1; From a0e8dfff6bba60ffd89226c09357803d4c3aed80 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 13:40:24 -0500 Subject: [PATCH 059/191] Add additional benchmarks --- test/benchmark_uints.cpp | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 2df68fff..cd79f104 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -129,6 +129,10 @@ int main() // Two word operations { + std::cout << "\n---------------------------\n"; + std::cout << "Two Word Operations\n"; + std::cout << "---------------------------\n\n"; + const auto old_vector = generate_random_vector(); const auto new_vector = generate_random_vector(); const auto builtin_vector = generate_random_vector(); @@ -157,27 +161,35 @@ int main() } // Single word operations { + std::cout << "\n---------------------------\n"; + std::cout << "One Word Operations\n"; + std::cout << "---------------------------\n\n"; + const auto old_vector = generate_random_vector(); const auto new_vector = generate_random_vector(); const auto builtin_vector = generate_random_vector(); + test_comparisons(builtin_vector, "builtin"); + test_comparisons(old_vector, "old"); + test_comparisons(new_vector, "new"); + std::cout << std::endl; - test_two_element_operation(builtin_vector, std::plus<>(), "One word add", "Builtin"); - test_two_element_operation(old_vector, std::plus<>(), "One word add", "Old"); - test_two_element_operation(new_vector, std::plus<>(), "One word add", "New"); + test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); + test_two_element_operation(old_vector, std::plus<>(), "add", "Old"); + test_two_element_operation(new_vector, std::plus<>(), "add", "New"); std::cout << std::endl; - test_two_element_operation(builtin_vector, std::minus<>(), "One word sub", "Builtin"); - test_two_element_operation(old_vector, std::minus<>(), "One word sub", "Old"); - test_two_element_operation(new_vector, std::minus<>(), "One word sub", "New"); + test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); + test_two_element_operation(old_vector, std::minus<>(), "sub", "Old"); + test_two_element_operation(new_vector, std::minus<>(), "sub", "New"); std::cout << std::endl; - test_two_element_operation(builtin_vector, std::multiplies<>(), "One word mul", "Builtin"); - test_two_element_operation(old_vector, std::multiplies<>(), "One word mul", "Old"); - test_two_element_operation(new_vector, std::multiplies<>(), "One word mul", "New"); + test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); + test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); + test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); std::cout << std::endl; } From 2cdcbf8c6fad5fc9cd19776b986fc6f162c05328 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 13:40:38 -0500 Subject: [PATCH 060/191] Remove complicated and slower impls --- include/boost/decimal/detail/u128.hpp | 366 -------------------------- 1 file changed, 366 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 756fe662..697d4214 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1115,196 +1115,19 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const std: return temp; } -#if defined(__aarch64__) || defined(_M_ARM64) - -// Inline ASM is not constexpr until C++20 so we offload into this function -// in the non-constant evaluated case. -BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_add(const u128 lhs, const u128 rhs) noexcept -{ - std::uint64_t result_low {}; - std::uint64_t result_high {}; - - // Use inline assembly to access the carry flag directly - // Roughly equivalent to the ADX instructions below for x64 platforms - __asm__ volatile( - "adds %0, %2, %3\n" // adds sets carry flag if overflow occurs - "adc %1, %4, %5\n" // adc adds with carry from previous operation - : "=r" (result_low), "=r" (result_high) - : "r" (lhs.low), "r" (rhs.low), "r" (lhs.high), "r" (rhs.high) - : "cc" // clobbering condition codes (flags) - ); - - return {result_high, result_low}; -} - -BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_add(const u128 lhs, const std::uint64_t rhs) noexcept -{ - std::uint64_t result_low {}; - std::uint64_t result_high {}; - - // Use inline assembly to access the carry flag directly - // Roughly equivalent to the ADX instructions below for x64 platforms - __asm__ volatile( - "adds %0, %2, %3\n" // adds sets carry flag if overflow occurs - "adc %1, %4, xzr\n" // add carry to high - : "=r" (result_low), "=r" (result_high) - : "r" (lhs.low), "r" (rhs), "r" (lhs.high) - : "cc" // clobbering condition codes (flags) - ); - - return {result_high, result_low}; -} - -BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_sub(const u128 lhs, const u128 rhs) noexcept -{ - std::uint64_t result_low {}; - std::uint64_t result_high {}; - - // Use inline assembly to access the carry flag directly - // Roughly equivalent to the ADX instructions below for x64 platforms - __asm__ volatile( - "subs %0, %2, %3\n" // adds sets carry flag if overflow occurs - "sbc %1, %4, %5\n" // adc adds with carry from previous operation - : "=r" (result_low), "=r" (result_high) - : "r" (lhs.low), "r" (rhs.low), "r" (lhs.high), "r" (rhs.high) - : "cc" // clobbering condition codes (flags) - ); - - return {result_high, result_low}; -} - -BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_sub(const u128 lhs, const std::uint64_t rhs) noexcept -{ - std::uint64_t result_low {}; - std::uint64_t result_high {}; - - // Use inline assembly to access the carry flag directly - // Roughly equivalent to the ADX instructions below for x64 platforms - __asm__ volatile( - "subs %0, %2, %3\n" // subs sets carry flag if underflow occurs - "sbc %1, %4, xzr\n" // subtract carry from high value - : "=r" (result_low), "=r" (result_high) - : "r" (lhs.low), "r" (rhs), "r" (lhs.high) - : "cc" // clobbering condition codes (flags) - ); - - return {result_high, result_low}; -} - -#endif // defined(__aarch64__) || defined(_M_ARM64) - -#ifdef BOOST_DECIMAL_ADD_CARRY - -BOOST_DECIMAL_FORCE_INLINE u128 adx_add(const u128 lhs, const u128 rhs) noexcept -{ - // Intel ADX instructions are specifically for Multi-Precision Arithmetic - unsigned long long int res_low {}; - unsigned long long int res_high {}; - - const unsigned char carry {BOOST_DECIMAL_ADD_CARRY(0, lhs.low, rhs.low, &res_low)}; - BOOST_DECIMAL_ADD_CARRY(carry, lhs.high, rhs.high, &res_high); - - return {res_high, res_low}; -} - -BOOST_DECIMAL_FORCE_INLINE u128 adx_add(const u128 lhs, const std::uint64_t rhs) noexcept -{ - // Intel ADX instructions are specifically for Multi-Precision Arithmetic - unsigned long long int res_low {}; - const unsigned char carry {BOOST_DECIMAL_ADD_CARRY(0, lhs.low, rhs, &res_low)}; - - // Unconditionally add the carry to lhs.high since we don't have multiple additions here - return {lhs.high + static_cast(carry), res_low}; -} - -BOOST_DECIMAL_FORCE_INLINE u128 adx_sub(const u128 lhs, const u128 rhs) noexcept -{ - unsigned long long int res_low {}; - unsigned long long int res_high {}; - - const unsigned char carry {BOOST_DECIMAL_SUB_BORROW(0, lhs.low, rhs.low, &res_low)}; - BOOST_DECIMAL_SUB_BORROW(carry, lhs.high, rhs.high, &res_high); - - return {res_high, res_low}; -} - -BOOST_DECIMAL_FORCE_INLINE u128 adx_sub(const u128 lhs, const std::uint64_t rhs) noexcept -{ - unsigned long long int res_low {}; - const unsigned char carry {BOOST_DECIMAL_SUB_BORROW(0, lhs.low, rhs, &res_low)}; - - return {lhs.high - static_cast(carry), res_low}; -} - -#endif // BOOST_DECIMAL_ADD_CARRY - } // namespace impl template || std::is_same::value, bool> = true> constexpr u128 operator+(const u128 lhs, const UnsignedInteger rhs) noexcept { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_add(lhs, rhs); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_add(lhs, rhs); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_add(lhs, rhs); - - #else - - return impl::default_add(lhs, rhs); - - #endif - } - - #else - return impl::default_add(lhs, rhs); - - #endif } // Since unsigned addition is trivially commutative we just reverse the order of the operands into the impl functions template , bool> = true> constexpr u128 operator+(const UnsignedInteger lhs, const u128 rhs) noexcept { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_add(rhs, lhs); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_add(rhs, lhs); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_add(rhs, lhs); - - #else - - return impl::default_add(rhs, lhs); - - #endif - } - - #else - return impl::default_add(rhs, lhs); - - #endif } template , bool> = true> @@ -1312,66 +1135,12 @@ constexpr u128 operator+(const u128 lhs, const SignedInteger rhs) noexcept { if (rhs > 0) { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_add(lhs, static_cast(rhs)); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_add(lhs, static_cast(rhs)); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_add(lhs, static_cast(rhs)); - - #else - - return impl::default_add(lhs, static_cast(rhs)); - - #endif - } - - #else - return impl::default_add(lhs, static_cast(rhs)); - - #endif } else { const auto unsigned_rhs {detail::make_positive_unsigned(rhs)}; - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_sub(lhs, unsigned_rhs); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_sub(lhs, unsigned_rhs); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_sub(lhs, unsigned_rhs); - - #else - - return impl::default_sub(lhs, unsigned_rhs); - - #endif - } - - #else - return impl::default_sub(lhs, unsigned_rhs); - - #endif } } @@ -1382,66 +1151,12 @@ constexpr u128 operator+(const SignedInteger lhs, const u128 rhs) noexcept { if (lhs > 0) { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_add(rhs, static_cast(lhs)); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_add(rhs, static_cast(lhs)); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_add(rhs, static_cast(lhs)); - - #else - - return impl::default_add(rhs, static_cast(lhs)); - - #endif - } - - #else - return impl::default_add(rhs, static_cast(lhs)); - - #endif } else { const auto unsigned_lhs {detail::make_positive_unsigned(lhs)}; - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_sub(rhs, unsigned_lhs); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_sub(rhs, unsigned_lhs); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_sub(rhs, unsigned_lhs); - - #else - - return impl::default_sub(rhs, unsigned_lhs); - - #endif - } - - #else - return impl::default_sub(rhs, unsigned_lhs); - - #endif } } @@ -1516,34 +1231,7 @@ constexpr u128& u128::operator+=(const unsigned __int128 rhs) noexcept template || std::is_same::value, bool> = true> constexpr u128 operator-(const u128 lhs, const UnsignedInteger rhs) noexcept { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_sub(lhs, rhs); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_sub(lhs, rhs); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_sub(lhs, rhs); - - #else - - return impl::default_sub(lhs, rhs); - - #endif - } - - #else - return impl::default_sub(lhs, rhs); - - #endif } template , bool> = true> @@ -1558,66 +1246,12 @@ constexpr u128 operator-(const u128 lhs, const SignedInteger rhs) noexcept { if (rhs < 0) { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_add(lhs, static_cast(rhs)); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_add(lhs, static_cast(rhs)); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_add(lhs, static_cast(rhs)); - - #else - - return impl::default_add(lhs, static_cast(rhs)); - - #endif - } - - #else - return impl::default_add(lhs, static_cast(rhs)); - - #endif } else { const auto unsigned_rhs {detail::make_positive_unsigned(rhs)}; - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_sub(lhs, unsigned_rhs); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_sub(lhs, unsigned_rhs); - - #elif defined(BOOST_DECIMAL_ADD_CARRY) - - return impl::adx_sub(lhs, unsigned_rhs); - - #else - - return impl::default_sub(lhs, unsigned_rhs); - - #endif - } - - #else - return impl::default_sub(lhs, unsigned_rhs); - - #endif } } From 57e52a0bd8685de5a4e170b5e681c7ffdeabeed1 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 14:12:05 -0500 Subject: [PATCH 061/191] Simplify mul --- include/boost/decimal/detail/u128.hpp | 299 +------------------------- 1 file changed, 10 insertions(+), 289 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 697d4214..fbc7675c 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1381,202 +1381,8 @@ constexpr u128 default_signed_mul(const u128 lhs, const std::int64_t rhs) noexce return res; } -#if defined(__aarch64__) || defined(_M_ARM64) - -BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_mul(const u128 lhs, const u128 rhs) noexcept -{ - u128 result; - - // Does not compensate or track overflow of the high word - __asm__ volatile( - // Multiply lhs.low * rhs.low - "mul %[result_low], %[lhs_low], %[rhs_low]\n" // result.low = lhs.low * rhs.low (low 64 bits) - "umulh %[result_high], %[lhs_low], %[rhs_low]\n" // result.high = lhs.low * rhs.low (high 64 bits) - - // Multiply lhs.high * rhs.low and add to result.high - "mul x8, %[lhs_high], %[rhs_low]\n" // x8 = lhs.high * rhs.low - "adds %[result_high], %[result_high], x8\n" // result.high += x8 (with carry) - - // Multiply lhs.low * rhs.high and add to result.high - "mul x8, %[lhs_low], %[rhs_high]\n" // x8 = lhs.low * rhs.high - "adds %[result_high], %[result_high], x8\n" // result.high += x8 (with carry) - - // Output operands - : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) - // Input operands - : [lhs_low] "r" (lhs.low), [lhs_high] "r" (lhs.high), - [rhs_low] "r" (rhs.low), [rhs_high] "r" (rhs.high) - // Clobbered registers - : "x8", "cc" - ); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_mul(const u128 lhs, const std::uint64_t rhs) noexcept -{ - u128 result; - - // Does not compensate or track overflow of the high word - __asm__ volatile( - // Multiply lhs.low * rhs - "mul %[result_low], %[lhs_low], %[rhs]\n" // result.low = lhs.low * rhs (low 64 bits) - "umulh %[result_high], %[lhs_low], %[rhs]\n" // result.high = lhs.low * rhs (high 64 bits) - - // Multiply lhs.high * rhs and add to result.high - "mul x8, %[lhs_high], %[rhs]\n" // x8 = lhs.high * rhs - "adds %[result_high], %[result_high], x8\n" // result.high += x8 (with carry) - - // Output operands - : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) - // Input operands - : [lhs_low] "r" (lhs.low), [lhs_high] "r" (lhs.high), - [rhs] "r" (rhs) - // Clobbered registers - : "x8", "cc" - ); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 arm_asm_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept -{ - u128 result; - - __asm__ volatile( - // Compute abs(rhs) and save sign bit - "mov x9, %[rhs]\n" // x9 = rhs - "asr x10, x9, #63\n" // x10 = sign extension (0 if rhs >= 0, -1 if rhs < 0) - "eor x9, x9, x10\n" // Flip bits if negative - "sub x9, x9, x10\n" // Add 1 if negative (complete two's complement) - - // Perform unsigned multiplication - "mul %[result_low], %[lhs_low], x9\n" // result.low = lhs.low * abs(rhs) - "umulh %[result_high], %[lhs_low], x9\n" // result.high = high part of lhs.low * abs(rhs) - "mul x8, %[lhs_high], x9\n" // x8 = lhs.high * abs(rhs) - "adds %[result_high], %[result_high], x8\n" // result.high += x8 - - // Conditionally negate the result based on sign bit - "eor %[result_low], %[result_low], x10\n" // Flip bits if rhs < 0 - "eor %[result_high], %[result_high], x10\n" // Flip bits if rhs < 0 - - // Extract just 1 or 0 from the sign mask for addition - "and x8, x10, #1\n" // x8 = 1 if rhs < 0, 0 otherwise - "adds %[result_low], %[result_low], x8\n" // Add 1 if rhs < 0 - "adc %[result_high], %[result_high], xzr\n" // Add carry - - : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) - : [lhs_low] "r" (lhs.low), [lhs_high] "r" (lhs.high), - [rhs] "r" (rhs) - : "x8", "x9", "x10", "cc" - ); - - return result; -} - -#elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) - -BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept -{ - u128 result; - - __asm__ volatile( - // Multiply lhs.low * rhs.low - "movq %[lhs_low], %%rax\n" - "mulq %[rhs_low]\n" // rdx:rax = lhs.low * rhs.low - "movq %%rax, %[result_low]\n" - "movq %%rdx, %%r8\n" // Save high part in r8 - - // Multiply lhs.high * rhs.low and add to r8 - "movq %[lhs_high], %%rax\n" - "mulq %[rhs_low]\n" // rdx:rax = lhs.high * rhs.low - "addq %%rax, %%r8\n" - "adcq $0, %%rdx\n" // Add with carry - - // Multiply lhs.low * rhs.high and add to r8:rdx - "movq %[lhs_low], %%rax\n" - "mulq %[rhs_high]\n" // rdx:rax = lhs.low * rhs.high - "addq %%rax, %%r8\n" - "movq %%r8, %[result_high]\n" - - : [result_low] "=m" (result.low), [result_high] "=m" (result.high) - : [lhs_low] "m" (lhs.low), [lhs_high] "m" (lhs.high), - [rhs_low] "m" (rhs.low), [rhs_high] "m" (rhs.high) - : "rax", "rdx", "r8", "cc" - ); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) noexcept -{ - u128 result; - - __asm__ volatile( - // Multiply lhs.low * rhs - "movq %[lhs_low], %%rax\n" - "mulq %[rhs]\n" // rdx:rax = lhs.low * rhs - "movq %%rax, %[result_low]\n" - "movq %%rdx, %%r8\n" // Save high part in r8 - - // Multiply lhs.high * rhs and add to r8 - "movq %[lhs_high], %%rax\n" - "mulq %[rhs]\n" // rdx:rax = lhs.high * rhs - "addq %%rax, %%r8\n" - "movq %%r8, %[result_high]\n" - - : [result_low] "=m" (result.low), [result_high] "=m" (result.high) - : [lhs_low] "m" (lhs.low), [lhs_high] "m" (lhs.high), - [rhs] "m" (rhs) - : "rax", "rdx", "r8", "cc" - ); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept -{ - u128 result; - - __asm__ volatile( - // Prepare abs(rhs) and save sign - "movq %[rhs], %%r10\n" // r10 = rhs - "movq %%r10, %%r11\n" // r11 = rhs (save original) - "sarq $63, %%r11\n" // r11 = 0 if rhs >= 0, r11 = -1 if rhs < 0 - "xorq %%r11, %%r10\n" // Conditionally invert bits - "subq %%r11, %%r10\n" // Add 1 if negative (two's complement) - - // Multiply lhs.low * abs(rhs) - "movq %[lhs_low], %%rax\n" - "mulq %%r10\n" // rdx:rax = lhs.low * abs(rhs) - "movq %%rax, %%r8\n" // r8 = low result - "movq %%rdx, %%r9\n" // r9 = high part - - // Multiply lhs.high * abs(rhs) - "movq %[lhs_high], %%rax\n" - "mulq %%r10\n" // rdx:rax = lhs.high * abs(rhs) - "addq %%rax, %%r9\n" // r9 += rax - - // Conditionally negate the result - "xorq %%r11, %%r8\n" // Conditionally invert bits of low part - "xorq %%r11, %%r9\n" // Conditionally invert bits of high part - "subq %%r11, %%r8\n" // Add 1 if needed (low part) - "sbbq %%r11, %%r9\n" // Subtract borrow (high part) - - // Store the result - "movq %%r8, %[result_low]\n" - "movq %%r9, %[result_high]\n" - - : [result_low] "=&r" (result.low), [result_high] "=&r" (result.high) - : [lhs_low] "rm" (lhs.low), [lhs_high] "rm" (lhs.high), - [rhs] "rm" (rhs) - : "rax", "rdx", "r8", "r9", "r10", "r11", "cc" - ); - - return result; -} - -#elif defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) +// TODO(mborland): Run benchmarks on MSVC to see if these are worth it or not +#if defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept { @@ -1638,6 +1444,12 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_ template || std::is_same::value, bool> = true> constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept { + #ifndef _MSVC_LANG + + return impl::default_mul(lhs, rhs); + + #else + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) @@ -1646,19 +1458,7 @@ constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept } else { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_mul(lhs, rhs); - - #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) - return impl::x64_mul(lhs, rhs); - - #else - - return impl::default_mul(lhs, rhs); - - #endif } #else @@ -1666,105 +1466,26 @@ constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept return impl::default_mul(lhs, rhs); #endif + + #endif // _MSVC_LANG } template , bool> = true> constexpr u128 operator*(const UnsignedInteger lhs, const u128 rhs) noexcept { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_mul(rhs, static_cast(lhs)); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_mul(rhs, static_cast(lhs)); - - #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) - - return impl::x64_mul(rhs, static_cast(lhs)); - - #else - - return impl::default_mul(rhs, static_cast(lhs)); - - #endif - } - - #else - return impl::default_mul(rhs, static_cast(lhs)); - - #endif } template , bool> = true> constexpr u128 operator*(const u128 lhs, const SignedInteger rhs) noexcept { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_signed_mul(lhs, static_cast(rhs)); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_signed_mul(lhs, static_cast(rhs)); - - #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) - - return impl::x64_signed_mul(lhs, static_cast(rhs)); - - #else - - return impl::default_signed_mul(lhs, static_cast(rhs)); - - #endif - } - - #else - return impl::default_signed_mul(lhs, static_cast(rhs)); - - #endif } template , bool> = true> constexpr u128 operator*(const SignedInteger lhs, const u128 rhs) noexcept { - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_signed_mul(rhs, static_cast(lhs)); - } - else - { - #if defined(__aarch64__) || defined(_M_ARM64) - - return impl::arm_asm_signed_mul(rhs, static_cast(lhs)); - - #elif defined(BOOST_DECIMAL_HAS_X64_INTRINSICS) || defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) - - return impl::x64_signed_mul(rhs, static_cast(lhs)); - - #else - - return impl::default_signed_mul(rhs, static_cast(lhs)); - - #endif - } - - #else - return impl::default_signed_mul(rhs, static_cast(lhs)); - - #endif } #ifdef BOOST_DECIMAL_HAS_INT128 From cc96acb7e5f56240422256c514d61205c0b6095f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 14:12:22 -0500 Subject: [PATCH 062/191] Use native x64 u128 for sub --- include/boost/decimal/detail/u128.hpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index fbc7675c..0f73a2d8 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1091,6 +1091,12 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const u128 BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const u128 rhs) noexcept { + #ifdef __x86_64__ + + return static_cast(static_cast(lhs) - static_cast(rhs)); + + #else + u128 temp {lhs.high - rhs.high, lhs.low - rhs.low}; // Check for carry @@ -1100,6 +1106,8 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const u128 } return temp; + + #endif } BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const std::uint64_t rhs) noexcept From 4098c3c0fb8c1323c34271e7b454ef016063d8bb Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 14:12:31 -0500 Subject: [PATCH 063/191] Improve ordering --- include/boost/decimal/detail/u128.hpp | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 0f73a2d8..30f9ba98 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -309,7 +309,17 @@ constexpr bool operator==(const UnsignedInteger lhs, const u128 rhs) noexcept constexpr bool operator==(const u128 lhs, const u128 rhs) noexcept { + // Intel and ARM like the values in opposite directions + + #if defined(__aarch64__) || defined(_M_ARM64) + return lhs.low == rhs.low && lhs.high == rhs.high; + + #else + + return lhs.high == rhs.high && lhs.low == rhs.low; + + #endif } #ifdef BOOST_DECIMAL_HAS_INT128 @@ -376,7 +386,15 @@ constexpr bool operator!=(const UnsignedInteger lhs, const u128 rhs) noexcept constexpr bool operator!=(const u128 lhs, const u128 rhs) noexcept { + #if defined(__aarch64__) || defined(_M_ARM64) + return lhs.low != rhs.low || lhs.high != rhs.high; + + #else + + return lhs.high != rhs.high || lhs.low != rhs.low; + + #endif } #ifdef BOOST_DECIMAL_HAS_INT128 @@ -547,7 +565,7 @@ constexpr bool operator>(const UnsignedInteger lhs, const u128 rhs) noexcept constexpr bool operator>(const u128 lhs, const u128 rhs) noexcept { - return lhs.high > rhs.high || (lhs.high == rhs.high && lhs.low > rhs.low); + return lhs.high == rhs.high ? rhs.low < lhs.low : rhs.high < lhs.high; } #ifdef BOOST_DECIMAL_HAS_INT128 @@ -604,7 +622,7 @@ constexpr bool operator>=(const UnsignedInteger lhs, const u128 rhs) noexcept constexpr bool operator>=(const u128 lhs, const u128 rhs) noexcept { - return lhs.high > rhs.high || (lhs.high == rhs.high && lhs.low >= rhs.low); + return lhs.high == rhs.high ? rhs.low <= lhs.low : rhs.high <= lhs.high; } #ifdef BOOST_DECIMAL_HAS_INT128 From 31e79818362e203578988b5172e294e1712ae0ff Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 5 Mar 2025 17:27:13 -0500 Subject: [PATCH 064/191] Micro optimizations --- include/boost/decimal/detail/u128.hpp | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 30f9ba98..35a24fc9 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1357,18 +1357,31 @@ constexpr u128& u128::operator-=(const unsigned __int128 rhs) noexcept namespace impl { +BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) noexcept +{ + return {low >> 32, low << 32}; +} + constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { + #if defined(__aarch64__) + + // This did remarkably worse on x64 platforms + return static_cast(static_cast(lhs) * static_cast(rhs)); + + #else + const auto a = static_cast(lhs.low >> 32); const auto b = static_cast(lhs.low & UINT32_MAX); const auto c = static_cast(rhs.low >> 32); const auto d = static_cast(rhs.low & UINT32_MAX); u128 result { lhs.high * rhs.low + lhs.low * rhs.high + a * c, b * d }; - result += u128{0, a * d} << 32; - result += u128{0, b * c} << 32; + result += shift_left_32(a * d) + shift_left_32(b * c); return result; + + #endif } constexpr u128 default_mul(const u128 lhs, const std::uint64_t rhs) noexcept @@ -1379,8 +1392,7 @@ constexpr u128 default_mul(const u128 lhs, const std::uint64_t rhs) noexcept const auto b = static_cast(lhs.low & UINT32_MAX); u128 result{lhs.high * rhs, b * d}; - result += u128{0, a * d} << 32; - result += u128{0, b * c} << 32; + result += shift_left_32(a * d) + shift_left_32(b * c); return result; } @@ -1470,7 +1482,7 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_ template || std::is_same::value, bool> = true> constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept { - #ifndef _MSVC_LANG + #ifndef BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS return impl::default_mul(lhs, rhs); From 9428b0d7041402d97fe9822cc28bdad445d18dbb Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 08:47:10 -0500 Subject: [PATCH 065/191] Make benchmarks safe for platforms without builtin --- test/benchmark_uints.cpp | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index cd79f104..caaca1df 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -61,10 +61,12 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) } else { + #ifdef BOOST_DECIMAL_HAS_INT128 BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value) { result[i] = T{boost::decimal::detail::u128{dist(gen), dist(gen)}}; } + #endif else { result[i] = T{dist(gen), dist(gen)}; @@ -135,27 +137,39 @@ int main() const auto old_vector = generate_random_vector(); const auto new_vector = generate_random_vector(); - const auto builtin_vector = generate_random_vector(); + #ifdef BOOST_DECIMAL_HAS_INT128 + const auto builtin_vector = generate_random_vector(); test_comparisons(builtin_vector, "builtin"); + #endif + test_comparisons(old_vector, "old"); test_comparisons(new_vector, "new"); std::cout << std::endl; + #ifdef BOOST_DECIMAL_HAS_INT128 test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); + #endif + test_two_element_operation(old_vector, std::plus<>(), "add", "Old"); test_two_element_operation(new_vector, std::plus<>(), "add", "New"); std::cout << std::endl; + #ifdef BOOST_DECIMAL_HAS_INT128 test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); + #endif + test_two_element_operation(old_vector, std::minus<>(), "sub", "Old"); test_two_element_operation(new_vector, std::minus<>(), "sub", "New"); std::cout << std::endl; + #ifdef BOOST_DECIMAL_HAS_INT128 test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); + #endif + test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); } @@ -167,27 +181,39 @@ int main() const auto old_vector = generate_random_vector(); const auto new_vector = generate_random_vector(); - const auto builtin_vector = generate_random_vector(); + #ifdef BOOST_DECIMAL_HAS_INT128 + const auto builtin_vector = generate_random_vector(); test_comparisons(builtin_vector, "builtin"); + #endif + test_comparisons(old_vector, "old"); test_comparisons(new_vector, "new"); std::cout << std::endl; + #ifdef BOOST_DECIMAL_HAS_INT128 test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); + #endif + test_two_element_operation(old_vector, std::plus<>(), "add", "Old"); test_two_element_operation(new_vector, std::plus<>(), "add", "New"); std::cout << std::endl; + #ifdef BOOST_DECIMAL_HAS_INT128 test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); + #endif + test_two_element_operation(old_vector, std::minus<>(), "sub", "Old"); test_two_element_operation(new_vector, std::minus<>(), "sub", "New"); std::cout << std::endl; + #ifdef BOOST_DECIMAL_HAS_INT128 test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); + #endif + test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); From 51db7f4d085c3007d28d9a195b02884204f73fd7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 09:20:33 -0500 Subject: [PATCH 066/191] Fix config for ARM Windows --- include/boost/decimal/detail/config.hpp | 2 +- test/benchmark_uints.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index e812f6c4..ed2e2925 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -84,7 +84,7 @@ #endif // Include intrinsics if available -#if defined(_MSC_VER) +#if defined(_MSC_VER) && defined(_M_X64) # ifndef BOOST_DECIMAL_BUILD_MODULE # include # endif diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index caaca1df..a4c28c24 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -66,8 +66,8 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) { result[i] = T{boost::decimal::detail::u128{dist(gen), dist(gen)}}; } - #endif else + #endif { result[i] = T{dist(gen), dist(gen)}; } From 855c3fbcc454456fe3e5142fb87153d2868eb904 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 10:46:29 -0500 Subject: [PATCH 067/191] Force inline mul --- include/boost/decimal/detail/u128.hpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 35a24fc9..4c11c279 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1362,15 +1362,8 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) return {low >> 32, low << 32}; } -constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { - #if defined(__aarch64__) - - // This did remarkably worse on x64 platforms - return static_cast(static_cast(lhs) * static_cast(rhs)); - - #else - const auto a = static_cast(lhs.low >> 32); const auto b = static_cast(lhs.low & UINT32_MAX); const auto c = static_cast(rhs.low >> 32); @@ -1380,11 +1373,9 @@ constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept result += shift_left_32(a * d) + shift_left_32(b * c); return result; - - #endif } -constexpr u128 default_mul(const u128 lhs, const std::uint64_t rhs) noexcept +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const std::uint64_t rhs) noexcept { const auto c = static_cast(rhs >> 32); const auto d = static_cast(rhs & UINT32_MAX); From 9b6efb98f1347d6dae730cf843937629c31b7116 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 10:46:52 -0500 Subject: [PATCH 068/191] Fix benchmarks for pre C++17 --- test/benchmark_uints.cpp | 43 +++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index a4c28c24..a0b241d5 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -61,16 +61,35 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) } else { - #ifdef BOOST_DECIMAL_HAS_INT128 - BOOST_DECIMAL_IF_CONSTEXPR (std::is_same::value) - { - result[i] = T{boost::decimal::detail::u128{dist(gen), dist(gen)}}; - } - else - #endif - { - result[i] = T{dist(gen), dist(gen)}; - } + result[i] = T{dist(gen), dist(gen)}; + } + } + + return result; +} + +template +std::vector generate_random_builtin_vector(std::size_t size = N, unsigned seed = 42U) +{ + if (seed == 0) + { + std::random_device rd; + seed = rd(); + } + + std::mt19937_64 gen(seed); + std::uniform_int_distribution dist(UINT64_C(0), UINT64_MAX); + + std::vector result(size); + for (std::size_t i = 0; i < size; ++i) + { + BOOST_DECIMAL_IF_CONSTEXPR (one_word) + { + result[i] = dist(gen); + } + else + { + result[i] = static_cast(boost::decimal::detail::u128{dist(gen), dist(gen)}); } } @@ -139,7 +158,7 @@ int main() const auto new_vector = generate_random_vector(); #ifdef BOOST_DECIMAL_HAS_INT128 - const auto builtin_vector = generate_random_vector(); + const auto builtin_vector = generate_random_builtin_vector(); test_comparisons(builtin_vector, "builtin"); #endif @@ -183,7 +202,7 @@ int main() const auto new_vector = generate_random_vector(); #ifdef BOOST_DECIMAL_HAS_INT128 - const auto builtin_vector = generate_random_vector(); + const auto builtin_vector = generate_random_builtin_vector(); test_comparisons(builtin_vector, "builtin"); #endif From 71c5926e89393aebe50193b0d7389b64d638ac16 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 10:49:52 -0500 Subject: [PATCH 069/191] Add div benchmarks --- test/benchmark_uints.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index a0b241d5..071635d3 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -191,6 +191,15 @@ int main() test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); + //test_two_element_operation(new_vector, std::divides<>(), "div", "New"); } // Single word operations { @@ -237,6 +246,13 @@ int main() test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); + //test_two_element_operation(new_vector, std::divides<>(), "div", "New"); } return 1; From 9dba617651ce9a558f37e9a495b7a5dde78e933b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 12:58:14 -0500 Subject: [PATCH 070/191] Fix testing on GCC --- test/test_u128.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 691a0118..813cb665 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -554,7 +554,7 @@ void test_operator_sub() } } -template +template = true> void test_operator_mul() { const auto root_max {static_cast(std::sqrt(std::numeric_limits::max()))}; @@ -574,6 +574,26 @@ void test_operator_mul() } } +template sizeof(std::uint64_t)), bool> = true> +void test_operator_mul() +{ + constexpr auto root_max {UINT64_MAX}; + const auto root_min {std::is_same::value ? 0 : -root_max}; + + boost::random::uniform_int_distribution dist(root_min, root_max); + + for (std::size_t i {}; i < N; ++i) + { + const IntType value {dist(rng)}; + const IntType value2 {dist(rng)}; + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value * value2) == (builtin_value * value2)); + BOOST_TEST((value2 * emulated_value) == (value2 * builtin_value)); + } +} + int main() { test_traits(); From b359e397553cb0c52c60806d682eda7cf8ec4d0e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 15:09:43 -0500 Subject: [PATCH 071/191] Add additional benchmark permutations --- test/benchmark_uints.cpp | 215 ++++++++++++++++++++++++++++++++++----- 1 file changed, 190 insertions(+), 25 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 071635d3..6100e978 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -40,7 +40,11 @@ using namespace std::chrono_literals; # define BOOST_DECIMAL_NO_INLINE __attribute__ ((__noinline__)) #endif -template +// 0 = 1 word +// 1 = 2 words +// 2 = 2 word / 1 word alternating + +template std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) { if (seed == 0) @@ -55,20 +59,47 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) std::vector result(size); for (std::size_t i = 0; i < size; ++i) { - BOOST_DECIMAL_IF_CONSTEXPR (one_word) - { - result[i] = T{dist(gen)}; - } - else + switch (words) { - result[i] = T{dist(gen), dist(gen)}; + case 0: + result[i] = T{dist(gen)}; + break; + + case 1: + result[i] = T{dist(gen), dist(gen)}; + break; + + case 2: + if (i % 2 == 0) + { + result[i] = T{dist(gen), dist(gen)}; + } + else + { + result[i] = T{dist(gen)}; + } + break; + + case 3: + if (i % 2 == 1) + { + result[i] = T{dist(gen), dist(gen)}; + } + else + { + result[i] = T{dist(gen)}; + } + break; + + default: + BOOST_DECIMAL_UNREACHABLE; } } return result; } -template +template std::vector generate_random_builtin_vector(std::size_t size = N, unsigned seed = 42U) { if (seed == 0) @@ -83,16 +114,42 @@ std::vector generate_random_builtin_vector(std::size_t size = std::vector result(size); for (std::size_t i = 0; i < size; ++i) { - BOOST_DECIMAL_IF_CONSTEXPR (one_word) + switch (words) { - result[i] = dist(gen); - } - else - { - result[i] = static_cast(boost::decimal::detail::u128{dist(gen), dist(gen)}); + case 0: + result[i] = dist(gen); + break; + + case 1: + result[i] = static_cast(boost::decimal::detail::u128{dist(gen), dist(gen)}); + break; + + case 2: + if (i % 2 == 0) + { + result[i] = static_cast(boost::decimal::detail::u128{dist(gen), dist(gen)}); + } + else + { + result[i] = dist(gen); + } + break; + + case 3: + if (i % 2 == 1) + { + result[i] = static_cast(boost::decimal::detail::u128{dist(gen), dist(gen)}); + } + else + { + result[i] = dist(gen); + } + break; + + default: + BOOST_DECIMAL_UNREACHABLE; } } - return result; } @@ -119,7 +176,7 @@ BOOST_DECIMAL_NO_INLINE void test_comparisons(const std::vector& data_vec, co const auto t2 = std::chrono::steady_clock::now(); - std::cout << "comparisons<" << std::left << std::setw(11) << label << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << "comp<" << std::left << std::setw(11) << label << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } template @@ -140,7 +197,7 @@ BOOST_DECIMAL_NO_INLINE void test_two_element_operation(const std::vector& da const auto t2 = std::chrono::steady_clock::now(); - std::cout << operation << "<" << std::left << std::setw(11) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; + std::cout << operation << " <" << std::left << std::setw(11) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } @@ -154,11 +211,11 @@ int main() std::cout << "Two Word Operations\n"; std::cout << "---------------------------\n\n"; - const auto old_vector = generate_random_vector(); - const auto new_vector = generate_random_vector(); + const auto old_vector = generate_random_vector<0, uint128>(); + const auto new_vector = generate_random_vector<0, u128>(); #ifdef BOOST_DECIMAL_HAS_INT128 - const auto builtin_vector = generate_random_builtin_vector(); + const auto builtin_vector = generate_random_builtin_vector<0>(); test_comparisons(builtin_vector, "builtin"); #endif @@ -199,7 +256,7 @@ int main() #endif test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); - //test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + test_two_element_operation(new_vector, std::divides<>(), "div", "New"); } // Single word operations { @@ -207,11 +264,119 @@ int main() std::cout << "One Word Operations\n"; std::cout << "---------------------------\n\n"; - const auto old_vector = generate_random_vector(); - const auto new_vector = generate_random_vector(); + const auto old_vector = generate_random_vector<1, uint128>(); + const auto new_vector = generate_random_vector<1, u128>(); + + #ifdef BOOST_DECIMAL_HAS_INT128 + const auto builtin_vector = generate_random_builtin_vector<1>(); + test_comparisons(builtin_vector, "builtin"); + #endif + + test_comparisons(old_vector, "old"); + test_comparisons(new_vector, "new"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::plus<>(), "add", "Old"); + test_two_element_operation(new_vector, std::plus<>(), "add", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::minus<>(), "sub", "Old"); + test_two_element_operation(new_vector, std::minus<>(), "sub", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); + test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); + test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + } + { + // Two word and one word operations Even = 2, odd = 1 + + std::cout << "\n---------------------------\n"; + std::cout << "Two-One Word Operations\n"; + std::cout << "---------------------------\n\n"; + + const auto old_vector = generate_random_vector<2, uint128>(); + const auto new_vector = generate_random_vector<2, u128>(); + + #ifdef BOOST_DECIMAL_HAS_INT128 + const auto builtin_vector = generate_random_builtin_vector<2>(); + test_comparisons(builtin_vector, "builtin"); + #endif + + test_comparisons(old_vector, "old"); + test_comparisons(new_vector, "new"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::plus<>(), "add", "Old"); + test_two_element_operation(new_vector, std::plus<>(), "add", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::minus<>(), "sub", "Old"); + test_two_element_operation(new_vector, std::minus<>(), "sub", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); + test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); + test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + } + { + // Two word and one word operations Even = 1, odd = 2 + + std::cout << "\n---------------------------\n"; + std::cout << "One-Two Word Operations\n"; + std::cout << "---------------------------\n\n"; + + const auto old_vector = generate_random_vector<3, uint128>(); + const auto new_vector = generate_random_vector<3, u128>(); #ifdef BOOST_DECIMAL_HAS_INT128 - const auto builtin_vector = generate_random_builtin_vector(); + const auto builtin_vector = generate_random_builtin_vector<3>(); test_comparisons(builtin_vector, "builtin"); #endif @@ -252,7 +417,7 @@ int main() #endif test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); - //test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + test_two_element_operation(new_vector, std::divides<>(), "div", "New"); } return 1; From 8a535172910388504b0a9862100af558f2d871fe Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 16:29:01 -0500 Subject: [PATCH 072/191] Add random width benchmarks --- test/benchmark_uints.cpp | 82 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 6100e978..744c0732 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -43,6 +43,8 @@ using namespace std::chrono_literals; // 0 = 1 word // 1 = 2 words // 2 = 2 word / 1 word alternating +// 3 = 1 word / 2 word alternating +// 4 = Random width template std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) @@ -55,6 +57,7 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) std::mt19937_64 gen(seed); std::uniform_int_distribution dist(UINT64_C(0), UINT64_MAX); + std::uniform_int_distribution size_dist(0, 1); std::vector result(size); for (std::size_t i = 0; i < size; ++i) @@ -89,7 +92,18 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) { result[i] = T{dist(gen)}; } - break; + break; + + case 4: + if (size_dist(gen) == 1) + { + result[i] = T{dist(gen), dist(gen)}; + } + else + { + result[i] = T{dist(gen)}; + } + break; default: BOOST_DECIMAL_UNREACHABLE; @@ -110,6 +124,7 @@ std::vector generate_random_builtin_vector(std::size_t size = std::mt19937_64 gen(seed); std::uniform_int_distribution dist(UINT64_C(0), UINT64_MAX); + std::uniform_int_distribution size_dist(0, 1); std::vector result(size); for (std::size_t i = 0; i < size; ++i) @@ -146,6 +161,17 @@ std::vector generate_random_builtin_vector(std::size_t size = } break; + case 4: + if (size_dist(gen) == 1) + { + result[i] = static_cast(boost::decimal::detail::u128{dist(gen), dist(gen)}); + } + else + { + result[i] = dist(gen); + } + break; + default: BOOST_DECIMAL_UNREACHABLE; } @@ -419,6 +445,60 @@ int main() test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); test_two_element_operation(new_vector, std::divides<>(), "div", "New"); } + { + // Two word and one word operations Even = 1, odd = 2 + + std::cout << "\n---------------------------\n"; + std::cout << "Random Width Operations\n"; + std::cout << "---------------------------\n\n"; + + const auto old_vector = generate_random_vector<4, uint128>(); + const auto new_vector = generate_random_vector<4, u128>(); + + #ifdef BOOST_DECIMAL_HAS_INT128 + const auto builtin_vector = generate_random_builtin_vector<4>(); + test_comparisons(builtin_vector, "builtin"); + #endif + + test_comparisons(old_vector, "old"); + test_comparisons(new_vector, "new"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::plus<>(), "add", "Old"); + test_two_element_operation(new_vector, std::plus<>(), "add", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::minus<>(), "sub", "Old"); + test_two_element_operation(new_vector, std::minus<>(), "sub", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::multiplies<>(), "mul", "Old"); + test_two_element_operation(new_vector, std::multiplies<>(), "mul", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); + test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + } return 1; } From 1814263c55c4a529adcb8672b324bdc9722aeb00 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 16:35:23 -0500 Subject: [PATCH 073/191] Add division test set --- test/test_u128.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 813cb665..e1cc63b7 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -594,6 +594,34 @@ void test_operator_mul() } } +template +void test_operator_div() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + IntType value {0}; + IntType value2 {0}; + + while (value == 0) + { + value = dist(rng); + } + while (value2 == 0) + { + value2 = dist(rng); + } + + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value / value2) == (builtin_value / value2)); + BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); + } +} + int main() { test_traits(); @@ -815,6 +843,18 @@ int main() test_operator_mul(); test_operator_mul(); + //test_operator_div(); + //test_operator_div(); + //test_operator_div(); + //test_operator_div(); + test_operator_div<__int128>(); + + //test_operator_div(); + //test_operator_div(); + //test_operator_div(); + //test_operator_div(); + test_operator_div(); + return boost::report_errors(); } From 2c94c928703511f48a69a1da4dad46b1ed3ddd79 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 16:57:26 -0500 Subject: [PATCH 074/191] Add 128-bit division --- include/boost/decimal/detail/u128.hpp | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 4c11c279..26f2dd84 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1541,6 +1541,126 @@ constexpr u128 operator*(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +namespace impl { + +using wide_integer_uint128 = ::boost::decimal::math::wide_integer::uint128_t; + +constexpr auto u128_to_wide_integer(const u128& src) -> wide_integer_uint128 +{ + wide_integer_uint128 dst { }; + + using local_limb_type = typename wide_integer_uint128::limb_type; + + static_assert(sizeof(local_limb_type) == static_cast(UINT8_C(4)) && std::is_same::value, "Error: Configuration of external wide-integer limbs not OK"); + + auto rep = dst.representation(); + + rep[0] = static_cast(src.low); + rep[1] = static_cast(src.low >> 32U); + rep[2] = static_cast(src.high); + rep[3] = static_cast(src.high >> 32U); + + return dst; +} + +constexpr auto wide_integer_to_u128(const wide_integer_uint128& src) -> u128 +{ + u128 dst {}; + + const auto rep = src.crepresentation(); + + dst.low = static_cast(rep[0]) | (static_cast(rep[1]) << 32); + dst.high = static_cast(rep[2]) | (static_cast(rep[3]) << 32U); + + return dst; +} + +BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u128& rhs, u128& quotient, u128& remainder) noexcept +{ + // Mash-Up: Use Knuth long-division from wide-integer (requires limb-conversions on input/output). + + auto lhs_wide = u128_to_wide_integer(lhs); + + wide_integer_uint128 rem_wide { }; + + lhs_wide.eval_divide_knuth(u128_to_wide_integer(rhs), rem_wide); + + remainder = wide_integer_to_u128(rem_wide); + quotient = wide_integer_to_u128(lhs_wide); +} + +} + +constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept +{ + // On ARM64 this is unconditionally better + // On x64 this is only better when both lhs and rhs are two word numbers + #ifdef __aarch64__ + + return static_cast(static_cast(lhs) / static_cast(rhs)); + + #else + + BOOST_DECIMAL_ASSERT_MSG(rhs != 0, "Division by zero"); + + if (lhs.high != 0 && rhs.high != 0) + { + #ifdef BOOST_DECIMAL_HAS_INT128 + + return static_cast(static_cast(lhs) / static_cast(rhs)); + + #else + + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, rhs, quotient, remainder); + return quotient; + + #endif + } + else if (lhs < rhs) + { + return {0, 0}; + } + else if (lhs.high == 0 && rhs.high == 0) + { + return {0, lhs.high / rhs.high}; + } + else + { + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, rhs, quotient, remainder); + return quotient; + } + + #endif +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator/(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs / static_cast(rhs); +} + +constexpr u128 operator/(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) / rhs; +} + +constexpr u128 operator/(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs / static_cast(rhs); +} + +constexpr u128 operator/(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) / rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost From 07ae38cf65d1539bf4004e49856e083f620bb430 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 6 Mar 2025 17:00:55 -0500 Subject: [PATCH 075/191] Add compound multiplication operator --- include/boost/decimal/detail/u128.hpp | 58 +++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 26f2dd84..8d479bb5 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -180,6 +180,20 @@ u128 constexpr u128& operator-=(__int128 rhs) noexcept; constexpr u128& operator-=(unsigned __int128 rhs) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 + + // Compound Multiplication Operators + template , bool> = true> + constexpr u128& operator*=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator*=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator*=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator*=(__int128 rhs) noexcept; + constexpr u128& operator*=(unsigned __int128 rhs) noexcept; + #endif // BOOST_DECIMAL_HAS_INT128 }; // Signed assignment operators @@ -1541,6 +1555,50 @@ constexpr u128 operator*(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound Multiplication Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator*=(const SignedInteger rhs) noexcept +{ + *this = *this * rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator*=(const UnsignedInteger rhs) noexcept +{ + *this = *this * rhs; + return *this; +} + +constexpr u128& u128::operator*=(const u128 rhs) noexcept +{ + *this = *this * rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator*=(const __int128 rhs) noexcept +{ + *this = *this * rhs; + return *this; +} + +constexpr u128& u128::operator*=(const unsigned __int128 rhs) noexcept +{ + *this = *this * rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + +//===================================== +// Division Operator +//===================================== + namespace impl { using wide_integer_uint128 = ::boost::decimal::math::wide_integer::uint128_t; From d51cd6f9cd136caa314da146a6fbff12fb7ed5f9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 10:52:25 -0500 Subject: [PATCH 076/191] Improve x64 mul --- include/boost/decimal/detail/u128.hpp | 34 ++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8d479bb5..db1433bc 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1487,11 +1487,7 @@ BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_ template || std::is_same::value, bool> = true> constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept { - #ifndef BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS - - return impl::default_mul(lhs, rhs); - - #else + #if BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION @@ -1510,7 +1506,33 @@ constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept #endif - #endif // _MSVC_LANG + #elif defined(__x86_64__) && !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return impl::default_mul(lhs, rhs); + } + else + { + unsigned __int128 new_lhs; + unsigned __int128 new_rhs; + + std::memcpy(&new_lhs, &lhs, sizeof(new_lhs)); + std::memcpy(&new_rhs, &rhs, sizeof(new_rhs)); + + const auto res = new_lhs * new_rhs; + + u128 new_result; + std::memcpy(&new_result, &res, sizeof(new_result)); + + return new_result; + } + + #else + + return impl::default_mul(lhs, rhs); + + #endif } template , bool> = true> From 61b668cb4d3641bf2febe0c1cfc0ae163bdfdc7e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 13:43:20 -0500 Subject: [PATCH 077/191] Fix benchmarks for MSVC --- test/benchmark_uints.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 744c0732..5f20f8dd 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -18,6 +18,7 @@ #include #include #include +#include constexpr unsigned N = 20'000'000; constexpr unsigned K = 5; @@ -113,6 +114,8 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) return result; } +#ifdef BOOST_DECIMAL_HAS_INT128 + template std::vector generate_random_builtin_vector(std::size_t size = N, unsigned seed = 42U) { @@ -179,6 +182,8 @@ std::vector generate_random_builtin_vector(std::size_t size = return result; } +#endif + template BOOST_DECIMAL_NO_INLINE void test_comparisons(const std::vector& data_vec, const char* label) { From 1c15be5b73522fdcb9e03f874eb0e76338a17c93 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 13:50:35 -0500 Subject: [PATCH 078/191] Remove MSVC specialized mul since it's worse --- include/boost/decimal/detail/u128.hpp | 81 --------------------------- 1 file changed, 81 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8d479bb5..f05bf7f6 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1424,93 +1424,12 @@ constexpr u128 default_signed_mul(const u128 lhs, const std::int64_t rhs) noexce return res; } -// TODO(mborland): Run benchmarks on MSVC to see if these are worth it or not -#if defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) - -BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept -{ - u128 result; - - // Multiply lhs.low * rhs.low (full 128-bit result) - result.low = _umul128(lhs.low, rhs.low, &result.high); - - // Add lhs.high * rhs.low to result.high - unsigned char carry = BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * rhs.low, &result.high); - - // Add lhs.low * rhs.high to result.high - BOOST_DECIMAL_ADD_CARRY(carry, result.high, lhs.low * rhs.high, &result.high); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) noexcept -{ - u128 result; - - // Multiply lhs.low * rhs (full 128-bit result) - result.low = _umul128(lhs.low, rhs, &result.high); - - // Add lhs.high * rhs to result.high - BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * rhs, &result.high); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept -{ - const bool is_negative = rhs < 0; - - const std::uint64_t abs_rhs = is_negative ? static_cast(-rhs) : static_cast(rhs); - - u128 result; - result.low = _umul128(lhs.low, abs_rhs, &result.high); - BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * abs_rhs, &result.high); - - if (is_negative) - { - // Two's complement negation - result.high = ~result.high; - result.low = ~result.low; - - // Add 1 with carry handling - unsigned char carry = BOOST_DECIMAL_ADD_CARRY(0, result.low, 1ULL, &result.low); - BOOST_DECIMAL_ADD_CARRY(carry, result.high, 0ULL, &result.high); - } - - return result; -} - -#endif - } // namespace impl template || std::is_same::value, bool> = true> constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept { - #ifndef BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS - return impl::default_mul(lhs, rhs); - - #else - - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_mul(lhs, rhs); - } - else - { - return impl::x64_mul(lhs, rhs); - } - - #else - - return impl::default_mul(lhs, rhs); - - #endif - - #endif // _MSVC_LANG } template , bool> = true> From e45a6d58e63c213151d0399059ec86b45cb85f86 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 13:52:10 -0500 Subject: [PATCH 079/191] Avoid division trapping on windows platforms --- include/boost/decimal/detail/u128.hpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index f05bf7f6..95947f61 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1601,7 +1601,21 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept } else if (lhs.high == 0 && rhs.high == 0) { - return {0, lhs.high / rhs.high}; + // Only Windows platforms trap on division by zero + #ifdef _WIN32 + if (rhs.high != 0) + { + return { 0, lhs.high / rhs.high }; + } + else + { + return { 0, 0 }; + } + #else + + return { 0, lhs.high / rhs.high }; + + #endif } else { From d60aa8280fcd7058cd9c9b335a806b407ba3d678 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 15:00:35 -0500 Subject: [PATCH 080/191] Begin adding tests for platforms without native __int128 --- test/test_u128.cpp | 244 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 241 insertions(+), 3 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index e1cc63b7..a9582635 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -5,8 +5,6 @@ #include #include -#ifdef BOOST_DECIMAL_HAS_INT128 - #include #include #include @@ -63,6 +61,8 @@ void test_traits() static_assert(!is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); } +#ifdef BOOST_DECIMAL_HAS_INT128 + template void test_arithmetic_constructor() { @@ -860,9 +860,247 @@ int main() #else +using boost::decimal::detail::uint128; + +template +void test_arithmetic_constructor() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + uint128 builtin_value { value }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(builtin_value.high == emulated_value.high && builtin_value.low == emulated_value.low); + } +} + +template +void test_assignment_operators() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + uint128 builtin_value; + builtin_value = value; + boost::decimal::detail::u128 emulated_value{}; + emulated_value = value; + + BOOST_TEST(builtin_value.high == emulated_value.high && builtin_value.low == emulated_value.low); + } +} + +template +void test_integer_conversion_operators() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + uint128 builtin_value; + builtin_value = value; + boost::decimal::detail::u128 emulated_value{}; + emulated_value = value; + + const auto builtin_value_return = static_cast(builtin_value); + const auto emulated_value_return = static_cast(emulated_value); + + BOOST_TEST_EQ(builtin_value_return, emulated_value_return); + + // Hits the implicit bool conversion + if (builtin_value) + { + BOOST_TEST(emulated_value); + } + } +} + +template +void test_float_conversion_operators() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const auto value{ dist(rng) }; + uint128 builtin_value; + builtin_value = value; + boost::decimal::detail::u128 emulated_value{}; + emulated_value = value; + + const auto builtin_value_return = static_cast(builtin_value); + const auto emulated_value_return = static_cast(emulated_value); + + BOOST_TEST(std::abs(builtin_value_return - emulated_value_return) < std::numeric_limits::epsilon()); + } +} + +template +void test_unary_plus() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + uint128 builtin_value = value; + boost::decimal::detail::u128 emulated_value{ value }; + emulated_value = +emulated_value; + + BOOST_TEST(builtin_value.high == emulated_value.high && builtin_value.low == builtin_value.low); + } +} + +template +void test_unary_minus() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + uint128 builtin_value = value; + builtin_value = -builtin_value; + boost::decimal::detail::u128 emulated_value{ value }; + emulated_value = -emulated_value; + + BOOST_TEST(builtin_value.high == emulated_value.high && builtin_value.low == builtin_value.low); + } +} + +template +void test_operator_equality() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + // Always equal + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value == emulated_value) == (emulated_value == value)) == (value == value)); + } + + // Potentially equal + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value2 == emulated_value) == (value2 == value)) == + ((emulated_value == value2) == (value == value2))); + } + + boost::decimal::detail::u128 bool_val{ dist(rng) }; + BOOST_TEST((true == bool_val) == (bool_val == true)); +} + +template +void test_operator_inequality() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + // Always equal + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value != emulated_value) == (emulated_value != value)) == !(value != value)); + } + + // Potentially equal + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value2 != emulated_value) == (value2 != value)) == + ((emulated_value != value2) == (value != value2))); + } + + boost::decimal::detail::u128 bool_val{ dist(rng) }; + BOOST_TEST((true == bool_val) == (bool_val == true)); +} + int main() { - return 0; + test_traits(); + + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + test_arithmetic_constructor(); + + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + test_assignment_operators(); + + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + test_integer_conversion_operators(); + + test_float_conversion_operators(); + test_float_conversion_operators(); + test_float_conversion_operators(); + + test_unary_plus(); + test_unary_minus(); + + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + test_operator_equality(); + + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + test_operator_inequality(); + + return boost::report_errors(); } #endif From c2511fec659f936cbc8fa131c1233d2bd1a85117 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 15:53:35 -0500 Subject: [PATCH 081/191] Add comparisons testing --- test/test_u128.cpp | 136 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index a9582635..b83ca2bf 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -1039,6 +1039,92 @@ void test_operator_inequality() BOOST_TEST((true == bool_val) == (bool_val == true)); } +template +void test_operator_less() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value2 < emulated_value) == (value2 < value)) == + ((emulated_value < value2) == (value < value2))); + } +} + +template +void test_operator_le() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value2 <= emulated_value) == (value2 <= value)) == + ((emulated_value <= value2) == (value <= value2))); + } +} + +template +void test_operator_greater() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value2 > emulated_value) == (value2 > value)) == + ((emulated_value > value2) == (value > value2))); + } +} + +template +void test_operator_ge() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST(((value2 >= emulated_value) == (value2 >= value)) == + ((emulated_value >= value2) == (value >= value2))); + } +} + +template +void test_operator_not() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + uint128 builtin_value{ value }; + builtin_value = ~builtin_value; + boost::decimal::detail::u128 emulated_value{ value }; + emulated_value = ~emulated_value; + + BOOST_TEST(builtin_value.high == emulated_value.high && builtin_value.low == emulated_value.low); + } +} + int main() { test_traits(); @@ -1100,6 +1186,56 @@ int main() test_operator_inequality(); test_operator_inequality(); + test_operator_less(); + test_operator_less(); + test_operator_less(); + test_operator_less(); + + test_operator_less(); + test_operator_less(); + test_operator_less(); + test_operator_less(); + + test_operator_le(); + test_operator_le(); + test_operator_le(); + test_operator_le(); + + test_operator_le(); + test_operator_le(); + test_operator_le(); + test_operator_le(); + + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + test_operator_greater(); + + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + test_operator_ge(); + + test_operator_not(); + test_operator_not(); + test_operator_not(); + test_operator_not(); + + test_operator_not(); + test_operator_not(); + test_operator_not(); + test_operator_not(); + return boost::report_errors(); } From 3c2948ac131752b56785e44e69fe2c52ce96eead Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 16:16:03 -0500 Subject: [PATCH 082/191] Default init to fix warnings --- include/boost/decimal/detail/u128.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index db1433bc..e95f3590 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1514,15 +1514,15 @@ constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept } else { - unsigned __int128 new_lhs; - unsigned __int128 new_rhs; + unsigned __int128 new_lhs {}; + unsigned __int128 new_rhs {}; std::memcpy(&new_lhs, &lhs, sizeof(new_lhs)); std::memcpy(&new_rhs, &rhs, sizeof(new_rhs)); const auto res = new_lhs * new_rhs; - u128 new_result; + u128 new_result {}; std::memcpy(&new_result, &res, sizeof(new_result)); return new_result; From 93d88f7d93666b3e4967cdf281e1b8cc88dc3c31 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 13:43:20 -0500 Subject: [PATCH 083/191] Fix benchmarks for MSVC --- test/benchmark_uints.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 744c0732..5f20f8dd 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -18,6 +18,7 @@ #include #include #include +#include constexpr unsigned N = 20'000'000; constexpr unsigned K = 5; @@ -113,6 +114,8 @@ std::vector generate_random_vector(std::size_t size = N, unsigned seed = 42U) return result; } +#ifdef BOOST_DECIMAL_HAS_INT128 + template std::vector generate_random_builtin_vector(std::size_t size = N, unsigned seed = 42U) { @@ -179,6 +182,8 @@ std::vector generate_random_builtin_vector(std::size_t size = return result; } +#endif + template BOOST_DECIMAL_NO_INLINE void test_comparisons(const std::vector& data_vec, const char* label) { From e3502934e73463a694f07ee7503054791a39b21d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 16:23:57 -0500 Subject: [PATCH 084/191] Remove MSVC specialized mul since it's worse --- include/boost/decimal/detail/u128.hpp | 103 -------------------------- 1 file changed, 103 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index e95f3590..f05bf7f6 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1424,115 +1424,12 @@ constexpr u128 default_signed_mul(const u128 lhs, const std::int64_t rhs) noexce return res; } -// TODO(mborland): Run benchmarks on MSVC to see if these are worth it or not -#if defined(BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS) - -BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const u128 rhs) noexcept -{ - u128 result; - - // Multiply lhs.low * rhs.low (full 128-bit result) - result.low = _umul128(lhs.low, rhs.low, &result.high); - - // Add lhs.high * rhs.low to result.high - unsigned char carry = BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * rhs.low, &result.high); - - // Add lhs.low * rhs.high to result.high - BOOST_DECIMAL_ADD_CARRY(carry, result.high, lhs.low * rhs.high, &result.high); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 x64_mul(const u128 lhs, const std::uint64_t rhs) noexcept -{ - u128 result; - - // Multiply lhs.low * rhs (full 128-bit result) - result.low = _umul128(lhs.low, rhs, &result.high); - - // Add lhs.high * rhs to result.high - BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * rhs, &result.high); - - return result; -} - -BOOST_DECIMAL_FORCE_INLINE u128 x64_signed_mul(const u128 lhs, const std::int64_t rhs) noexcept -{ - const bool is_negative = rhs < 0; - - const std::uint64_t abs_rhs = is_negative ? static_cast(-rhs) : static_cast(rhs); - - u128 result; - result.low = _umul128(lhs.low, abs_rhs, &result.high); - BOOST_DECIMAL_ADD_CARRY(0, result.high, lhs.high * abs_rhs, &result.high); - - if (is_negative) - { - // Two's complement negation - result.high = ~result.high; - result.low = ~result.low; - - // Add 1 with carry handling - unsigned char carry = BOOST_DECIMAL_ADD_CARRY(0, result.low, 1ULL, &result.low); - BOOST_DECIMAL_ADD_CARRY(carry, result.high, 0ULL, &result.high); - } - - return result; -} - -#endif - } // namespace impl template || std::is_same::value, bool> = true> constexpr u128 operator*(const u128 lhs, const UnsignedInteger rhs) noexcept { - #if BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS - - #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_mul(lhs, rhs); - } - else - { - return impl::x64_mul(lhs, rhs); - } - - #else - return impl::default_mul(lhs, rhs); - - #endif - - #elif defined(__x86_64__) && !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) - - if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) - { - return impl::default_mul(lhs, rhs); - } - else - { - unsigned __int128 new_lhs {}; - unsigned __int128 new_rhs {}; - - std::memcpy(&new_lhs, &lhs, sizeof(new_lhs)); - std::memcpy(&new_rhs, &rhs, sizeof(new_rhs)); - - const auto res = new_lhs * new_rhs; - - u128 new_result {}; - std::memcpy(&new_result, &res, sizeof(new_result)); - - return new_result; - } - - #else - - return impl::default_mul(lhs, rhs); - - #endif } template , bool> = true> From 1de3932243c60296045e6236a4aa69a1f81d9344 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 13:52:10 -0500 Subject: [PATCH 085/191] Avoid division trapping on windows platforms --- include/boost/decimal/detail/u128.hpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index f05bf7f6..95947f61 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1601,7 +1601,21 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept } else if (lhs.high == 0 && rhs.high == 0) { - return {0, lhs.high / rhs.high}; + // Only Windows platforms trap on division by zero + #ifdef _WIN32 + if (rhs.high != 0) + { + return { 0, lhs.high / rhs.high }; + } + else + { + return { 0, 0 }; + } + #else + + return { 0, lhs.high / rhs.high }; + + #endif } else { From d9b35f9c251fc4e26421816edc833bb4eaea49fb Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 16:24:30 -0500 Subject: [PATCH 086/191] Add testing of operator and --- test/test_u128.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index b83ca2bf..9d1bc5d0 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -33,6 +33,10 @@ # pragma GCC diagnostic ignored "-Wsign-conversion" # pragma GCC diagnostic ignored "-Wsign-compare" # pragma GCC diagnostic ignored "-Wfloat-equal" + +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4389) #endif #include @@ -1125,6 +1129,24 @@ void test_operator_not() } } + +template +void test_operator_and() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST((emulated_value.low & value2) == (value & value2)); + BOOST_TEST((value2 & value) == (value2 & emulated_value.low)); + } +} + int main() { test_traits(); @@ -1236,6 +1258,16 @@ int main() test_operator_not(); test_operator_not(); + test_operator_and(); + test_operator_and(); + test_operator_and(); + test_operator_and(); + + test_operator_and(); + test_operator_and(); + test_operator_and(); + test_operator_and(); + return boost::report_errors(); } From 25533b9b59a00c2714fce830aba18a98de9b966c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 16:52:12 -0500 Subject: [PATCH 087/191] Fix comparisons with negative numbers --- test/test_u128.cpp | 60 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 52 insertions(+), 8 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 9d1bc5d0..bd03194e 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -1055,8 +1055,19 @@ void test_operator_less() const IntType value2{ dist(rng) }; boost::decimal::detail::u128 emulated_value{ value }; - BOOST_TEST(((value2 < emulated_value) == (value2 < value)) == - ((emulated_value < value2) == (value < value2))); + if (value2 >= 0) + { + BOOST_TEST(((value2 < emulated_value) == (value2 < value)) == + ((emulated_value < value2) == (value < value2))); + } + else if (value2 < value) + { + BOOST_TEST((value2 < emulated_value) == (value2 < value)); + } + else + { + BOOST_TEST((value2 < emulated_value) != (value2 < value)); + } } } @@ -1072,8 +1083,19 @@ void test_operator_le() const IntType value2{ dist(rng) }; boost::decimal::detail::u128 emulated_value{ value }; - BOOST_TEST(((value2 <= emulated_value) == (value2 <= value)) == - ((emulated_value <= value2) == (value <= value2))); + if (value2 >= 0) + { + BOOST_TEST(((value2 <= emulated_value) == (value2 <= value)) == + ((emulated_value <= value2) == (value <= value2))); + } + else if (value2 <= value) + { + BOOST_TEST((value2 <= emulated_value) == (value2 <= value)); + } + else + { + BOOST_TEST((value2 <= emulated_value) != (value2 <= value)); + } } } @@ -1089,8 +1111,19 @@ void test_operator_greater() const IntType value2{ dist(rng) }; boost::decimal::detail::u128 emulated_value{ value }; - BOOST_TEST(((value2 > emulated_value) == (value2 > value)) == - ((emulated_value > value2) == (value > value2))); + if (value2 >= 0) + { + BOOST_TEST(((value2 > emulated_value) == (value2 > value)) == + ((emulated_value > value2) == (value > value2))); + } + else if (value2 > value) + { + BOOST_TEST((value2 > emulated_value) != (value2 > value)); + } + else + { + BOOST_TEST((value2 > emulated_value) == (value2 > value)); + } } } @@ -1106,8 +1139,19 @@ void test_operator_ge() const IntType value2{ dist(rng) }; boost::decimal::detail::u128 emulated_value{ value }; - BOOST_TEST(((value2 >= emulated_value) == (value2 >= value)) == - ((emulated_value >= value2) == (value >= value2))); + if (value2 >= 0) + { + BOOST_TEST(((value2 >= emulated_value) == (value2 >= value)) == + ((emulated_value >= value2) == (value >= value2))); + } + else if (value2 >= value) + { + BOOST_TEST((value2 >= emulated_value) != (value2 >= value)); + } + else + { + BOOST_TEST((value2 >= emulated_value) == (value2 >= value)); + } } } From 68ab0d19ebcf3fb27fe1237543563a00c1074f1d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 17:21:03 -0500 Subject: [PATCH 088/191] Add operators testing --- test/test_u128.cpp | 160 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index bd03194e..292e43b7 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -1191,6 +1191,116 @@ void test_operator_and() } } +template +void test_operator_xor() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + boost::decimal::detail::u128 emulated_value{ value }; + + BOOST_TEST((emulated_value.low | value2) == (value | value2)); + BOOST_TEST((value2 | value) == (value2 | emulated_value.low)); + } +} + + +template +void test_operator_add() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + uint128 builtin_value{ value }; + boost::decimal::detail::u128 emulated_value{ value }; + + const auto builtin_res_left = builtin_value + value2; + const auto builtin_res_right = value2 + builtin_value; + + const auto emulated_res_left = emulated_value + value2; + const auto emulated_res_right = value2 + emulated_value; + + BOOST_TEST(emulated_res_left.high == builtin_res_left.high && emulated_res_left.low == builtin_res_left.low); + BOOST_TEST(emulated_res_right.high == builtin_res_right.high && emulated_res_right.low == builtin_res_right.low); + } +} + +template +void test_operator_sub() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + uint128 builtin_value{ value }; + boost::decimal::detail::u128 emulated_value{ value }; + + // uint128 does not have all direction subtraction operators implemented + + //const auto builtin_res_left = builtin_value - value2; + const auto builtin_res_right = value2 - builtin_value; + + //const auto emulated_res_left = emulated_value - value2; + const auto emulated_res_right = value2 - emulated_value; + + //BOOST_TEST(emulated_res_left.high == builtin_res_left.high && emulated_res_left.low == builtin_res_left.low); + BOOST_TEST(emulated_res_right.high == builtin_res_right.high && emulated_res_right.low == builtin_res_right.low); + } +} + +template +void test_operator_mul() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + uint128 builtin_value{ value }; + boost::decimal::detail::u128 emulated_value{ value }; + + + const auto builtin_res_left = builtin_value * static_cast(value2); + const auto emulated_res_left = emulated_value * static_cast(value2); + + BOOST_TEST(emulated_res_left.high == builtin_res_left.high && emulated_res_left.low == builtin_res_left.low); + + } +} + +template +void test_operator_div() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + const IntType value2{ dist(rng) }; + uint128 builtin_value{ value }; + boost::decimal::detail::u128 emulated_value{ value }; + + + const auto builtin_res_left = builtin_value / static_cast(value2); + const auto emulated_res_left = emulated_value / static_cast(value2); + + BOOST_TEST(emulated_res_left.high == builtin_res_left.high && emulated_res_left.low == builtin_res_left.low); + + } +} + int main() { test_traits(); @@ -1312,6 +1422,56 @@ int main() test_operator_and(); test_operator_and(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + test_operator_xor(); + + test_operator_add(); + test_operator_add(); + test_operator_add(); + test_operator_add(); + + test_operator_add(); + test_operator_add(); + test_operator_add(); + test_operator_add(); + + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + test_operator_sub(); + + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + test_operator_mul(); + + test_operator_div(); + test_operator_div(); + test_operator_div(); + test_operator_div(); + + test_operator_div(); + test_operator_div(); + test_operator_div(); + test_operator_div(); + return boost::report_errors(); } From 40b425263e7cae25a3ce08ae0a387ad051c7d471 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 7 Mar 2025 17:32:51 -0500 Subject: [PATCH 089/191] Use two word div and avoid trapping on zero --- test/test_u128.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 292e43b7..05306343 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -1288,9 +1288,15 @@ void test_operator_div() for (std::size_t i{}; i < N; ++i) { const IntType value{ dist(rng) }; - const IntType value2{ dist(rng) }; - uint128 builtin_value{ value }; - boost::decimal::detail::u128 emulated_value{ value }; + IntType value2{ dist(rng) }; + + while (value2 == 0) + { + value2 = dist(rng); + } + + uint128 builtin_value{ static_cast(value), static_cast(value) }; + boost::decimal::detail::u128 emulated_value{ static_cast(value), static_cast(value) }; const auto builtin_res_left = builtin_value / static_cast(value2); From d17d03ce17398e603956727f0b5615f9804b1155 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 10 Mar 2025 10:02:36 -0400 Subject: [PATCH 090/191] Fix inverted division --- include/boost/decimal/detail/u128.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 95947f61..58daef6c 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1603,9 +1603,9 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept { // Only Windows platforms trap on division by zero #ifdef _WIN32 - if (rhs.high != 0) + if (rhs.low != 0) { - return { 0, lhs.high / rhs.high }; + return { 0, lhs.low / rhs.low }; } else { @@ -1613,7 +1613,7 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept } #else - return { 0, lhs.high / rhs.high }; + return { 0, lhs.low / rhs.low }; #endif } From 19af2d172753ba6d55242fdd305bf52a62f76436 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 10 Mar 2025 10:10:35 -0400 Subject: [PATCH 091/191] Add and test AARCH64 div operator --- include/boost/decimal/detail/u128.hpp | 24 ++++++++++++++++++++++++ test/test_u128.cpp | 8 ++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 58daef6c..1f87daa1 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1566,6 +1566,30 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u1 quotient = wide_integer_to_u128(lhs_wide); } +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept +{ + return static_cast(static_cast(lhs) / rhs); +} + +} + +template , bool> = true> +constexpr u128 operator/(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return impl::default_div(lhs, static_cast(rhs)); +} + +template , bool> = true> +constexpr u128 operator/(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + if (rhs.high != 0) + { + return {0, 0}; + } + else + { + return {0, lhs / rhs.low}; + } } constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 05306343..a194c60b 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -853,10 +853,10 @@ int main() //test_operator_div(); test_operator_div<__int128>(); - //test_operator_div(); - //test_operator_div(); - //test_operator_div(); - //test_operator_div(); + test_operator_div(); + test_operator_div(); + test_operator_div(); + test_operator_div(); test_operator_div(); return boost::report_errors(); From 24439145f124dd3287985c1e73ac58fc88221252 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 10 Mar 2025 12:26:40 -0400 Subject: [PATCH 092/191] Add and test x64 DIV --- include/boost/decimal/detail/u128.hpp | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 1f87daa1..ae120e9d 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1566,11 +1566,60 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u1 quotient = wide_integer_to_u128(lhs_wide); } +#ifdef __aarch64__ + +// This is unconditionally better on ARM64 BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept { return static_cast(static_cast(lhs) / rhs); } +#else + +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) + { + return static_cast(static_cast(lhs) / rhs); + } + else if (lhs.high != 0) + { + // TODO(mborland): Can we abbreviate Knuth division for this case? + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, u128{0, rhs}, quotient, remainder); + return quotient; + } + else + { + // Windows traps on division by 0 + #ifdef _WIN32 + if (rhs.low != 0) + { + return { 0, lhs.low / rhs }; + } + else + { + return { 0, 0 }; + } + #else + + return {0, lhs.low / rhs}; + + #endif + } + + #else + + return static_cast(static_cast(lhs) / rhs); + + #endif +} + +#endif + } template , bool> = true> From a45c86334ca68ebc16c6f182f0f9c857e5bd2f0c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 10 Mar 2025 12:35:12 -0400 Subject: [PATCH 093/191] Speed up x86 mul by using memcpy instead of cast --- include/boost/decimal/detail/u128.hpp | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ae120e9d..4be73d27 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1376,6 +1376,41 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) return {low >> 32, low << 32}; } +#ifdef __x86_64__ + +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(lhs)) + { + return static_cast(static_cast(lhs) * static_cast(rhs)); + } + else + { + unsigned __int128 new_lhs {}; + unsigned __int128 new_rhs {}; + + std::memcpy(&new_lhs, &lhs, sizeof(new_lhs)); + std::memcpy(&new_rhs, &rhs, sizeof(new_rhs)); + + const auto res {new_lhs * new_rhs}; + + u128 new_res {}; + std::memcpy(&new_res, &res, sizeof(new_res)); + + return new_res; + } + + #else + + return static_cast(static_cast(lhs) * static_cast(rhs)); + + #endif +} + +#else + BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { const auto a = static_cast(lhs.low >> 32); @@ -1389,6 +1424,8 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 return result; } +#endif // x64 + BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const std::uint64_t rhs) noexcept { const auto c = static_cast(rhs >> 32); From e7c1a44288b053b95827158b4c4c11049b9ea2ec Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 10 Mar 2025 15:17:59 -0400 Subject: [PATCH 094/191] Add signed division --- include/boost/decimal/detail/u128.hpp | 12 ++++++++++++ test/test_u128.cpp | 20 ++++++++++++++++---- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 4be73d27..c482d8c4 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1738,6 +1738,18 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept #endif } +template , bool> = true> +constexpr u128 operator/(const u128 lhs, const SignedInteger rhs) noexcept +{ + return rhs > 0 ? lhs / static_cast(rhs) : lhs / static_cast(rhs); +} + +template , bool> = true> +constexpr u128 operator/(const SignedInteger lhs, const u128 rhs) noexcept +{ + return lhs > 0 ? static_cast(lhs) / rhs : static_cast(lhs) / rhs; +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator/(const u128 lhs, const __int128 rhs) noexcept diff --git a/test/test_u128.cpp b/test/test_u128.cpp index a194c60b..2b591432 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -626,6 +626,16 @@ void test_operator_div() } } +template +void test_spot_operator_div(IntType value, IntType value2) +{ + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value / value2) == (builtin_value / value2)); + BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); +} + int main() { test_traits(); @@ -847,10 +857,10 @@ int main() test_operator_mul(); test_operator_mul(); - //test_operator_div(); - //test_operator_div(); - //test_operator_div(); - //test_operator_div(); + test_operator_div(); + test_operator_div(); + test_operator_div(); + test_operator_div(); test_operator_div<__int128>(); test_operator_div(); @@ -859,6 +869,8 @@ int main() test_operator_div(); test_operator_div(); + test_spot_operator_div(1, -94); + return boost::report_errors(); } From e59701770244af937c2b0a38a711aa7d9e87e1c5 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 10 Mar 2025 15:20:08 -0400 Subject: [PATCH 095/191] Add compound division operator --- include/boost/decimal/detail/u128.hpp | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index c482d8c4..6ddd0cd9 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -194,6 +194,20 @@ u128 constexpr u128& operator*=(__int128 rhs) noexcept; constexpr u128& operator*=(unsigned __int128 rhs) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 + + // Compound Division Operators + template , bool> = true> + constexpr u128& operator/=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator/=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator/=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator/=(__int128 rhs) noexcept; + constexpr u128& operator/=(unsigned __int128 rhs) noexcept; + #endif // BOOST_DECIMAL_HAS_INT128 }; // Signed assignment operators @@ -1774,6 +1788,46 @@ constexpr u128 operator/(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound Division Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator/=(const SignedInteger rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator/=(const UnsignedInteger rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +constexpr u128& u128::operator/=(const u128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator/=(const __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost From 62ad488c62b139a7c94934b3f70e6b1be199bc8b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 10 Mar 2025 16:53:44 -0400 Subject: [PATCH 096/191] Fix division logic --- include/boost/decimal/detail/u128.hpp | 29 +++++---------------------- 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 6ddd0cd9..76f8c9b2 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1719,34 +1719,15 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept #endif } - else if (lhs < rhs) + else if (rhs.high == 0) { - return {0, 0}; - } - else if (lhs.high == 0 && rhs.high == 0) - { - // Only Windows platforms trap on division by zero - #ifdef _WIN32 - if (rhs.low != 0) - { - return { 0, lhs.low / rhs.low }; - } - else - { - return { 0, 0 }; - } - #else - - return { 0, lhs.low / rhs.low }; - - #endif + // Becomes u128 / std::uint64_t abbreviated division + return impl::default_div(lhs, rhs.low); } else { - u128 quotient {}; - u128 remainder {}; - impl::div_mod_impl(lhs, rhs, quotient, remainder); - return quotient; + // This would imply rhs.high != 0 and lhs.high == 0 which is always 0 + return {0, 0}; } #endif From b06aa217a5ed46ebeab20dcc0ebbaa8179159958 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 09:55:52 -0400 Subject: [PATCH 097/191] Add std::numeric_limits overload --- include/boost/decimal/detail/u128.hpp | 50 +++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 76f8c9b2..3a472d98 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1813,4 +1813,54 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept } // namespace decimal } // namespace boost +// Non-standard libraries may add specializations for library-provided types +namespace std { + +template <> +struct numeric_limits +{ + // Member constants + static constexpr bool is_specialized = true; + static constexpr bool is_signed = false; + static constexpr bool is_integer = true; + static constexpr bool is_exact = true; + static constexpr bool has_infinity = false; + static constexpr bool has_quiet_NaN = false; + static constexpr bool has_signaling_NaN = false; + + // These members were deprecated in C++23 + #if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) + static constexpr std::float_denorm_style has_denorm = std::denorm_absent; + static constexpr bool has_denorm_loss = false; + #endif + + static constexpr std::float_round_style round_style = std::round_toward_zero; + static constexpr bool is_iec559 = false; + static constexpr bool is_bounded = true; + static constexpr bool is_modulo = true; + static constexpr int digits = 128; + static constexpr int digits10 = 38; + static constexpr int max_digits10 = 0; + static constexpr int radix = 2; + static constexpr int min_exponent = 0; + static constexpr int min_exponent10 = 0; + static constexpr int max_exponent = 0; + static constexpr int max_exponent10 = 0; + static constexpr bool traps = std::numeric_limits::traps; + static constexpr bool tinyness_before = false; + + // Member functions + static constexpr auto (min) () -> boost::decimal::detail::u128 { return {0, 0}; } + static constexpr auto lowest () -> boost::decimal::detail::u128 { return {0, 0}; } + static constexpr auto (max) () -> boost::decimal::detail::u128 { return {UINT64_MAX, UINT64_MAX}; } + static constexpr auto epsilon () -> boost::decimal::detail::u128 { return {0, 0}; } + static constexpr auto round_error () -> boost::decimal::detail::u128 { return {0, 0}; } + static constexpr auto infinity () -> boost::decimal::detail::u128 { return {0, 0}; } + static constexpr auto quiet_NaN () -> boost::decimal::detail::u128 { return {0, 0}; } + static constexpr auto signaling_NaN() -> boost::decimal::detail::u128 { return {0, 0}; } + static constexpr auto denorm_min () -> boost::decimal::detail::u128 { return {0, 0}; } +}; + +} // namespace std + #endif // BOOST_DECIMAL_DETAIL_U128_HPP From 8cd43cbdc2769913bae720042cfb0ad188303d5b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 10:08:12 -0400 Subject: [PATCH 098/191] Add portable definition of assume --- include/boost/decimal/detail/config.hpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index ed2e2925..14b04c3c 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -356,4 +356,24 @@ typedef unsigned __int128 uint128_t; # endif #endif +#ifdef _MSC_VER +# define BOOST_DECIMAL_ASSUME(expr) __assume(expr) +#elif defined(__clang__) +# define BOOST_DECIMAL_ASSUME(expr) __builtin_assume(expr) +#elif defined(__GNUC__) +# if __GNUC__ >= 5 && __GNUC__ < 13 +# define BOOST_DECIMAL_ASSUME(expr) if (expr) {} else { __builtin_unreachable(); } +# else +# define BOOST_DECIMAL_ASSUME(expr) __attribute__((assume(expr))) +# endif +#elif defined(__has_cpp_attribute) +# if __has_cpp_attribute(assume) +# define BOOST_DECIMAL_ASSUME(expr) [[assume(expr)]] +# else +# define BOOST_DECIMAL_ASSUME(expr) BOOST_DECIMAL_ASSERT(expr) +# endif +#else +# define BOOST_DECIMAL_ASSUME(expr) BOOST_DECIMAL_ASSERT(expr) +#endif + #endif // BOOST_DECIMAL_DETAIL_CONFIG_HPP From 2839528f65dce8e4ff14fc3196c47edb223320df Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 10:49:23 -0400 Subject: [PATCH 099/191] Fix references --- include/boost/decimal/detail/u128.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 3a472d98..c50c9654 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1581,7 +1581,7 @@ constexpr auto u128_to_wide_integer(const u128& src) -> wide_integer_uint128 static_assert(sizeof(local_limb_type) == static_cast(UINT8_C(4)) && std::is_same::value, "Error: Configuration of external wide-integer limbs not OK"); - auto rep = dst.representation(); + auto& rep = dst.representation(); rep[0] = static_cast(src.low); rep[1] = static_cast(src.low >> 32U); @@ -1595,7 +1595,7 @@ constexpr auto wide_integer_to_u128(const wide_integer_uint128& src) -> u128 { u128 dst {}; - const auto rep = src.crepresentation(); + const auto& rep = src.crepresentation(); dst.low = static_cast(rep[0]) | (static_cast(rep[1]) << 32); dst.high = static_cast(rep[2]) | (static_cast(rep[3]) << 32U); From 4667c2da350abb668e76626ddc9b3acdb7f78ea2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 10:49:36 -0400 Subject: [PATCH 100/191] Add additional default division path --- include/boost/decimal/detail/u128.hpp | 34 +++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index c50c9654..415f2692 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1606,15 +1606,39 @@ constexpr auto wide_integer_to_u128(const wide_integer_uint128& src) -> u128 BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u128& rhs, u128& quotient, u128& remainder) noexcept { // Mash-Up: Use Knuth long-division from wide-integer (requires limb-conversions on input/output). + if (rhs.high == UINT64_C(0) && rhs.low < UINT64_C(0x100000000)) + { + const auto rhs32 = static_cast(rhs.low); + + auto current = static_cast(lhs.high >> 32U); + quotient.high = static_cast(static_cast(static_cast(current / rhs32)) << 32U); + remainder.low = static_cast(current % rhs32); - auto lhs_wide = u128_to_wide_integer(lhs); + current = static_cast(remainder.low << 32U) | static_cast(lhs.high); + quotient.high |= static_cast(current / rhs32); + remainder.low = static_cast(current % rhs32); - wide_integer_uint128 rem_wide { }; + current = static_cast(remainder.low << 32U) | static_cast(lhs.low >> 32U); + quotient.low = static_cast(static_cast(static_cast(current / rhs32)) << 32U); + remainder.low = static_cast(current % rhs32); - lhs_wide.eval_divide_knuth(u128_to_wide_integer(rhs), rem_wide); + current = remainder.low << 32U | static_cast(lhs.low); + quotient.low |= static_cast(current / rhs32); + remainder.low = static_cast(current % rhs32); - remainder = wide_integer_to_u128(rem_wide); - quotient = wide_integer_to_u128(lhs_wide); + remainder.high = UINT64_C(0); + } + else + { + auto lhs_wide = u128_to_wide_integer(lhs); + + wide_integer_uint128 rem_wide { }; + + lhs_wide.eval_divide_knuth(u128_to_wide_integer(rhs), rem_wide); + + remainder = wide_integer_to_u128(rem_wide); + quotient = wide_integer_to_u128(lhs_wide); + } } #ifdef __aarch64__ From 520bde76eb4854f637266b40fca1c979ecc6b679 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 10:50:27 -0400 Subject: [PATCH 101/191] Adjust switching logic --- include/boost/decimal/detail/u128.hpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 415f2692..afb0383a 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1743,15 +1743,17 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept #endif } - else if (rhs.high == 0) + else if (rhs > lhs) { - // Becomes u128 / std::uint64_t abbreviated division - return impl::default_div(lhs, rhs.low); + // This would imply rhs.high != 0 and lhs.high == 0 which is always 0 + return {0, 0}; } else { - // This would imply rhs.high != 0 and lhs.high == 0 which is always 0 - return {0, 0}; + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, rhs, quotient, remainder); + return quotient; } #endif From 7310583279c8695fe829e0f3feacb18033f502e9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 11:08:05 -0400 Subject: [PATCH 102/191] Add compound bitwise operators --- include/boost/decimal/detail/u128.hpp | 270 ++++++++++++++++++++++++++ 1 file changed, 270 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index afb0383a..14fb2fa4 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -208,6 +208,76 @@ u128 constexpr u128& operator/=(__int128 rhs) noexcept; constexpr u128& operator/=(unsigned __int128 rhs) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 + + // Compound And + template , bool> = true> + constexpr u128& operator&=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator&=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator&=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator&=(__int128 rhs) noexcept; + constexpr u128& operator&=(unsigned __int128 rhs) noexcept; + #endif + + // Compound Or + template , bool> = true> + constexpr u128& operator|=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator|=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator|=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator|=(__int128 rhs) noexcept; + constexpr u128& operator|=(unsigned __int128 rhs) noexcept; + #endif + + // Compound XOR + template , bool> = true> + constexpr u128& operator^=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator^=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator^=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator^=(__int128 rhs) noexcept; + constexpr u128& operator^=(unsigned __int128 rhs) noexcept; + #endif + + // Compound Left Shift + template , bool> = true> + constexpr u128& operator<<=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator<<=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator<<=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator<<=(__int128 rhs) noexcept; + constexpr u128& operator<<=(unsigned __int128 rhs) noexcept; + #endif + + // Compound Right Shift + template , bool> = true> + constexpr u128& operator>>=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator>>=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator>>=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator>>=(__int128 rhs) noexcept; + constexpr u128& operator>>=(unsigned __int128 rhs) noexcept; + #endif }; // Signed assignment operators @@ -743,6 +813,46 @@ constexpr u128 operator|(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound OR Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator|=(const SignedInteger rhs) noexcept +{ + *this = *this | rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator|=(const UnsignedInteger rhs) noexcept +{ + *this = *this | rhs; + return *this; +} + +constexpr u128& u128::operator|=(const u128 rhs) noexcept +{ + *this = *this | rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator|=(const __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +constexpr u128& u128::operator|=(const unsigned __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + //===================================== // And Operator //===================================== @@ -800,6 +910,46 @@ constexpr u128 operator&(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound And Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator&=(const SignedInteger rhs) noexcept +{ + *this = *this & rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator&=(const UnsignedInteger rhs) noexcept +{ + *this = *this & rhs; + return *this; +} + +constexpr u128& u128::operator&=(const u128 rhs) noexcept +{ + *this = *this & rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator&=(const __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +constexpr u128& u128::operator&=(const unsigned __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + //===================================== // Xor Operator //===================================== @@ -857,6 +1007,46 @@ constexpr u128 operator^(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound XOR Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator^=(const SignedInteger rhs) noexcept +{ + *this = *this ^ rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator^=(const UnsignedInteger rhs) noexcept +{ + *this = *this ^ rhs; + return *this; +} + +constexpr u128& u128::operator^=(const u128 rhs) noexcept +{ + *this = *this ^ rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator^=(const __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +constexpr u128& u128::operator^=(const unsigned __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + //===================================== // Left Shift Operator //===================================== @@ -981,6 +1171,46 @@ constexpr unsigned __int128 operator<<(const unsigned __int128 lhs, const u128 r #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound Left Shift Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator<<=(const SignedInteger rhs) noexcept +{ + *this = *this << rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator<<=(const UnsignedInteger rhs) noexcept +{ + *this = *this << rhs; + return *this; +} + +constexpr u128& u128::operator<<=(const u128 rhs) noexcept +{ + *this = *this << rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator<<=(const __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +constexpr u128& u128::operator<<=(const unsigned __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + //===================================== // Right Shift Operator //===================================== @@ -1105,6 +1335,46 @@ constexpr unsigned __int128 operator>>(const unsigned __int128 lhs, const u128 r #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound Right Shift Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator>>=(const SignedInteger rhs) noexcept +{ + *this = *this >> rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator>>=(const UnsignedInteger rhs) noexcept +{ + *this = *this >> rhs; + return *this; +} + +constexpr u128& u128::operator>>=(const u128 rhs) noexcept +{ + *this = *this >> rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator>>=(const __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +constexpr u128& u128::operator>>=(const unsigned __int128 rhs) noexcept +{ + *this = *this / rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + //===================================== // Addition Operator //===================================== From 2f437e25217aa53ce464e5bbb1b07140eecb82f7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 11:32:14 -0400 Subject: [PATCH 103/191] Fix MSVC error class vs struct --- include/boost/decimal/detail/u128.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 14fb2fa4..2d7fa392 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2113,8 +2113,9 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept namespace std { template <> -struct numeric_limits +class numeric_limits { +public: // Member constants static constexpr bool is_specialized = true; static constexpr bool is_signed = false; From fefb9ef2a50fda0b7fab7253ff615c8a5586d021 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 11:54:23 -0400 Subject: [PATCH 104/191] Add testing of numeric limits --- test/test_u128.cpp | 56 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 2b591432..83a0dd83 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -65,6 +65,58 @@ void test_traits() static_assert(!is_signed_integer_v && !is_unsigned_integer_v, "Wrong answer"); } +void test_numeric_limits() +{ + using namespace boost::decimal::detail; + + BOOST_TEST(std::numeric_limits::is_specialized); + BOOST_TEST(!std::numeric_limits::is_signed); + BOOST_TEST(std::numeric_limits::is_integer); + BOOST_TEST(std::numeric_limits::is_exact); + BOOST_TEST(!std::numeric_limits::has_infinity); + BOOST_TEST(!std::numeric_limits::has_quiet_NaN); + BOOST_TEST(!std::numeric_limits::has_signaling_NaN); + + BOOST_TEST(std::numeric_limits::round_style == std::round_toward_zero); + BOOST_TEST(!std::numeric_limits::is_iec559); + BOOST_TEST(std::numeric_limits::is_bounded); + BOOST_TEST(std::numeric_limits::is_modulo); + BOOST_TEST_EQ(std::numeric_limits::digits, sizeof(u128) * 8U); + BOOST_TEST_EQ(std::numeric_limits::digits10, static_cast(std::numeric_limits::digits * std::log10(2))); + BOOST_TEST_EQ(std::numeric_limits::max_digits10, std::numeric_limits::max_digits10); + BOOST_TEST_EQ(std::numeric_limits::radix, std::numeric_limits::radix); + BOOST_TEST_EQ(std::numeric_limits::min_exponent, std::numeric_limits::min_exponent); + BOOST_TEST_EQ(std::numeric_limits::min_exponent10, std::numeric_limits::min_exponent10); + BOOST_TEST_EQ(std::numeric_limits::max_exponent, std::numeric_limits::max_exponent); + BOOST_TEST_EQ(std::numeric_limits::max_exponent10, std::numeric_limits::max_exponent10); + BOOST_TEST_EQ(std::numeric_limits::traps, std::numeric_limits::traps); + BOOST_TEST_EQ(std::numeric_limits::tinyness_before, std::numeric_limits::tinyness_before); + + BOOST_TEST(std::numeric_limits::min() == std::numeric_limits::min()); + BOOST_TEST(std::numeric_limits::lowest() == std::numeric_limits::lowest()); + + #ifndef BOOST_DECIMAL_HAS_INT128 + + constexpr u128 two128 {std::numeric_limits::max(), std::numeric_limits::max()}; + BOOST_TEST(std::numeric_limits::max() == two128); + + #else + + unsigned __int128 max_value {std::numeric_limits::max()}; + max_value <<= 64U; + max_value |= std::numeric_limits::max(); + BOOST_TEST(std::numeric_limits::max() == max_value); + + #endif + + BOOST_TEST(std::numeric_limits::epsilon() == std::numeric_limits::epsilon()); + BOOST_TEST(std::numeric_limits::round_error() == std::numeric_limits::round_error()); + BOOST_TEST(std::numeric_limits::infinity() == std::numeric_limits::infinity()); + BOOST_TEST(std::numeric_limits::quiet_NaN() == std::numeric_limits::quiet_NaN()); + BOOST_TEST(std::numeric_limits::signaling_NaN() == std::numeric_limits::signaling_NaN()); + BOOST_TEST(std::numeric_limits::denorm_min() == std::numeric_limits::denorm_min()); +} + #ifdef BOOST_DECIMAL_HAS_INT128 template @@ -640,6 +692,8 @@ int main() { test_traits(); + test_numeric_limits(); + test_arithmetic_constructor(); test_arithmetic_constructor(); test_arithmetic_constructor(); @@ -1323,6 +1377,8 @@ int main() { test_traits(); + test_numeric_limits(); + test_arithmetic_constructor(); test_arithmetic_constructor(); test_arithmetic_constructor(); From b75659e0c7e34379fc79952283ff06ba719632c8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 11:54:31 -0400 Subject: [PATCH 105/191] Fix C++14 linkage --- include/boost/decimal/detail/u128.hpp | 44 ++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 2d7fa392..9a355497 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2110,12 +2110,18 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept } // namespace boost // Non-standard libraries may add specializations for library-provided types -namespace std { - template <> -class numeric_limits +#ifdef _MSC_VER +class std::numeric_limitsboost::decimal::detail::u1282> +#else +struct std::numeric_limits +#endif { + +#ifdef _MSC_VER public: +#endif + // Member constants static constexpr bool is_specialized = true; static constexpr bool is_signed = false; @@ -2158,6 +2164,36 @@ class numeric_limits static constexpr auto denorm_min () -> boost::decimal::detail::u128 { return {0, 0}; } }; -} // namespace std +#if (!defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L ) && (!defined(_MSC_VER) || _MSC_VER != 1900) + +constexpr bool std::numeric_limits::is_specialized; +constexpr bool std::numeric_limits::is_signed; +constexpr bool std::numeric_limits::is_integer; +constexpr bool std::numeric_limits::is_exact; +constexpr bool std::numeric_limits::has_infinity; +constexpr bool std::numeric_limits::has_quiet_NaN; +constexpr bool std::numeric_limits::has_signaling_NaN; +constexpr std::float_round_style std::numeric_limits::round_style; +constexpr bool std::numeric_limits::is_iec559; +constexpr bool std::numeric_limits::is_bounded; +constexpr bool std::numeric_limits::is_modulo; +constexpr int std::numeric_limits::digits; +constexpr int std::numeric_limits::digits10; +constexpr int std::numeric_limits::max_digits10; +constexpr int std::numeric_limits::radix; +constexpr int std::numeric_limits::min_exponent; +constexpr int std::numeric_limits::min_exponent10; +constexpr int std::numeric_limits::max_exponent; +constexpr int std::numeric_limits::max_exponent10; +constexpr bool std::numeric_limits::traps; +constexpr bool std::numeric_limits::tinyness_before; + +// Also add the deprecated members if needed +#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) +constexpr std::float_denorm_style std::numeric_limits::has_denorm; +constexpr bool std::numeric_limits::has_denorm_loss; +#endif + +#endif #endif // BOOST_DECIMAL_DETAIL_U128_HPP From b179d1d6bcf52fd10181dc33356026893c7527ab Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 12:34:24 -0400 Subject: [PATCH 106/191] Fix 128 by 64 division for windows --- include/boost/decimal/detail/u128.hpp | 56 ++++++++++++++++++++------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 9a355497..944d82bd 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1919,7 +1919,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std: return static_cast(static_cast(lhs) / rhs); } -#else +#elif defined(BOOST_DECIMAL_HAS_INT128) BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept { @@ -1939,26 +1939,53 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std: } else { - // Windows traps on division by 0 - #ifdef _WIN32 - if (rhs.low != 0) - { - return { 0, lhs.low / rhs }; - } - else + return {0, lhs.low / rhs}; + } + + #else + + return static_cast(static_cast(lhs) / rhs); + + #endif +} + +#else + +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) + { + u128 quotient{}; + u128 remainder{}; + impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + return quotient; + } + else if (lhs.high != 0) + { + // TODO(mborland): Can we abbreviate Knuth division for this case? + u128 quotient{}; + u128 remainder{}; + impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + return quotient; + } + else + { + if (rhs == 0) { return { 0, 0 }; } - #else - - return {0, lhs.low / rhs}; - #endif + return { 0, lhs.low / rhs }; } #else - return static_cast(static_cast(lhs) / rhs); + u128 quotient{}; + u128 remainder{}; + impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + return quotient; #endif } @@ -2109,10 +2136,11 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept } // namespace decimal } // namespace boost + // Non-standard libraries may add specializations for library-provided types template <> #ifdef _MSC_VER -class std::numeric_limitsboost::decimal::detail::u1282> +class std::numeric_limits #else struct std::numeric_limits #endif From cbacfbd0fa39ad67ff8eb7ed5335c40e0ec8ecce Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 16:36:25 -0400 Subject: [PATCH 107/191] Improve PPC64LE and S390x DIV performance --- include/boost/decimal/detail/u128.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 944d82bd..51733c9e 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1911,9 +1911,9 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u1 } } -#ifdef __aarch64__ +#if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) -// This is unconditionally better on ARM64 +// This is unconditionally better on ARM64, PPC64LE, and S390X BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept { return static_cast(static_cast(lhs) / rhs); @@ -2015,9 +2015,9 @@ constexpr u128 operator/(const UnsignedInteger lhs, const u128 rhs) noexcept constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept { - // On ARM64 this is unconditionally better + // On ARM64 and PPC64LE this is unconditionally better // On x64 this is only better when both lhs and rhs are two word numbers - #ifdef __aarch64__ + #if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) return static_cast(static_cast(lhs) / static_cast(rhs)); From 33b32efca49b73b8a74f21d22ca29b77dbd6af95 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 11 Mar 2025 17:07:39 -0400 Subject: [PATCH 108/191] Optimize x64 division --- include/boost/decimal/detail/u128.hpp | 38 +++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 51733c9e..e0c1e65e 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2025,6 +2025,42 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept BOOST_DECIMAL_ASSERT_MSG(rhs != 0, "Division by zero"); + // The is best x64 path assuming the user has consteval detection + #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_HAS_INT128) + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) + { + return static_cast(static_cast(lhs) / static_cast(rhs)); + } + else if (lhs.high != 0 && rhs.high != 0) + { + unsigned __int128 new_lhs {}; + unsigned __int128 new_rhs {}; + + std::memcpy(&new_lhs, &lhs, sizeof(new_lhs)); + std::memcpy(&new_rhs, &rhs, sizeof(new_rhs)); + + const auto res {new_lhs / new_rhs}; + + u128 new_res {}; + std::memcpy(&new_res, &res, sizeof(new_res)); + + return new_res; + } + else if (rhs > lhs) + { + return {0, 0}; + } + else + { + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, rhs, quotient, remainder); + return quotient; + } + + #else + if (lhs.high != 0 && rhs.high != 0) { #ifdef BOOST_DECIMAL_HAS_INT128 @@ -2052,6 +2088,8 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept impl::div_mod_impl(lhs, rhs, quotient, remainder); return quotient; } + + #endif #endif } From 53d9b5329ed7637322eccfe77ac7742ea8369014 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 10:24:07 -0400 Subject: [PATCH 109/191] Improve PPC64 mul --- include/boost/decimal/detail/u128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index e0c1e65e..c87b10ba 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1660,7 +1660,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) return {low >> 32, low << 32}; } -#ifdef __x86_64__ +#if defined(__x86_64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { From 11e8b2fa619ee2ffc0eea400c923b0ba006b8f88 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 10:36:57 -0400 Subject: [PATCH 110/191] Fix testing of conversion to __float128 and __ibm128 --- test/test_u128.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 83a0dd83..e8bda5af 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -192,6 +192,12 @@ void test_float_conversion_operators() boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); + // Float128 won't have numerics limit defined all the time, + // Plus this affords some additional tolerance + constexpr FloatType error_tol {std::is_same::value ? + static_cast(std::numeric_limits::epsilon()) : + static_cast(std::numeric_limits::epsilon())}; + for (std::size_t i {}; i < N; ++i) { const auto value {dist(rng)}; @@ -202,8 +208,12 @@ void test_float_conversion_operators() const auto builtin_value_return = static_cast(builtin_value); const auto emulated_value_return = static_cast(emulated_value); + FloatType distance = builtin_value_return - emulated_value_return; - BOOST_TEST(std::abs(builtin_value_return - emulated_value_return) < std::numeric_limits::epsilon()); + // We don't want to pull in quad math for a simple abs calculation... + distance = distance < 0 ? -distance : distance; + + BOOST_TEST(distance < error_tol); } } From b41e58156ea3f1a48d857e05cee3ef603c071766 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 10:50:09 -0400 Subject: [PATCH 111/191] Simplify float conversion operators --- include/boost/decimal/detail/u128.hpp | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index c87b10ba..d9174f48 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -306,30 +306,41 @@ constexpr u128& u128::operator=(const UnsignedInteger value) noexcept { low = st constexpr u128& u128::operator=(const unsigned __int128 value) noexcept { low = static_cast(value & low_word_mask); high = static_cast(value >> 64U); return *this; } #endif +//===================================== +// 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 +namespace impl { + +template +static constexpr T offset_value_v = static_cast(std::numeric_limits::max()); + +} + constexpr u128::operator float() const noexcept { - constexpr float offset {static_cast(std::numeric_limits::max())}; - return static_cast(high) * offset + static_cast(low); + return static_cast(high) * impl::offset_value_v + static_cast(low); } constexpr u128::operator double() const noexcept { - constexpr double offset {static_cast(std::numeric_limits::max())}; - return static_cast(high) * offset + static_cast(low); + return static_cast(high) * impl::offset_value_v + static_cast(low); } constexpr u128::operator long double() const noexcept { - constexpr long double offset {static_cast(std::numeric_limits::max())}; - return static_cast(high) * offset + static_cast(low); + return static_cast(high) * impl::offset_value_v + static_cast(low); } #ifdef BOOST_DECIMAL_HAS_FLOAT128 constexpr u128::operator __float128() const noexcept { - constexpr __float128 offset {static_cast<__float128>(std::numeric_limits::max())}; - return static_cast<__float128>(high) * offset + static_cast<__float128>(low); + return static_cast<__float128>(high) * impl::offset_value_v<__float128> + static_cast<__float128>(low); } #endif // BOOST_DECIMAL_HAS_FLOAT128 From baeda79023cd282e1c0c08f0d87d3964c4d63a6b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:12:36 -0400 Subject: [PATCH 112/191] Add tests for modulo --- test/test_u128.cpp | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index e8bda5af..318059d3 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -688,6 +688,34 @@ void test_operator_div() } } +template +void test_operator_mod() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + std::numeric_limits::max()); + + for (std::size_t i {}; i < N; ++i) + { + IntType value {0}; + IntType value2 {0}; + + while (value == 0) + { + value = dist(rng); + } + while (value2 == 0) + { + value2 = dist(rng); + } + + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + BOOST_TEST((emulated_value % value2) == (builtin_value % value2)); + BOOST_TEST((value2 % emulated_value) == (value2 % builtin_value)); + } +} + template void test_spot_operator_div(IntType value, IntType value2) { @@ -935,6 +963,18 @@ int main() test_spot_operator_div(1, -94); + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + test_operator_mod<__int128>(); + + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + return boost::report_errors(); } From d1afc8bf9a23334b3ac401b4034914b0e8c4bbb0 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:12:52 -0400 Subject: [PATCH 113/191] Add benchmarks for modulo --- test/benchmark_uints.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 5f20f8dd..cffb7322 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -503,6 +503,15 @@ int main() test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_two_element_operation(builtin_vector, std::modulus<>(), "mod", "Builtin"); + #endif + + test_two_element_operation(old_vector, std::modulus<>(), "mod", "Old"); + test_two_element_operation(new_vector, std::modulus<>(), "div", "New"); } return 1; From b0f37baab9c01e325d35f373048e0b88a4716c02 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:14:41 -0400 Subject: [PATCH 114/191] Add 128-bit modulo --- include/boost/decimal/detail/u128.hpp | 108 ++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index d9174f48..8c99be7e 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2181,6 +2181,114 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Modulo Operator +//===================================== + +constexpr u128 operator%(const u128 lhs, const u128 rhs) noexcept +{ + // On ARM64 and PPC64LE this is unconditionally better + // On x64 this is only better when both lhs and rhs are two word numbers + #if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) + + return static_cast(static_cast(lhs) % static_cast(rhs)); + + #else + + BOOST_DECIMAL_ASSERT_MSG(rhs != 0, "Division by zero"); + + // The is best x64 path assuming the user has consteval detection + #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_HAS_INT128) + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) + { + return static_cast(static_cast(lhs) % static_cast(rhs)); + } + else if (lhs.high != 0 && rhs.high != 0) + { + unsigned __int128 new_lhs {}; + unsigned __int128 new_rhs {}; + + std::memcpy(&new_lhs, &lhs, sizeof(new_lhs)); + std::memcpy(&new_rhs, &rhs, sizeof(new_rhs)); + + const auto res {new_lhs % new_rhs}; + + u128 new_res {}; + std::memcpy(&new_res, &res, sizeof(new_res)); + + return new_res; + } + else if (rhs > lhs) + { + return lhs; + } + else + { + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, rhs, quotient, remainder); + return remainder; + } + + #else + + if (lhs.high != 0 && rhs.high != 0) + { + #ifdef BOOST_DECIMAL_HAS_INT128 + + return static_cast(static_cast(lhs) % static_cast(rhs)); + + #else + + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, rhs, quotient, remainder); + return remainder; + + #endif + } + else if (rhs > lhs) + { + return lhs; + } + else + { + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, rhs, quotient, remainder); + return remainder; + } + + #endif + + #endif +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128 operator%(const u128 lhs, const __int128 rhs) noexcept +{ + return lhs % static_cast(rhs); +} + +constexpr u128 operator%(const __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) % rhs; +} + +constexpr u128 operator%(const u128 lhs, const unsigned __int128 rhs) noexcept +{ + return lhs % static_cast(rhs); +} + +constexpr u128 operator%(const unsigned __int128 lhs, const u128 rhs) noexcept +{ + return static_cast(lhs) / rhs; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost From c238814168d874aca397cebf8e43fdf7a57cf166 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:29:22 -0400 Subject: [PATCH 115/191] Add modulo implementation details --- include/boost/decimal/detail/u128.hpp | 85 +++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8c99be7e..552147f6 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2185,6 +2185,91 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept // Modulo Operator //===================================== +namespace impl { + +#if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) + +// This is unconditionally better on ARM64, PPC64LE, and S390X +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mod(const u128 lhs, const std::uint64_t rhs) noexcept +{ + return static_cast(static_cast(lhs) % rhs); +} + +#elif defined(BOOST_DECIMAL_HAS_INT128) + +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mod(const u128 lhs, const std::uint64_t rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) + { + return static_cast(static_cast(lhs) % rhs); + } + else if (lhs.high != 0) + { + // TODO(mborland): Can we abbreviate Knuth division for this case? + u128 quotient {}; + u128 remainder {}; + impl::div_mod_impl(lhs, u128{0, rhs}, quotient, remainder); + return remainder; + } + else + { + return {0, lhs.low % rhs}; + } + + #else + + return static_cast(static_cast(lhs) % rhs); + + #endif +} + +#else + +BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mod(const u128 lhs, const std::uint64_t rhs) noexcept +{ + #ifndef BOOST_DECIMAL_NO_CONSTEVAL_DETECTION + + if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) + { + u128 quotient{}; + u128 remainder{}; + impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + return remainder; + } + else if (lhs.high != 0) + { + // TODO(mborland): Can we abbreviate Knuth division for this case? + u128 quotient{}; + u128 remainder{}; + impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + return remainder; + } + else + { + if (rhs == 0) + { + return { 0, 0 }; + } + + return { 0, lhs.low % rhs }; + } + + #else + + u128 quotient{}; + u128 remainder{}; + impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + return remainder; + + #endif +} + +#endif + +} // namespace impl + constexpr u128 operator%(const u128 lhs, const u128 rhs) noexcept { // On ARM64 and PPC64LE this is unconditionally better From a211bde330c091813f6c3930e89a742eeaf9ab17 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:35:26 -0400 Subject: [PATCH 116/191] Implement generic unsigned modulo --- include/boost/decimal/detail/u128.hpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 552147f6..ed7ff79d 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2350,6 +2350,30 @@ constexpr u128 operator%(const u128 lhs, const u128 rhs) noexcept #endif } +template , bool> = true> +constexpr u128 operator%(const u128 lhs, const UnsignedInteger rhs) noexcept +{ + return impl::default_mod(lhs, static_cast(rhs)); +} + +template , bool> = true> +constexpr u128 operator%(const UnsignedInteger lhs, const u128 rhs) noexcept +{ + if (rhs.high != 0) + { + return lhs; + } + else + { + if (rhs.low == 0) + { + return {0, 0}; + } + + return {0, lhs % rhs.low}; + } +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator%(const u128 lhs, const __int128 rhs) noexcept From 24d202342d1df29edde9833b16ae9e4bc83e67f5 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:37:37 -0400 Subject: [PATCH 117/191] Fix implicit type conversion --- include/boost/decimal/detail/u128.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ed7ff79d..78aeb1a0 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2361,7 +2361,7 @@ constexpr u128 operator%(const UnsignedInteger lhs, const u128 rhs) noexcept { if (rhs.high != 0) { - return lhs; + return {0, static_cast(lhs)}; } else { @@ -2369,8 +2369,10 @@ constexpr u128 operator%(const UnsignedInteger lhs, const u128 rhs) noexcept { return {0, 0}; } - - return {0, lhs % rhs.low}; + else + { + return {0, lhs % rhs.low}; + } } } From e3593ab98ef924f44a742415be9d8f7922bc660a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:38:08 -0400 Subject: [PATCH 118/191] Fix copy-paste error --- include/boost/decimal/detail/u128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 78aeb1a0..b306e003 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2395,7 +2395,7 @@ constexpr u128 operator%(const u128 lhs, const unsigned __int128 rhs) noexcept constexpr u128 operator%(const unsigned __int128 lhs, const u128 rhs) noexcept { - return static_cast(lhs) / rhs; + return static_cast(lhs) % rhs; } #endif // BOOST_DECIMAL_HAS_INT128 From b89a782b16967bd9bcf5cfca1726fbd7354088e9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 12:45:39 -0400 Subject: [PATCH 119/191] Add signed modulo operator --- include/boost/decimal/detail/u128.hpp | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index b306e003..a0a7eb68 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2376,6 +2376,39 @@ constexpr u128 operator%(const UnsignedInteger lhs, const u128 rhs) noexcept } } +template , bool> = true> +constexpr u128 operator%(const u128 lhs, const SignedInteger rhs) noexcept +{ + if (rhs > 0) + { + return impl::default_mod(lhs, static_cast(rhs)); + } + else + { + return lhs % static_cast(rhs); + } +} + +template , bool> = true> +constexpr u128 operator%(const SignedInteger lhs, const u128 rhs) noexcept +{ + if (lhs > 0) + { + if (rhs.high == 0) + { + return {0, static_cast(lhs) % rhs.low}; + } + else + { + return {0, static_cast(lhs)}; + } + } + else + { + return static_cast(lhs) % rhs; + } +} + #ifdef BOOST_DECIMAL_HAS_INT128 constexpr u128 operator%(const u128 lhs, const __int128 rhs) noexcept From 58dd8e3426df18f477f6f3c5c34be57f754f19ef Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:07:14 -0400 Subject: [PATCH 120/191] Add compound modulo operators --- include/boost/decimal/detail/u128.hpp | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index a0a7eb68..94f036cd 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -209,6 +209,20 @@ u128 constexpr u128& operator/=(unsigned __int128 rhs) noexcept; #endif // BOOST_DECIMAL_HAS_INT128 + // Compound Modulo Operators + template , bool> = true> + constexpr u128& operator%=(SignedInteger rhs) noexcept; + + template , bool> = true> + constexpr u128& operator%=(UnsignedInteger rhs) noexcept; + + constexpr u128& operator%=(u128 rhs) noexcept; + + #ifdef BOOST_DECIMAL_HAS_INT128 + constexpr u128& operator%=(__int128 rhs) noexcept; + constexpr u128& operator%=(unsigned __int128 rhs) noexcept; + #endif // BOOST_DECIMAL_HAS_INT128 + // Compound And template , bool> = true> constexpr u128& operator&=(SignedInteger rhs) noexcept; @@ -2433,6 +2447,46 @@ constexpr u128 operator%(const unsigned __int128 lhs, const u128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +//===================================== +// Compound Modulo Operator +//===================================== + +template , bool>> +constexpr u128& u128::operator%=(const SignedInteger rhs) noexcept +{ + *this = *this % rhs; + return *this; +} + +template , bool>> +constexpr u128& u128::operator%=(const UnsignedInteger rhs) noexcept +{ + *this = *this % rhs; + return *this; +} + +constexpr u128& u128::operator%=(const u128 rhs) noexcept +{ + *this = *this % rhs; + return *this; +} + +#ifdef BOOST_DECIMAL_HAS_INT128 + +constexpr u128& u128::operator%=(const __int128 rhs) noexcept +{ + *this = *this % rhs; + return *this; +} + +constexpr u128& u128::operator%=(const unsigned __int128 rhs) noexcept +{ + *this = *this % rhs; + return *this; +} + +#endif // BOOST_DECIMAL_HAS_INT128 + } // namespace detail } // namespace decimal } // namespace boost From a9be79b2d0f3eb40583d473c2910f6a340a097f7 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:10:52 -0400 Subject: [PATCH 121/191] Fix and test compound or --- include/boost/decimal/detail/u128.hpp | 4 ++-- test/test_u128.cpp | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 94f036cd..ad0ab8d5 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -866,13 +866,13 @@ constexpr u128& u128::operator|=(const u128 rhs) noexcept constexpr u128& u128::operator|=(const __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this | rhs; return *this; } constexpr u128& u128::operator|=(const unsigned __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this | rhs; return *this; } diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 318059d3..06d22988 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -497,7 +497,10 @@ void test_operator_or() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value | value2) == (builtin_value | value2)); + auto check_1_value {emulated_value}; + check_1_value |= value2; + + BOOST_TEST(check_1_value == (builtin_value | value2)); BOOST_TEST((value2 | emulated_value) == (value2 | builtin_value)); } } From feaac25a5ad7b6c79967a715b722c38a55f59280 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:12:04 -0400 Subject: [PATCH 122/191] Fix and test compound and --- include/boost/decimal/detail/u128.hpp | 4 ++-- test/test_u128.cpp | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ad0ab8d5..3648cf66 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -963,13 +963,13 @@ constexpr u128& u128::operator&=(const u128 rhs) noexcept constexpr u128& u128::operator&=(const __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this & rhs; return *this; } constexpr u128& u128::operator&=(const unsigned __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this & rhs; return *this; } diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 06d22988..b3a513f4 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -518,7 +518,10 @@ void test_operator_and() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value & value2) == (builtin_value & value2)); + auto check_1_value {emulated_value}; + check_1_value &= value2; + + BOOST_TEST(check_1_value == (builtin_value & value2)); BOOST_TEST((value2 & emulated_value) == (value2 & builtin_value)); } } From f84fda5ff3c61158d967008cf080378303db1146 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:12:51 -0400 Subject: [PATCH 123/191] Fix and test compound xor --- include/boost/decimal/detail/u128.hpp | 4 ++-- test/test_u128.cpp | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 3648cf66..7c9baed6 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1060,13 +1060,13 @@ constexpr u128& u128::operator^=(const u128 rhs) noexcept constexpr u128& u128::operator^=(const __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this ^ rhs; return *this; } constexpr u128& u128::operator^=(const unsigned __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this ^ rhs; return *this; } diff --git a/test/test_u128.cpp b/test/test_u128.cpp index b3a513f4..59571ac1 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -539,7 +539,10 @@ void test_operator_xor() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value ^ value2) == (builtin_value ^ value2)); + auto check_1_value {emulated_value}; + check_1_value ^= value2; + + BOOST_TEST(check_1_value == (builtin_value ^ value2)); BOOST_TEST((value2 ^ emulated_value) == (value2 ^ builtin_value)); } } From 59208e4d614aee15af94aa7dfa5b411007cf90e0 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:13:50 -0400 Subject: [PATCH 124/191] Fix and test compound left shift --- include/boost/decimal/detail/u128.hpp | 4 ++-- test/test_u128.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 7c9baed6..fd5d5a7f 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1224,13 +1224,13 @@ constexpr u128& u128::operator<<=(const u128 rhs) noexcept constexpr u128& u128::operator<<=(const __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this << rhs; return *this; } constexpr u128& u128::operator<<=(const unsigned __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this << rhs; return *this; } diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 59571ac1..30808486 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -562,7 +562,9 @@ void test_operator_left_shift() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value << shift_value) == (builtin_value << shift_value)); + auto check_1_value {emulated_value}; + check_1_value <<= shift_value; + BOOST_TEST(check_1_value == (builtin_value << shift_value)); emulated_value = shift_value; builtin_value = shift_value; From dd2719091aae306fcb4e9137fe09ac9d2c3ae1b8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:14:43 -0400 Subject: [PATCH 125/191] Fix and test compound right shift --- include/boost/decimal/detail/u128.hpp | 4 ++-- test/test_u128.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index fd5d5a7f..7a7360bc 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1388,13 +1388,13 @@ constexpr u128& u128::operator>>=(const u128 rhs) noexcept constexpr u128& u128::operator>>=(const __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this >> rhs; return *this; } constexpr u128& u128::operator>>=(const unsigned __int128 rhs) noexcept { - *this = *this / rhs; + *this = *this >> rhs; return *this; } diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 30808486..211835fe 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -587,7 +587,9 @@ void test_operator_right_shift() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value >> shift_value) == (builtin_value >> shift_value)); + auto check_1_value {emulated_value}; + check_1_value >>= shift_value; + BOOST_TEST(check_1_value == (builtin_value >> shift_value)); emulated_value = shift_value; builtin_value = shift_value; From c3cf01b15e5ae3936c95e6e26409a9289bb3a244 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:19:30 -0400 Subject: [PATCH 126/191] Add compound add testing and fix copy-paste error sub test --- test/test_u128.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 211835fe..f2c21ac4 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -610,7 +610,10 @@ void test_operator_add() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value + value2) == (builtin_value + value2)); + + auto check_1_value {emulated_value}; + check_1_value += value2; + BOOST_TEST(check_1_value == (builtin_value + value2)); BOOST_TEST((value2 + emulated_value) == (value2 + builtin_value)); } } @@ -628,8 +631,10 @@ void test_operator_sub() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value + value2) == (builtin_value + value2)); - BOOST_TEST((value2 + emulated_value) == (value2 + builtin_value)); + auto check_1_value {emulated_value}; + check_1_value -= value2; + BOOST_TEST(check_1_value == (builtin_value - value2)); + BOOST_TEST((value2 - emulated_value) == (value2 - builtin_value)); } } From fb868673c1b9c7ee764fdf3f548b9cf59ad36ca9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:30:04 -0400 Subject: [PATCH 127/191] Fix signed subtraction --- include/boost/decimal/detail/u128.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 7a7360bc..1b06a8e2 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1595,12 +1595,11 @@ constexpr u128 operator-(const u128 lhs, const SignedInteger rhs) noexcept { if (rhs < 0) { - return impl::default_add(lhs, static_cast(rhs)); + return impl::default_add(lhs, static_cast(-rhs)); } else { - const auto unsigned_rhs {detail::make_positive_unsigned(rhs)}; - return impl::default_sub(lhs, unsigned_rhs); + return impl::default_sub(lhs, static_cast(rhs)); } } From a810fef9cdb15005c5080e704202b4d5ccc69d8f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:37:10 -0400 Subject: [PATCH 128/191] Test remaining compound operators --- test/test_u128.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index f2c21ac4..f4f52f9a 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -653,7 +653,9 @@ void test_operator_mul() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value * value2) == (builtin_value * value2)); + auto check_1_value {emulated_value}; + check_1_value *= value2; + BOOST_TEST(check_1_value == (builtin_value * value2)); BOOST_TEST((value2 * emulated_value) == (value2 * builtin_value)); } } @@ -673,7 +675,9 @@ void test_operator_mul() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value * value2) == (builtin_value * value2)); + auto check_1_value {emulated_value}; + check_1_value *= value2; + BOOST_TEST(check_1_value == (builtin_value * value2)); BOOST_TEST((value2 * emulated_value) == (value2 * builtin_value)); } } @@ -701,7 +705,9 @@ void test_operator_div() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value / value2) == (builtin_value / value2)); + auto check_1_value {emulated_value}; + check_1_value /= value2; + BOOST_TEST(check_1_value == (builtin_value / value2)); BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); } } @@ -729,7 +735,9 @@ void test_operator_mod() unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; - BOOST_TEST((emulated_value % value2) == (builtin_value % value2)); + auto check_1_value {emulated_value}; + check_1_value %= value2; + BOOST_TEST(check_1_value == (builtin_value % value2)); BOOST_TEST((value2 % emulated_value) == (value2 % builtin_value)); } } From 1f50d75191c2da048f140a979be2040d01442967 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 14:57:14 -0400 Subject: [PATCH 129/191] Add ostream operator --- include/boost/decimal/detail/u128.hpp | 99 +++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 1b06a8e2..3f352d60 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2486,6 +2486,105 @@ constexpr u128& u128::operator%=(const unsigned __int128 rhs) noexcept #endif // BOOST_DECIMAL_HAS_INT128 +#if !defined(BOOST_DECIMAL_DISABLE_IOSTREAM) + +namespace impl { + +inline char* u128_to_dec(char (&buffer)[ 64 ], u128 v) +{ + char* p = buffer + 64; + *--p = '\0'; + + do + { + *--p = "0123456789"[ static_cast(v.low % 10) ]; + v /= 10; + } while ( v != 0 ); + + return p; +} + +inline char* u128_to_hex(char (&buffer)[ 64 ], u128 v, bool upper_case) +{ + const auto alphabet = upper_case ? "0123456789ABCDEF" : "0123456789abcdef"; + + char* p = buffer + 64; + *--p = '\0'; + + do + { + const auto last_byte = v.low & 0xF; + *--p = alphabet[ static_cast(last_byte)]; + v >>= 4; + } while (v != 0); + + return p; +} + +inline char* u128_to_octal(char (&buffer)[ 64 ], u128 v) +{ + char* p = buffer + 64; + *--p = '\0'; + + do + { + const auto last_octet = v.low & 0x7; + *--p = "01234567"[ static_cast(last_octet)]; + v >>= 3; + } while (v != 0); + + return p; +} + +inline char* u128_to_binary(char (&buffer)[ 64 ], u128 v) +{ + char* p = buffer + 64; + *--p = '\0'; + + do + { + const auto last_bit = v.low & 0x1; + *--p = "01"[ static_cast(last_bit)]; + v >>= 1; + } while (v != 0); + + return p; +} + +} // namespace impl + +template +std::basic_ostream& operator<<(std::basic_ostream& os, u128 v) +{ + char buffer[64]; + + auto os_flags {os.flags()}; + + switch (os_flags & std::ios::basefield) + { + case std::ios::dec: + os << impl::u128_to_dec(buffer, v); + break; + case std::ios::hex: + os << impl::u128_to_hex(buffer, v, os_flags & std::ios::uppercase); + break; + case std::ios::oct: + os << impl::u128_to_octal(buffer, v); + break; + case std::ios::binary: + os << impl::u128_to_binary(buffer, v); + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + + return os; +} + +#endif // BOOST_DECIMAL_DISABLE_IOSTREAM + } // namespace detail } // namespace decimal } // namespace boost From eac0584a5e90c7a1b3edfe2b6397c84af278bca6 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 15:30:58 -0400 Subject: [PATCH 130/191] Add ostream operator testing --- include/boost/decimal/detail/u128.hpp | 18 ------------------ test/test_u128.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 3f352d60..4dd40b2b 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2536,21 +2536,6 @@ inline char* u128_to_octal(char (&buffer)[ 64 ], u128 v) return p; } -inline char* u128_to_binary(char (&buffer)[ 64 ], u128 v) -{ - char* p = buffer + 64; - *--p = '\0'; - - do - { - const auto last_bit = v.low & 0x1; - *--p = "01"[ static_cast(last_bit)]; - v >>= 1; - } while (v != 0); - - return p; -} - } // namespace impl template @@ -2571,9 +2556,6 @@ std::basic_ostream& operator<<(std::basic_ostream& case std::ios::oct: os << impl::u128_to_octal(buffer, v); break; - case std::ios::binary: - os << impl::u128_to_binary(buffer, v); - break; // LCOV_EXCL_START default: BOOST_DECIMAL_UNREACHABLE; diff --git a/test/test_u128.cpp b/test/test_u128.cpp index f4f52f9a..d7dd5438 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include #if defined(__clang__) # pragma clang diagnostic push @@ -752,6 +755,26 @@ void test_spot_operator_div(IntType value, IntType value2) BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); } +void test_ostream_operator() +{ + std::stringstream out; + constexpr boost::decimal::detail::u128 small_num {0, 15}; + + out << small_num; + BOOST_TEST_CSTR_EQ(out.str().c_str(), "15"); + + std::stringstream out_hex_upper; + std::stringstream out_hex_lower; + out_hex_upper << std::hex << std::uppercase << small_num; + out_hex_lower << std::hex << small_num; + BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F"); + BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f"); + + std::stringstream out_oct; + out_oct << std::oct << small_num; + BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "17"); +} + int main() { test_traits(); @@ -1001,6 +1024,8 @@ int main() test_operator_mod(); test_operator_mod(); + test_ostream_operator(); + return boost::report_errors(); } From 58ad2c290aa16cee472bb91322afc2a3390523bd Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 16:07:07 -0400 Subject: [PATCH 131/191] Add testing of numbers with high word --- test/test_u128.cpp | 52 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index d7dd5438..9c87267c 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -757,22 +757,42 @@ void test_spot_operator_div(IntType value, IntType value2) void test_ostream_operator() { - std::stringstream out; - constexpr boost::decimal::detail::u128 small_num {0, 15}; - - out << small_num; - BOOST_TEST_CSTR_EQ(out.str().c_str(), "15"); - - std::stringstream out_hex_upper; - std::stringstream out_hex_lower; - out_hex_upper << std::hex << std::uppercase << small_num; - out_hex_lower << std::hex << small_num; - BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F"); - BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f"); - - std::stringstream out_oct; - out_oct << std::oct << small_num; - BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "17"); + { + std::stringstream out; + constexpr boost::decimal::detail::u128 small_num {0, 15}; + out << small_num; + BOOST_TEST_CSTR_EQ(out.str().c_str(), "15"); + + std::stringstream out_hex_upper; + std::stringstream out_hex_lower; + out_hex_upper << std::hex << std::uppercase << small_num; + out_hex_lower << std::hex << small_num; + BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F"); + BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f"); + + std::stringstream out_oct; + out_oct << std::oct << small_num; + BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "17"); + } + + { + std::stringstream out; + constexpr boost::decimal::detail::u128 big_num {0xF, 0}; + + out << big_num; + BOOST_TEST_CSTR_EQ(out.str().c_str(), "276701161105643274240"); + + std::stringstream out_hex_upper; + std::stringstream out_hex_lower; + out_hex_upper << std::hex << std::uppercase << big_num; + out_hex_lower << std::hex << big_num; + BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F0000000000000000"); + BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f0000000000000000"); + + std::stringstream out_oct; + out_oct << std::oct << big_num; + BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "36000000000000000000000"); + } } int main() From 441629d5ae160a023868584ba1e9f186b97951ef Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 16:07:17 -0400 Subject: [PATCH 132/191] Fix modulo of big num --- include/boost/decimal/detail/u128.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 4dd40b2b..3f40c445 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2497,8 +2497,8 @@ inline char* u128_to_dec(char (&buffer)[ 64 ], u128 v) do { - *--p = "0123456789"[ static_cast(v.low % 10) ]; - v /= 10; + *--p = "0123456789"[ static_cast(v % UINT64_C(10)) ]; + v /= UINT64_C(10); } while ( v != 0 ); return p; From b74337978c56fa74c42b4a3554b1cf0e4a50d9f9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 16:56:33 -0400 Subject: [PATCH 133/191] Add istream operator testing --- test/test_u128.cpp | 161 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 121 insertions(+), 40 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 9c87267c..114cb67c 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -120,6 +120,123 @@ void test_numeric_limits() BOOST_TEST(std::numeric_limits::denorm_min() == std::numeric_limits::denorm_min()); } +void test_ostream_operator() +{ + { + std::stringstream out; + constexpr boost::decimal::detail::u128 small_num {0, 15}; + out << small_num; + BOOST_TEST_CSTR_EQ(out.str().c_str(), "15"); + + std::stringstream out_hex_upper; + std::stringstream out_hex_lower; + out_hex_upper << std::hex << std::uppercase << small_num; + out_hex_lower << std::hex << small_num; + BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F"); + BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f"); + + std::stringstream out_oct; + out_oct << std::oct << small_num; + BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "17"); + } + + { + std::stringstream out; + constexpr boost::decimal::detail::u128 big_num {0xF, 0}; + + out << big_num; + BOOST_TEST_CSTR_EQ(out.str().c_str(), "276701161105643274240"); + + std::stringstream out_hex_upper; + std::stringstream out_hex_lower; + out_hex_upper << std::hex << std::uppercase << big_num; + out_hex_lower << std::hex << big_num; + BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F0000000000000000"); + BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f0000000000000000"); + + std::stringstream out_oct; + out_oct << std::oct << big_num; + BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "36000000000000000000000"); + } +} + +void test_istream_operator() +{ + { + std::stringstream in; + in.str("15"); + boost::decimal::detail::u128 num; + in >> num; + BOOST_TEST_EQ(num.low, UINT64_C(15)); + + num = 0; + std::stringstream hex_upper; + hex_upper.str("F"); + hex_upper >> std::hex; + hex_upper >> num; + BOOST_TEST_EQ(num.low, UINT64_C(15)); + + num = 0; + std::stringstream hex_lower; + hex_lower.str("f"); + hex_lower >> std::hex; + hex_lower >> num; + BOOST_TEST_EQ(num.low, UINT64_C(15)); + + num = 0; + std::stringstream octal_lower; + octal_lower.str("17"); + octal_lower >> std::oct; + octal_lower >> num; + BOOST_TEST_EQ(num.low, UINT64_C(15)); + + num = 0; + auto wide_str = L"15"; + std::wstringstream wide_dec; + wide_dec.str(wide_str); + wide_dec >> num; + BOOST_TEST_EQ(num.low, UINT64_C(15)); + } + + { + constexpr boost::decimal::detail::u128 res {0xF, 0}; + + std::stringstream in; + in.str("276701161105643274240"); + boost::decimal::detail::u128 num; + in >> num; + BOOST_TEST_EQ(num, res); + + num = 0; + std::stringstream hex_upper; + hex_upper.str("F0000000000000000"); + hex_upper >> std::hex; + hex_upper >> num; + BOOST_TEST_EQ(num, res); + + num = 0; + std::stringstream hex_lower; + hex_lower.str("f0000000000000000"); + hex_lower >> std::hex; + hex_lower >> num; + BOOST_TEST_EQ(num, res); + + num = 0; + std::stringstream octal_lower; + octal_lower.str("36000000000000000000000"); + octal_lower >> std::oct; + octal_lower >> num; + BOOST_TEST_EQ(num, res); + + num = 0; + auto wide_str = L"276701161105643274240"; + std::wstringstream wide_dec; + wide_dec.str(wide_str); + wide_dec >> num; + BOOST_TEST_EQ(num, res); + } +} + #ifdef BOOST_DECIMAL_HAS_INT128 template @@ -755,46 +872,6 @@ void test_spot_operator_div(IntType value, IntType value2) BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); } -void test_ostream_operator() -{ - { - std::stringstream out; - constexpr boost::decimal::detail::u128 small_num {0, 15}; - out << small_num; - BOOST_TEST_CSTR_EQ(out.str().c_str(), "15"); - - std::stringstream out_hex_upper; - std::stringstream out_hex_lower; - out_hex_upper << std::hex << std::uppercase << small_num; - out_hex_lower << std::hex << small_num; - BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F"); - BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f"); - - std::stringstream out_oct; - out_oct << std::oct << small_num; - BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "17"); - } - - { - std::stringstream out; - constexpr boost::decimal::detail::u128 big_num {0xF, 0}; - - out << big_num; - BOOST_TEST_CSTR_EQ(out.str().c_str(), "276701161105643274240"); - - std::stringstream out_hex_upper; - std::stringstream out_hex_lower; - out_hex_upper << std::hex << std::uppercase << big_num; - out_hex_lower << std::hex << big_num; - BOOST_TEST_CSTR_EQ(out_hex_upper.str().c_str(), "F0000000000000000"); - BOOST_TEST_CSTR_EQ(out_hex_lower.str().c_str(), "f0000000000000000"); - - std::stringstream out_oct; - out_oct << std::oct << big_num; - BOOST_TEST_CSTR_EQ(out_oct.str().c_str(), "36000000000000000000000"); - } -} - int main() { test_traits(); @@ -1045,6 +1122,7 @@ int main() test_operator_mod(); test_ostream_operator(); + test_istream_operator(); return boost::report_errors(); } @@ -1667,6 +1745,9 @@ int main() test_operator_div(); test_operator_div(); + test_ostream_operator(); + test_istream_operator(); + return boost::report_errors(); } From c8378b3bb18ee8ce385b864481fb1da43c9427e2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 16:56:50 -0400 Subject: [PATCH 134/191] Add istream operator --- include/boost/decimal/detail/u128.hpp | 110 ++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 3f40c445..e38c72f6 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -9,6 +9,7 @@ #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -2536,6 +2537,54 @@ inline char* u128_to_octal(char (&buffer)[ 64 ], u128 v) return p; } +static 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, + 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, + 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, + 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, 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, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 +}; + +static_assert(sizeof(uchar_values) == 256, "uchar_values should represent all 256 values of unsigned char"); + +constexpr unsigned char digit_from_char(char val) noexcept +{ + return uchar_values[static_cast(val)]; +} + +inline void base_to_u128(char (&buffer)[1024], u128& v, unsigned base) +{ + const char* end = buffer + 1024; + char* next = buffer; + + const unsigned char first_digit = digit_from_char(*next); + + if (static_cast(first_digit) >= base) + { + return; + } + + unsigned char current_digit = first_digit; + while (next < end && current_digit != 255) + { + v = static_cast(v * base + current_digit); + ++next; + current_digit = digit_from_char(*next); + } +} + } // namespace impl template @@ -2565,6 +2614,67 @@ std::basic_ostream& operator<<(std::basic_ostream& return os; } +template +constexpr std::ptrdiff_t generic_strlen(T* ptr) +{ + std::ptrdiff_t dist {}; + while (*ptr != static_cast(0)) + { + ++dist; + ++ptr; + } + + return dist; +} + +template +std::basic_istream& operator>>(std::basic_istream& is, u128& v) +{ + charT t_buffer[1024] {}; + is >> t_buffer; + + char buffer[1024] {}; + + BOOST_DECIMAL_IF_CONSTEXPR (!std::is_same::value) + { + auto first = buffer; + auto t_first = t_buffer; + auto t_buffer_end = t_buffer + generic_strlen(t_buffer); + + while (t_first != t_buffer_end) + { + *first++ = static_cast(*t_first++); + } + } + else + { + std::memcpy(buffer, t_buffer, sizeof(t_buffer)); + } + + convert_string_to_c_locale(buffer); + + auto is_flags {is.flags()}; + + switch (is_flags & std::ios::basefield) + { + case std::ios::dec: + impl::base_to_u128(buffer, v, 10U); + break; + case std::ios::hex: + impl::base_to_u128(buffer, v, 16U); + break; + case std::ios::oct: + impl::base_to_u128(buffer, v, 8U); + break; + // LCOV_EXCL_START + default: + BOOST_DECIMAL_UNREACHABLE; + // LCOV_EXCL_STOP + } + + return is; +} + #endif // BOOST_DECIMAL_DISABLE_IOSTREAM } // namespace detail From e13bf193bd1c2ebb3a4801bb963dd4b8aecb8bf1 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 12 Mar 2025 17:05:13 -0400 Subject: [PATCH 135/191] Fix MSVC warning/error --- include/boost/decimal/detail/u128.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index e38c72f6..bab28b43 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2627,6 +2627,11 @@ constexpr std::ptrdiff_t generic_strlen(T* ptr) return dist; } +#ifdef _MSC_VER +# pragma warning (push) +# pragma warning (disable: 4127) // conditional expression is constant +#endif + template std::basic_istream& operator>>(std::basic_istream& is, u128& v) { @@ -2675,6 +2680,10 @@ std::basic_istream& operator>>(std::basic_istream& return is; } +#ifdef _MSC_VER +# pragma warning (pop) +#endif + #endif // BOOST_DECIMAL_DISABLE_IOSTREAM } // namespace detail From f32c50b1e97be3df59dc278a5ca28ae74c972c51 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 08:53:03 -0400 Subject: [PATCH 136/191] Collect ARM64 benchmarks --- test/benchmark_uints.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index cffb7322..f92b431f 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -2,7 +2,9 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt +#if defined(__aarch64__) && defined(NDEBUG) #define BOOST_DECIMAL_BENCHMARK_U128 +#endif #include From 47ff462e6fe23dbdd46188ea7b55c919fea5b4d8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 09:16:16 -0400 Subject: [PATCH 137/191] Specialize countl_zero --- include/boost/decimal/detail/u128.hpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index bab28b43..ffc18bb2 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2686,6 +2686,17 @@ std::basic_istream& operator>>(std::basic_istream& #endif // BOOST_DECIMAL_DISABLE_IOSTREAM +template <> +constexpr int countl_zero(u128 x) noexcept +{ + if (x.high == 0) + { + return 64 + countl_zero(x.low); + } + + return countl_zero(x.high); +} + } // namespace detail } // namespace decimal } // namespace boost From bc391f518014fb19f61dacfeb9d582043e05913f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 09:44:08 -0400 Subject: [PATCH 138/191] Add table of the powers of 10 --- include/boost/decimal/detail/power_tables.hpp | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/include/boost/decimal/detail/power_tables.hpp b/include/boost/decimal/detail/power_tables.hpp index b51dd2c9..911dc709 100644 --- a/include/boost/decimal/detail/power_tables.hpp +++ b/include/boost/decimal/detail/power_tables.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -75,6 +76,52 @@ BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128 emulated_128_pow10[] = static_assert(sizeof(emulated_128_pow10) == sizeof(uint128) * 40, "Should have 10^0 to 10^39"); +BOOST_DECIMAL_CONSTEXPR_VARIABLE u128 emulated_u128_pow10[] = +{ + u128 {UINT64_C(0), UINT64_C(1)}, + u128 {UINT64_C(0), UINT64_C(10)}, + u128 {UINT64_C(0), UINT64_C(100)}, + u128 {UINT64_C(0), UINT64_C(1000)}, + u128 {UINT64_C(0), UINT64_C(10000)}, + u128 {UINT64_C(0), UINT64_C(100000)}, + u128 {UINT64_C(0), UINT64_C(1000000)}, + u128 {UINT64_C(0), UINT64_C(10000000)}, + u128 {UINT64_C(0), UINT64_C(100000000)}, + u128 {UINT64_C(0), UINT64_C(1000000000)}, + u128 {UINT64_C(0), UINT64_C(10000000000)}, + u128 {UINT64_C(0), UINT64_C(100000000000)}, + u128 {UINT64_C(0), UINT64_C(1000000000000)}, + u128 {UINT64_C(0), UINT64_C(10000000000000)}, + u128 {UINT64_C(0), UINT64_C(100000000000000)}, + u128 {UINT64_C(0), UINT64_C(1000000000000000)}, + u128 {UINT64_C(0), UINT64_C(10000000000000000)}, + u128 {UINT64_C(0), UINT64_C(100000000000000000)}, + u128 {UINT64_C(0), UINT64_C(1000000000000000000)}, + u128 {UINT64_C(0), UINT64_C(10000000000000000000)}, + u128 {UINT64_C(5), UINT64_C(7766279631452241920)}, + u128 {UINT64_C(54), UINT64_C(3875820019684212736)}, + u128 {UINT64_C(542), UINT64_C(1864712049423024128)}, + u128 {UINT64_C(5421), UINT64_C(200376420520689664)}, + u128 {UINT64_C(54210), UINT64_C(2003764205206896640)}, + u128 {UINT64_C(542101), UINT64_C(1590897978359414784)}, + u128 {UINT64_C(5421010), UINT64_C(15908979783594147840)}, + u128 {UINT64_C(54210108), UINT64_C(11515845246265065472)}, + u128 {UINT64_C(542101086), UINT64_C(4477988020393345024)}, + u128 {UINT64_C(5421010862), UINT64_C(7886392056514347008)}, + u128 {UINT64_C(54210108624), UINT64_C(5076944270305263616)}, + u128 {UINT64_C(542101086242), UINT64_C(13875954555633532928)}, + u128 {UINT64_C(5421010862427), UINT64_C(9632337040368467968)}, + u128 {UINT64_C(54210108624275), UINT64_C(4089650035136921600)}, + u128 {UINT64_C(542101086242752), UINT64_C(4003012203950112768)}, + u128 {UINT64_C(5421010862427522), UINT64_C(3136633892082024448)}, + u128 {UINT64_C(54210108624275221), UINT64_C(12919594847110692864)}, + u128 {UINT64_C(542101086242752217), UINT64_C(68739955140067328)}, + u128 {UINT64_C(5421010862427522170), UINT64_C(687399551400673280)}, + u128 {UINT64_C(17316620476856118468), UINT64_C(6873995514006732800)}, +}; + +static_assert(sizeof(emulated_128_pow10) == sizeof(uint128) * 40, "Should have 10^0 to 10^39"); + #ifdef BOOST_DECIMAL_HAS_INT128 BOOST_DECIMAL_CONSTEXPR_VARIABLE uint128_t builtin_128_pow10[] = { From 4d7a5772d7a747cb2479c65bddc8b1e6b3254d59 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 09:44:57 -0400 Subject: [PATCH 139/191] Add digit counting method for new type --- .../decimal/detail/integer_search_trees.hpp | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/include/boost/decimal/detail/integer_search_trees.hpp b/include/boost/decimal/detail/integer_search_trees.hpp index a8a319d8..01134ad1 100644 --- a/include/boost/decimal/detail/integer_search_trees.hpp +++ b/include/boost/decimal/detail/integer_search_trees.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE #include @@ -255,6 +256,34 @@ constexpr auto num_digits(const uint128_t& x) noexcept -> int #endif // Has int128 +constexpr auto num_digits(const u128& x) noexcept -> int +{ + unsigned msb {}; + if (x.high != 0) + { + msb = 64 + (64 - countl_zero(x.high)); + } + else + { + msb = 64 - countl_zero(x.low); + } + + const auto estimated_digits {(msb * 1000) / 3322 + 1}; + + if (x >= impl::emulated_u128_pow10[estimated_digits]) + { + return estimated_digits != 39 ? estimated_digits + 1 : estimated_digits; + } + else if (estimated_digits > 1 && x < impl::emulated_u128_pow10[estimated_digits - 1]) + { + return estimated_digits - 1; + } + else + { + return estimated_digits; + } +} + // Specializations with pruned branches for constructors // Since we already have partial information we can greatly speed things up in this case template From fb42f8f4e644b703186e9400bc834251abb9d252 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 09:45:24 -0400 Subject: [PATCH 140/191] Add testing of digit counting --- test/test_u128.cpp | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 114cb67c..002e75a9 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -4,8 +4,8 @@ #include #include - #include +#include #include #include #include @@ -872,6 +872,21 @@ void test_spot_operator_div(IntType value, IntType value2) BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); } +void test_digit_counting() +{ + unsigned __int128 builtin_val = 1; + boost::decimal::detail::u128 emulated_val {1}; + + for (int i {}; i < 38; ++i) + { + BOOST_TEST_EQ(boost::decimal::detail::num_digits(builtin_val), + boost::decimal::detail::num_digits(emulated_val)); + + builtin_val *= 10U; + emulated_val *= 10U; + } +} + int main() { test_traits(); @@ -1124,6 +1139,8 @@ int main() test_ostream_operator(); test_istream_operator(); + test_digit_counting(); + return boost::report_errors(); } From 0f5778eaaa6e17003a82b2bbae4cc3a7e3ad930e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 09:45:41 -0400 Subject: [PATCH 141/191] Add benchmark of digit counting --- test/benchmark_uints.cpp | 65 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index f92b431f..27805a8b 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -233,6 +234,25 @@ BOOST_DECIMAL_NO_INLINE void test_two_element_operation(const std::vector& da std::cout << operation << " <" << std::left << std::setw(11) << type << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; } +template +BOOST_DECIMAL_NO_INLINE void test_digit_counting(const std::vector& data_vec, const char* label) +{ + const auto t1 = std::chrono::steady_clock::now(); + std::size_t s = 0; // discard variable + + for (std::size_t k {}; k < K; ++k) + { + for (std::size_t i {}; i < data_vec.size(); ++i) + { + const auto val1 = data_vec[i]; + s += boost::decimal::detail::num_digits(val1); + } + } + + const auto t2 = std::chrono::steady_clock::now(); + + std::cout << "digits<" << std::left << std::setw(11) << label << ">: " << std::setw( 10 ) << ( t2 - t1 ) / 1us << " us (s=" << s << ")\n"; +} int main() { @@ -290,6 +310,15 @@ int main() test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_digit_counting(builtin_vector, "builtin"); + #endif + + test_digit_counting(old_vector, "old"); + test_digit_counting(new_vector, "new"); } // Single word operations { @@ -343,6 +372,15 @@ int main() test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_digit_counting(builtin_vector, "builtin"); + #endif + + test_digit_counting(old_vector, "old"); + test_digit_counting(new_vector, "new"); } { // Two word and one word operations Even = 2, odd = 1 @@ -397,6 +435,15 @@ int main() test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_digit_counting(builtin_vector, "builtin"); + #endif + + test_digit_counting(old_vector, "old"); + test_digit_counting(new_vector, "new"); } { // Two word and one word operations Even = 1, odd = 2 @@ -451,6 +498,15 @@ int main() test_two_element_operation(old_vector, std::divides<>(), "div", "Old"); test_two_element_operation(new_vector, std::divides<>(), "div", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_digit_counting(builtin_vector, "builtin"); + #endif + + test_digit_counting(old_vector, "old"); + test_digit_counting(new_vector, "new"); } { // Two word and one word operations Even = 1, odd = 2 @@ -514,6 +570,15 @@ int main() test_two_element_operation(old_vector, std::modulus<>(), "mod", "Old"); test_two_element_operation(new_vector, std::modulus<>(), "div", "New"); + + std::cout << std::endl; + + #ifdef BOOST_DECIMAL_HAS_INT128 + test_digit_counting(builtin_vector, "builtin"); + #endif + + test_digit_counting(old_vector, "old"); + test_digit_counting(new_vector, "new"); } return 1; From df1aa30451b9f217ee52d246a6731053c0c40dea Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 10:36:46 -0400 Subject: [PATCH 142/191] Fix unsigned conversion warning --- include/boost/decimal/detail/integer_search_trees.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/integer_search_trees.hpp b/include/boost/decimal/detail/integer_search_trees.hpp index 01134ad1..2ea0ef79 100644 --- a/include/boost/decimal/detail/integer_search_trees.hpp +++ b/include/boost/decimal/detail/integer_search_trees.hpp @@ -258,7 +258,7 @@ constexpr auto num_digits(const uint128_t& x) noexcept -> int constexpr auto num_digits(const u128& x) noexcept -> int { - unsigned msb {}; + int msb {}; if (x.high != 0) { msb = 64 + (64 - countl_zero(x.high)); From ecd25ad3992e1c9cfd41e50baddc79ff70b111d9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 10:38:21 -0400 Subject: [PATCH 143/191] Fix multiple definition warnings --- include/boost/decimal/detail/u128.hpp | 32 -------------- test/test_u128.cpp | 64 +++++++++++++-------------- 2 files changed, 32 insertions(+), 64 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ffc18bb2..7ed7652e 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2757,36 +2757,4 @@ struct std::numeric_limits static constexpr auto denorm_min () -> boost::decimal::detail::u128 { return {0, 0}; } }; -#if (!defined(__cpp_inline_variables) || __cpp_inline_variables < 201606L ) && (!defined(_MSC_VER) || _MSC_VER != 1900) - -constexpr bool std::numeric_limits::is_specialized; -constexpr bool std::numeric_limits::is_signed; -constexpr bool std::numeric_limits::is_integer; -constexpr bool std::numeric_limits::is_exact; -constexpr bool std::numeric_limits::has_infinity; -constexpr bool std::numeric_limits::has_quiet_NaN; -constexpr bool std::numeric_limits::has_signaling_NaN; -constexpr std::float_round_style std::numeric_limits::round_style; -constexpr bool std::numeric_limits::is_iec559; -constexpr bool std::numeric_limits::is_bounded; -constexpr bool std::numeric_limits::is_modulo; -constexpr int std::numeric_limits::digits; -constexpr int std::numeric_limits::digits10; -constexpr int std::numeric_limits::max_digits10; -constexpr int std::numeric_limits::radix; -constexpr int std::numeric_limits::min_exponent; -constexpr int std::numeric_limits::min_exponent10; -constexpr int std::numeric_limits::max_exponent; -constexpr int std::numeric_limits::max_exponent10; -constexpr bool std::numeric_limits::traps; -constexpr bool std::numeric_limits::tinyness_before; - -// Also add the deprecated members if needed -#if ((!defined(_MSC_VER) && (__cplusplus <= 202002L)) || (defined(_MSC_VER) && (_MSVC_LANG <= 202002L))) -constexpr std::float_denorm_style std::numeric_limits::has_denorm; -constexpr bool std::numeric_limits::has_denorm_loss; -#endif - -#endif - #endif // BOOST_DECIMAL_DETAIL_U128_HPP diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 002e75a9..48a091db 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -72,36 +72,36 @@ void test_numeric_limits() { using namespace boost::decimal::detail; - BOOST_TEST(std::numeric_limits::is_specialized); - BOOST_TEST(!std::numeric_limits::is_signed); - BOOST_TEST(std::numeric_limits::is_integer); - BOOST_TEST(std::numeric_limits::is_exact); - BOOST_TEST(!std::numeric_limits::has_infinity); - BOOST_TEST(!std::numeric_limits::has_quiet_NaN); - BOOST_TEST(!std::numeric_limits::has_signaling_NaN); - - BOOST_TEST(std::numeric_limits::round_style == std::round_toward_zero); - BOOST_TEST(!std::numeric_limits::is_iec559); - BOOST_TEST(std::numeric_limits::is_bounded); - BOOST_TEST(std::numeric_limits::is_modulo); - BOOST_TEST_EQ(std::numeric_limits::digits, sizeof(u128) * 8U); - BOOST_TEST_EQ(std::numeric_limits::digits10, static_cast(std::numeric_limits::digits * std::log10(2))); - BOOST_TEST_EQ(std::numeric_limits::max_digits10, std::numeric_limits::max_digits10); - BOOST_TEST_EQ(std::numeric_limits::radix, std::numeric_limits::radix); - BOOST_TEST_EQ(std::numeric_limits::min_exponent, std::numeric_limits::min_exponent); - BOOST_TEST_EQ(std::numeric_limits::min_exponent10, std::numeric_limits::min_exponent10); - BOOST_TEST_EQ(std::numeric_limits::max_exponent, std::numeric_limits::max_exponent); - BOOST_TEST_EQ(std::numeric_limits::max_exponent10, std::numeric_limits::max_exponent10); - BOOST_TEST_EQ(std::numeric_limits::traps, std::numeric_limits::traps); - BOOST_TEST_EQ(std::numeric_limits::tinyness_before, std::numeric_limits::tinyness_before); - - BOOST_TEST(std::numeric_limits::min() == std::numeric_limits::min()); - BOOST_TEST(std::numeric_limits::lowest() == std::numeric_limits::lowest()); + static_assert(std::numeric_limits::is_specialized, "incorrect"); + static_assert(!std::numeric_limits::is_signed, "incorrect"); + static_assert(std::numeric_limits::is_integer, "incorrect"); + static_assert(std::numeric_limits::is_exact, "incorrect"); + static_assert(!std::numeric_limits::has_infinity, "incorrect"); + static_assert(!std::numeric_limits::has_quiet_NaN, "incorrect"); + static_assert(!std::numeric_limits::has_signaling_NaN, "incorrect"); + + static_assert(std::numeric_limits::round_style == std::round_toward_zero, "incorrect"); + static_assert(!std::numeric_limits::is_iec559, "incorrect"); + static_assert(std::numeric_limits::is_bounded, "incorrect"); + static_assert(std::numeric_limits::is_modulo, "incorrect"); + static_assert(std::numeric_limits::digits == sizeof(u128) * 8U, "incorrect"); + static_assert(std::numeric_limits::digits10 == static_cast(std::numeric_limits::digits * 0.30102999566398119521), "incorrect"); + static_assert(std::numeric_limits::max_digits10 == std::numeric_limits::max_digits10, "incorrect"); + static_assert(std::numeric_limits::radix == std::numeric_limits::radix, "incorrect"); + static_assert(std::numeric_limits::min_exponent == std::numeric_limits::min_exponent, "incorrect"); + static_assert(std::numeric_limits::min_exponent10 == std::numeric_limits::min_exponent10, "incorrect"); + static_assert(std::numeric_limits::max_exponent == std::numeric_limits::max_exponent, "incorrect"); + static_assert(std::numeric_limits::max_exponent10 == std::numeric_limits::max_exponent10, "incorrect"); + static_assert(std::numeric_limits::traps == std::numeric_limits::traps, "incorrect"); + static_assert(std::numeric_limits::tinyness_before == std::numeric_limits::tinyness_before, "incorrect"); + + static_assert(std::numeric_limits::min() == std::numeric_limits::min(), "incorrect"); + static_assert(std::numeric_limits::lowest() == std::numeric_limits::lowest(), "incorrect"); #ifndef BOOST_DECIMAL_HAS_INT128 constexpr u128 two128 {std::numeric_limits::max(), std::numeric_limits::max()}; - BOOST_TEST(std::numeric_limits::max() == two128); + BOOST_TEST(std::numeric_limits::max() == two128, "incorrect"); #else @@ -112,12 +112,12 @@ void test_numeric_limits() #endif - BOOST_TEST(std::numeric_limits::epsilon() == std::numeric_limits::epsilon()); - BOOST_TEST(std::numeric_limits::round_error() == std::numeric_limits::round_error()); - BOOST_TEST(std::numeric_limits::infinity() == std::numeric_limits::infinity()); - BOOST_TEST(std::numeric_limits::quiet_NaN() == std::numeric_limits::quiet_NaN()); - BOOST_TEST(std::numeric_limits::signaling_NaN() == std::numeric_limits::signaling_NaN()); - BOOST_TEST(std::numeric_limits::denorm_min() == std::numeric_limits::denorm_min()); + static_assert(std::numeric_limits::epsilon() == std::numeric_limits::epsilon(), "incorrect"); + static_assert(std::numeric_limits::round_error() == std::numeric_limits::round_error(), "incorrect"); + static_assert(std::numeric_limits::infinity() == std::numeric_limits::infinity(), "incorrect"); + static_assert(std::numeric_limits::quiet_NaN() == std::numeric_limits::quiet_NaN(), "incorrect"); + static_assert(std::numeric_limits::signaling_NaN() == std::numeric_limits::signaling_NaN(), "incorrect"); + static_assert(std::numeric_limits::denorm_min() == std::numeric_limits::denorm_min(), "incorrect"); } void test_ostream_operator() From 3be23047d4ad459ddb2292f238b4e46f90195408 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 10:44:29 -0400 Subject: [PATCH 144/191] Fix potential buffer overrun --- include/boost/decimal/detail/u128.hpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 7ed7652e..8eed3e39 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2564,9 +2564,10 @@ constexpr unsigned char digit_from_char(char val) noexcept return uchar_values[static_cast(val)]; } -inline void base_to_u128(char (&buffer)[1024], u128& v, unsigned base) +template +void base_to_u128(char (&buffer)[N], u128& v, unsigned base) { - const char* end = buffer + 1024; + const char* end = buffer + N; char* next = buffer; const unsigned char first_digit = digit_from_char(*next); @@ -2638,7 +2639,7 @@ std::basic_istream& operator>>(std::basic_istream& charT t_buffer[1024] {}; is >> t_buffer; - char buffer[1024] {}; + char buffer[1024 * (sizeof(charT) / sizeof(char))] {}; BOOST_DECIMAL_IF_CONSTEXPR (!std::is_same::value) { @@ -2653,6 +2654,7 @@ std::basic_istream& operator>>(std::basic_istream& } else { + static_assert(sizeof(buffer) == sizeof(t_buffer), "Need to be equivalent"); std::memcpy(buffer, t_buffer, sizeof(t_buffer)); } From a365eb5e842e9943cfca868e0fe924b1d841fb20 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 10:44:42 -0400 Subject: [PATCH 145/191] Move generic strlen to it's own header --- include/boost/decimal/detail/strlen.hpp | 37 +++++++++++++++++++++++++ include/boost/decimal/detail/u128.hpp | 14 +--------- 2 files changed, 38 insertions(+), 13 deletions(-) create mode 100644 include/boost/decimal/detail/strlen.hpp diff --git a/include/boost/decimal/detail/strlen.hpp b/include/boost/decimal/detail/strlen.hpp new file mode 100644 index 00000000..cb067600 --- /dev/null +++ b/include/boost/decimal/detail/strlen.hpp @@ -0,0 +1,37 @@ +// 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_STRLEN_HPP +#define BOOST_DECIMAL_DETAIL_STRLEN_HPP + +#include + +#ifndef BOOST_DECIMAL_BUILD_MODULE + +#include + +#endif + +namespace boost { +namespace decimal { +namespace detail { + +template +constexpr std::ptrdiff_t generic_strlen(T* ptr) +{ + std::ptrdiff_t dist {}; + while (*ptr != static_cast(0)) + { + ++dist; + ++ptr; + } + + return dist; +} + +} // namespace detail +} // namespace decimal +} // namespace boost + +#endif // BOOST_DECIMAL_DETAIL_STRLEN_HPP diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8eed3e39..e4eb7007 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #ifndef BOOST_DECIMAL_BUILD_MODULE @@ -2615,19 +2616,6 @@ std::basic_ostream& operator<<(std::basic_ostream& return os; } -template -constexpr std::ptrdiff_t generic_strlen(T* ptr) -{ - std::ptrdiff_t dist {}; - while (*ptr != static_cast(0)) - { - ++dist; - ++ptr; - } - - return dist; -} - #ifdef _MSC_VER # pragma warning (push) # pragma warning (disable: 4127) // conditional expression is constant From 0bc2b6c680047bad8e0edca0804d3c4bb8c2911e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 10:50:28 -0400 Subject: [PATCH 146/191] Fix decimal type io --- include/boost/decimal/detail/io.hpp | 3 ++- include/boost/decimal/detail/u128.hpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/io.hpp b/include/boost/decimal/detail/io.hpp index 9fa9c3df..83923db0 100644 --- a/include/boost/decimal/detail/io.hpp +++ b/include/boost/decimal/detail/io.hpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #if !defined(BOOST_DECIMAL_DISABLE_CLIB) @@ -45,7 +46,7 @@ auto operator>>(std::basic_istream& is, DecimalType& d) { auto first = buffer; auto t_first = t_buffer; - auto t_buffer_end = t_buffer + std::strlen(t_buffer); + const auto t_buffer_end = t_buffer + detail::generic_strlen(t_buffer); while (t_first != t_buffer_end) { diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index e4eb7007..77da7a77 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2633,7 +2633,7 @@ std::basic_istream& operator>>(std::basic_istream& { auto first = buffer; auto t_first = t_buffer; - auto t_buffer_end = t_buffer + generic_strlen(t_buffer); + const auto t_buffer_end = t_buffer + generic_strlen(t_buffer); while (t_first != t_buffer_end) { From d926a7108b221d18ae6216e6d087bdc138f71e0d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 11:12:49 -0400 Subject: [PATCH 147/191] Fix sign conversion warning in benchmarks --- test/benchmark_uints.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 27805a8b..7cf65183 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -40,8 +40,10 @@ using namespace std::chrono_literals; # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wfloat-equal" # pragma GCC diagnostic ignored "-Wold-style-cast" -# pragma GCC diagnostic ignored "-Wstringop-overread" # define BOOST_DECIMAL_NO_INLINE __attribute__ ((__noinline__)) +# if __GNUC__ >= 11 +# pragma GCC diagnostic ignored "-Wstringop-overread" +# endif #endif // 0 = 1 word @@ -245,7 +247,7 @@ BOOST_DECIMAL_NO_INLINE void test_digit_counting(const std::vector& data_vec, for (std::size_t i {}; i < data_vec.size(); ++i) { const auto val1 = data_vec[i]; - s += boost::decimal::detail::num_digits(val1); + s += static_cast(boost::decimal::detail::num_digits(val1)); } } From 3b3830c5bd2b5231577087ffcca633fc1824fe77 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 12:05:59 -0400 Subject: [PATCH 148/191] Fix copy paste error --- test/test_u128.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 48a091db..540718b7 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -101,7 +101,7 @@ void test_numeric_limits() #ifndef BOOST_DECIMAL_HAS_INT128 constexpr u128 two128 {std::numeric_limits::max(), std::numeric_limits::max()}; - BOOST_TEST(std::numeric_limits::max() == two128, "incorrect"); + BOOST_TEST(std::numeric_limits::max() == two128); #else From 0f4bc137593807b4719dffdc43aff074f1291b91 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 12:06:52 -0400 Subject: [PATCH 149/191] Deactivate qemu run for now --- .github/workflows/{ => broken}/qemu.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{ => broken}/qemu.yml (100%) diff --git a/.github/workflows/qemu.yml b/.github/workflows/broken/qemu.yml similarity index 100% rename from .github/workflows/qemu.yml rename to .github/workflows/broken/qemu.yml From b3b0e8dcb4aaea1ad86864295247571985eee748 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 13:02:47 -0400 Subject: [PATCH 150/191] Ignore additional warnings/errors --- include/boost/decimal/detail/u128.hpp | 18 ++++++++++++++++++ test/test_u128.cpp | 1 + 2 files changed, 19 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 77da7a77..ddada3a0 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -408,6 +408,14 @@ constexpr bool operator==(const bool lhs, const u128 rhs) noexcept return rhs.high == UINT64_C(0) && rhs.low == static_cast(lhs); } +#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" +#endif + template , bool> = true> constexpr bool operator==(const u128 lhs, const SignedInteger rhs) noexcept { @@ -1679,6 +1687,10 @@ constexpr u128& u128::operator-=(const unsigned __int128 rhs) noexcept // Multiplication Operator //===================================== +#if defined(__GNUC__) && __GNUC__ >= 5 +# pragma GCC diagnostic ignored "-Wclass-memaccess" +#endif + namespace impl { BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) noexcept @@ -2486,6 +2498,12 @@ constexpr u128& u128::operator%=(const unsigned __int128 rhs) noexcept return *this; } +#if defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif + #endif // BOOST_DECIMAL_HAS_INT128 #if !defined(BOOST_DECIMAL_DISABLE_IOSTREAM) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 540718b7..09df4206 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -23,6 +23,7 @@ # pragma clang diagnostic ignored "-Wconversion" # pragma clang diagnostic ignored "-Wsign-conversion" # pragma clang diagnostic ignored "-Wfloat-equal" +# pragma clang diagnostic ignored "-Wsign-compare" # if (__clang_major__ >= 10 && !defined(__APPLE__)) || __clang_major__ >= 13 # pragma clang diagnostic ignored "-Wdeprecated-copy" From 4301fc1303e0008215e89ed15732c8d09f976468 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 13:51:04 -0400 Subject: [PATCH 151/191] Bring down non-apple AARCH64 mul timing --- include/boost/decimal/detail/u128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ddada3a0..65871b87 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1698,7 +1698,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) return {low >> 32, low << 32}; } -#if defined(__x86_64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) +#if defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { From c8cec3a089fa6de5cbaf51801545ef9d2c4cfb39 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 14:00:16 -0400 Subject: [PATCH 152/191] Fix macro for x64 windows platforms --- include/boost/decimal/detail/u128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 65871b87..48ef3434 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1442,7 +1442,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const u128 BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const u128 rhs) noexcept { - #ifdef __x86_64__ + #if defined(__x86_64__) && defined(BOOST_DECIMAL_HAS_INT128) return static_cast(static_cast(lhs) - static_cast(rhs)); From 628903fa3557e51af5b79be79d4b4e65777b5a9c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 14:02:02 -0400 Subject: [PATCH 153/191] Fix ignored diagnostic for older GCC versions --- include/boost/decimal/detail/u128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 48ef3434..6d77347d 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1687,7 +1687,7 @@ constexpr u128& u128::operator-=(const unsigned __int128 rhs) noexcept // Multiplication Operator //===================================== -#if defined(__GNUC__) && __GNUC__ >= 5 +#if defined(__GNUC__) && __GNUC__ >= 8 # pragma GCC diagnostic ignored "-Wclass-memaccess" #endif From 8c7d20ec53219cdd3c79dad528d6932e2af4ac4b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 15:57:24 -0400 Subject: [PATCH 154/191] Add method that is better for slow CLZ instructions --- .../decimal/detail/integer_search_trees.hpp | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/boost/decimal/detail/integer_search_trees.hpp b/include/boost/decimal/detail/integer_search_trees.hpp index 2ea0ef79..3b2107b9 100644 --- a/include/boost/decimal/detail/integer_search_trees.hpp +++ b/include/boost/decimal/detail/integer_search_trees.hpp @@ -256,6 +256,9 @@ constexpr auto num_digits(const uint128_t& x) noexcept -> int #endif // Has int128 +// Check benchmark_uints.cpp for timing +#if defined(__aarch64__) && defined(__APPLE__) + constexpr auto num_digits(const u128& x) noexcept -> int { int msb {}; @@ -284,6 +287,25 @@ constexpr auto num_digits(const u128& x) noexcept -> int } } +#else + +constexpr int num_digits(const u128& x) noexcept +{ + int guess {}; + if (x.high != UINT64_C(0)) + { + guess = num_digits(x.high) + 19; + } + else + { + return num_digits(x.low); + } + + return x >= impl::emulated_u128_pow10[guess] && guess != 39 ? guess + 1 : guess; +} + +#endif + // Specializations with pruned branches for constructors // Since we already have partial information we can greatly speed things up in this case template From 696efbccc1e7b2eadfebbda480be9c3c7fd7e808 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 13 Mar 2025 17:01:48 -0400 Subject: [PATCH 155/191] Remove shift of negative values --- test/benchmark_uints.cpp | 2 +- test/test_u128.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 7cf65183..1dd272b3 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -571,7 +571,7 @@ int main() #endif test_two_element_operation(old_vector, std::modulus<>(), "mod", "Old"); - test_two_element_operation(new_vector, std::modulus<>(), "div", "New"); + test_two_element_operation(new_vector, std::modulus<>(), "mod", "New"); std::cout << std::endl; diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 09df4206..abd77cb7 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -671,7 +671,7 @@ void test_operator_xor() template void test_operator_left_shift() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + boost::random::uniform_int_distribution dist(static_cast(0), std::numeric_limits::max()); boost::random::uniform_int_distribution shift_dist(0, sizeof(IntType) * CHAR_BIT - 1); @@ -696,7 +696,7 @@ void test_operator_left_shift() template void test_operator_right_shift() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), + boost::random::uniform_int_distribution dist(static_cast(0), std::numeric_limits::max()); boost::random::uniform_int_distribution shift_dist(0, sizeof(IntType) * CHAR_BIT - 1); From 930ae229c3239843fdac23169133abe1376ccccd Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 14 Mar 2025 11:23:06 -0400 Subject: [PATCH 156/191] Reduce runtime --- .github/workflows/ci.yml | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9b22f758..b3b3564c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,22 +35,43 @@ jobs: matrix: include: - toolset: gcc-7 - cxxstd: "03,11,14,17" - address_model: 32,64 + cxxstd: "14,17" + address_model: 64 + os: ubuntu-latest + container: ubuntu:18.04 + install: + - g++-7-multilib + - toolset: gcc-7 + cxxstd: 14,17" + address_model: 32 os: ubuntu-latest container: ubuntu:18.04 install: - g++-7-multilib - toolset: gcc-8 - cxxstd: "03,11,14,17,2a" - address_model: 32,64 + cxxstd: "14,17,2a" + address_model: 64 + os: ubuntu-latest + container: ubuntu:18.04 + install: + - g++-8-multilib + - toolset: gcc-8 + cxxstd: "14,17,2a" + address_model: 32 os: ubuntu-latest container: ubuntu:18.04 install: - g++-8-multilib - toolset: gcc-9 - cxxstd: "03,11,14,17,2a" - address_model: 32,64 + cxxstd: "17,2a" + address_model: 32 + os: ubuntu-latest + container: ubuntu:20.04 + install: + - g++-9-multilib + - toolset: gcc-9 + cxxstd: "17,2a" + address_model: 64 os: ubuntu-latest container: ubuntu:20.04 install: From 0f101793288a13dc0b891c37c34d72b5d111058a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 14 Mar 2025 11:27:37 -0400 Subject: [PATCH 157/191] Deactivate signed shift testing with UBSAN --- test/test_u128.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index abd77cb7..5b6cb9ee 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -1051,24 +1051,32 @@ int main() test_operator_xor(); test_operator_xor(); + #ifndef UBSAN + test_operator_left_shift(); test_operator_left_shift(); test_operator_left_shift(); test_operator_left_shift(); test_operator_left_shift<__int128>(); + #endif + test_operator_left_shift(); test_operator_left_shift(); test_operator_left_shift(); test_operator_left_shift(); test_operator_left_shift(); + #ifndef UBSAN + test_operator_right_shift(); test_operator_right_shift(); test_operator_right_shift(); test_operator_right_shift(); test_operator_right_shift<__int128>(); + #endif + test_operator_right_shift(); test_operator_right_shift(); test_operator_right_shift(); From c70ff2f1bc3266bc4f06b87d70bfd4bd85c2f8ab Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Fri, 14 Mar 2025 11:34:46 -0400 Subject: [PATCH 158/191] Fix syntax error --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b3b3564c..97d40cc1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,7 +42,7 @@ jobs: install: - g++-7-multilib - toolset: gcc-7 - cxxstd: 14,17" + cxxstd: "14,17" address_model: 32 os: ubuntu-latest container: ubuntu:18.04 From dafd20a18411720d699f63f79db951bbe2559b32 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 17 Mar 2025 11:52:36 -0400 Subject: [PATCH 159/191] Attempt reduction and see where timeout is occuring --- test/test_u128.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 5b6cb9ee..d850bb66 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -48,7 +48,11 @@ // Used defined seed for repeatability static std::mt19937_64 rng(42); +#if (defined(__clang_major__) && (__clang_major__ <= 10)) || (defined(__GNUC__) && (__GNUC__ >= 5) && (__GNUC__ < 10)) +constexpr std::size_t N = 128; +#else constexpr std::size_t N = 1024; +#endif void test_traits() { @@ -894,6 +898,8 @@ int main() test_numeric_limits(); + std::cout << "constructors" << std::endl; + test_arithmetic_constructor(); test_arithmetic_constructor(); test_arithmetic_constructor(); @@ -906,6 +912,8 @@ int main() test_arithmetic_constructor(); test_arithmetic_constructor(); + std::cout << "assignment" << std::endl; + test_assignment_operators(); test_assignment_operators(); test_assignment_operators(); @@ -918,6 +926,8 @@ int main() test_assignment_operators(); test_arithmetic_constructor(); + std::cout << "integer conversion" << std::endl; + test_integer_conversion_operators(); test_integer_conversion_operators(); test_integer_conversion_operators(); @@ -930,6 +940,8 @@ int main() test_integer_conversion_operators(); test_arithmetic_constructor(); + std::cout << "float conversion" << std::endl; + test_float_conversion_operators(); test_float_conversion_operators(); test_float_conversion_operators(); @@ -938,9 +950,13 @@ int main() test_float_conversion_operators<__float128>(); #endif + std::cout << "unary" << std::endl; + test_unary_plus(); test_unary_minus(); + std::cout << "equality" << std::endl; + test_operator_equality(); test_operator_equality(); test_operator_equality(); @@ -953,6 +969,8 @@ int main() test_operator_equality(); test_operator_equality(); + std::cout << "inequality" << std::endl; + test_operator_inequality(); test_operator_inequality(); test_operator_inequality(); @@ -965,6 +983,8 @@ int main() test_operator_inequality(); test_operator_inequality(); + std::cout << "less" << std::endl; + test_operator_less(); test_operator_less(); test_operator_less(); @@ -977,6 +997,8 @@ int main() test_operator_less(); test_operator_less(); + std::cout << "le" << std::endl; + test_operator_le(); test_operator_le(); test_operator_le(); @@ -989,6 +1011,8 @@ int main() test_operator_le(); test_operator_le(); + std::cout << "greater" << std::endl; + test_operator_greater(); test_operator_greater(); test_operator_greater(); @@ -1001,6 +1025,8 @@ int main() test_operator_greater(); test_operator_greater(); + std::cout << "ge" << std::endl; + test_operator_ge(); test_operator_ge(); test_operator_ge(); @@ -1013,6 +1039,8 @@ int main() test_operator_ge(); test_operator_ge(); + std::cout << "or" << std::endl; + test_operator_not(); test_operator_or(); @@ -1027,6 +1055,8 @@ int main() test_operator_or(); test_operator_or(); + std::cout << "and" << std::endl; + test_operator_and(); test_operator_and(); test_operator_and(); @@ -1039,6 +1069,8 @@ int main() test_operator_and(); test_operator_and(); + std::cout << "xor" << std::endl; + test_operator_xor(); test_operator_xor(); test_operator_xor(); @@ -1051,6 +1083,8 @@ int main() test_operator_xor(); test_operator_xor(); + std::cout << "leftshift" << std::endl; + #ifndef UBSAN test_operator_left_shift(); @@ -1067,6 +1101,8 @@ int main() test_operator_left_shift(); test_operator_left_shift(); + std::cout << "rightshift" << std::endl; + #ifndef UBSAN test_operator_right_shift(); @@ -1083,6 +1119,8 @@ int main() test_operator_right_shift(); test_operator_right_shift(); + std::cout << "add" << std::endl; + test_operator_add(); test_operator_add(); test_operator_add(); @@ -1095,6 +1133,8 @@ int main() test_operator_add(); test_operator_add(); + std::cout << "sub" << std::endl; + test_operator_sub(); test_operator_sub(); test_operator_sub(); @@ -1107,6 +1147,8 @@ int main() test_operator_sub(); test_operator_sub(); + std::cout << "mul" << std::endl; + test_operator_mul(); test_operator_mul(); test_operator_mul(); @@ -1119,6 +1161,8 @@ int main() test_operator_mul(); test_operator_mul(); + std::cout << "div" << std::endl; + test_operator_div(); test_operator_div(); test_operator_div(); @@ -1133,6 +1177,8 @@ int main() test_spot_operator_div(1, -94); + std::cout << "mod" << std::endl; + test_operator_mod(); test_operator_mod(); test_operator_mod(); @@ -1145,9 +1191,13 @@ int main() test_operator_mod(); test_operator_mod(); + std::cout << "stream" << std::endl; + test_ostream_operator(); test_istream_operator(); + std::cout << "count" << std::endl; + test_digit_counting(); return boost::report_errors(); From 4d013289963a30288659d64c810f71f47dc66dd8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 17 Mar 2025 11:55:11 -0400 Subject: [PATCH 160/191] Workaround for MSVC chrono missing --- .github/workflows/ci.yml | 2 +- test/test_u128.cpp | 46 ++++++++++++++++++++-------------------- test/test_zeta.cpp | 13 ++++++++++++ 3 files changed, 37 insertions(+), 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97d40cc1..659b7a03 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -557,7 +557,7 @@ jobs: shell: cmd run: | cd ../boost-root - b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release link=static,shared embed-manifest-via=linker + b2 -j3 libs/%LIBRARY%/test toolset=${{matrix.toolset}} cxxstd=${{matrix.cxxstd}} address-model=${{matrix.addrmd}} variant=debug,release link=static,shared embed-manifest-via=linker define="BOOST_DECIMAL_GHA_MSVC" posix-cmake-subdir: strategy: diff --git a/test/test_u128.cpp b/test/test_u128.cpp index d850bb66..4698b748 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -898,7 +898,7 @@ int main() test_numeric_limits(); - std::cout << "constructors" << std::endl; + std::cerr << "constructors" << std::endl; test_arithmetic_constructor(); test_arithmetic_constructor(); @@ -912,7 +912,7 @@ int main() test_arithmetic_constructor(); test_arithmetic_constructor(); - std::cout << "assignment" << std::endl; + std::cerr << "assignment" << std::endl; test_assignment_operators(); test_assignment_operators(); @@ -926,7 +926,7 @@ int main() test_assignment_operators(); test_arithmetic_constructor(); - std::cout << "integer conversion" << std::endl; + std::cerr << "integer conversion" << std::endl; test_integer_conversion_operators(); test_integer_conversion_operators(); @@ -940,7 +940,7 @@ int main() test_integer_conversion_operators(); test_arithmetic_constructor(); - std::cout << "float conversion" << std::endl; + std::cerr << "float conversion" << std::endl; test_float_conversion_operators(); test_float_conversion_operators(); @@ -950,12 +950,12 @@ int main() test_float_conversion_operators<__float128>(); #endif - std::cout << "unary" << std::endl; + std::cerr << "unary" << std::endl; test_unary_plus(); test_unary_minus(); - std::cout << "equality" << std::endl; + std::cerr << "equality" << std::endl; test_operator_equality(); test_operator_equality(); @@ -969,7 +969,7 @@ int main() test_operator_equality(); test_operator_equality(); - std::cout << "inequality" << std::endl; + std::cerr << "inequality" << std::endl; test_operator_inequality(); test_operator_inequality(); @@ -983,7 +983,7 @@ int main() test_operator_inequality(); test_operator_inequality(); - std::cout << "less" << std::endl; + std::cerr << "less" << std::endl; test_operator_less(); test_operator_less(); @@ -997,7 +997,7 @@ int main() test_operator_less(); test_operator_less(); - std::cout << "le" << std::endl; + std::cerr << "le" << std::endl; test_operator_le(); test_operator_le(); @@ -1011,7 +1011,7 @@ int main() test_operator_le(); test_operator_le(); - std::cout << "greater" << std::endl; + std::cerr << "greater" << std::endl; test_operator_greater(); test_operator_greater(); @@ -1025,7 +1025,7 @@ int main() test_operator_greater(); test_operator_greater(); - std::cout << "ge" << std::endl; + std::cerr << "ge" << std::endl; test_operator_ge(); test_operator_ge(); @@ -1039,7 +1039,7 @@ int main() test_operator_ge(); test_operator_ge(); - std::cout << "or" << std::endl; + std::cerr << "or" << std::endl; test_operator_not(); @@ -1055,7 +1055,7 @@ int main() test_operator_or(); test_operator_or(); - std::cout << "and" << std::endl; + std::cerr << "and" << std::endl; test_operator_and(); test_operator_and(); @@ -1069,7 +1069,7 @@ int main() test_operator_and(); test_operator_and(); - std::cout << "xor" << std::endl; + std::cerr << "xor" << std::endl; test_operator_xor(); test_operator_xor(); @@ -1083,7 +1083,7 @@ int main() test_operator_xor(); test_operator_xor(); - std::cout << "leftshift" << std::endl; + std::cerr << "leftshift" << std::endl; #ifndef UBSAN @@ -1101,7 +1101,7 @@ int main() test_operator_left_shift(); test_operator_left_shift(); - std::cout << "rightshift" << std::endl; + std::cerr << "rightshift" << std::endl; #ifndef UBSAN @@ -1119,7 +1119,7 @@ int main() test_operator_right_shift(); test_operator_right_shift(); - std::cout << "add" << std::endl; + std::cerr << "add" << std::endl; test_operator_add(); test_operator_add(); @@ -1133,7 +1133,7 @@ int main() test_operator_add(); test_operator_add(); - std::cout << "sub" << std::endl; + std::cerr << "sub" << std::endl; test_operator_sub(); test_operator_sub(); @@ -1147,7 +1147,7 @@ int main() test_operator_sub(); test_operator_sub(); - std::cout << "mul" << std::endl; + std::cerr << "mul" << std::endl; test_operator_mul(); test_operator_mul(); @@ -1161,7 +1161,7 @@ int main() test_operator_mul(); test_operator_mul(); - std::cout << "div" << std::endl; + std::cerr << "div" << std::endl; test_operator_div(); test_operator_div(); @@ -1177,7 +1177,7 @@ int main() test_spot_operator_div(1, -94); - std::cout << "mod" << std::endl; + std::cerr << "mod" << std::endl; test_operator_mod(); test_operator_mod(); @@ -1191,12 +1191,12 @@ int main() test_operator_mod(); test_operator_mod(); - std::cout << "stream" << std::endl; + std::cerr << "stream" << std::endl; test_ostream_operator(); test_istream_operator(); - std::cout << "count" << std::endl; + std::cerr << "count" << std::endl; test_digit_counting(); diff --git a/test/test_zeta.cpp b/test/test_zeta.cpp index d36b0225..0f027eb7 100644 --- a/test/test_zeta.cpp +++ b/test/test_zeta.cpp @@ -8,6 +8,10 @@ #include +// Github actions MSVC is broken with chrono +// Drone and local run just fine +#ifndef BOOST_DECIMAL_GHA_MSVC + #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wold-style-cast" @@ -441,3 +445,12 @@ int main() template auto my_zero() -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_zero { 0 }; return val_zero; } template auto my_nan () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_nan { std::numeric_limits::quiet_NaN() }; return val_nan; } template auto my_inf () -> DecimalType& { using decimal_type = DecimalType; static decimal_type val_inf { std::numeric_limits::infinity() }; return val_inf; } + +#else + +int main() +{ + return 0; +} + +#endif From 707307a86d2f3dd5a2874bc6769558fab0b7e6f2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 17 Mar 2025 13:35:33 -0400 Subject: [PATCH 161/191] Fix macro definition for clang on win --- include/boost/decimal/detail/u128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 6d77347d..8fd8c2f2 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1698,7 +1698,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) return {low >> 32, low << 32}; } -#if defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) +#if (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__))) && defined(BOOST_DECIMAL_HAS_INT128) BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { From 7c72a3f93e13802d4d9098c7eefa1615eb539d75 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 17 Mar 2025 15:35:26 -0400 Subject: [PATCH 162/191] Change logic for old clangs --- include/boost/decimal/detail/u128.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8fd8c2f2..7080ab0c 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1698,7 +1698,8 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) return {low >> 32, low << 32}; } -#if (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__))) && defined(BOOST_DECIMAL_HAS_INT128) +#if (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__))) && defined(BOOST_DECIMAL_HAS_INT128) \ + && (!defined(__clang_major__) || __clang_major__ >= 10) BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { From 5a995868587a0c51a68e3c1b8c055fdc01615208 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 17 Mar 2025 15:35:40 -0400 Subject: [PATCH 163/191] Significantly reduce test runs on old platforms for debug --- test/test_u128.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 4698b748..2eb088e9 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -49,7 +49,7 @@ static std::mt19937_64 rng(42); #if (defined(__clang_major__) && (__clang_major__ <= 10)) || (defined(__GNUC__) && (__GNUC__ >= 5) && (__GNUC__ < 10)) -constexpr std::size_t N = 128; +constexpr std::size_t N = 5; #else constexpr std::size_t N = 1024; #endif From c3fada618279faa09f87d4512d1b741b41bbad82 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 09:07:20 -0400 Subject: [PATCH 164/191] Update div macro --- include/boost/decimal/detail/u128.hpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 7080ab0c..b160832a 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1950,7 +1950,8 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u1 } } -#if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) +#if (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__))) && defined(BOOST_DECIMAL_HAS_INT128) \ + && (!defined(__clang_major__) || __clang_major__ >= 10) // This is unconditionally better on ARM64, PPC64LE, and S390X BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept From a973b069eee56d231bc7a594530613c3a0c8a3bc Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 09:26:19 -0400 Subject: [PATCH 165/191] Update div macros --- include/boost/decimal/detail/u128.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index b160832a..1aff3fc9 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2057,7 +2057,8 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept { // On ARM64 and PPC64LE this is unconditionally better // On x64 this is only better when both lhs and rhs are two word numbers - #if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) + #if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) \ + && (!defined(__clang_major__) || __clang_major__ >= 10) return static_cast(static_cast(lhs) / static_cast(rhs)); @@ -2216,7 +2217,8 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept namespace impl { -#if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) +#if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) \ + && (!defined(__clang_major__) || __clang_major__ >= 10) // This is unconditionally better on ARM64, PPC64LE, and S390X BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mod(const u128 lhs, const std::uint64_t rhs) noexcept From 84fe447a1822dd2460b66aa3032f3f6122118605 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 09:26:25 -0400 Subject: [PATCH 166/191] Update sub macro --- include/boost/decimal/detail/u128.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 1aff3fc9..8437f0eb 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1442,7 +1442,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const u128 BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const u128 rhs) noexcept { - #if defined(__x86_64__) && defined(BOOST_DECIMAL_HAS_INT128) + #if defined(__x86_64__) && defined(BOOST_DECIMAL_HAS_INT128) && (!defined(__clang_major__) || __clang_major__ >= 10) return static_cast(static_cast(lhs) - static_cast(rhs)); From 3af824a8b24c1b64d7b964d0d498f38c73945a34 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 11:35:45 -0400 Subject: [PATCH 167/191] Reduce duplication and increase readability --- include/boost/decimal/detail/u128.hpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 8437f0eb..1ec06fbc 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -27,6 +27,12 @@ #endif // BOOST_DECIMAL_BUILD_MODULE +// This is used frequently enough that I'd rather not have this spaghetti everytime +// These older clang and GCC versions return incorrect numerical results or sometimes hang +#define BOOST_DECIMAL_OLD_NON_GNU_COMPILER ((defined(__clang__) && __clang_major__ < 11) || \ + (defined(__GNUC__) && !defined(__clang__) && \ + (__GNUC__ < 10 || (defined(__STRICT_ANSI__) && __GNUC__ >= 10)))) + namespace boost { namespace decimal { namespace detail { @@ -1442,7 +1448,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_add(const u128 lhs, const u128 BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_sub(const u128 lhs, const u128 rhs) noexcept { - #if defined(__x86_64__) && defined(BOOST_DECIMAL_HAS_INT128) && (!defined(__clang_major__) || __clang_major__ >= 10) + #if defined(__x86_64__) && defined(BOOST_DECIMAL_HAS_INT128) && !defined(BOOST_DECIMAL_OLD_NON_GNU_COMPILER) return static_cast(static_cast(lhs) - static_cast(rhs)); @@ -1699,7 +1705,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 shift_left_32(const std::uint64_t low) } #if (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__))) && defined(BOOST_DECIMAL_HAS_INT128) \ - && (!defined(__clang_major__) || __clang_major__ >= 10) + && !defined(BOOST_DECIMAL_OLD_NON_GNU_COMPILER) BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mul(const u128 lhs, const u128 rhs) noexcept { @@ -1951,7 +1957,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u1 } #if (defined(__x86_64__) || (defined(__aarch64__) && !defined(__APPLE__)) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__))) && defined(BOOST_DECIMAL_HAS_INT128) \ - && (!defined(__clang_major__) || __clang_major__ >= 10) + && !defined(BOOST_DECIMAL_OLD_NON_GNU_COMPILER) // This is unconditionally better on ARM64, PPC64LE, and S390X BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std::uint64_t rhs) noexcept @@ -2058,7 +2064,7 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept // On ARM64 and PPC64LE this is unconditionally better // On x64 this is only better when both lhs and rhs are two word numbers #if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) \ - && (!defined(__clang_major__) || __clang_major__ >= 10) + && !defined(BOOST_DECIMAL_OLD_NON_GNU_COMPILER) return static_cast(static_cast(lhs) / static_cast(rhs)); @@ -2067,7 +2073,7 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept BOOST_DECIMAL_ASSERT_MSG(rhs != 0, "Division by zero"); // The is best x64 path assuming the user has consteval detection - #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_HAS_INT128) + #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_HAS_INT128) && !defined(BOOST_DECIMAL_OLD_NON_GNU_COMPILER) if (BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) { @@ -2104,7 +2110,7 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept if (lhs.high != 0 && rhs.high != 0) { - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) && !defined(BOOST_DECIMAL_OLD_NON_GNU_COMPILER) return static_cast(static_cast(lhs) / static_cast(rhs)); @@ -2218,7 +2224,7 @@ constexpr u128& u128::operator/=(const unsigned __int128 rhs) noexcept namespace impl { #if defined(__aarch64__) || (defined(__ppc64__) || defined(__PPC64__) || defined(__ppc64le__) || defined(__PPC64LE__)) || defined(__s390x__) \ - && (!defined(__clang_major__) || __clang_major__ >= 10) + && !defined(BOOST_DECIMAL_OLD_NON_GNU_COMPILER) // This is unconditionally better on ARM64, PPC64LE, and S390X BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mod(const u128 lhs, const std::uint64_t rhs) noexcept @@ -2769,4 +2775,6 @@ struct std::numeric_limits static constexpr auto denorm_min () -> boost::decimal::detail::u128 { return {0, 0}; } }; +#undef BOOST_DECIMAL_OLD_NON_GNU_COMPILER + #endif // BOOST_DECIMAL_DETAIL_U128_HPP From ba02bce722fa5d020831edc38b04d1b032853a85 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 12:33:46 -0400 Subject: [PATCH 168/191] Revert hang inspection --- test/test_u128.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 2eb088e9..78500cdc 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -48,11 +48,7 @@ // Used defined seed for repeatability static std::mt19937_64 rng(42); -#if (defined(__clang_major__) && (__clang_major__ <= 10)) || (defined(__GNUC__) && (__GNUC__ >= 5) && (__GNUC__ < 10)) -constexpr std::size_t N = 5; -#else constexpr std::size_t N = 1024; -#endif void test_traits() { From f8cf0c5271d9d37cea06de1652f2f44d5f239e71 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 12:34:03 -0400 Subject: [PATCH 169/191] Add additional div path where numeric limits not defined --- test/test_u128.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 78500cdc..73e77b00 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -803,7 +803,7 @@ void test_operator_mul() } } -template +template = true> void test_operator_div() { boost::random::uniform_int_distribution dist(std::numeric_limits::min(), @@ -833,6 +833,38 @@ void test_operator_div() } } +template sizeof(std::uint64_t)), bool> = true> +void test_operator_div() +{ + boost::random::uniform_int_distribution dist(0, UINT64_MAX); + + for (std::size_t i {}; i < N; ++i) + { + IntType value {0}; + IntType value2 {0}; + + while (value == 0) + { + value = dist(rng); + } + while (value2 == 0) + { + value2 = dist(rng); + } + + value *= 1000000000; + value2 *= 1000000000; + + unsigned __int128 builtin_value = static_cast(value); + boost::decimal::detail::u128 emulated_value {value}; + + auto check_1_value {emulated_value}; + check_1_value /= value2; + BOOST_TEST(check_1_value == (builtin_value / value2)); + BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); + } +} + template void test_operator_mod() { From 9141ce6b25279eea361111bd8f721e32ad5eaafa Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 13:29:54 -0400 Subject: [PATCH 170/191] Add generic function to replace max/min --- test/test_u128.cpp | 151 +++++++++++++++++++++++---------------------- 1 file changed, 78 insertions(+), 73 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 73e77b00..ea688017 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -41,8 +41,45 @@ #elif defined(_MSC_VER) # pragma warning(push) # pragma warning(disable : 4389) +# pragma warning(disable : 4127) #endif +template +constexpr IntType get_max() +{ + return std::numeric_limits::max(); +} + +template +constexpr IntType get_min() +{ + return std::numeric_limits::min(); +} + +template <> +constexpr unsigned __int128 get_max() +{ + return static_cast(UINT64_MAX) << 64 | UINT64_MAX; +} + +template <> +constexpr unsigned __int128 get_min() +{ + return 0; +} + +template <> +constexpr __int128 get_max<__int128>() +{ + return (static_cast<__int128>(1) << 127) - 1; +} + +template <> +constexpr __int128 get_min<__int128>() +{ + return -get_max<__int128>() - 1; +} + #include // Used defined seed for repeatability @@ -243,8 +280,8 @@ void test_istream_operator() template void test_arithmetic_constructor() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -262,8 +299,8 @@ void test_arithmetic_constructor() template void test_assignment_operators() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -283,8 +320,8 @@ void test_assignment_operators() template void test_integer_conversion_operators() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -341,8 +378,8 @@ void test_float_conversion_operators() template void test_unary_plus() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -361,8 +398,8 @@ void test_unary_plus() template void test_unary_minus() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -382,8 +419,8 @@ void test_unary_minus() template void test_operator_equality() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); // Always equal for (std::size_t i {}; i < N; ++i) @@ -415,8 +452,8 @@ void test_operator_equality() template void test_operator_inequality() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); // Always equal for (std::size_t i {}; i < N; ++i) @@ -448,8 +485,8 @@ void test_operator_inequality() template void test_operator_less() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -484,8 +521,8 @@ void test_operator_less() template void test_operator_le() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -520,8 +557,8 @@ void test_operator_le() template void test_operator_greater() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -556,8 +593,8 @@ void test_operator_greater() template void test_operator_ge() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -592,8 +629,8 @@ void test_operator_ge() template void test_operator_not() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -608,8 +645,8 @@ void test_operator_not() template void test_operator_or() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -629,8 +666,8 @@ void test_operator_or() template void test_operator_and() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -650,8 +687,8 @@ void test_operator_and() template void test_operator_xor() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -672,7 +709,7 @@ template void test_operator_left_shift() { boost::random::uniform_int_distribution dist(static_cast(0), - std::numeric_limits::max()); + get_max()); boost::random::uniform_int_distribution shift_dist(0, sizeof(IntType) * CHAR_BIT - 1); @@ -697,7 +734,7 @@ template void test_operator_right_shift() { boost::random::uniform_int_distribution dist(static_cast(0), - std::numeric_limits::max()); + get_max()); boost::random::uniform_int_distribution shift_dist(0, sizeof(IntType) * CHAR_BIT - 1); @@ -721,8 +758,8 @@ void test_operator_right_shift() template void test_operator_add() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -742,8 +779,8 @@ void test_operator_add() template void test_operator_sub() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -762,7 +799,7 @@ void test_operator_sub() template = true> void test_operator_mul() { - const auto root_max {static_cast(std::sqrt(std::numeric_limits::max()))}; + const auto root_max {static_cast(std::sqrt(get_max()))}; const auto root_min {std::is_unsigned::value ? 0 : -root_max}; boost::random::uniform_int_distribution dist(root_min, root_max); @@ -806,37 +843,8 @@ void test_operator_mul() template = true> void test_operator_div() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); - - for (std::size_t i {}; i < N; ++i) - { - IntType value {0}; - IntType value2 {0}; - - while (value == 0) - { - value = dist(rng); - } - while (value2 == 0) - { - value2 = dist(rng); - } - - unsigned __int128 builtin_value = static_cast(value); - boost::decimal::detail::u128 emulated_value {value}; - - auto check_1_value {emulated_value}; - check_1_value /= value2; - BOOST_TEST(check_1_value == (builtin_value / value2)); - BOOST_TEST((value2 / emulated_value) == (value2 / builtin_value)); - } -} - -template sizeof(std::uint64_t)), bool> = true> -void test_operator_div() -{ - boost::random::uniform_int_distribution dist(0, UINT64_MAX); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { @@ -852,9 +860,6 @@ void test_operator_div() value2 = dist(rng); } - value *= 1000000000; - value2 *= 1000000000; - unsigned __int128 builtin_value = static_cast(value); boost::decimal::detail::u128 emulated_value {value}; @@ -868,8 +873,8 @@ void test_operator_div() template void test_operator_mod() { - boost::random::uniform_int_distribution dist(std::numeric_limits::min(), - std::numeric_limits::max()); + boost::random::uniform_int_distribution dist(get_min(), + get_max()); for (std::size_t i {}; i < N; ++i) { From eddc124cbc9501adc16375c157280acc3672eced Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 13:44:55 -0400 Subject: [PATCH 171/191] Fix stackoverflow --- test/test_u128.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index ea688017..05ae1180 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -24,6 +24,7 @@ # pragma clang diagnostic ignored "-Wsign-conversion" # pragma clang diagnostic ignored "-Wfloat-equal" # pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Woverflow" # if (__clang_major__ >= 10 && !defined(__APPLE__)) || __clang_major__ >= 13 # pragma clang diagnostic ignored "-Wdeprecated-copy" @@ -37,6 +38,7 @@ # pragma GCC diagnostic ignored "-Wsign-conversion" # pragma GCC diagnostic ignored "-Wsign-compare" # pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Woverflow" #elif defined(_MSC_VER) # pragma warning(push) @@ -56,10 +58,12 @@ constexpr IntType get_min() return std::numeric_limits::min(); } +// We reduce the max end of the 128 bit types as they can cause a stack overflow in boost.random + template <> constexpr unsigned __int128 get_max() { - return static_cast(UINT64_MAX) << 64 | UINT64_MAX; + return static_cast(UINT64_MAX) << 64 | UINT64_MAX / 32; } template <> @@ -71,7 +75,7 @@ constexpr unsigned __int128 get_min() template <> constexpr __int128 get_max<__int128>() { - return (static_cast<__int128>(1) << 127) - 1; + return ((static_cast<__int128>(1) << 127) - 1) / 32; } template <> @@ -840,7 +844,7 @@ void test_operator_mul() } } -template = true> +template void test_operator_div() { boost::random::uniform_int_distribution dist(get_min(), From c4fcc35c82587ff8084681e9b85d4fd12ae85b96 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 13:58:08 -0400 Subject: [PATCH 172/191] Fix intel warning --- test/test_u128.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 05ae1180..073f097d 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -75,7 +75,7 @@ constexpr unsigned __int128 get_min() template <> constexpr __int128 get_max<__int128>() { - return ((static_cast<__int128>(1) << 127) - 1) / 32; + return static_cast<__int128>((static_cast(1) << 127) - 1) / 32; } template <> From c8c87dd3161ca580f28788036493fa3f416dc47f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 14:48:06 -0400 Subject: [PATCH 173/191] Fix definition location --- test/test_u128.cpp | 52 +++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 073f097d..00fc728a 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -58,32 +58,6 @@ constexpr IntType get_min() return std::numeric_limits::min(); } -// We reduce the max end of the 128 bit types as they can cause a stack overflow in boost.random - -template <> -constexpr unsigned __int128 get_max() -{ - return static_cast(UINT64_MAX) << 64 | UINT64_MAX / 32; -} - -template <> -constexpr unsigned __int128 get_min() -{ - return 0; -} - -template <> -constexpr __int128 get_max<__int128>() -{ - return static_cast<__int128>((static_cast(1) << 127) - 1) / 32; -} - -template <> -constexpr __int128 get_min<__int128>() -{ - return -get_max<__int128>() - 1; -} - #include // Used defined seed for repeatability @@ -281,6 +255,32 @@ void test_istream_operator() #ifdef BOOST_DECIMAL_HAS_INT128 +// We reduce the max end of the 128 bit types as they can cause a stack overflow in boost.random + +template <> +constexpr unsigned __int128 get_max() +{ + return static_cast(UINT64_MAX) << 64 | UINT64_MAX / 32; +} + +template <> +constexpr unsigned __int128 get_min() +{ + return 0; +} + +template <> +constexpr __int128 get_max<__int128>() +{ + return static_cast<__int128>((static_cast(1) << 127) - 1) / 32; +} + +template <> +constexpr __int128 get_min<__int128>() +{ + return -get_max<__int128>() - 1; +} + template void test_arithmetic_constructor() { From eb67ff5b41b28c6ad2df0c0fe76936339d05129b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 16:42:57 -0400 Subject: [PATCH 174/191] Ignore big endian platforms reorder constructor warning --- include/boost/decimal/detail/u128.hpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 1ec06fbc..43d7fb04 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -88,6 +88,14 @@ u128 constexpr u128& operator=(const u128& other) noexcept = default; constexpr u128& operator=(u128&& other) noexcept = default; + #ifdef BOOST_DECIMAL_ENDIAN_BIG_BYTE + # if defined(__GNUC__) + # pragma GCC diagnostic push + # pragma GCC diagnostic ignored "-Wreorder" + # define BOOST_DECIMAL_PUSHED_BIG_WARNING + # endif + #endif // Big endian + // Direct construction of the number constexpr u128(const std::uint64_t hi, const std::uint64_t lo) noexcept : low{lo}, high{hi} {} @@ -111,6 +119,11 @@ u128 high {static_cast(value >> 64U)} {} #endif // BOOST_DECIMAL_HAS_INT128 + #ifdef BOOST_DECIMAL_PUSHED_BIG_WARNING + # pragma GCC diagnostic pop + # undef BOOST_DECIMAL_PUSHED_BIG_WARNING + #endif + // Signed assignment operators template , bool> = true> constexpr u128& operator=(SignedInteger value) noexcept; From 11319cd288cc7b38448ef6e1c139e76a6f874526 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 17:08:44 -0400 Subject: [PATCH 175/191] Add UBSAN define to drone run --- .drone.jsonnet | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index 6c68476c..78759317 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -365,7 +365,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") = linux_pipeline( "Linux 22.04 Clang 14 UBSAN", "cppalliance/droneubuntu2204:1", - { TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20,2b' } + ubsan, + { TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20,2b', DEFINE: 'UBSAN' } + ubsan, "clang-14", ), @@ -410,7 +410,7 @@ local windows_pipeline(name, image, environment, arch = "amd64") = macos_pipeline( "MacOS 12.4 Xcode 13.4.1 UBSAN", - { TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,17,20,2b' } + ubsan, + { TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,17,20,2b', DEFINE: 'UBSAN' } + ubsan, xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64", ), From dc0d920d275f43f35c7e53e2ec68f5741ce85b6b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 18 Mar 2025 18:11:27 -0400 Subject: [PATCH 176/191] Change reorder ignore --- include/boost/decimal/detail/u128.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 43d7fb04..e3b5459f 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -73,6 +73,14 @@ u128 public: + #ifdef BOOST_DECIMAL_ENDIAN_BIG_BYTE + # if defined(__GNUC__) + # pragma GCC diagnostic push + # pragma GCC diagnostic ignored "-Wreorder" + # define BOOST_DECIMAL_PUSHED_BIG_WARNING + # endif + #endif // Big endian + #if BOOST_DECIMAL_ENDIAN_LITTLE_BYTE std::uint64_t low {}; std::uint64_t high {}; @@ -88,14 +96,6 @@ u128 constexpr u128& operator=(const u128& other) noexcept = default; constexpr u128& operator=(u128&& other) noexcept = default; - #ifdef BOOST_DECIMAL_ENDIAN_BIG_BYTE - # if defined(__GNUC__) - # pragma GCC diagnostic push - # pragma GCC diagnostic ignored "-Wreorder" - # define BOOST_DECIMAL_PUSHED_BIG_WARNING - # endif - #endif // Big endian - // Direct construction of the number constexpr u128(const std::uint64_t hi, const std::uint64_t lo) noexcept : low{lo}, high{hi} {} From 8fdeb398044c3c30f2de6424eeb35b4840962925 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 19 Mar 2025 09:58:00 -0400 Subject: [PATCH 177/191] Move UBSAN to GHA --- .drone.jsonnet | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/.drone.jsonnet b/.drone.jsonnet index 78759317..b332f86d 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -362,13 +362,6 @@ local windows_pipeline(name, image, environment, arch = "amd64") = "clang-13", ), - linux_pipeline( - "Linux 22.04 Clang 14 UBSAN", - "cppalliance/droneubuntu2204:1", - { TOOLSET: 'clang', COMPILER: 'clang++-14', CXXSTD: '03,11,14,17,20,2b', DEFINE: 'UBSAN' } + ubsan, - "clang-14", - ), - linux_pipeline( "Linux 22.04 Clang 14 ASAN", "cppalliance/droneubuntu2204:1", @@ -408,12 +401,6 @@ local windows_pipeline(name, image, environment, arch = "amd64") = ["deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-18 main"], ), - macos_pipeline( - "MacOS 12.4 Xcode 13.4.1 UBSAN", - { TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,17,20,2b', DEFINE: 'UBSAN' } + ubsan, - xcode_version = "13.4.1", osx_version = "monterey", arch = "arm64", - ), - macos_pipeline( "MacOS 12.4 Xcode 13.4.1 ASAN", { TOOLSET: 'clang', COMPILER: 'clang++', CXXSTD: '03,11,14,17,20,2b' } + asan, From 6dd3bf9edb69f10f01295a6dd2e964559f5d01b2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 24 Mar 2025 10:11:47 -0400 Subject: [PATCH 178/191] Add benchmark against MSVC internal std::_Unsigned128 --- test/benchmark_uints.cpp | 139 +++++++++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 33 deletions(-) diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 1dd272b3..6e14af9d 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -2,14 +2,17 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -#if defined(__aarch64__) && defined(NDEBUG) #define BOOST_DECIMAL_BENCHMARK_U128 -#endif #include #ifdef BOOST_DECIMAL_BENCHMARK_U128 +#if __has_include(<__msvc_int128.hpp>) +#include <__msvc_int128.hpp> +#define BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128 +#endif + #include #include #include @@ -189,6 +192,76 @@ std::vector generate_random_builtin_vector(std::size_t size = #endif +#ifdef BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128 + +template +std::vector generate_random_builtin_vector(std::size_t size = N, unsigned seed = 42U) +{ + if (seed == 0) + { + std::random_device rd; + seed = rd(); + } + + std::mt19937_64 gen(seed); + std::uniform_int_distribution dist(UINT64_C(0), UINT64_MAX); + std::uniform_int_distribution size_dist(0, 1); + + std::vector result(size); + for (std::size_t i = 0; i < size; ++i) + { + switch (words) + { + case 0: + result[i] = dist(gen); + break; + + case 1: + result[i] = static_cast(dist(gen)) << 64U | dist(gen); + break; + + case 2: + if (i % 2 == 0) + { + result[i] = static_cast(dist(gen)) << 64U | dist(gen); + } + else + { + result[i] = dist(gen); + } + break; + + case 3: + if (i % 2 == 1) + { + result[i] = static_cast(dist(gen)) << 64U | dist(gen); + } + else + { + result[i] = dist(gen); + } + break; + + case 4: + if (size_dist(gen) == 1) + { + result[i] = static_cast(dist(gen)) << 64U | dist(gen); + } + else + { + result[i] = dist(gen); + } + break; + + default: + BOOST_DECIMAL_UNREACHABLE; + } + } + return result; +} + +#endif + template BOOST_DECIMAL_NO_INLINE void test_comparisons(const std::vector& data_vec, const char* label) { @@ -269,7 +342,7 @@ int main() const auto old_vector = generate_random_vector<0, uint128>(); const auto new_vector = generate_random_vector<0, u128>(); - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) const auto builtin_vector = generate_random_builtin_vector<0>(); test_comparisons(builtin_vector, "builtin"); #endif @@ -279,7 +352,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); #endif @@ -288,7 +361,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); #endif @@ -297,7 +370,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); #endif @@ -306,7 +379,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); #endif @@ -315,7 +388,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_digit_counting(builtin_vector, "builtin"); #endif @@ -331,7 +404,7 @@ int main() const auto old_vector = generate_random_vector<1, uint128>(); const auto new_vector = generate_random_vector<1, u128>(); - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) const auto builtin_vector = generate_random_builtin_vector<1>(); test_comparisons(builtin_vector, "builtin"); #endif @@ -341,7 +414,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); #endif @@ -350,7 +423,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); #endif @@ -359,7 +432,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); #endif @@ -368,7 +441,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); #endif @@ -377,7 +450,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_digit_counting(builtin_vector, "builtin"); #endif @@ -394,7 +467,7 @@ int main() const auto old_vector = generate_random_vector<2, uint128>(); const auto new_vector = generate_random_vector<2, u128>(); - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) const auto builtin_vector = generate_random_builtin_vector<2>(); test_comparisons(builtin_vector, "builtin"); #endif @@ -404,7 +477,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); #endif @@ -413,7 +486,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); #endif @@ -422,7 +495,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); #endif @@ -431,7 +504,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); #endif @@ -440,7 +513,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_digit_counting(builtin_vector, "builtin"); #endif @@ -457,7 +530,7 @@ int main() const auto old_vector = generate_random_vector<3, uint128>(); const auto new_vector = generate_random_vector<3, u128>(); - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) const auto builtin_vector = generate_random_builtin_vector<3>(); test_comparisons(builtin_vector, "builtin"); #endif @@ -467,7 +540,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); #endif @@ -476,7 +549,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); #endif @@ -485,7 +558,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); #endif @@ -494,7 +567,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); #endif @@ -503,7 +576,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_digit_counting(builtin_vector, "builtin"); #endif @@ -520,7 +593,7 @@ int main() const auto old_vector = generate_random_vector<4, uint128>(); const auto new_vector = generate_random_vector<4, u128>(); - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) const auto builtin_vector = generate_random_builtin_vector<4>(); test_comparisons(builtin_vector, "builtin"); #endif @@ -530,7 +603,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::plus<>(), "add", "Builtin"); #endif @@ -539,7 +612,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::minus<>(), "sub", "Builtin"); #endif @@ -548,7 +621,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::multiplies<>(), "mul", "Builtin"); #endif @@ -557,7 +630,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::divides<>(), "div", "Builtin"); #endif @@ -566,7 +639,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_two_element_operation(builtin_vector, std::modulus<>(), "mod", "Builtin"); #endif @@ -575,7 +648,7 @@ int main() std::cout << std::endl; - #ifdef BOOST_DECIMAL_HAS_INT128 + #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) test_digit_counting(builtin_vector, "builtin"); #endif From fe80ab2823a79a2acdb1347ded2d68986ac59331 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 24 Mar 2025 11:28:40 -0400 Subject: [PATCH 179/191] Add x64 specific intrinsics path --- include/boost/decimal/detail/config.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/boost/decimal/detail/config.hpp b/include/boost/decimal/detail/config.hpp index 14b04c3c..7f144901 100644 --- a/include/boost/decimal/detail/config.hpp +++ b/include/boost/decimal/detail/config.hpp @@ -90,6 +90,10 @@ # endif # if defined(_WIN64) # define BOOST_DECIMAL_HAS_MSVC_64BIT_INTRINSICS +# ifndef _M_ARM64 +# include +# define BOOST_DECIMAL_HAS_MSVC_X64_INTRINSICS +# endif # else # define BOOST_DECIMAL_HAS_MSVC_32BIT_INTRINSICS # endif From a70809b8933ac4e6b6ab6691af93facae2b1c902 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 24 Mar 2025 12:20:44 -0400 Subject: [PATCH 180/191] Add additional path for x64 MSVC intrinsics --- include/boost/decimal/detail/u128.hpp | 7 +++++++ test/benchmark_uints.cpp | 30 +++++++++++++-------------- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index e3b5459f..16aa3ffb 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1956,6 +1956,13 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u1 remainder.high = UINT64_C(0); } + #ifdef BOOST_DECIMAL_HAS_MSVC_X64_INTRINSICS + else if (rhs.high == UINT64_C(0)) + { + // Since low is at least 2^32 the quotient will be less than 2^64 + quotient.low = _udiv128(lhs.high, lhs.low, rhs.low, &remainder.low); + } + #endif else { auto lhs_wide = u128_to_wide_integer(lhs); diff --git a/test/benchmark_uints.cpp b/test/benchmark_uints.cpp index 6e14af9d..c13aa00a 100644 --- a/test/benchmark_uints.cpp +++ b/test/benchmark_uints.cpp @@ -389,11 +389,11 @@ int main() std::cout << std::endl; #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) - test_digit_counting(builtin_vector, "builtin"); + // test_digit_counting(builtin_vector, "builtin"); #endif - test_digit_counting(old_vector, "old"); - test_digit_counting(new_vector, "new"); + // test_digit_counting(old_vector, "old"); + // test_digit_counting(new_vector, "new"); } // Single word operations { @@ -451,11 +451,11 @@ int main() std::cout << std::endl; #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) - test_digit_counting(builtin_vector, "builtin"); + // test_digit_counting(builtin_vector, "builtin"); #endif - test_digit_counting(old_vector, "old"); - test_digit_counting(new_vector, "new"); + // test_digit_counting(old_vector, "old"); + // test_digit_counting(new_vector, "new"); } { // Two word and one word operations Even = 2, odd = 1 @@ -514,11 +514,11 @@ int main() std::cout << std::endl; #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) - test_digit_counting(builtin_vector, "builtin"); + // test_digit_counting(builtin_vector, "builtin"); #endif - test_digit_counting(old_vector, "old"); - test_digit_counting(new_vector, "new"); + // test_digit_counting(old_vector, "old"); + // test_digit_counting(new_vector, "new"); } { // Two word and one word operations Even = 1, odd = 2 @@ -577,11 +577,11 @@ int main() std::cout << std::endl; #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) - test_digit_counting(builtin_vector, "builtin"); + // test_digit_counting(builtin_vector, "builtin"); #endif - test_digit_counting(old_vector, "old"); - test_digit_counting(new_vector, "new"); + // test_digit_counting(old_vector, "old"); + // test_digit_counting(new_vector, "new"); } { // Two word and one word operations Even = 1, odd = 2 @@ -649,11 +649,11 @@ int main() std::cout << std::endl; #if defined(BOOST_DECIMAL_HAS_INT128) || defined(BOOST_DECIMAL_HAS_MSVC_INTERNAL_U128) - test_digit_counting(builtin_vector, "builtin"); + // test_digit_counting(builtin_vector, "builtin"); #endif - test_digit_counting(old_vector, "old"); - test_digit_counting(new_vector, "new"); + // test_digit_counting(old_vector, "old"); + // test_digit_counting(new_vector, "new"); } return 1; From bc8a90487951647ae4a50868186ee1887affd20f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 10:14:17 -0400 Subject: [PATCH 181/191] Add reduced adaptation of knuth division --- include/boost/decimal/detail/u128.hpp | 68 +++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 16aa3ffb..ab13fbed 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1931,6 +1931,74 @@ constexpr auto wide_integer_to_u128(const wide_integer_uint128& src) -> u128 return dst; } +// Adapted from ckormanyos/wide-integer +// +// Use Knuth's long division algorithm. +// The loop-ordering of indices in Knuth's original +// algorithm has been reversed due to the data format +// used here. Several optimizations and combinations +// of logic have been carried out in the source code. +// +// See also: +// D.E. Knuth, "The Art of Computer Programming, Volume 2: +// Seminumerical Algorithms", Addison-Wesley (1998), +// Section 4.3.1 Algorithm D and Exercise 16. + +template +constexpr void divide_knuth_core(std::uint32_t (&u)[u_size], + std::uint32_t (&v)[v_size], + std::uint32_t (&q)[q_size]) noexcept +{ + const auto d { u_size - v_size - 1 }; + + for (std::size_t j = d; j >= 0; --j) + { + const auto next_digits{ static_cast(u[j + v_size] << 32) | static_cast(u[j + v_size - 1]) }; + auto q_hat{ next_digits / v[v_size - 1] }; + auto r_hat{ next_digits % v[v_size - 1] }; + + while ((q_hat >> 32) != 0 || ( q_hat * v[v_size - 2] > (r_hat << 32 | u[j + v_size - 2]))) + { + --q_hat; + r_hat += v[v_size - 1]; + if (r_hat >> 32 != 0) + { + break; + } + } + + std::int64_t k{}; + std::int64_t t{}; + + for (std::size_t i = 0; i < v_size; ++i) + { + const auto product{ static_cast(q_hat) * static_cast(v[i]) }; + t = u[i + j] - k - product; + u[i + j] = static_cast(t); + k = static_cast(product >> 32) - (t >> 32); + } + + t = static_cast(u[j + v_size]) - k; + u[j + v_size] = static_cast(t); + q[j] = static_cast(q_hat); + + if (t < 0) + { + --q[j]; + k = 0; + + for (std::size_t i = 0; i < v_size; ++i) + { + t = static_cast(u[i + j]) + k + static_cast(v[i]); + u[i + j] = static_cast(t); + k = t >> 32; + } + + u[j + v_size] += static_cast(k); + } + } +} + BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u128& rhs, u128& quotient, u128& remainder) noexcept { // Mash-Up: Use Knuth long-division from wide-integer (requires limb-conversions on input/output). From 068924599d177138bd7cc5f76f2b94741dcb4616 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 11:09:34 -0400 Subject: [PATCH 182/191] Specialize knuth division --- include/boost/decimal/detail/u128.hpp | 47 +++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index ab13fbed..d99da82c 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1999,6 +1999,53 @@ constexpr void divide_knuth_core(std::uint32_t (&u)[u_size], } } +BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const std::uint64_t rhs, u128& quotient, u128& remainder) noexcept +{ + // If rhs is greater than 2^32 the result is trivial to find + if (rhs >= UINT32_MAX) + { + remainder.low = (lhs.high << 32U) | (lhs.low >> 32U); + auto res = remainder.low / rhs; + remainder.low = (remainder.low % rhs) << 32 | lhs.low; + res = (res << 32) | (remainder.low / rhs); + remainder.low %= rhs; + } + + // Setup for Knuth Division + const auto offset { countl_zero(rhs >> 32) }; + + std::uint32_t u[5]{}; + + if (offset != 0) + { + u[0] = static_cast(lhs.low << offset); + u[1] = static_cast(lhs.low >> (32 - offset)); + u[2] = static_cast(lhs.high << offset) | + static_cast(lhs.low >> (64 - offset)); + u[3] = static_cast(lhs.high >> (32 - offset)); + u[4] = static_cast(lhs.high >> (64 - offset)); + } + else + { + u[0] = static_cast(lhs.low); + u[1] = static_cast(lhs.low >> 32); + u[2] = static_cast(lhs.high); + u[3] = static_cast(lhs.high >> 32); + } + + std::uint32_t v[2] + { + static_cast(rhs << offset), + static_cast(rhs >> (32 - offset)) + }; + + std::uint32_t q[3]{}; + + divide_knuth_core(u, v, q); + + +} + BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u128& rhs, u128& quotient, u128& remainder) noexcept { // Mash-Up: Use Knuth long-division from wide-integer (requires limb-conversions on input/output). From ae9fc899e6454538d82533bc04e20f7e13c14dae Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 11:14:21 -0400 Subject: [PATCH 183/191] Use x64 intrinsic if available and not constexpr --- include/boost/decimal/detail/u128.hpp | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index d99da82c..c668f2d2 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2004,11 +2004,20 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const st // If rhs is greater than 2^32 the result is trivial to find if (rhs >= UINT32_MAX) { - remainder.low = (lhs.high << 32U) | (lhs.low >> 32U); - auto res = remainder.low / rhs; - remainder.low = (remainder.low % rhs) << 32 | lhs.low; - res = (res << 32) | (remainder.low / rhs); - remainder.low %= rhs; + #if !defined(BOOST_DECIMAL_NO_CONSTEVAL_DETECTION) && defined(BOOST_DECIMAL_HAS_MSVC_X64_INTRINSICS) + if (!BOOST_DECIMAL_IS_CONSTANT_EVALUATED(rhs)) + { + quotient.low = _udiv128(lhs.high, lhs.low, rhs, &remainder.low); + } + else + #endif + { + remainder.low = (lhs.high << 32U) | (lhs.low >> 32U); + auto res = remainder.low / rhs; + remainder.low = (remainder.low % rhs) << 32 | lhs.low; + res = (res << 32) | (remainder.low / rhs); + remainder.low %= rhs; + } } // Setup for Knuth Division @@ -2043,7 +2052,9 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const st divide_knuth_core(u, v, q); - + quotient.low = static_cast(q[1] << 32) | q[0]; + quotient.high = q[3]; + remainder.low = static_cast(u[1] << (32 - offset)) | static_cast(u[0] >> offset); } BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u128& rhs, u128& quotient, u128& remainder) noexcept From 1fe66a7a9668147eeea19022a068c07ee3acf19e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 11:21:59 -0400 Subject: [PATCH 184/191] Use new impl in 128 by 64 division case --- include/boost/decimal/detail/u128.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index c668f2d2..398f4c54 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2151,15 +2151,14 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std: { u128 quotient{}; u128 remainder{}; - impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + impl::div_mod_impl(lhs, rhs, quotient, remainder); return quotient; } else if (lhs.high != 0) { - // TODO(mborland): Can we abbreviate Knuth division for this case? u128 quotient{}; u128 remainder{}; - impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + impl::div_mod_impl(lhs, rhs, quotient, remainder); return quotient; } else From bdbe32a8a6ba787b7b9ff431b2179fd795de7de9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 11:54:41 -0400 Subject: [PATCH 185/191] Fix parens --- include/boost/decimal/detail/u128.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 398f4c54..85c20073 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -1951,11 +1951,13 @@ constexpr void divide_knuth_core(std::uint32_t (&u)[u_size], { const auto d { u_size - v_size - 1 }; - for (std::size_t j = d; j >= 0; --j) + for (int j = d; j >= 0; --j) { - const auto next_digits{ static_cast(u[j + v_size] << 32) | static_cast(u[j + v_size - 1]) }; - auto q_hat{ next_digits / v[v_size - 1] }; - auto r_hat{ next_digits % v[v_size - 1] }; + const auto next_digits{ (static_cast(u[j + v_size]) << 32) | static_cast(u[j + v_size - 1]) }; + const auto div{ v[v_size - 1] }; + BOOST_DECIMAL_ASSUME(div != 0); + auto q_hat{ next_digits / div }; + auto r_hat{ next_digits % div }; while ((q_hat >> 32) != 0 || ( q_hat * v[v_size - 2] > (r_hat << 32 | u[j + v_size - 2]))) { @@ -2052,9 +2054,9 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const st divide_knuth_core(u, v, q); - quotient.low = static_cast(q[1] << 32) | q[0]; + quotient.low = (static_cast(q[1]) << 32) | q[0]; quotient.high = q[3]; - remainder.low = static_cast(u[1] << (32 - offset)) | static_cast(u[0] >> offset); + remainder.low = (static_cast(u[1]) << (32 - offset)) | (static_cast(u[0]) >> offset); } BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u128& rhs, u128& quotient, u128& remainder) noexcept From 99fe6925065c5526ce3e29ec892666a33054cd5e Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 11:57:13 -0400 Subject: [PATCH 186/191] Add non-zero divisor assumption --- include/boost/decimal/detail/u128.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 85c20073..0f866107 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2001,8 +2001,10 @@ constexpr void divide_knuth_core(std::uint32_t (&u)[u_size], } } -BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const std::uint64_t rhs, u128& quotient, u128& remainder) noexcept +constexpr void div_mod_impl(const u128& lhs, const std::uint64_t rhs, u128& quotient, u128& remainder) noexcept { + BOOST_DECIMAL_ASSUME(rhs != 0); + // If rhs is greater than 2^32 the result is trivial to find if (rhs >= UINT32_MAX) { @@ -2055,7 +2057,7 @@ BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const st divide_knuth_core(u, v, q); quotient.low = (static_cast(q[1]) << 32) | q[0]; - quotient.high = q[3]; + quotient.high = q[2]; remainder.low = (static_cast(u[1]) << (32 - offset)) | (static_cast(u[0]) >> offset); } From 93de98dc580cdd3799cb662523575c72c533c85d Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 13:27:35 -0400 Subject: [PATCH 187/191] Fix overcounting zeros --- include/boost/decimal/detail/u128.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 0f866107..27822bea 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2025,7 +2025,8 @@ constexpr void div_mod_impl(const u128& lhs, const std::uint64_t rhs, u128& quot } // Setup for Knuth Division - const auto offset { countl_zero(rhs >> 32) }; + // Includes the normalization step here instead of in main function for simplicity + const auto offset { countl_zero(static_cast(rhs >> 32)) }; std::uint32_t u[5]{}; @@ -2158,6 +2159,10 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std: impl::div_mod_impl(lhs, rhs, quotient, remainder); return quotient; } + if (rhs == 0) + { + return { 0,0 }; + } else if (lhs.high != 0) { u128 quotient{}; @@ -2167,11 +2172,6 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_div(const u128 lhs, const std: } else { - if (rhs == 0) - { - return { 0, 0 }; - } - return { 0, lhs.low / rhs }; } From 6c492f39d020e13ed794eb32f676e01f606ec97b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 13:33:00 -0400 Subject: [PATCH 188/191] Update mod methods --- include/boost/decimal/detail/u128.hpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 27822bea..1527ca15 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2421,24 +2421,22 @@ BOOST_DECIMAL_FORCE_INLINE constexpr u128 default_mod(const u128 lhs, const std: { u128 quotient{}; u128 remainder{}; - impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + impl::div_mod_impl(lhs, rhs, quotient, remainder); return remainder; } + else if (rhs == 0) + { + return { 0, 0 }; + } else if (lhs.high != 0) { - // TODO(mborland): Can we abbreviate Knuth division for this case? u128 quotient{}; u128 remainder{}; - impl::div_mod_impl(lhs, u128{ 0, rhs }, quotient, remainder); + impl::div_mod_impl(lhs, rhs, quotient, remainder); return remainder; } else { - if (rhs == 0) - { - return { 0, 0 }; - } - return { 0, lhs.low % rhs }; } From 10771cb833d60ed34327e9932543468324519477 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 14:51:42 -0400 Subject: [PATCH 189/191] Add MSVC testing of operator mod --- test/test_u128.cpp | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/test/test_u128.cpp b/test/test_u128.cpp index 00fc728a..46db1c9b 100644 --- a/test/test_u128.cpp +++ b/test/test_u128.cpp @@ -1685,6 +1685,33 @@ void test_operator_div() } } +template +void test_operator_mod() +{ + boost::random::uniform_int_distribution dist(std::numeric_limits::min(), std::numeric_limits::max()); + + for (std::size_t i{}; i < N; ++i) + { + const IntType value{ dist(rng) }; + IntType value2{ dist(rng) }; + + while (value2 == 0) + { + value2 = dist(rng); + } + + uint128 builtin_value{ static_cast(value), static_cast(value) }; + boost::decimal::detail::u128 emulated_value{ static_cast(value), static_cast(value) }; + + + const auto builtin_res_left = builtin_value % static_cast(value2); + const auto emulated_res_left = emulated_value % static_cast(value2); + + BOOST_TEST(emulated_res_left.high == builtin_res_left.high && emulated_res_left.low == builtin_res_left.low); + + } +} + int main() { test_traits(); @@ -1858,6 +1885,16 @@ int main() test_operator_div(); test_operator_div(); + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + test_operator_mod(); + test_ostream_operator(); test_istream_operator(); From 9341dc7d488eba5a5d1557df07c0a9e00759a516 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 25 Mar 2025 16:08:18 -0400 Subject: [PATCH 190/191] Simplify mixed width div --- include/boost/decimal/detail/u128.hpp | 52 ++++++++++----------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/include/boost/decimal/detail/u128.hpp b/include/boost/decimal/detail/u128.hpp index 1527ca15..c9ce3a50 100644 --- a/include/boost/decimal/detail/u128.hpp +++ b/include/boost/decimal/detail/u128.hpp @@ -2023,43 +2023,26 @@ constexpr void div_mod_impl(const u128& lhs, const std::uint64_t rhs, u128& quot remainder.low %= rhs; } } - - // Setup for Knuth Division - // Includes the normalization step here instead of in main function for simplicity - const auto offset { countl_zero(static_cast(rhs >> 32)) }; - - std::uint32_t u[5]{}; - - if (offset != 0) - { - u[0] = static_cast(lhs.low << offset); - u[1] = static_cast(lhs.low >> (32 - offset)); - u[2] = static_cast(lhs.high << offset) | - static_cast(lhs.low >> (64 - offset)); - u[3] = static_cast(lhs.high >> (32 - offset)); - u[4] = static_cast(lhs.high >> (64 - offset)); - } else { - u[0] = static_cast(lhs.low); - u[1] = static_cast(lhs.low >> 32); - u[2] = static_cast(lhs.high); - u[3] = static_cast(lhs.high >> 32); - } + const auto rhs32 = static_cast(rhs); - std::uint32_t v[2] - { - static_cast(rhs << offset), - static_cast(rhs >> (32 - offset)) - }; + auto current = static_cast(lhs.high >> 32U); + quotient.high = static_cast(static_cast(static_cast(current / rhs32)) << 32U); + remainder.low = static_cast(current % rhs32); - std::uint32_t q[3]{}; + current = static_cast(remainder.low << 32U) | static_cast(lhs.high); + quotient.high |= static_cast(current / rhs32); + remainder.low = static_cast(current % rhs32); - divide_knuth_core(u, v, q); + current = static_cast(remainder.low << 32U) | static_cast(lhs.low >> 32U); + quotient.low = static_cast(static_cast(static_cast(current / rhs32)) << 32U); + remainder.low = static_cast(current % rhs32); - quotient.low = (static_cast(q[1]) << 32) | q[0]; - quotient.high = q[2]; - remainder.low = (static_cast(u[1]) << (32 - offset)) | (static_cast(u[0]) >> offset); + current = remainder.low << 32U | static_cast(lhs.low); + quotient.low |= static_cast(current / rhs32); + remainder.low = static_cast(current % rhs32); + } } BOOST_DECIMAL_FORCE_INLINE constexpr void div_mod_impl(const u128& lhs, const u128& rhs, u128& quotient, u128& remainder) noexcept @@ -2272,11 +2255,14 @@ constexpr u128 operator/(const u128 lhs, const u128 rhs) noexcept #endif } - else if (rhs > lhs) + else if ((lhs.high == 0 && rhs.high != 0) || rhs.low == 0) { - // This would imply rhs.high != 0 and lhs.high == 0 which is always 0 return {0, 0}; } + else if (lhs.high == 0) + { + return { 0, lhs.low / rhs.low }; + } else { u128 quotient {}; From c89cb39b66cfc99930767b38608f111c68a63259 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Wed, 26 Mar 2025 09:26:18 -0400 Subject: [PATCH 191/191] Restore PPC64LE runs --- .github/workflows/{broken => }/qemu.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{broken => }/qemu.yml (100%) diff --git a/.github/workflows/broken/qemu.yml b/.github/workflows/qemu.yml similarity index 100% rename from .github/workflows/broken/qemu.yml rename to .github/workflows/qemu.yml