From 4dc53b9937ae78bc56dd139307243fd59b84968c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 13:49:36 -0500 Subject: [PATCH 01/14] Add exception free bitwise operators --- .../detail/unsigned_integer_basis.hpp | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 0ebc47a..7de12be 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -1873,6 +1873,37 @@ template } } +template +constexpr auto operator~(const detail::unsigned_integer_basis lhs) noexcept +{ + using return_type = detail::unsigned_integer_basis; + return return_type{~detail::raw_value(lhs)}; +} + +template +constexpr auto operator&(const detail::unsigned_integer_basis lhs, + const detail::unsigned_integer_basis rhs) noexcept +{ + using return_type = detail::unsigned_integer_basis; + return return_type{detail::raw_value(lhs) & detail::raw_value(rhs)}; +} + +template +constexpr auto operator|(const detail::unsigned_integer_basis lhs, + const detail::unsigned_integer_basis rhs) noexcept +{ + using return_type = detail::unsigned_integer_basis; + return return_type{detail::raw_value(lhs) | detail::raw_value(rhs)}; +} + +template +constexpr auto operator^(const detail::unsigned_integer_basis lhs, + const detail::unsigned_integer_basis rhs) noexcept +{ + using return_type = detail::unsigned_integer_basis; + return return_type{detail::raw_value(lhs) ^ detail::raw_value(rhs)}; +} + } // namespace boost::safe_numbers #undef BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP From 3cc68b575f31224c560b41e43ebbb6dea63e3d52 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 13:57:04 -0500 Subject: [PATCH 02/14] Add draft of left shift operator --- .../detail/unsigned_integer_basis.hpp | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 7de12be..6e391c4 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -13,6 +13,7 @@ #ifndef BOOST_SAFE_NUMBERS_BUILD_MODULE #include +#include #include #include #include @@ -1904,6 +1905,25 @@ constexpr auto operator^(const detail::unsigned_integer_basis lhs, return return_type{detail::raw_value(lhs) ^ detail::raw_value(rhs)}; } +template +constexpr auto operator<<(const detail::unsigned_integer_basis lhs, + const detail::unsigned_integer_basis rhs) +{ + using return_type = detail::unsigned_integer_basis; + + const auto raw_lhs {detail::raw_value(lhs)}; + const auto raw_rhs {detail::raw_value(rhs)}; + + const auto lhs_width {core::bit_width(raw_lhs)}; + + if (lhs_width + raw_lhs >= std::numeric_limits::digits) + { + BOOST_THROW_EXCEPTION(std::overflow_error("Left shift past the end of the type width")); + } + + return return_type{raw_lhs << raw_rhs}; +} + } // namespace boost::safe_numbers #undef BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP From 81e7fa9f145d72eb602328b7505d60e49d5ee424 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 13:57:52 -0500 Subject: [PATCH 03/14] Fix possible type error --- include/boost/safe_numbers/detail/unsigned_integer_basis.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 6e391c4..1a42545 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -1914,7 +1914,7 @@ constexpr auto operator<<(const detail::unsigned_integer_basis lhs, const auto raw_lhs {detail::raw_value(lhs)}; const auto raw_rhs {detail::raw_value(rhs)}; - const auto lhs_width {core::bit_width(raw_lhs)}; + const auto lhs_width {static_cast(core::bit_width(raw_lhs))}; if (lhs_width + raw_lhs >= std::numeric_limits::digits) { From eeeb35fd09df3a2a7ce787ba916dd1dfc9671e50 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 14:45:01 -0500 Subject: [PATCH 04/14] Add draft right shift --- .../detail/unsigned_integer_basis.hpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 1a42545..95f728f 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -1924,6 +1924,25 @@ constexpr auto operator<<(const detail::unsigned_integer_basis lhs, return return_type{raw_lhs << raw_rhs}; } +template +constexpr auto operator>>(const detail::unsigned_integer_basis lhs, + const detail::unsigned_integer_basis rhs) +{ + using return_type = detail::unsigned_integer_basis; + + const auto raw_lhs {detail::raw_value(lhs)}; + const auto raw_rhs {detail::raw_value(rhs)}; + + const auto lhs_first_one {core::countl_zero(raw_lhs)}; + + if (lhs_first_one < raw_rhs) + { + BOOST_THROW_EXCEPTION(std::overflow_error("Right shift past the end of the type width")); + } + + return return_type{raw_lhs >> raw_rhs}; +} + } // namespace boost::safe_numbers #undef BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP From 34bda5a72a40e8c5049b88d98629c309e9854933 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 15:08:49 -0500 Subject: [PATCH 05/14] Change raw_value getter to constexpr from consteval --- include/boost/safe_numbers/detail/fwd.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/safe_numbers/detail/fwd.hpp b/include/boost/safe_numbers/detail/fwd.hpp index d0945f5..009154e 100644 --- a/include/boost/safe_numbers/detail/fwd.hpp +++ b/include/boost/safe_numbers/detail/fwd.hpp @@ -78,7 +78,7 @@ concept valid_bound = !std::is_same_v && (is_unsigned_library_type_v template requires valid_bound -consteval auto raw_value(T val) noexcept +constexpr auto raw_value(T val) noexcept { if constexpr (is_unsigned_library_type_v) { From 67a8e562172086a7c964cfd3693c4a8f4e57aae9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 15:09:06 -0500 Subject: [PATCH 06/14] Fix u128 support for bit shifting --- .../safe_numbers/detail/unsigned_integer_basis.hpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 95f728f..202d8ff 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #ifndef BOOST_SAFE_NUMBERS_BUILD_MODULE @@ -1910,13 +1911,14 @@ constexpr auto operator<<(const detail::unsigned_integer_basis lhs, const detail::unsigned_integer_basis rhs) { using return_type = detail::unsigned_integer_basis; + using core::bit_width; const auto raw_lhs {detail::raw_value(lhs)}; const auto raw_rhs {detail::raw_value(rhs)}; - const auto lhs_width {static_cast(core::bit_width(raw_lhs))}; + const auto lhs_width {static_cast(bit_width(raw_lhs))}; - if (lhs_width + raw_lhs >= std::numeric_limits::digits) + if (lhs_width + raw_rhs >= std::numeric_limits::digits) { BOOST_THROW_EXCEPTION(std::overflow_error("Left shift past the end of the type width")); } @@ -1929,13 +1931,10 @@ constexpr auto operator>>(const detail::unsigned_integer_basis lhs, const detail::unsigned_integer_basis rhs) { using return_type = detail::unsigned_integer_basis; - const auto raw_lhs {detail::raw_value(lhs)}; const auto raw_rhs {detail::raw_value(rhs)}; - const auto lhs_first_one {core::countl_zero(raw_lhs)}; - - if (lhs_first_one < raw_rhs) + if (raw_rhs >= static_cast(std::numeric_limits::digits)) { BOOST_THROW_EXCEPTION(std::overflow_error("Right shift past the end of the type width")); } From d10c231caaf95ed01c65790e4cada4c3e9e1e5b2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 15:09:17 -0500 Subject: [PATCH 07/14] Add testing of bitwise ops --- test/Jamfile | 1 + test/test_unsigned_bitwise_ops.cpp | 501 +++++++++++++++++++++++++++++ 2 files changed, 502 insertions(+) create mode 100644 test/test_unsigned_bitwise_ops.cpp diff --git a/test/Jamfile b/test/Jamfile index 2ec2e0f..a8fd43b 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -71,6 +71,7 @@ run test_unsigned_std_format.cpp ; run test_unsigned_fmt_format.cpp ; run test_unsigned_charconv.cpp ; run test_bit.cpp ; +run test_unsigned_bitwise_ops.cpp ; run test_unsigned_saturating_addition.cpp ; run test_unsigned_comparisons.cpp ; run test_unsigned_saturating_subtraction.cpp ; diff --git a/test/test_unsigned_bitwise_ops.cpp b/test/test_unsigned_bitwise_ops.cpp new file mode 100644 index 0000000..7726494 --- /dev/null +++ b/test/test_unsigned_bitwise_ops.cpp @@ -0,0 +1,501 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#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" +# 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" +# 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 "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wfloat-equal" +# pragma GCC diagnostic ignored "-Woverflow" + +#elif defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4389) +# pragma warning(disable : 4127) +# pragma warning(disable : 4305) +# pragma warning(disable : 4309) +#endif + +#define BOOST_SAFE_NUMBERS_DETAIL_INT128_ALLOW_SIGN_COMPARE +#define BOOST_SAFE_NUMBERS_DETAIL_INT128_ALLOW_SIGN_CONVERSION + +#include + +#ifdef __clang__ +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#elif defined(_MSC_VER) +# pragma warning(pop) +#endif + +// Ignore [[nodiscard]] on the test that we know are going to throw +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-result" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-result" +#elif defined(_MSC_VER) +# pragma warning (push) +# pragma warning (disable: 4834) +#endif + +#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE + +import boost.safe_numbers; + +#else + +#include +#include +#include +#include + +#endif + +using namespace boost::safe_numbers; + +inline std::mt19937_64 rng{42}; +inline constexpr std::size_t N {1024}; + +// ============================================= +// Bitwise NOT (~) +// ============================================= + +template +void test_bitwise_not() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw {dist(rng)}; + const T wrapped {raw}; + const auto result {~wrapped}; + const T expected {static_cast(~raw)}; + + BOOST_TEST(expected == result); + } + + // Edge cases + BOOST_TEST(T{std::numeric_limits::max()} == ~T{0}); + BOOST_TEST(T{0} == ~T{std::numeric_limits::max()}); +} + +// ============================================= +// Bitwise AND (&) +// ============================================= + +template +void test_bitwise_and() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + const T a {raw_a}; + const T b {raw_b}; + const auto result {a & b}; + const T expected {static_cast(raw_a & raw_b)}; + + BOOST_TEST(expected == result); + } + + // Edge cases + BOOST_TEST(T{0} == (T{0} & T{std::numeric_limits::max()})); + BOOST_TEST(T{std::numeric_limits::max()} == (T{std::numeric_limits::max()} & T{std::numeric_limits::max()})); + + // x & x == x + const T val {42}; + BOOST_TEST(val == (val & val)); +} + +// ============================================= +// Bitwise OR (|) +// ============================================= + +template +void test_bitwise_or() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + const T a {raw_a}; + const T b {raw_b}; + const auto result {a | b}; + const T expected {static_cast(raw_a | raw_b)}; + + BOOST_TEST(expected == result); + } + + // Edge cases + BOOST_TEST(T{std::numeric_limits::max()} == (T{0} | T{std::numeric_limits::max()})); + BOOST_TEST(T{0} == (T{0} | T{0})); + + // x | 0 == x + const T val {42}; + BOOST_TEST(val == (val | T{0})); +} + +// ============================================= +// Bitwise XOR (^) +// ============================================= + +template +void test_bitwise_xor() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + const T a {raw_a}; + const T b {raw_b}; + const auto result {a ^ b}; + const T expected {static_cast(raw_a ^ raw_b)}; + + BOOST_TEST(expected == result); + } + + // Edge cases: x ^ x == 0 + const T val {42}; + BOOST_TEST(T{0} == (val ^ val)); + + // x ^ 0 == x + BOOST_TEST(val == (val ^ T{0})); +} + +// ============================================= +// Left Shift (<<) - Success +// ============================================= + +template +void test_left_shift_success() +{ + using basis_type = detail::underlying_type_t; + constexpr auto digits {std::numeric_limits::digits}; + + // Shifting zero by any amount should be fine + BOOST_TEST(T{0} == (T{0} << T{0})); + + // Shift 1 left by amounts that fit + for (int i {0}; i < digits - 1; ++i) + { + const auto shift {static_cast(i)}; + const auto expected {static_cast(static_cast(1) << shift)}; + const auto result {T{1} << T{shift}}; + + BOOST_TEST(T{expected} == result); + } + + // Shift small values by small amounts + const T two {2}; + BOOST_TEST(T{4} == (two << T{1})); + BOOST_TEST(T{8} == (two << T{2})); +} + +// ============================================= +// Left Shift (<<) - Failure (throws) +// ============================================= + +template +void test_left_shift_failure() +{ + using basis_type = detail::underlying_type_t; + constexpr auto digits {std::numeric_limits::digits}; + + // Shifting by the full type width should throw + BOOST_TEST_THROWS(T{1} << T{static_cast(digits)}, std::overflow_error); + + // Shifting a large value left should throw + BOOST_TEST_THROWS(T{std::numeric_limits::max()} << T{1}, std::overflow_error); + + // Shifting the high bit left by 1 should throw + const auto high_bit {static_cast(static_cast(1) << (digits - 1))}; + BOOST_TEST_THROWS(T{high_bit} << T{1}, std::overflow_error); +} + +// ============================================= +// Right Shift (>>) - Success +// ============================================= + +template +void test_right_shift_success() +{ + using basis_type = detail::underlying_type_t; + constexpr auto digits {std::numeric_limits::digits}; + + // Shifting zero by any amount should be fine + BOOST_TEST(T{0} == (T{0} >> T{0})); + + // Shift the high bit right by increasing amounts + const auto high_bit {static_cast(static_cast(1) << (digits - 1))}; + for (int i {0}; i < digits; ++i) + { + const auto shift {static_cast(i)}; + const auto expected {static_cast(high_bit >> shift)}; + const auto result {T{high_bit} >> T{shift}}; + + BOOST_TEST(T{expected} == result); + } + + // Shift a value by 0 returns the same value + const T val {42}; + BOOST_TEST(val == (val >> T{0})); +} + +// ============================================= +// Right Shift (>>) - Failure (throws) +// ============================================= + +template +void test_right_shift_failure() +{ + using basis_type = detail::underlying_type_t; + constexpr auto digits {std::numeric_limits::digits}; + + // Shifting by the full type width should throw + BOOST_TEST_THROWS(T{1} >> T{static_cast(digits)}, std::overflow_error); + + // Shifting by more than the type width should throw + if constexpr (digits < std::numeric_limits::max()) + { + BOOST_TEST_THROWS(T{1} >> T{static_cast(digits + 1)}, std::overflow_error); + } +} + +// ============================================= +// u128 specific tests (boost::random supports uint128_t) +// ============================================= + +void test_bitwise_not_u128() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw {dist(rng)}; + const u128 wrapped {raw}; + const auto result {~wrapped}; + const u128 expected {~raw}; + + BOOST_TEST(expected == result); + } + + BOOST_TEST(u128{std::numeric_limits::max()} == ~u128{0}); + BOOST_TEST(u128{0} == ~u128{std::numeric_limits::max()}); +} + +void test_bitwise_and_u128() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + const u128 a {raw_a}; + const u128 b {raw_b}; + const auto result {a & b}; + const u128 expected {raw_a & raw_b}; + + BOOST_TEST(expected == result); + } + + BOOST_TEST(u128{0} == (u128{0} & u128{std::numeric_limits::max()})); + + const u128 val {42}; + BOOST_TEST(val == (val & val)); +} + +void test_bitwise_or_u128() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + const u128 a {raw_a}; + const u128 b {raw_b}; + const auto result {a | b}; + const u128 expected {raw_a | raw_b}; + + BOOST_TEST(expected == result); + } + + BOOST_TEST(u128{std::numeric_limits::max()} == (u128{0} | u128{std::numeric_limits::max()})); + + const u128 val {42}; + BOOST_TEST(val == (val | u128{0})); +} + +void test_bitwise_xor_u128() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + const u128 a {raw_a}; + const u128 b {raw_b}; + const auto result {a ^ b}; + const u128 expected {raw_a ^ raw_b}; + + BOOST_TEST(expected == result); + } + + const u128 val {42}; + BOOST_TEST(u128{0} == (val ^ val)); + BOOST_TEST(val == (val ^ u128{0})); +} + +void test_left_shift_success_u128() +{ + BOOST_TEST(u128{0} == (u128{0} << u128{0})); + + for (int i {0}; i < 127; ++i) + { + using basis_type = detail::underlying_type_t; + const auto shift {static_cast(i)}; + const auto expected {static_cast(static_cast(1) << shift)}; + const auto result {u128{1} << u128{shift}}; + + BOOST_TEST(u128{expected} == result); + } + + const u128 two {2}; + BOOST_TEST(u128{4} == (two << u128{1})); + BOOST_TEST(u128{8} == (two << u128{2})); +} + +void test_left_shift_failure_u128() +{ + using basis_type = detail::underlying_type_t; + + BOOST_TEST_THROWS(u128{1} << u128{128}, std::overflow_error); + BOOST_TEST_THROWS(u128{std::numeric_limits::max()} << u128{1}, std::overflow_error); +} + +void test_right_shift_success_u128() +{ + using basis_type = detail::underlying_type_t; + + BOOST_TEST(u128{0} == (u128{0} >> u128{0})); + + const auto high_bit {static_cast(static_cast(1) << 127)}; + for (int i {0}; i < 128; ++i) + { + const auto shift {static_cast(i)}; + const auto expected {static_cast(high_bit >> shift)}; + const auto result {u128{high_bit} >> u128{shift}}; + + BOOST_TEST(u128{expected} == result); + } + + const u128 val {42}; + BOOST_TEST(val == (val >> u128{0})); +} + +void test_right_shift_failure_u128() +{ + BOOST_TEST_THROWS(u128{1} >> u128{128}, std::overflow_error); + BOOST_TEST_THROWS(u128{1} >> u128{200}, std::overflow_error); +} + +int main() +{ + // Bitwise NOT + test_bitwise_not(); + test_bitwise_not(); + test_bitwise_not(); + test_bitwise_not(); + test_bitwise_not_u128(); + + // Bitwise AND + test_bitwise_and(); + test_bitwise_and(); + test_bitwise_and(); + test_bitwise_and(); + test_bitwise_and_u128(); + + // Bitwise OR + test_bitwise_or(); + test_bitwise_or(); + test_bitwise_or(); + test_bitwise_or(); + test_bitwise_or_u128(); + + // Bitwise XOR + test_bitwise_xor(); + test_bitwise_xor(); + test_bitwise_xor(); + test_bitwise_xor(); + test_bitwise_xor_u128(); + + // Left shift - success + test_left_shift_success(); + test_left_shift_success(); + test_left_shift_success(); + test_left_shift_success(); + test_left_shift_success_u128(); + + // Left shift - failure + test_left_shift_failure(); + test_left_shift_failure(); + test_left_shift_failure(); + test_left_shift_failure(); + test_left_shift_failure_u128(); + + // Right shift - success + test_right_shift_success(); + test_right_shift_success(); + test_right_shift_success(); + test_right_shift_success(); + test_right_shift_success_u128(); + + // Right shift - failure + test_right_shift_failure(); + test_right_shift_failure(); + test_right_shift_failure(); + test_right_shift_failure(); + test_right_shift_failure_u128(); + + return boost::report_errors(); +} From 16d110bfcd30a324ddfb9dd8eb7aaf794dfd1bba Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 15:16:29 -0500 Subject: [PATCH 08/14] Add and test compound bitwise operators --- .../detail/unsigned_integer_basis.hpp | 66 +++++- test/test_unsigned_bitwise_ops.cpp | 217 ++++++++++++++++++ 2 files changed, 277 insertions(+), 6 deletions(-) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 202d8ff..7ef9ec8 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -76,6 +76,16 @@ class unsigned_integer_basis template constexpr auto operator%=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; + constexpr auto operator&=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; + + constexpr auto operator|=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; + + constexpr auto operator^=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; + + constexpr auto operator<<=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; + + constexpr auto operator>>=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; + constexpr auto operator++() -> unsigned_integer_basis&; constexpr auto operator++(int) -> unsigned_integer_basis; @@ -1879,7 +1889,7 @@ template constexpr auto operator~(const detail::unsigned_integer_basis lhs) noexcept { using return_type = detail::unsigned_integer_basis; - return return_type{~detail::raw_value(lhs)}; + return return_type{static_cast(~detail::raw_value(lhs))}; } template @@ -1887,7 +1897,7 @@ constexpr auto operator&(const detail::unsigned_integer_basis lhs, const detail::unsigned_integer_basis rhs) noexcept { using return_type = detail::unsigned_integer_basis; - return return_type{detail::raw_value(lhs) & detail::raw_value(rhs)}; + return return_type{static_cast(detail::raw_value(lhs) & detail::raw_value(rhs))}; } template @@ -1895,7 +1905,7 @@ constexpr auto operator|(const detail::unsigned_integer_basis lhs, const detail::unsigned_integer_basis rhs) noexcept { using return_type = detail::unsigned_integer_basis; - return return_type{detail::raw_value(lhs) | detail::raw_value(rhs)}; + return return_type{static_cast(detail::raw_value(lhs) | detail::raw_value(rhs))}; } template @@ -1903,7 +1913,7 @@ constexpr auto operator^(const detail::unsigned_integer_basis lhs, const detail::unsigned_integer_basis rhs) noexcept { using return_type = detail::unsigned_integer_basis; - return return_type{detail::raw_value(lhs) ^ detail::raw_value(rhs)}; + return return_type{static_cast(detail::raw_value(lhs) ^ detail::raw_value(rhs))}; } template @@ -1923,7 +1933,7 @@ constexpr auto operator<<(const detail::unsigned_integer_basis lhs, BOOST_THROW_EXCEPTION(std::overflow_error("Left shift past the end of the type width")); } - return return_type{raw_lhs << raw_rhs}; + return return_type{static_cast(raw_lhs << raw_rhs)}; } template @@ -1939,7 +1949,51 @@ constexpr auto operator>>(const detail::unsigned_integer_basis lhs, BOOST_THROW_EXCEPTION(std::overflow_error("Right shift past the end of the type width")); } - return return_type{raw_lhs >> raw_rhs}; + return return_type{static_cast(raw_lhs >> raw_rhs)}; +} + +// ------------------------------ +// Compound bitwise operators +// ------------------------------ + +template +constexpr auto detail::unsigned_integer_basis::operator&=(const unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis& +{ + *this = *this & rhs; + return *this; +} + +template +constexpr auto detail::unsigned_integer_basis::operator|=(const unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis& +{ + *this = *this | rhs; + return *this; +} + +template +constexpr auto detail::unsigned_integer_basis::operator^=(const unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis& +{ + *this = *this ^ rhs; + return *this; +} + +template +constexpr auto detail::unsigned_integer_basis::operator<<=(const unsigned_integer_basis rhs) + -> unsigned_integer_basis& +{ + *this = *this << rhs; + return *this; +} + +template +constexpr auto detail::unsigned_integer_basis::operator>>=(const unsigned_integer_basis rhs) + -> unsigned_integer_basis& +{ + *this = *this >> rhs; + return *this; } } // namespace boost::safe_numbers diff --git a/test/test_unsigned_bitwise_ops.cpp b/test/test_unsigned_bitwise_ops.cpp index 7726494..1157c4e 100644 --- a/test/test_unsigned_bitwise_ops.cpp +++ b/test/test_unsigned_bitwise_ops.cpp @@ -295,6 +295,112 @@ void test_right_shift_failure() } } +// ============================================= +// Compound bitwise operators (&=, |=, ^=, <<=, >>=) +// ============================================= + +template +void test_compound_and() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + T a {raw_a}; + const T b {raw_b}; + a &= b; + const T expected {static_cast(raw_a & raw_b)}; + + BOOST_TEST(expected == a); + } +} + +template +void test_compound_or() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + T a {raw_a}; + const T b {raw_b}; + a |= b; + const T expected {static_cast(raw_a | raw_b)}; + + BOOST_TEST(expected == a); + } +} + +template +void test_compound_xor() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + T a {raw_a}; + const T b {raw_b}; + a ^= b; + const T expected {static_cast(raw_a ^ raw_b)}; + + BOOST_TEST(expected == a); + } +} + +template +void test_compound_left_shift() +{ + using basis_type = detail::underlying_type_t; + + // Valid compound left shift + T val {1}; + val <<= T{3}; + BOOST_TEST(T{8} == val); + + // Chained compound left shifts + T v {1}; + v <<= T{1}; + BOOST_TEST(T{2} == v); + v <<= T{1}; + BOOST_TEST(T{4} == v); + + // Compound left shift that overflows should throw + T big {std::numeric_limits::max()}; + BOOST_TEST_THROWS(big <<= T{1}, std::overflow_error); +} + +template +void test_compound_right_shift() +{ + using basis_type = detail::underlying_type_t; + constexpr auto digits {std::numeric_limits::digits}; + + // Valid compound right shift + T val {8}; + val >>= T{3}; + BOOST_TEST(T{1} == val); + + // Chained compound right shifts + T v {16}; + v >>= T{1}; + BOOST_TEST(T{8} == v); + v >>= T{2}; + BOOST_TEST(T{2} == v); + + // Compound right shift past type width should throw + T one {1}; + BOOST_TEST_THROWS(one >>= T{static_cast(digits)}, std::overflow_error); +} + // ============================================= // u128 specific tests (boost::random supports uint128_t) // ============================================= @@ -439,6 +545,82 @@ void test_right_shift_failure_u128() BOOST_TEST_THROWS(u128{1} >> u128{200}, std::overflow_error); } +void test_compound_and_u128() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + u128 a {raw_a}; + const u128 b {raw_b}; + a &= b; + const u128 expected {raw_a & raw_b}; + + BOOST_TEST(expected == a); + } +} + +void test_compound_or_u128() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + u128 a {raw_a}; + const u128 b {raw_b}; + a |= b; + const u128 expected {raw_a | raw_b}; + + BOOST_TEST(expected == a); + } +} + +void test_compound_xor_u128() +{ + using basis_type = detail::underlying_type_t; + boost::random::uniform_int_distribution dist {0, std::numeric_limits::max()}; + + for (std::size_t i {0}; i < N; ++i) + { + const auto raw_a {dist(rng)}; + const auto raw_b {dist(rng)}; + u128 a {raw_a}; + const u128 b {raw_b}; + a ^= b; + const u128 expected {raw_a ^ raw_b}; + + BOOST_TEST(expected == a); + } +} + +void test_compound_left_shift_u128() +{ + using basis_type = detail::underlying_type_t; + + u128 val {1}; + val <<= u128{3}; + BOOST_TEST(u128{8} == val); + + u128 big {std::numeric_limits::max()}; + BOOST_TEST_THROWS(big <<= u128{1}, std::overflow_error); +} + +void test_compound_right_shift_u128() +{ + u128 val {8}; + val >>= u128{3}; + BOOST_TEST(u128{1} == val); + + u128 one {1}; + BOOST_TEST_THROWS(one >>= u128{128}, std::overflow_error); +} + int main() { // Bitwise NOT @@ -497,5 +679,40 @@ int main() test_right_shift_failure(); test_right_shift_failure_u128(); + // Compound &= + test_compound_and(); + test_compound_and(); + test_compound_and(); + test_compound_and(); + test_compound_and_u128(); + + // Compound |= + test_compound_or(); + test_compound_or(); + test_compound_or(); + test_compound_or(); + test_compound_or_u128(); + + // Compound ^= + test_compound_xor(); + test_compound_xor(); + test_compound_xor(); + test_compound_xor(); + test_compound_xor_u128(); + + // Compound <<= + test_compound_left_shift(); + test_compound_left_shift(); + test_compound_left_shift(); + test_compound_left_shift(); + test_compound_left_shift_u128(); + + // Compound >>= + test_compound_right_shift(); + test_compound_right_shift(); + test_compound_right_shift(); + test_compound_right_shift(); + test_compound_right_shift_u128(); + return boost::report_errors(); } From 7f8d4f9d9151ebab02610ca15be45145e520b4b2 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 15:30:11 -0500 Subject: [PATCH 09/14] Add bitwise operations example --- examples/bitwise_ops.cpp | 88 ++++++++++++++++++++++++++++++++++++++++ test/Jamfile | 1 + 2 files changed, 89 insertions(+) create mode 100644 examples/bitwise_ops.cpp diff --git a/examples/bitwise_ops.cpp b/examples/bitwise_ops.cpp new file mode 100644 index 0000000..1c37171 --- /dev/null +++ b/examples/bitwise_ops.cpp @@ -0,0 +1,88 @@ +// Copyright 2026 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include + +int main() +{ + using namespace boost::safe_numbers; + + const auto a = u32{0xFF00FF00u}; + const auto b = u32{0x0F0F0F0Fu}; + + // Bitwise NOT + std::cout << std::hex; + std::cout << "~a = 0x" << ~a << '\n'; + + // Bitwise AND + std::cout << "a & b = 0x" << (a & b) << '\n'; + + // Bitwise OR + std::cout << "a | b = 0x" << (a | b) << '\n'; + + // Bitwise XOR + std::cout << "a ^ b = 0x" << (a ^ b) << '\n'; + + std::cout << std::dec << '\n'; + + // Left shift (throws if bits would be shifted past the type width) + const auto one = u8{1}; + const auto shift = u8{4}; + std::cout << "u8(1) << 4 = " << static_cast(one << shift) << '\n'; + + // Right shift (throws if the shift amount >= type width) + const auto val = u8{0x80}; + std::cout << "u8(128) >> 4 = " << static_cast(val >> shift) << '\n'; + + std::cout << '\n'; + + // Compound assignment operators + auto x = u32{0xFF00u}; + x &= u32{0x0F0Fu}; + std::cout << std::hex; + std::cout << "x &= 0x0F0F -> 0x" << x << '\n'; + + x |= u32{0xF000u}; + std::cout << "x |= 0xF000 -> 0x" << x << '\n'; + + x ^= u32{0x00FFu}; + std::cout << "x ^= 0x00FF -> 0x" << x << '\n'; + + std::cout << std::dec; + auto y = u32{1}; + y <<= u32{8}; + std::cout << "y <<= 8 -> " << y << '\n'; + + y >>= u32{4}; + std::cout << "y >>= 4 -> " << y << '\n'; + + std::cout << '\n'; + + // Shift overflow detection + try + { + const auto big = u8{0xFF}; + const auto result = big << u8{1}; // Would shift bits past width + std::cout << "Should not reach here: " << static_cast(result) << '\n'; + } + catch (const std::overflow_error& e) + { + std::cerr << "Left shift error: " << e.what() << '\n'; + } + + try + { + const auto val2 = u8{1}; + const auto result = val2 >> u8{8}; // Shift amount >= type width + std::cout << "Should not reach here: " << static_cast(result) << '\n'; + } + catch (const std::overflow_error& e) + { + std::cerr << "Right shift error: " << e.what() << '\n'; + } + + return 0; +} diff --git a/test/Jamfile b/test/Jamfile index a8fd43b..9d40b75 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -141,3 +141,4 @@ run ../examples/iostream.cpp ; run ../examples/charconv.cpp ; run ../examples/bit.cpp ; run ../examples/bounded_limits.cpp ; +run ../examples/bitwise_ops.cpp ; From d162121b602ae8b6465b9b5df6d0cfaacaacfd37 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 15:31:40 -0500 Subject: [PATCH 10/14] Add bitwise ops to documentation page --- doc/modules/ROOT/pages/examples.adoc | 34 +++++++ doc/modules/ROOT/pages/unsigned_integers.adoc | 95 ++++++++++++++++++- 2 files changed, 128 insertions(+), 1 deletion(-) diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index 5b9d955..0d199b8 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -361,6 +361,40 @@ byteswap(0x12345678) = 0x78563412 ---- ==== +[#examples_bitwise_ops] +== Bitwise Operations + +The library provides bitwise operators (`~`, `&`, `|`, `^`, `<<`, `>>`) and their compound assignment forms (`&=`, `|=`, `^=`, `<<=`, `>>=`). +The NOT, AND, OR, and XOR operators are `noexcept`, while the shift operators throw `std::overflow_error` if bits would be shifted past the type width. + +.This https://github.com/boostorg/safe_numbers/blob/develop/examples/bitwise_ops.cpp[example] demonstrates the bitwise operators and shift overflow detection. +==== +[source, c++] +---- +include::example$bitwise_ops.cpp[] +---- + +Output: +---- +~a = 0xff00ff +a & b = 0xf000f00 +a | b = 0xff0fff0f +a ^ b = 0xf00ff00f + +u8(1) << 4 = 16 +u8(128) >> 4 = 8 + +x &= 0x0F0F -> 0xf00 +x |= 0xF000 -> 0xff00 +x ^= 0x00FF -> 0xffff +y <<= 8 -> 256 +y >>= 4 -> 16 + +Left shift error: Left shift past the end of the type width +Right shift error: Right shift past the end of the type width +---- +==== + [#examples_policy_comparison] == Policy Comparison diff --git a/doc/modules/ROOT/pages/unsigned_integers.adoc b/doc/modules/ROOT/pages/unsigned_integers.adoc index 1a1480f..cb37d57 100644 --- a/doc/modules/ROOT/pages/unsigned_integers.adoc +++ b/doc/modules/ROOT/pages/unsigned_integers.adoc @@ -59,7 +59,7 @@ public: friend constexpr auto operator<=>(unsigned_integer_basis lhs, unsigned_integer_basis rhs) noexcept -> std::strong_ordering = default; - // Compound assignment operators + // Compound assignment operators (arithmetic) template constexpr auto operator+=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; @@ -75,6 +75,13 @@ public: template constexpr auto operator%=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; + // Compound assignment operators (bitwise) + constexpr auto operator&=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; + constexpr auto operator|=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; + constexpr auto operator^=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; + constexpr auto operator<<=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; + constexpr auto operator>>=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; + // Increment and decrement operators constexpr auto operator++() -> unsigned_integer_basis&; constexpr auto operator++(int) -> unsigned_integer_basis; @@ -104,6 +111,36 @@ template constexpr auto operator%(unsigned_integer_basis lhs, unsigned_integer_basis rhs) -> unsigned_integer_basis; +// Bitwise operators +template +constexpr auto operator~(unsigned_integer_basis lhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator&(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator|(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator^(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator<<(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) + -> unsigned_integer_basis; + +template +constexpr auto operator>>(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) + -> unsigned_integer_basis; + // Saturating arithmetic (clamp to min/max on overflow/underflow) template constexpr T saturating_add(T lhs, T rhs) noexcept; @@ -313,6 +350,62 @@ constexpr auto operator%=(unsigned_integer_basis rhs) -> unsigned_in Compound assignment operators follow the same exception behavior as their corresponding arithmetic operators. +=== Bitwise Operators + +[source,c++] +---- +template +constexpr auto operator~(unsigned_integer_basis lhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator&(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator|(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator^(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) noexcept + -> unsigned_integer_basis; + +template +constexpr auto operator<<(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) + -> unsigned_integer_basis; + +template +constexpr auto operator>>(unsigned_integer_basis lhs, + unsigned_integer_basis rhs) + -> unsigned_integer_basis; +---- + +The bitwise NOT, AND, OR, and XOR operators (`~`, `&`, `|`, `^`) are `noexcept` and operate directly on the underlying values, since these operations cannot overflow. + +The shift operators (`<<`, `>>`) perform runtime bounds checking: + +- `<<`: Throws `std::overflow_error` if the left shift would move bits past the type width. Specifically, this occurs when `bit_width(lhs) + rhs >= std::numeric_limits::digits`. +- `>>`: Throws `std::overflow_error` if the shift amount is greater than or equal to the type width (i.e., `rhs >= std::numeric_limits::digits`). + +=== Compound Bitwise Assignment Operators + +[source,c++] +---- +constexpr auto operator&=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; +constexpr auto operator|=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; +constexpr auto operator^=(unsigned_integer_basis rhs) noexcept -> unsigned_integer_basis&; +constexpr auto operator<<=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; +constexpr auto operator>>=(unsigned_integer_basis rhs) -> unsigned_integer_basis&; +---- + +Compound bitwise assignment operators delegate to the corresponding free-function bitwise operators and follow the same exception behavior. +`&=`, `|=`, and `^=` are `noexcept`. +`<<=` and `>>=` throw `std::overflow_error` under the same conditions as `<<` and `>>`. + === Increment and Decrement Operators [source,c++] From bf0ef22a7911f2ff1ac4cabb87e565c52b2e7d45 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 9 Feb 2026 15:54:45 -0500 Subject: [PATCH 11/14] Fully qualify calls to operator<< and >> for GCC-11 --- include/boost/safe_numbers/detail/unsigned_integer_basis.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index 7ef9ec8..0a3e4d3 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -1984,7 +1984,7 @@ template constexpr auto detail::unsigned_integer_basis::operator<<=(const unsigned_integer_basis rhs) -> unsigned_integer_basis& { - *this = *this << rhs; + *this = boost::safe_numbers::operator<<(*this, rhs); return *this; } @@ -1992,7 +1992,7 @@ template constexpr auto detail::unsigned_integer_basis::operator>>=(const unsigned_integer_basis rhs) -> unsigned_integer_basis& { - *this = *this >> rhs; + *this = boost::safe_numbers::operator>>(*this, rhs); return *this; } From 31f93687a3a6263c2167184ea14729ada1550b35 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 10 Feb 2026 13:16:10 -0500 Subject: [PATCH 12/14] Fix potentially unaligned loads --- .../safe_numbers/detail/int128/detail/uint128_imp.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp b/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp index c2f57cd..1f5d9e2 100644 --- a/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp +++ b/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp @@ -402,8 +402,8 @@ BOOST_SAFE_NUMBERS_DETAIL_INT128_EXPORT constexpr bool operator==(const uint128_ } else { - __m128i a = _mm_load_si128(reinterpret_cast(&lhs)); - __m128i b = _mm_load_si128(reinterpret_cast(&rhs)); + __m128i a = _mm_loadu_si128(reinterpret_cast(&lhs)); + __m128i b = _mm_loadu_si128(reinterpret_cast(&rhs)); __m128i cmp = _mm_cmpeq_epi32(a, b); return _mm_movemask_ps(_mm_castsi128_ps(cmp)) == 0xF; @@ -538,8 +538,8 @@ BOOST_SAFE_NUMBERS_DETAIL_INT128_EXPORT constexpr bool operator!=(const uint128_ } else { - __m128i a = _mm_load_si128(reinterpret_cast(&lhs)); - __m128i b = _mm_load_si128(reinterpret_cast(&rhs)); + __m128i a = _mm_loadu_si128(reinterpret_cast(&lhs)); + __m128i b = _mm_loadu_si128(reinterpret_cast(&rhs)); __m128i cmp = _mm_cmpeq_epi32(a, b); return _mm_movemask_ps(_mm_castsi128_ps(cmp)) != 0xF; From 487545422512118e871d8c44fd99441202da1bd9 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 10 Feb 2026 13:23:31 -0500 Subject: [PATCH 13/14] Only default init to fix 32-bit codegen problems --- include/boost/safe_numbers/bounded_integers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/safe_numbers/bounded_integers.hpp b/include/boost/safe_numbers/bounded_integers.hpp index ba31b12..b571a6c 100644 --- a/include/boost/safe_numbers/bounded_integers.hpp +++ b/include/boost/safe_numbers/bounded_integers.hpp @@ -43,7 +43,7 @@ class bounded_uint private: using underlying_type = detail::underlying_type_t; - basis_type basis_ {static_cast(detail::raw_value(Min))}; + basis_type basis_ {}; public: From ea1c86a6dd909df49d729d1614481f0458191b9f Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 10 Feb 2026 13:26:09 -0500 Subject: [PATCH 14/14] Match u128 and i128 alignment --- .../boost/safe_numbers/detail/int128/detail/uint128_imp.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp b/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp index 1f5d9e2..16b76fe 100644 --- a/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp +++ b/include/boost/safe_numbers/detail/int128/detail/uint128_imp.hpp @@ -25,8 +25,8 @@ namespace boost { namespace int128 { -BOOST_SAFE_NUMBERS_DETAIL_INT128_EXPORT struct - #ifdef BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_INT128 +struct + #if defined(BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_INT128) || defined(BOOST_SAFE_NUMBERS_DETAIL_INT128_HAS_MSVC_INT128) alignas(alignof(detail::builtin_u128)) #else alignas(16)