From 58042611063ca7c304b36d180ff678529c6cea20 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 21 Jan 2025 15:54:45 -0500 Subject: [PATCH 1/5] Update decimalxx_fast equality impl for +0 = -0 --- include/boost/decimal/decimal128_fast.hpp | 20 ++++++++++++---- include/boost/decimal/decimal32_fast.hpp | 29 +++++++++++++++++------ include/boost/decimal/decimal64_fast.hpp | 20 ++++++++++++---- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/include/boost/decimal/decimal128_fast.hpp b/include/boost/decimal/decimal128_fast.hpp index 57920d613..0e12d320f 100644 --- a/include/boost/decimal/decimal128_fast.hpp +++ b/include/boost/decimal/decimal128_fast.hpp @@ -537,16 +537,28 @@ constexpr auto not_finite(const decimal128_fast& val) noexcept -> bool constexpr auto operator==(const decimal128_fast& lhs, const decimal128_fast& rhs) noexcept -> bool { + if (lhs.exponent_ != rhs.exponent_) + { + return false; + } + if (lhs.significand_ != rhs.significand_) + { + return false; + } + #ifndef BOOST_DECIMAL_FAST_MATH - if (isnan(lhs) || isnan(rhs)) + if (isnan(lhs)) { return false; } #endif - return lhs.sign_ == rhs.sign_ && - lhs.exponent_ == rhs.exponent_ && - lhs.significand_ == rhs.significand_; + if (lhs.significand_ == 0) + { + return true; // -0 == +0 + } + + return lhs.sign_ == rhs.sign_; } template diff --git a/include/boost/decimal/decimal32_fast.hpp b/include/boost/decimal/decimal32_fast.hpp index 1841bf15e..5e8b34b92 100644 --- a/include/boost/decimal/decimal32_fast.hpp +++ b/include/boost/decimal/decimal32_fast.hpp @@ -490,13 +490,28 @@ constexpr auto isfinite(decimal32_fast val) noexcept -> bool constexpr auto operator==(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool { - return - #ifndef BOOST_DECIMAL_FAST_MATH - !isnan(lhs) && !isnan(rhs) && - #endif - (lhs.sign_ == rhs.sign_) && - (lhs.exponent_ == rhs.exponent_) && - (lhs.significand_ == rhs.significand_); + if (lhs.exponent_ != rhs.exponent_) + { + return false; + } + if (lhs.significand_ != rhs.significand_) + { + return false; + } + + #ifndef BOOST_DECIMAL_FAST_MATH + if (isnan(lhs)) + { + return false; + } + #endif + + if (lhs.significand_ == 0) + { + return true; // -0 == +0 + } + + return lhs.sign_ == rhs.sign_; } constexpr auto operator!=(decimal32_fast lhs, decimal32_fast rhs) noexcept -> bool diff --git a/include/boost/decimal/decimal64_fast.hpp b/include/boost/decimal/decimal64_fast.hpp index 26cc32bbf..f15a278a5 100644 --- a/include/boost/decimal/decimal64_fast.hpp +++ b/include/boost/decimal/decimal64_fast.hpp @@ -518,16 +518,28 @@ constexpr auto not_finite(decimal64_fast val) noexcept -> bool constexpr auto operator==(decimal64_fast lhs, decimal64_fast rhs) noexcept -> bool { + if (lhs.exponent_ != rhs.exponent_) + { + return false; + } + if (lhs.significand_ != rhs.significand_) + { + return false; + } + #ifndef BOOST_DECIMAL_FAST_MATH - if (isnan(lhs) || isnan(rhs)) + if (isnan(lhs)) { return false; } #endif - return lhs.sign_ == rhs.sign_ && - lhs.exponent_ == rhs.exponent_ && - lhs.significand_ == rhs.significand_; + if (lhs.significand_ == 0) + { + return true; // -0 == +0 + } + + return lhs.sign_ == rhs.sign_; } template From c74222cc6d394c5ef4f5b34a995109bac3c823ee Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 21 Jan 2025 15:54:55 -0500 Subject: [PATCH 2/5] Add testing of -0 = +0 --- test/random_decimal128_fast_comp.cpp | 4 ++++ test/random_decimal32_fast_comp.cpp | 4 ++++ test/random_decimal64_fast_comp.cpp | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/test/random_decimal128_fast_comp.cpp b/test/random_decimal128_fast_comp.cpp index 84711e2b1..78eed0f73 100644 --- a/test/random_decimal128_fast_comp.cpp +++ b/test/random_decimal128_fast_comp.cpp @@ -593,5 +593,9 @@ int main() random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); #endif + constexpr auto pos_zero = boost::decimal::decimal128_fast{0, 0, false}; + constexpr auto neg_zero = boost::decimal::decimal128_fast{0, 0, true}; + BOOST_TEST_EQ(pos_zero, neg_zero); + return boost::report_errors(); } diff --git a/test/random_decimal32_fast_comp.cpp b/test/random_decimal32_fast_comp.cpp index 4a7f5c381..a33f30d29 100644 --- a/test/random_decimal32_fast_comp.cpp +++ b/test/random_decimal32_fast_comp.cpp @@ -593,5 +593,9 @@ int main() random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); #endif + constexpr auto pos_zero = boost::decimal::decimal32_fast{0, 0, false}; + constexpr auto neg_zero = boost::decimal::decimal32_fast{0, 0, true}; + BOOST_TEST_EQ(pos_zero, neg_zero); + return boost::report_errors(); } diff --git a/test/random_decimal64_fast_comp.cpp b/test/random_decimal64_fast_comp.cpp index ea24b9fa3..614638bf6 100644 --- a/test/random_decimal64_fast_comp.cpp +++ b/test/random_decimal64_fast_comp.cpp @@ -593,5 +593,9 @@ int main() random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); #endif + constexpr auto pos_zero = boost::decimal::decimal64_fast{0, 0, false}; + constexpr auto neg_zero = boost::decimal::decimal64_fast{0, 0, true}; + BOOST_TEST_EQ(pos_zero, neg_zero); + return boost::report_errors(); } From 9397839eba4fc37c555d8dbccb6542cd411db6a3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 21 Jan 2025 16:04:42 -0500 Subject: [PATCH 3/5] Update regular decimal comparison --- include/boost/decimal/detail/comparison.hpp | 28 +++++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/include/boost/decimal/detail/comparison.hpp b/include/boost/decimal/detail/comparison.hpp index 41bec656e..2ec308ec9 100644 --- a/include/boost/decimal/detail/comparison.hpp +++ b/include/boost/decimal/detail/comparison.hpp @@ -38,7 +38,21 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, Decimal } #endif - // Step 3: Check signs + // Step 2: Fast path + if (lhs.bits_ == rhs.bits_) + { + return true; + } + + // Step 3: Check -0 == +0 + auto lhs_sig {lhs.full_significand()}; + auto rhs_sig {rhs.full_significand()}; + if (lhs_sig == 0 && rhs_sig == 0) + { + return true; + } + + // Step 4: Check signs const auto lhs_neg {lhs.isneg()}; const auto rhs_neg {rhs.isneg()}; @@ -47,27 +61,19 @@ BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, Decimal return false; } - // Step 4: Check the exponents + // Step 5: Check the exponents // If the difference is greater than we can represent in the significand than we can assume they are different const auto lhs_exp {lhs.biased_exponent()}; const auto rhs_exp {rhs.biased_exponent()}; - auto lhs_sig {lhs.full_significand()}; - auto rhs_sig {rhs.full_significand()}; - const auto delta_exp {lhs_exp - rhs_exp}; - if (lhs_sig == 0 && rhs_sig == 0) - { - // The difference in exponent is irrelevant here - return true; - } if (delta_exp > detail::precision_v || delta_exp < -detail::precision_v) { return false; } - // Step 5: Normalize the significand and compare + // Step 6: Normalize the significand and compare // Instead of multiplying the larger number, divide the smaller one if (delta_exp >= 0) { From ad3d010f0782b5a4c619dd2c72bbf7dc03f57a3a Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 21 Jan 2025 16:14:48 -0500 Subject: [PATCH 4/5] Update testing --- test/random_decimal128_comp.cpp | 4 ++++ test/random_decimal32_comp.cpp | 4 ++++ test/random_decimal64_comp.cpp | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/test/random_decimal128_comp.cpp b/test/random_decimal128_comp.cpp index 735b22b77..b7a24e159 100644 --- a/test/random_decimal128_comp.cpp +++ b/test/random_decimal128_comp.cpp @@ -574,5 +574,9 @@ int main() random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); #endif + constexpr auto pos_zero = boost::decimal::decimal128{0, 0, false}; + constexpr auto neg_zero = boost::decimal::decimal128{0, 0, true}; + BOOST_TEST(pos_zero == neg_zero); + return boost::report_errors(); } diff --git a/test/random_decimal32_comp.cpp b/test/random_decimal32_comp.cpp index ad68386f3..01182eace 100644 --- a/test/random_decimal32_comp.cpp +++ b/test/random_decimal32_comp.cpp @@ -606,5 +606,9 @@ int main() random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); #endif + constexpr auto pos_zero = boost::decimal::decimal32{0, 0, false}; + constexpr auto neg_zero = boost::decimal::decimal32{0, 0, true}; + BOOST_TEST_EQ(pos_zero, neg_zero); + return boost::report_errors(); } diff --git a/test/random_decimal64_comp.cpp b/test/random_decimal64_comp.cpp index 9b568f418..4b6d94e0e 100644 --- a/test/random_decimal64_comp.cpp +++ b/test/random_decimal64_comp.cpp @@ -571,5 +571,9 @@ int main() random_mixed_SPACESHIP(std::numeric_limits::min(), std::numeric_limits::max()); #endif + constexpr auto pos_zero = boost::decimal::decimal64{0, 0, false}; + constexpr auto neg_zero = boost::decimal::decimal64{0, 0, true}; + BOOST_TEST_EQ(pos_zero, neg_zero); + return boost::report_errors(); } From f25014b4ba6ec4149d77eefa14f0d6031435445c Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Tue, 21 Jan 2025 16:15:05 -0500 Subject: [PATCH 5/5] Make equality_impl a friend of decimal128 --- include/boost/decimal/decimal128.hpp | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/include/boost/decimal/decimal128.hpp b/include/boost/decimal/decimal128.hpp index ab1df71a9..125dd58f5 100644 --- a/include/boost/decimal/decimal128.hpp +++ b/include/boost/decimal/decimal128.hpp @@ -194,6 +194,9 @@ BOOST_DECIMAL_EXPORT class decimal128 final template friend constexpr auto to_decimal(Decimal val) noexcept -> TargetType; + template + friend BOOST_DECIMAL_FORCE_INLINE constexpr auto equality_impl(DecimalType lhs, DecimalType rhs) noexcept -> bool; + // Equality template between any integer type and decimal128 template friend constexpr auto mixed_equality_impl(Decimal lhs, Integer rhs) noexcept @@ -1202,16 +1205,7 @@ constexpr auto operator-(decimal128 rhs) noexcept-> decimal128 constexpr auto operator==(decimal128 lhs, decimal128 rhs) noexcept -> bool { - #ifndef BOOST_DECIMAL_FAST_MATH - // Check for IEEE requirement that nan != nan - if (isnan(lhs) || isnan(rhs)) - { - return false; - } - #endif - - return equal_parts_impl(lhs.full_significand(), lhs.biased_exponent(), lhs.isneg(), - rhs.full_significand(), rhs.biased_exponent(), rhs.isneg()); + return equality_impl(lhs, rhs); } template