From 833a0e7082f0e577640c514971270ad8e2f7e924 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 23 Feb 2026 13:05:32 -0500 Subject: [PATCH 1/4] Remove wrapping policy --- include/boost/safe_numbers/overflow_policy.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/include/boost/safe_numbers/overflow_policy.hpp b/include/boost/safe_numbers/overflow_policy.hpp index 09c15cc..59c7c94 100644 --- a/include/boost/safe_numbers/overflow_policy.hpp +++ b/include/boost/safe_numbers/overflow_policy.hpp @@ -13,7 +13,6 @@ enum class overflow_policy saturate, overflow_tuple, checked, - wrapping, strict, widen, }; From 01a2e077b7bc7a39b35987ea347284e5a63eb0d8 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 23 Feb 2026 13:05:58 -0500 Subject: [PATCH 2/4] Remove wrapping policy overloads --- .../detail/unsigned_integer_basis.hpp | 303 +----------------- 1 file changed, 5 insertions(+), 298 deletions(-) diff --git a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp index fce4d4b..d59d238 100644 --- a/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp +++ b/include/boost/safe_numbers/detail/unsigned_integer_basis.hpp @@ -332,38 +332,6 @@ struct add_helper } }; -// Partial specialization for wrapping policy -template -struct add_helper -{ - [[nodiscard]] static constexpr auto apply(const unsigned_integer_basis lhs, - const unsigned_integer_basis rhs) noexcept - -> unsigned_integer_basis - { - using result_type = unsigned_integer_basis; - - const auto lhs_basis {static_cast(lhs)}; - const auto rhs_basis {static_cast(rhs)}; - BasisType res {}; - - if constexpr (!std::is_same_v) - { - #if BOOST_SAFE_NUMBERS_HAS_BUILTIN(__builtin_add_overflow) || BOOST_SAFE_NUMBERS_HAS_BUILTIN(_addcarry_u64) || defined(BOOST_SAFENUMBERS_HAS_WINDOWS_X86_INTRIN) - - if (!std::is_constant_evaluated()) - { - impl::unsigned_intrin_add(lhs_basis, rhs_basis, res); - return result_type{res}; - } - - #endif - } - - impl::unsigned_no_intrin_add(lhs_basis, rhs_basis, res); - return result_type{res}; - } -}; - // Partial specialization for widening policy template struct add_helper @@ -382,7 +350,7 @@ struct add_helper template [[nodiscard]] constexpr auto add_impl(const unsigned_integer_basis lhs, const unsigned_integer_basis rhs) - noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::wrapping || Policy == overflow_policy::strict || Policy == overflow_policy::widen) + noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::strict || Policy == overflow_policy::widen) { return add_helper::apply(lhs, rhs); } @@ -718,42 +686,10 @@ struct sub_helper } }; -// Partial specialization for wrapping policy -template -struct sub_helper -{ - [[nodiscard]] static constexpr auto apply(const unsigned_integer_basis lhs, - const unsigned_integer_basis rhs) noexcept - -> unsigned_integer_basis - { - using result_type = unsigned_integer_basis; - - const auto lhs_basis {static_cast(lhs)}; - const auto rhs_basis {static_cast(rhs)}; - BasisType res {}; - - if constexpr (!std::is_same_v) - { - #if BOOST_SAFE_NUMBERS_HAS_BUILTIN(__builtin_sub_overflow) || BOOST_SAFE_NUMBERS_HAS_BUILTIN(_subborrow_u64) || defined(BOOST_SAFENUMBERS_HAS_WINDOWS_X86_INTRIN) - - if (!std::is_constant_evaluated()) - { - impl::unsigned_intrin_sub(lhs_basis, rhs_basis, res); - return result_type{res}; - } - - #endif - } - - impl::unsigned_no_intrin_sub(lhs_basis, rhs_basis, res); - return result_type{res}; - } -}; - template [[nodiscard]] constexpr auto sub_impl(const unsigned_integer_basis lhs, const unsigned_integer_basis rhs) - noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::wrapping || Policy == overflow_policy::strict) + noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::strict) { return sub_helper::apply(lhs, rhs); } @@ -987,38 +923,6 @@ struct mul_helper } }; -// Partial specialization for wrapping policy -template -struct mul_helper -{ - [[nodiscard]] static constexpr auto apply(const unsigned_integer_basis lhs, - const unsigned_integer_basis rhs) noexcept - -> unsigned_integer_basis - { - using result_type = unsigned_integer_basis; - - const auto lhs_basis {static_cast(lhs)}; - const auto rhs_basis {static_cast(rhs)}; - BasisType res {}; - - if constexpr (!std::is_same_v) - { - #if BOOST_SAFE_NUMBERS_HAS_BUILTIN(__builtin_mul_overflow) || BOOST_SAFE_NUMBERS_HAS_BUILTIN(_umul128) - - if (!std::is_constant_evaluated()) - { - impl::unsigned_intrin_mul(lhs_basis, rhs_basis, res); - return result_type{res}; - } - - #endif - } - - impl::no_intrin_mul(lhs_basis, rhs_basis, res); - return result_type{res}; - } -}; - // Partial specialization for widening policy template struct mul_helper @@ -1037,7 +941,7 @@ struct mul_helper template [[nodiscard]] constexpr auto mul_impl(const unsigned_integer_basis lhs, const unsigned_integer_basis rhs) - noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::wrapping || Policy == overflow_policy::strict || Policy == overflow_policy::widen) + noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::strict || Policy == overflow_policy::widen) { return mul_helper::apply(lhs, rhs); } @@ -1160,35 +1064,6 @@ struct div_helper } }; -// Partial specialization for wrapping policy -// Note: unsigned division cannot overflow, so this just performs normal division -// Division by zero still throws -template -struct div_helper -{ - [[nodiscard]] static constexpr auto apply(const unsigned_integer_basis lhs, - const unsigned_integer_basis rhs) - -> unsigned_integer_basis - { - using result_type = unsigned_integer_basis; - - const auto divisor {static_cast(rhs)}; - if (divisor == 0U) [[unlikely]] - { - BOOST_THROW_EXCEPTION(std::domain_error("Unsigned division by zero")); - } - - if constexpr (std::is_same_v || std::is_same_v) - { - return result_type{static_cast(static_cast(lhs) / divisor)}; - } - else - { - return result_type{static_cast(lhs) / divisor}; - } - } -}; - template [[nodiscard]] constexpr auto div_impl(const unsigned_integer_basis lhs, const unsigned_integer_basis rhs) @@ -1315,35 +1190,6 @@ struct mod_helper } }; -// Partial specialization for wrapping policy -// Note: unsigned modulo cannot overflow, so this just performs normal modulo -// Modulo by zero still throws -template -struct mod_helper -{ - [[nodiscard]] static constexpr auto apply(const unsigned_integer_basis lhs, - const unsigned_integer_basis rhs) - -> unsigned_integer_basis - { - using result_type = unsigned_integer_basis; - - const auto divisor {static_cast(rhs)}; - if (divisor == 0U) [[unlikely]] - { - BOOST_THROW_EXCEPTION(std::domain_error("Unsigned modulo by zero")); - } - - if constexpr (std::is_same_v || std::is_same_v) - { - return result_type{static_cast(static_cast(lhs) % divisor)}; - } - else - { - return result_type{static_cast(lhs) % divisor}; - } - } -}; - template [[nodiscard]] constexpr auto mod_impl(const unsigned_integer_basis lhs, const unsigned_integer_basis rhs) @@ -1523,33 +1369,10 @@ struct shl_helper } }; -// Partial specialization for wrapping policy -template -struct shl_helper -{ - [[nodiscard]] static constexpr auto apply(const unsigned_integer_basis lhs, - const unsigned_integer_basis rhs) noexcept - -> unsigned_integer_basis - { - using result_type = unsigned_integer_basis; - - const auto raw_lhs {static_cast(lhs)}; - const auto raw_rhs {static_cast(rhs)}; - - // Guard against UB: shifting by >= digits is undefined behavior - if (raw_rhs >= static_cast(std::numeric_limits::digits)) - { - return result_type{0U}; - } - - return result_type{static_cast(raw_lhs << raw_rhs)}; - } -}; - template [[nodiscard]] constexpr auto shl_impl(const unsigned_integer_basis lhs, const unsigned_integer_basis rhs) - noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::wrapping || Policy == overflow_policy::strict) + noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::strict) { return shl_helper::apply(lhs, rhs); } @@ -1638,32 +1461,10 @@ struct shr_helper } }; -// Partial specialization for wrapping policy -template -struct shr_helper -{ - [[nodiscard]] static constexpr auto apply(const unsigned_integer_basis lhs, - const unsigned_integer_basis rhs) noexcept - -> unsigned_integer_basis - { - using result_type = unsigned_integer_basis; - - const auto raw_lhs {static_cast(lhs)}; - const auto raw_rhs {static_cast(rhs)}; - - if (raw_rhs >= static_cast(std::numeric_limits::digits)) - { - return result_type{0U}; - } - - return result_type{static_cast(raw_lhs >> raw_rhs)}; - } -}; - template [[nodiscard]] constexpr auto shr_impl(const unsigned_integer_basis lhs, const unsigned_integer_basis rhs) - noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::wrapping || Policy == overflow_policy::strict) + noexcept(Policy == overflow_policy::saturate || Policy == overflow_policy::overflow_tuple || Policy == overflow_policy::checked || Policy == overflow_policy::strict) { return shr_helper::apply(lhs, rhs); } @@ -1826,56 +1627,6 @@ template BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("checked modulo", checked_mod) -template -[[nodiscard]] constexpr auto wrapping_add(const detail::unsigned_integer_basis lhs, - const detail::unsigned_integer_basis rhs) noexcept - -> detail::unsigned_integer_basis -{ - return detail::add_impl(lhs, rhs); -} - -BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("wrapping addition", wrapping_add) - -template -[[nodiscard]] constexpr auto wrapping_sub(const detail::unsigned_integer_basis lhs, - const detail::unsigned_integer_basis rhs) noexcept - -> detail::unsigned_integer_basis -{ - return detail::sub_impl(lhs, rhs); -} - -BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("wrapping subtraction", wrapping_sub) - -template -[[nodiscard]] constexpr auto wrapping_mul(const detail::unsigned_integer_basis lhs, - const detail::unsigned_integer_basis rhs) noexcept - -> detail::unsigned_integer_basis -{ - return detail::mul_impl(lhs, rhs); -} - -BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("wrapping multiplication", wrapping_mul) - -template -[[nodiscard]] constexpr auto wrapping_div(const detail::unsigned_integer_basis lhs, - const detail::unsigned_integer_basis rhs) - -> detail::unsigned_integer_basis -{ - return detail::div_impl(lhs, rhs); -} - -BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("wrapping division", wrapping_div) - -template -[[nodiscard]] constexpr auto wrapping_mod(const detail::unsigned_integer_basis lhs, - const detail::unsigned_integer_basis rhs) - -> detail::unsigned_integer_basis -{ - return detail::mod_impl(lhs, rhs); -} - -BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("wrapping modulo", wrapping_mod) - template [[nodiscard]] constexpr auto strict_add(const detail::unsigned_integer_basis lhs, const detail::unsigned_integer_basis rhs) noexcept @@ -2016,30 +1767,6 @@ template BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("checked right shift", checked_shr) -// ------------------------------ -// Wrapping Shift -// ------------------------------ - -template -[[nodiscard]] constexpr auto wrapping_shl(const detail::unsigned_integer_basis lhs, - const detail::unsigned_integer_basis rhs) noexcept - -> detail::unsigned_integer_basis -{ - return detail::shl_impl(lhs, rhs); -} - -BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("wrapping left shift", wrapping_shl) - -template -[[nodiscard]] constexpr auto wrapping_shr(const detail::unsigned_integer_basis lhs, - const detail::unsigned_integer_basis rhs) noexcept - -> detail::unsigned_integer_basis -{ - return detail::shr_impl(lhs, rhs); -} - -BOOST_SAFE_NUMBERS_DEFINE_MIXED_UNSIGNED_INTEGER_OP("wrapping right shift", wrapping_shr) - // ------------------------------ // Strict Shift // ------------------------------ @@ -2089,10 +1816,6 @@ template { return checked_add(lhs, rhs); } - else if constexpr (Policy == overflow_policy::wrapping) - { - return wrapping_add(lhs, rhs); - } else if constexpr (Policy == overflow_policy::strict) { return strict_add(lhs, rhs); @@ -2128,10 +1851,6 @@ template { return checked_sub(lhs, rhs); } - else if constexpr (Policy == overflow_policy::wrapping) - { - return wrapping_sub(lhs, rhs); - } else if constexpr (Policy == overflow_policy::strict) { return strict_sub(lhs, rhs); @@ -2163,10 +1882,6 @@ template { return checked_mul(lhs, rhs); } - else if constexpr (Policy == overflow_policy::wrapping) - { - return wrapping_mul(lhs, rhs); - } else if constexpr (Policy == overflow_policy::strict) { return strict_mul(lhs, rhs); @@ -2202,10 +1917,6 @@ template { return checked_div(lhs, rhs); } - else if constexpr (Policy == overflow_policy::wrapping) - { - return wrapping_div(lhs, rhs); - } else if constexpr (Policy == overflow_policy::strict) { return strict_div(lhs, rhs); @@ -2237,10 +1948,6 @@ template { return checked_mod(lhs, rhs); } - else if constexpr (Policy == overflow_policy::wrapping) - { - return wrapping_mod(lhs, rhs); - } else if constexpr (Policy == overflow_policy::strict) { return strict_mod(lhs, rhs); From 22ca3b20b99c20fd25034df7a597cb9c1bc43698 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 23 Feb 2026 13:06:23 -0500 Subject: [PATCH 3/4] Remove testing and examples of wrapping policy --- examples/bitwise_ops.cpp | 8 - examples/generic_arithmetic.cpp | 6 - examples/safe_numerics_comparison.cpp | 7 +- examples/wrapping_arithmetic.cpp | 95 ----------- test/Jamfile | 6 - test/test_boundary_arithmetic.cpp | 70 -------- test/test_exhaustive_u8_arithmetic.cpp | 63 +------- test/test_unsigned_bitwise_ops.cpp | 74 --------- test/test_unsigned_wrapping_addition.cpp | 150 ----------------- test/test_unsigned_wrapping_division.cpp | 141 ---------------- test/test_unsigned_wrapping_mod.cpp | 141 ---------------- .../test_unsigned_wrapping_multiplication.cpp | 152 ----------------- test/test_unsigned_wrapping_subtraction.cpp | 153 ------------------ 13 files changed, 4 insertions(+), 1062 deletions(-) delete mode 100644 examples/wrapping_arithmetic.cpp delete mode 100644 test/test_unsigned_wrapping_addition.cpp delete mode 100644 test/test_unsigned_wrapping_division.cpp delete mode 100644 test/test_unsigned_wrapping_mod.cpp delete mode 100644 test/test_unsigned_wrapping_multiplication.cpp delete mode 100644 test/test_unsigned_wrapping_subtraction.cpp diff --git a/examples/bitwise_ops.cpp b/examples/bitwise_ops.cpp index fe80d39..798bd1c 100644 --- a/examples/bitwise_ops.cpp +++ b/examples/bitwise_ops.cpp @@ -138,14 +138,6 @@ int main() std::cout << '\n'; - // Wrapping shifts: perform the shift ignoring overflow - std::cout << "wrapping_shl(u8(255), 1) = " - << static_cast(wrapping_shl(u8{0xFF}, u8{1})) << '\n'; - std::cout << "wrapping_shr(u8(1), 8) = " - << static_cast(wrapping_shr(u8{1}, u8{8})) << '\n'; - - std::cout << '\n'; - // Generic policy-parameterized shifts std::cout << "shl(u32(1), 30) = " << shl(u32{1}, u32{30}) << '\n'; diff --git a/examples/generic_arithmetic.cpp b/examples/generic_arithmetic.cpp index 30fa721..ae2f78a 100644 --- a/examples/generic_arithmetic.cpp +++ b/examples/generic_arithmetic.cpp @@ -31,12 +31,10 @@ int main() { const auto throwing {add(a, b)}; const auto saturated {add(a, b)}; - const auto wrapped {add(a, b)}; const auto strict {add(a, b)}; std::cout << "add(100, 50) = " << throwing << std::endl; std::cout << "add(100, 50) = " << saturated << std::endl; - std::cout << "add(100, 50) = " << wrapped << std::endl; std::cout << "add(100, 50) = " << strict << std::endl; } @@ -69,10 +67,6 @@ int main() // saturate policy clamps to max const auto sat_result {add(max_val, one)}; std::cout << "add(max, 1) = " << sat_result << std::endl; - - // wrapping policy wraps around - const auto wrap_result {add(max_val, one)}; - std::cout << "add(max, 1) = " << wrap_result << std::endl; } return 0; diff --git a/examples/safe_numerics_comparison.cpp b/examples/safe_numerics_comparison.cpp index a5579c2..339b505 100644 --- a/examples/safe_numerics_comparison.cpp +++ b/examples/safe_numerics_comparison.cpp @@ -13,7 +13,7 @@ // 2. SafeNumbers requires explicit construction and forbids implicit // conversions; SafeNumerics allows implicit construction from built-ins. // 3. SafeNumbers provides named free functions for alternative overflow -// policies (wrapping_add, saturating_sub, checked_mul, etc.); +// policies (saturating_add, saturating_sub, checked_mul, etc.); // SafeNumerics selects behavior via template policy parameters on the type. // 4. SafeNumbers forbids mixed-width arithmetic at compile time; // SafeNumerics promotes operands using C++ native promotion rules. @@ -155,11 +155,6 @@ int main() const auto x = safe_num::u8{250U}; const auto y = safe_num::u8{10U}; - // Wrapping: modular arithmetic like built-in unsigned - const auto wrapped = safe_num::wrapping_add(x, y); - std::cout << "wrapping_add(250, 10) = " << wrapped << std::endl; - // Output: 4 - // Saturating: clamp at the max/min boundary const auto saturated = safe_num::saturating_add(x, y); std::cout << "saturating_add(250, 10) = " << saturated << std::endl; diff --git a/examples/wrapping_arithmetic.cpp b/examples/wrapping_arithmetic.cpp deleted file mode 100644 index f040bce..0000000 --- a/examples/wrapping_arithmetic.cpp +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2025 Matt Borland -// Distributed under the Boost Software License, Version 1.0. -// https://www.boost.org/LICENSE_1_0.txt - -// This example demonstrates the use of wrapping arithmetic operations. -// These functions perform standard C unsigned integer wrapping behavior - -// when overflow or underflow occurs, the result wraps around modulo 2^N. -// This matches the behavior of built-in unsigned integers in C/C++. - -#include -#include -#include -#include -#include - -int main() -{ - using boost::safe_numbers::u8; - using boost::safe_numbers::u32; - using boost::safe_numbers::wrapping_add; - using boost::safe_numbers::wrapping_sub; - using boost::safe_numbers::wrapping_mul; - - // Wrapping addition: wraps around on overflow - { - const u8 a {255U}; // UINT8_MAX - const u8 b {2U}; - const u8 result {wrapping_add(a, b)}; - - std::cout << "wrapping_add(255, 2) = " << result << std::endl; - // Output: 1 (255 + 2 = 257, wraps to 257 % 256 = 1) - } - - // Wrapping subtraction: wraps around on underflow - { - const u8 a {0U}; - const u8 b {1U}; - const u8 result {wrapping_sub(a, b)}; - - std::cout << "wrapping_sub(0, 1) = " << result << std::endl; - // Output: 255 (wraps to UINT8_MAX) - } - - // Wrapping multiplication: wraps around on overflow - { - const u8 a {200U}; - const u8 b {2U}; - const u8 result {wrapping_mul(a, b)}; - - std::cout << "wrapping_mul(200, 2) = " << result << std::endl; - // Output: 144 (200 * 2 = 400, wraps to 400 % 256 = 144) - } - - // Demonstration with u32 - { - const u32 max_val {std::numeric_limits::max()}; - const u32 one {1U}; - - std::cout << "wrapping_add(UINT32_MAX, 1) = " - << wrapping_add(max_val, one) << std::endl; - // Output: 0 (wraps around) - - const u32 zero {0U}; - std::cout << "wrapping_sub(0, 1) = " - << wrapping_sub(zero, one) << std::endl; - // Output: 4294967295 (UINT32_MAX) - } - - // Normal operations that don't overflow work as expected - { - const u32 a {100U}; - const u32 b {50U}; - - std::cout << "wrapping_add(100, 50) = " << wrapping_add(a, b) << std::endl; - std::cout << "wrapping_sub(100, 50) = " << wrapping_sub(a, b) << std::endl; - std::cout << "wrapping_mul(100, 50) = " << wrapping_mul(a, b) << std::endl; - // Output: 150, 50, 5000 - } - - // Useful for implementing counters that wrap - { - u8 counter {254U}; - std::cout << "Counter sequence: "; - for (int i = 0; i < 5; ++i) - { - std::cout << counter << " "; - counter = wrapping_add(counter, u8{1U}); - } - std::cout << std::endl; - // Output: 254 255 0 1 2 - } - - return 0; -} - diff --git a/test/Jamfile b/test/Jamfile index 599d9ff..5533422 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -90,11 +90,6 @@ run test_unsigned_checked_subtraction.cpp ; run test_unsigned_checked_multiplication.cpp ; run test_unsigned_checked_division.cpp ; run test_unsigned_checked_mod.cpp ; -run test_unsigned_wrapping_addition.cpp ; -run test_unsigned_wrapping_subtraction.cpp ; -run test_unsigned_wrapping_multiplication.cpp ; -run test_unsigned_wrapping_division.cpp ; -run test_unsigned_wrapping_mod.cpp ; run-fail test_unsigned_strict_addition.cpp ; run-fail test_unsigned_strict_subtraction.cpp ; run-fail test_unsigned_strict_multiplication.cpp ; @@ -167,7 +162,6 @@ run ../examples/construction_and_conversion.cpp ; run ../examples/saturating_arithmetic.cpp ; run ../examples/overflowing_arithmetic.cpp ; run ../examples/checked_arithmetic.cpp ; -run ../examples/wrapping_arithmetic.cpp ; run ../examples/strict_arithmetic.cpp ; run ../examples/generic_arithmetic.cpp ; run ../examples/literals.cpp ; diff --git a/test/test_boundary_arithmetic.cpp b/test/test_boundary_arithmetic.cpp index d896ff1..6c5a9ed 100644 --- a/test/test_boundary_arithmetic.cpp +++ b/test/test_boundary_arithmetic.cpp @@ -197,28 +197,6 @@ void test_boundary_checked_add() } } -template -void test_boundary_wrapping_add() -{ - const auto boundary_vals {get_boundary_values()}; - - for (const auto lhs_val : boundary_vals) - { - for (const auto rhs_val : boundary_vals) - { - const SafeT lhs {lhs_val}; - const SafeT rhs {rhs_val}; - - using wider = wider_type_t; - const auto wide_sum {static_cast(lhs_val) + static_cast(rhs_val)}; - const auto expected {static_cast(wide_sum)}; - - const auto result {wrapping_add(lhs, rhs)}; - BOOST_TEST_EQ(static_cast(result), expected); - } - } -} - // ============================================ // Subtraction boundary tests // ============================================ @@ -295,26 +273,6 @@ void test_boundary_checked_sub() } } -template -void test_boundary_wrapping_sub() -{ - const auto boundary_vals {get_boundary_values()}; - - for (const auto lhs_val : boundary_vals) - { - for (const auto rhs_val : boundary_vals) - { - const SafeT lhs {lhs_val}; - const SafeT rhs {rhs_val}; - - const auto expected {static_cast(lhs_val - rhs_val)}; - - const auto result {wrapping_sub(lhs, rhs)}; - BOOST_TEST_EQ(static_cast(result), expected); - } - } -} - // ============================================ // Multiplication boundary tests // ============================================ @@ -410,28 +368,6 @@ void test_boundary_checked_mul() } } -template -void test_boundary_wrapping_mul() -{ - const auto boundary_vals {get_boundary_values()}; - - for (const auto lhs_val : boundary_vals) - { - for (const auto rhs_val : boundary_vals) - { - const SafeT lhs {lhs_val}; - const SafeT rhs {rhs_val}; - - using wider = wider_type_t; - const auto wide_product {static_cast(lhs_val) * static_cast(rhs_val)}; - const auto expected {static_cast(wide_product)}; - - const auto result {wrapping_mul(lhs, rhs)}; - BOOST_TEST_EQ(static_cast(result), expected); - } - } -} - // ============================================ // Division boundary tests // ============================================ @@ -599,19 +535,16 @@ void run_all_boundary_tests() test_boundary_saturating_add(); test_boundary_overflowing_add(); test_boundary_checked_add(); - test_boundary_wrapping_add(); // Subtraction test_boundary_saturating_sub(); test_boundary_overflowing_sub(); test_boundary_checked_sub(); - test_boundary_wrapping_sub(); // Multiplication test_boundary_saturating_mul(); test_boundary_overflowing_mul(); test_boundary_checked_mul(); - test_boundary_wrapping_mul(); // Division (checked only, others throw on div by zero) test_boundary_checked_div(); @@ -628,7 +561,6 @@ void run_u128_boundary_tests() test_boundary_saturating_sub(); test_boundary_overflowing_sub(); test_boundary_checked_sub(); - test_boundary_wrapping_sub(); test_boundary_u128_saturating_mul(); test_boundary_u128_overflowing_mul(); test_boundary_checked_div(); @@ -636,9 +568,7 @@ void run_u128_boundary_tests() // These use the generic templates which work for subtraction, div, mod test_boundary_checked_add(); - test_boundary_wrapping_add(); test_boundary_checked_mul(); - test_boundary_wrapping_mul(); } int main() diff --git a/test/test_exhaustive_u8_arithmetic.cpp b/test/test_exhaustive_u8_arithmetic.cpp index aa8699d..4375a42 100644 --- a/test/test_exhaustive_u8_arithmetic.cpp +++ b/test/test_exhaustive_u8_arithmetic.cpp @@ -94,24 +94,6 @@ void test_exhaustive_checked_add() } } -void test_exhaustive_wrapping_add() -{ - for (std::uint32_t lhs_val {0}; lhs_val <= U8_MAX; ++lhs_val) - { - for (std::uint32_t rhs_val {0}; rhs_val <= U8_MAX; ++rhs_val) - { - const auto lhs {u8{static_cast(lhs_val)}}; - const auto rhs {u8{static_cast(rhs_val)}}; - - const auto wide_sum {lhs_val + rhs_val}; - const auto expected {static_cast(wide_sum & 0xFFU)}; - - const auto result {wrapping_add(lhs, rhs)}; - BOOST_TEST_EQ(static_cast(result), expected); - } - } -} - // ============================================ // Subtraction Tests // ============================================ @@ -180,24 +162,6 @@ void test_exhaustive_checked_sub() } } -void test_exhaustive_wrapping_sub() -{ - for (std::uint32_t lhs_val {0}; lhs_val <= U8_MAX; ++lhs_val) - { - for (std::uint32_t rhs_val {0}; rhs_val <= U8_MAX; ++rhs_val) - { - const auto lhs {u8{static_cast(lhs_val)}}; - const auto rhs {u8{static_cast(rhs_val)}}; - - const auto wide_diff {static_cast(lhs_val) - static_cast(rhs_val)}; - const auto expected {static_cast((wide_diff + 256) & 0xFFU)}; - - const auto result {wrapping_sub(lhs, rhs)}; - BOOST_TEST_EQ(static_cast(result), expected); - } - } -} - // ============================================ // Multiplication Tests // ============================================ @@ -267,24 +231,6 @@ void test_exhaustive_checked_mul() } } -void test_exhaustive_wrapping_mul() -{ - for (std::uint32_t lhs_val {0}; lhs_val <= U8_MAX; ++lhs_val) - { - for (std::uint32_t rhs_val {0}; rhs_val <= U8_MAX; ++rhs_val) - { - const auto lhs {u8{static_cast(lhs_val)}}; - const auto rhs {u8{static_cast(rhs_val)}}; - - const auto wide_product {lhs_val * rhs_val}; - const auto expected {static_cast(wide_product & 0xFFU)}; - - const auto result {wrapping_mul(lhs, rhs)}; - BOOST_TEST_EQ(static_cast(result), expected); - } - } -} - // ============================================ // Division Tests // ============================================ @@ -343,23 +289,20 @@ void test_exhaustive_checked_mod() int main() { - // Addition: 4 policies tested exhaustively + // Addition: 3 policies tested exhaustively test_exhaustive_saturating_add(); test_exhaustive_overflowing_add(); test_exhaustive_checked_add(); - test_exhaustive_wrapping_add(); - // Subtraction: 4 policies tested exhaustively + // Subtraction: 3 policies tested exhaustively test_exhaustive_saturating_sub(); test_exhaustive_overflowing_sub(); test_exhaustive_checked_sub(); - test_exhaustive_wrapping_sub(); - // Multiplication: 4 policies tested exhaustively + // Multiplication: 3 policies tested exhaustively test_exhaustive_saturating_mul(); test_exhaustive_overflowing_mul(); test_exhaustive_checked_mul(); - test_exhaustive_wrapping_mul(); // Division: checked policy tested exhaustively (others throw on div by zero) test_exhaustive_checked_div(); diff --git a/test/test_unsigned_bitwise_ops.cpp b/test/test_unsigned_bitwise_ops.cpp index eccddb3..5b79845 100644 --- a/test/test_unsigned_bitwise_ops.cpp +++ b/test/test_unsigned_bitwise_ops.cpp @@ -769,42 +769,6 @@ void test_checked_shr() } } -// ============================================= -// Wrapping Left Shift -// ============================================= - -template -void test_wrapping_shl() -{ - using basis_type = detail::underlying_type_t; - - // Non-overflow case: should return normal result - BOOST_TEST(T{4} == wrapping_shl(T{2}, T{1})); - BOOST_TEST(T{8} == wrapping_shl(T{1}, T{3})); - - // Overflow case: performs shift ignoring overflow (wraps) - const auto result {wrapping_shl(T{std::numeric_limits::max()}, T{1})}; - const auto expected {static_cast(static_cast(std::numeric_limits::max()) << 1)}; - BOOST_TEST(T{expected} == result); -} - -// ============================================= -// Wrapping Right Shift -// ============================================= - -template -void test_wrapping_shr() -{ - using basis_type = detail::underlying_type_t; - constexpr auto digits {std::numeric_limits::digits}; - - // Non-overflow case - BOOST_TEST(T{4} == wrapping_shr(T{8}, T{1})); - - // Overflow case: shift >= digits returns 0 - BOOST_TEST(T{0} == wrapping_shr(T{42}, T{static_cast(digits)})); -} - // ============================================= // Strict Left Shift (success cases only - failure calls exit()) // ============================================= @@ -860,9 +824,6 @@ void test_generic_shl() BOOST_TEST(!flag); } - // wrapping policy - BOOST_TEST(T{4} == shl(T{2}, T{1})); - // strict policy BOOST_TEST(T{4} == shl(T{2}, T{1})); } @@ -893,9 +854,6 @@ void test_generic_shr() BOOST_TEST(!flag); } - // wrapping policy - BOOST_TEST(T{4} == shr(T{8}, T{1})); - // strict policy BOOST_TEST(T{4} == shr(T{8}, T{1})); } @@ -974,29 +932,12 @@ void test_checked_shr_u128() } } -void test_wrapping_shl_u128() -{ - BOOST_TEST(u128{4} == wrapping_shl(u128{2}, u128{1})); - // Wrapping: just does the shift - using basis_type = detail::underlying_type_t; - const auto result {wrapping_shl(u128{std::numeric_limits::max()}, u128{1})}; - const auto expected {static_cast(std::numeric_limits::max() << 1)}; - BOOST_TEST(u128{expected} == result); -} - -void test_wrapping_shr_u128() -{ - BOOST_TEST(u128{4} == wrapping_shr(u128{8}, u128{1})); - BOOST_TEST(u128{0} == wrapping_shr(u128{42}, u128{128})); -} - void test_generic_shl_u128() { using basis_type = detail::underlying_type_t; BOOST_TEST(u128{4} == shl(u128{2}, u128{1})); BOOST_TEST(u128{std::numeric_limits::max()} == shl(u128{std::numeric_limits::max()}, u128{1})); - BOOST_TEST(u128{4} == shl(u128{2}, u128{1})); BOOST_TEST(u128{4} == shl(u128{2}, u128{1})); } @@ -1004,7 +945,6 @@ void test_generic_shr_u128() { BOOST_TEST(u128{4} == shr(u128{8}, u128{1})); BOOST_TEST(u128{0} == shr(u128{42}, u128{128})); - BOOST_TEST(u128{4} == shr(u128{8}, u128{1})); BOOST_TEST(u128{4} == shr(u128{8}, u128{1})); } @@ -1143,20 +1083,6 @@ int main() test_checked_shr(); test_checked_shr_u128(); - // Wrapping shl - test_wrapping_shl(); - test_wrapping_shl(); - test_wrapping_shl(); - test_wrapping_shl(); - test_wrapping_shl_u128(); - - // Wrapping shr - test_wrapping_shr(); - test_wrapping_shr(); - test_wrapping_shr(); - test_wrapping_shr(); - test_wrapping_shr_u128(); - // Strict shl (success only) test_strict_shl(); test_strict_shl(); diff --git a/test/test_unsigned_wrapping_addition.cpp b/test/test_unsigned_wrapping_addition.cpp deleted file mode 100644 index 83e8131..0000000 --- a/test/test_unsigned_wrapping_addition.cpp +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2025 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 - -#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE - -import boost.safe_numbers; - -#else - -#include -#include -#include -#include -#include -#include - -#endif - -using namespace boost::safe_numbers; - -inline std::mt19937_64 rng{42}; -inline constexpr std::size_t N {1024}; - -template -void test_valid_addition() -{ - using basis_type = detail::underlying_type_t; - boost::random::uniform_int_distribution dist {std::numeric_limits::min() / 2U, - std::numeric_limits::max() / 2U}; - - for (std::size_t i = 0; i < N; ++i) - { - const auto lhs_value {dist(rng)}; - const auto rhs_value {dist(rng)}; - - T ref_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - ref_value = static_cast(static_cast(static_cast(lhs_value + rhs_value))); - } - else - { - ref_value = static_cast(lhs_value + rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_add(lhs, rhs)}; - - BOOST_TEST_EQ(ref_value, res); - } -} - -template -void test_wrapping_addition() -{ - using basis_type = detail::underlying_type_t; - boost::random::uniform_int_distribution dist {2U, std::numeric_limits::max()}; - - for (std::size_t i = 0; i < N; ++i) - { - constexpr basis_type lhs_value {std::numeric_limits::max()}; - const auto rhs_value {dist(rng)}; - - // Calculate expected wrapped result - T expected_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - expected_value = static_cast(static_cast(static_cast(lhs_value) + rhs_value)); - } - else - { - expected_value = static_cast(lhs_value + rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_add(lhs, rhs)}; - - BOOST_TEST_EQ(res, expected_value); - } -} - -int main() -{ - test_valid_addition(); - test_wrapping_addition(); - - test_valid_addition(); - test_wrapping_addition(); - - test_valid_addition(); - test_wrapping_addition(); - - test_valid_addition(); - test_wrapping_addition(); - - test_valid_addition(); - test_wrapping_addition(); - - return boost::report_errors(); -} diff --git a/test/test_unsigned_wrapping_division.cpp b/test/test_unsigned_wrapping_division.cpp deleted file mode 100644 index 1d7a374..0000000 --- a/test/test_unsigned_wrapping_division.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2025 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 -#include -#include -#include - -#endif - -using namespace boost::safe_numbers; - -inline std::mt19937_64 rng{42}; -inline constexpr std::size_t N {1024}; - -template -void test_valid_division() -{ - using basis_type = detail::underlying_type_t; - boost::random::uniform_int_distribution dist {1U, std::numeric_limits::max()}; - - for (std::size_t i = 0; i < N; ++i) - { - const auto lhs_value {dist(rng)}; - const auto rhs_value {dist(rng)}; - - T ref_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - ref_value = static_cast(static_cast(static_cast(lhs_value) / rhs_value)); - } - else - { - ref_value = static_cast(lhs_value / rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_div(lhs, rhs)}; - - BOOST_TEST_EQ(ref_value, res); - } -} - -template -void test_division_by_zero_throws() -{ - const T lhs {42U}; - const T rhs {0U}; - - BOOST_TEST_THROWS(wrapping_div(lhs, rhs), std::domain_error); -} - -int main() -{ - test_valid_division(); - test_division_by_zero_throws(); - - test_valid_division(); - test_division_by_zero_throws(); - - test_valid_division(); - test_division_by_zero_throws(); - - test_valid_division(); - test_division_by_zero_throws(); - - test_valid_division(); - test_division_by_zero_throws(); - - return boost::report_errors(); -} diff --git a/test/test_unsigned_wrapping_mod.cpp b/test/test_unsigned_wrapping_mod.cpp deleted file mode 100644 index 5003876..0000000 --- a/test/test_unsigned_wrapping_mod.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2025 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 -#include -#include -#include - -#endif - -using namespace boost::safe_numbers; - -inline std::mt19937_64 rng{42}; -inline constexpr std::size_t N {1024}; - -template -void test_valid_modulo() -{ - using basis_type = detail::underlying_type_t; - boost::random::uniform_int_distribution dist {1U, std::numeric_limits::max()}; - - for (std::size_t i = 0; i < N; ++i) - { - const auto lhs_value {dist(rng)}; - const auto rhs_value {dist(rng)}; - - T ref_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - ref_value = static_cast(static_cast(static_cast(lhs_value) % rhs_value)); - } - else - { - ref_value = static_cast(lhs_value % rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_mod(lhs, rhs)}; - - BOOST_TEST_EQ(ref_value, res); - } -} - -template -void test_modulo_by_zero_throws() -{ - const T lhs {42U}; - const T rhs {0U}; - - BOOST_TEST_THROWS(wrapping_mod(lhs, rhs), std::domain_error); -} - -int main() -{ - test_valid_modulo(); - test_modulo_by_zero_throws(); - - test_valid_modulo(); - test_modulo_by_zero_throws(); - - test_valid_modulo(); - test_modulo_by_zero_throws(); - - test_valid_modulo(); - test_modulo_by_zero_throws(); - - test_valid_modulo(); - test_modulo_by_zero_throws(); - - return boost::report_errors(); -} diff --git a/test/test_unsigned_wrapping_multiplication.cpp b/test/test_unsigned_wrapping_multiplication.cpp deleted file mode 100644 index 7668938..0000000 --- a/test/test_unsigned_wrapping_multiplication.cpp +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2025 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 - -#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE - -import boost.safe_numbers; - -#else - -#include -#include -#include -#include -#include -#include - -#endif - -using namespace boost::safe_numbers; - -inline std::mt19937_64 rng{42}; -inline constexpr std::size_t N {1024}; - -template -void test_valid_multiplication() -{ - using basis_type = detail::underlying_type_t; - - // Use smaller range to avoid overflow in valid tests - constexpr auto max_val {static_cast(std::numeric_limits::max() >> (sizeof(basis_type) * 4U))}; - boost::random::uniform_int_distribution dist {0U, max_val}; - - for (std::size_t i = 0; i < N; ++i) - { - const auto lhs_value {dist(rng)}; - const auto rhs_value {dist(rng)}; - - T ref_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - ref_value = static_cast(static_cast(static_cast(lhs_value) * rhs_value)); - } - else - { - ref_value = static_cast(lhs_value * rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_mul(lhs, rhs)}; - - BOOST_TEST_EQ(ref_value, res); - } -} - -template -void test_wrapping_multiplication() -{ - using basis_type = detail::underlying_type_t; - boost::random::uniform_int_distribution dist {2U, std::numeric_limits::max()}; - - for (std::size_t i = 0; i < N; ++i) - { - constexpr basis_type lhs_value {std::numeric_limits::max()}; - const auto rhs_value {dist(rng)}; - - // Calculate expected wrapped result - T expected_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - expected_value = static_cast(static_cast(static_cast(lhs_value) * rhs_value)); - } - else - { - expected_value = static_cast(lhs_value * rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_mul(lhs, rhs)}; - - BOOST_TEST_EQ(res, expected_value); - } -} - -int main() -{ - test_valid_multiplication(); - test_wrapping_multiplication(); - - test_valid_multiplication(); - test_wrapping_multiplication(); - - test_valid_multiplication(); - test_wrapping_multiplication(); - - test_valid_multiplication(); - test_wrapping_multiplication(); - - test_valid_multiplication(); - test_wrapping_multiplication(); - - return boost::report_errors(); -} diff --git a/test/test_unsigned_wrapping_subtraction.cpp b/test/test_unsigned_wrapping_subtraction.cpp deleted file mode 100644 index 6d7f5fd..0000000 --- a/test/test_unsigned_wrapping_subtraction.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2025 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 - -#ifdef BOOST_SAFE_NUMBERS_BUILD_MODULE - -import boost.safe_numbers; - -#else - -#include -#include -#include -#include -#include -#include -#include - -#endif - -using namespace boost::safe_numbers; - -inline std::mt19937_64 rng{42}; -inline constexpr std::size_t N {1024}; - -template -void test_valid_subtraction() -{ - using basis_type = detail::underlying_type_t; - boost::random::uniform_int_distribution dist {std::numeric_limits::min() / 2U, - std::numeric_limits::max() / 2U}; - - for (std::size_t i = 0; i < N; ++i) - { - auto lhs_value {dist(rng)}; - auto rhs_value {dist(rng)}; - - if (lhs_value < rhs_value) - { - std::swap(lhs_value, rhs_value); - } - - T ref_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - ref_value = static_cast(static_cast(static_cast(lhs_value - rhs_value))); - } - else - { - ref_value = static_cast(lhs_value - rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_sub(lhs, rhs)}; - - BOOST_TEST(ref_value == res); - } -} - -template -void test_wrapping_subtraction() -{ - using basis_type = detail::underlying_type_t; - boost::random::uniform_int_distribution dist {1U, std::numeric_limits::max() - 1U}; - - for (std::size_t i = 0; i < N; ++i) - { - const auto lhs_value {dist(rng)}; - constexpr basis_type rhs_value {std::numeric_limits::max()}; - - // Calculate expected wrapped result (lhs - rhs wraps around) - T expected_value {}; - if constexpr (std::is_same_v || std::is_same_v) - { - expected_value = static_cast(static_cast(static_cast(lhs_value) - rhs_value)); - } - else - { - expected_value = static_cast(lhs_value - rhs_value); - } - - const T lhs {lhs_value}; - const T rhs {rhs_value}; - const T res {wrapping_sub(lhs, rhs)}; - - BOOST_TEST_EQ(res, expected_value); - } -} - -int main() -{ - test_valid_subtraction(); - test_valid_subtraction(); - test_valid_subtraction(); - test_valid_subtraction(); - test_valid_subtraction(); - - test_wrapping_subtraction(); - test_wrapping_subtraction(); - test_wrapping_subtraction(); - test_wrapping_subtraction(); - test_wrapping_subtraction(); - - return boost::report_errors(); -} From a88a91c78880d60eb48aa582519a7a03106eb537 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Mon, 23 Feb 2026 13:06:42 -0500 Subject: [PATCH 4/4] Remove all references to wrapping policy in the docs --- doc/modules/ROOT/nav.adoc | 1 - doc/modules/ROOT/pages/api_reference.adoc | 3 - doc/modules/ROOT/pages/comparisons.adoc | 3 +- doc/modules/ROOT/pages/design.adoc | 8 +-- doc/modules/ROOT/pages/examples.adoc | 32 ----------- doc/modules/ROOT/pages/policies.adoc | 55 ------------------- doc/modules/ROOT/pages/unsigned_integers.adoc | 33 +---------- doc/proposal/proposal.adoc | 3 - 8 files changed, 5 insertions(+), 133 deletions(-) diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index bae1705..d98d40d 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -7,7 +7,6 @@ ** xref:examples.adoc#examples_saturating[Saturating Arithmetic] ** xref:examples.adoc#examples_overflowing[Overflowing Arithmetic] ** xref:examples.adoc#examples_checked[Checked Arithmetic] -** xref:examples.adoc#examples_wrapping[Wrapping Arithmetic] ** xref:examples.adoc#examples_strict[Strict Arithmetic] ** xref:examples.adoc#examples_generic[Generic Policy-Parameterized Arithmetic] ** xref:examples.adoc#examples_literals[Literals] diff --git a/doc/modules/ROOT/pages/api_reference.adoc b/doc/modules/ROOT/pages/api_reference.adoc index 4678513..e5e186f 100644 --- a/doc/modules/ROOT/pages/api_reference.adoc +++ b/doc/modules/ROOT/pages/api_reference.adoc @@ -263,9 +263,6 @@ https://www.boost.org/LICENSE_1_0.txt | xref:policies.adoc[`checked_add`, `checked_sub`, `checked_mul`, `checked_div`, `checked_mod`] | Checked arithmetic (return `std::nullopt` on overflow) -| xref:policies.adoc[`wrapping_add`, `wrapping_sub`, `wrapping_mul`, `wrapping_div`, `wrapping_mod`] -| Wrapping arithmetic (wrap silently) - | xref:policies.adoc[`strict_add`, `strict_sub`, `strict_mul`, `strict_div`, `strict_mod`] | Strict arithmetic (call `std::exit(EXIT_FAILURE)` on error) diff --git a/doc/modules/ROOT/pages/comparisons.adoc b/doc/modules/ROOT/pages/comparisons.adoc index 21ae540..24f42cb 100644 --- a/doc/modules/ROOT/pages/comparisons.adoc +++ b/doc/modules/ROOT/pages/comparisons.adoc @@ -27,7 +27,7 @@ This page summarizes the key differences and provides a runnable example demonst | Implicit construction from built-in types is allowed, including from `bool`. | *Overflow policies* -| Named free functions per-operation: `wrapping_add`, `saturating_sub`, `checked_mul`, `overflowing_div`, etc. The default operator always throws. +| Named free functions per-operation: `saturating_add`, `saturating_sub`, `checked_mul`, `overflowing_div`, etc. The default operator always throws. | Policy is selected as a template parameter on the type itself (`safe`). No per-operation policy functions. | *Mixed-width arithmetic* @@ -93,7 +93,6 @@ safe_numbers threw: Underflow detected in unsigned subtraction safe_numerics: -1 --- Alternative policies (SafeNumbers) --- -wrapping_add(250, 10) = 4 saturating_add(250, 10) = 255 checked_add(250, 10) = nullopt overflowing_add(250, 10) = {4, true} diff --git a/doc/modules/ROOT/pages/design.adoc b/doc/modules/ROOT/pages/design.adoc index cb2a43a..5306971 100644 --- a/doc/modules/ROOT/pages/design.adoc +++ b/doc/modules/ROOT/pages/design.adoc @@ -84,7 +84,6 @@ auto b = u8{255} + u8{1}; // throws Different domains require different responses to overflow: - Signal processing often needs *saturation*: clamp to the representable range and continue. -- Cryptographic algorithms require *wrapping*: modular arithmetic is the correct behavior. - Safety-critical systems may require *hard termination*: no exceptions, no recovery, just stop. - General application code benefits from *exceptions*: the error is reported and can be handled. - Some code needs to *detect and branch*: check whether overflow occurred without changing control flow. @@ -97,12 +96,11 @@ For other needs, named free functions make the policy explicit at each call site using boost::safe_numbers::u8; auto a = saturating_add(u8{200}, u8{100}); // a == u8{255} -auto b = wrapping_add(u8{200}, u8{100}); // b == u8{44} -auto c = checked_add(u8{200}, u8{100}); // c == std::nullopt -auto [d, overflow] = overflowing_add(u8{200}, u8{100}); // d == u8{44}, overflow == true +auto b = checked_add(u8{200}, u8{100}); // b == std::nullopt +auto [c, overflow] = overflowing_add(u8{200}, u8{100}); // c == u8{44}, overflow == true ---- -This approach is inspired by Rust's primitive type API, where `checked_add`, `saturating_add`, and `wrapping_add` are methods on integer types. +This approach is inspired by Rust's primitive type API, where `checked_add` and `saturating_add` are methods on integer types. For generic code that needs to be parameterized on the overflow policy, the library provides policy-parameterized free functions: diff --git a/doc/modules/ROOT/pages/examples.adoc b/doc/modules/ROOT/pages/examples.adoc index 303a3f1..302ca13 100644 --- a/doc/modules/ROOT/pages/examples.adoc +++ b/doc/modules/ROOT/pages/examples.adoc @@ -123,33 +123,6 @@ Safe: 1000000000 * 5 = 5000000000 ---- ==== -[#examples_wrapping] -== Wrapping Arithmetic - -Wrapping arithmetic performs standard C unsigned integer wrapping behavior - results wrap around modulo 2^N. -This matches the behavior of built-in unsigned integers and is useful for implementing counters, checksums, or hash functions. - -.This https://github.com/boostorg/safe_numbers/blob/develop/examples/wrapping_arithmetic.cpp[example] demonstrates wrapping arithmetic operations. -==== -[source, c++] ----- -include::example$wrapping_arithmetic.cpp[] ----- - -Output: ----- -wrapping_add(255, 2) = 1 -wrapping_sub(0, 1) = 255 -wrapping_mul(200, 2) = 144 -wrapping_add(UINT32_MAX, 1) = 0 -wrapping_sub(0, 1) = 4294967295 -wrapping_add(100, 50) = 150 -wrapping_sub(100, 50) = 50 -wrapping_mul(100, 50) = 5000 -Counter sequence: 254 255 0 1 2 ----- -==== - [#examples_strict] == Strict Arithmetic @@ -193,13 +166,11 @@ Output: ---- add(100, 50) = 150 add(100, 50) = 150 -add(100, 50) = 150 add(100, 50) = 150 add(100, 50) = 150 (overflowed: false) add(100, 50) = 150 add(max, 1) = nullopt (overflow) add(max, 1) = 4294967295 -add(max, 1) = 0 ---- ==== @@ -405,9 +376,6 @@ checked_shl(u8(1), 4) = 16 checked_shl(u8(255), 1) = nullopt (overflow) checked_shr(u8(1), 8) = nullopt (overflow) -wrapping_shl(u8(255), 1) = 254 -wrapping_shr(u8(1), 8) = 0 - shl(u32(1), 30) = 1073741824 shl(u32(max), 1) = 4294967295 shr(u32(8), 1) = 4 diff --git a/doc/modules/ROOT/pages/policies.adoc b/doc/modules/ROOT/pages/policies.adoc index d54c1d1..19b11ed 100644 --- a/doc/modules/ROOT/pages/policies.adoc +++ b/doc/modules/ROOT/pages/policies.adoc @@ -27,7 +27,6 @@ enum class overflow_policy saturate, // Clamp to the representable range overflow_tuple, // Wrap and return a flag indicating overflow checked, // Return std::nullopt on overflow/underflow - wrapping, // Wrap silently strict, // Call std::exit(EXIT_FAILURE) on error widen, // Promote to the next wider type (add/mul only) }; @@ -60,11 +59,6 @@ enum class overflow_policy | Returns `std::nullopt` | Yes -| `wrapping` -| Wraps silently -| Throws `std::domain_error` -| Add/Sub/Mul: Yes, Div/Mod: No - | `strict` | Calls `std::exit(EXIT_FAILURE)` | Calls `std::exit(EXIT_FAILURE)` @@ -165,34 +159,6 @@ These functions return `std::nullopt` on overflow, underflow, or division by zer - `checked_div`: Returns the quotient, or `std::nullopt` on division by zero - `checked_mod`: Returns the remainder, or `std::nullopt` on division by zero -=== Wrapping Arithmetic - -[source,c++] ----- -template -constexpr T wrapping_add(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_sub(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_mul(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_div(T lhs, T rhs); - -template -constexpr T wrapping_mod(T lhs, T rhs); ----- - -These functions wrap on overflow without any indication: - -- `wrapping_add`: Returns the wrapped sum -- `wrapping_sub`: Returns the wrapped difference -- `wrapping_mul`: Returns the wrapped product -- `wrapping_div`: Returns the quotient; throws `std::domain_error` on division by zero -- `wrapping_mod`: Returns the remainder; throws `std::domain_error` on division by zero - === Strict Arithmetic [source,c++] @@ -297,20 +263,6 @@ constexpr std::optional checked_shr(T lhs, T rhs) noexcept; - `checked_shl`: Returns the shifted value, or `std::nullopt` on overflow - `checked_shr`: Returns the shifted value, or `std::nullopt` when the shift amount is >= the type width -=== Wrapping Shifts - -[source,c++] ----- -template -constexpr T wrapping_shl(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_shr(T lhs, T rhs) noexcept; ----- - -- `wrapping_shl`: Performs the shift, allowing bits to be lost without indication -- `wrapping_shr`: Returns `0` when the shift amount is >= the type width; otherwise performs the shift normally - === Strict Shifts [source,c++] @@ -372,9 +324,6 @@ The return type depends on the policy: | `overflow_policy::checked` | `std::optional` -| `overflow_policy::wrapping` -| `T` - | `overflow_policy::strict` | `T` @@ -449,8 +398,4 @@ The default operators and some named functions throw exceptions on error: | `overflowing_div`, `overflowing_mod` | `std::domain_error` | Division by zero - -| `wrapping_div`, `wrapping_mod` -| `std::domain_error` -| Division by zero |=== diff --git a/doc/modules/ROOT/pages/unsigned_integers.adoc b/doc/modules/ROOT/pages/unsigned_integers.adoc index 1a10e60..b0f12bc 100644 --- a/doc/modules/ROOT/pages/unsigned_integers.adoc +++ b/doc/modules/ROOT/pages/unsigned_integers.adoc @@ -193,22 +193,6 @@ constexpr std::optional checked_div(T lhs, T rhs) noexcept; template constexpr std::optional checked_mod(T lhs, T rhs) noexcept; -// Wrapping arithmetic (wrap without indication) -template -constexpr T wrapping_add(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_sub(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_mul(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_div(T lhs, T rhs); - -template -constexpr T wrapping_mod(T lhs, T rhs); - // Strict arithmetic (call std::exit(EXIT_FAILURE) on error) template constexpr T strict_add(T lhs, T rhs) noexcept; @@ -246,13 +230,6 @@ constexpr std::optional checked_shl(T lhs, T rhs) noexcept; template constexpr std::optional checked_shr(T lhs, T rhs) noexcept; -// Wrapping shifts (wrap without indication) -template -constexpr T wrapping_shl(T lhs, T rhs) noexcept; - -template -constexpr T wrapping_shr(T lhs, T rhs) noexcept; - // Strict shifts (call std::exit(EXIT_FAILURE) on error) template constexpr T strict_shl(T lhs, T rhs) noexcept; @@ -456,10 +433,6 @@ constexpr std::pair overflowing_shr(T lhs, T rhs) noexcept; constexpr std::optional checked_shl(T lhs, T rhs) noexcept; constexpr std::optional checked_shr(T lhs, T rhs) noexcept; -// Wrapping: perform the shift ignoring overflow -constexpr T wrapping_shl(T lhs, T rhs) noexcept; -constexpr T wrapping_shr(T lhs, T rhs) noexcept; - // Strict: call std::exit(EXIT_FAILURE) on overflow constexpr T strict_shl(T lhs, T rhs) noexcept; constexpr T strict_shr(T lhs, T rhs) noexcept; @@ -489,10 +462,6 @@ The behavior of each policy on shift overflow: | Returns `std::nullopt` | Returns `std::nullopt` -| `wrapping` -| Performs the shift (bits shifted out are lost) -| Returns `0` - | `strict` | Calls `std::exit(EXIT_FAILURE)` | Calls `std::exit(EXIT_FAILURE)` @@ -539,7 +508,7 @@ constexpr auto operator-() const noexcept; // compile-time error - `+`: Returns a copy of the value (identity). This is consistent with built-in unsigned integer behavior. - `-`: Deliberately disabled. Any use of unary minus on an unsigned safe integer is a compile-time error via `static_assert`. While pass:[C++] defines unary minus on unsigned integers as modular negation (`2^N - x`), this is a common source of bugs and is prohibited by this library. -Use a wrapping subtraction policy from zero or the maximum value if modular negation is needed. +Use an overflowing subtraction from zero or `std::numeric_limits::max()` if modular negation is needed. === Mixed-Width Operations diff --git a/doc/proposal/proposal.adoc b/doc/proposal/proposal.adoc index fb2cdfe..62fa16e 100644 --- a/doc/proposal/proposal.adoc +++ b/doc/proposal/proposal.adoc @@ -58,9 +58,6 @@ class u32 // Saturates to std::numeric_limits::max() friend constexpr u32 saturating_add(u32 lhs, u32 rhs) noexcept; - // Allows well defined wrapping behavior - friend constexpr u32 wrapping_add(u32 lhs, u32 rhs) noexcept; - ... }; ----