diff --git a/include/boost/multiprecision/detail/number_compare.hpp b/include/boost/multiprecision/detail/number_compare.hpp index d1606ad7..bddbf4a1 100644 --- a/include/boost/multiprecision/detail/number_compare.hpp +++ b/include/boost/multiprecision/detail/number_compare.hpp @@ -91,12 +91,42 @@ template , number > : public mpl::bool_, number >::value> {}; +template +inline BOOST_CONSTEXPR typename boost::enable_if_c::value != number_kind_floating_point, bool>::type is_unordered_value(const number&) +{ + return false; +} +template +inline BOOST_CONSTEXPR typename boost::enable_if_c::value == number_kind_floating_point, bool>::type is_unordered_value(const number& a) +{ + using default_ops::eval_fpclassify; + return eval_fpclassify(a.backend()) == FP_NAN; +} + +template +inline BOOST_CONSTEXPR typename boost::enable_if_c::value != number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic&) +{ + return false; +} +template +inline BOOST_CONSTEXPR typename boost::enable_if_c::value == number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic& a) +{ + return (boost::math::isnan)(a); +} + +template +inline BOOST_CONSTEXPR bool is_unordered_comparison(const T& a, const U& b) +{ + return is_unordered_value(a) || is_unordered_value(b); +} + } template inline bool operator == (const number& a, const number& b) { using default_ops::eval_eq; + if(detail::is_unordered_comparison(a, b)) return false; return eval_eq(a.backend(), b.backend()); } template @@ -104,6 +134,7 @@ inline typename enable_if_c& a, const Arithmetic& b) { using default_ops::eval_eq; + if(detail::is_unordered_comparison(a, b)) return false; return eval_eq(a.backend(), number::canonical_value(b)); } template @@ -111,6 +142,7 @@ inline typename enable_if_c& b) { using default_ops::eval_eq; + if(detail::is_unordered_comparison(a, b)) return false; return eval_eq(b.backend(), number::canonical_value(a)); } template @@ -120,6 +152,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_eq; result_type t(b); + if(detail::is_unordered_comparison(a, t)) return false; return eval_eq(t.backend(), result_type::canonical_value(a)); } template @@ -129,6 +162,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_eq; result_type t(a); + if(detail::is_unordered_comparison(t, b)) return false; return eval_eq(t.backend(), result_type::canonical_value(b)); } template @@ -138,6 +172,7 @@ inline typename enable_if::result_type t(a); typename detail::expression::result_type t2(b); + if(detail::is_unordered_comparison(t, t2)) return false; return eval_eq(t.backend(), t2.backend()); } @@ -145,6 +180,7 @@ template & a, const number& b) { using default_ops::eval_eq; + if(detail::is_unordered_comparison(a, b)) return true; return !eval_eq(a.backend(), b.backend()); } template @@ -152,6 +188,7 @@ inline typename enable_if_c& a, const Arithmetic& b) { using default_ops::eval_eq; + if(detail::is_unordered_comparison(a, b)) return true; return !eval_eq(a.backend(), number::canonical_value(b)); } template @@ -159,6 +196,7 @@ inline typename enable_if_c& b) { using default_ops::eval_eq; + if(detail::is_unordered_comparison(a, b)) return true; return !eval_eq(b.backend(), number::canonical_value(a)); } template @@ -168,6 +206,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_eq; result_type t(b); + if(detail::is_unordered_comparison(a, t)) return true; return !eval_eq(t.backend(), result_type::canonical_value(a)); } template @@ -177,6 +216,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_eq; result_type t(a); + if(detail::is_unordered_comparison(t, b)) return true; return !eval_eq(t.backend(), result_type::canonical_value(b)); } template @@ -186,6 +226,7 @@ inline typename enable_if::result_type t(a); typename detail::expression::result_type t2(b); + if(detail::is_unordered_comparison(t, t2)) return true; return !eval_eq(t.backend(), t2.backend()); } @@ -193,6 +234,7 @@ template & a, const number& b) { using default_ops::eval_lt; + if(detail::is_unordered_comparison(a, b)) return false; return eval_lt(a.backend(), b.backend()); } template @@ -200,6 +242,7 @@ inline typename enable_if_c& a, const Arithmetic& b) { using default_ops::eval_lt; + if(detail::is_unordered_comparison(a, b)) return false; return eval_lt(a.backend(), number::canonical_value(b)); } template @@ -207,6 +250,7 @@ inline typename enable_if_c& b) { using default_ops::eval_gt; + if(detail::is_unordered_comparison(a, b)) return false; return eval_gt(b.backend(), number::canonical_value(a)); } template @@ -216,6 +260,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_gt; result_type t(b); + if(detail::is_unordered_comparison(a, t)) return false; return eval_gt(t.backend(), result_type::canonical_value(a)); } template @@ -225,6 +270,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_lt; result_type t(a); + if(detail::is_unordered_comparison(t, b)) return false; return eval_lt(t.backend(), result_type::canonical_value(b)); } template @@ -234,6 +280,7 @@ inline typename enable_if::result_type t(a); typename detail::expression::result_type t2(b); + if(detail::is_unordered_comparison(t, t2)) return false; return eval_lt(t.backend(), t2.backend()); } @@ -241,6 +288,7 @@ template (const number& a, const number& b) { using default_ops::eval_gt; + if(detail::is_unordered_comparison(a, b)) return false; return eval_gt(a.backend(), b.backend()); } template @@ -248,6 +296,7 @@ inline typename enable_if_c (const number& a, const Arithmetic& b) { using default_ops::eval_gt; + if(detail::is_unordered_comparison(a, b)) return false; return eval_gt(a.backend(), number::canonical_value(b)); } template @@ -255,6 +304,7 @@ inline typename enable_if_c (const Arithmetic& a, const number& b) { using default_ops::eval_lt; + if(detail::is_unordered_comparison(a, b)) return false; return eval_lt(b.backend(), number::canonical_value(a)); } template @@ -264,7 +314,8 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_lt; result_type t(b); - return eval_lt(t.backend(), result_type::canonical_value(a)); + if(detail::is_unordered_comparison(a, t)) return false; + return a > t; } template inline typename enable_if_c::result_type, Arithmetic>::value, bool>::type @@ -273,7 +324,8 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_gt; result_type t(a); - return eval_gt(t.backend(), result_type::canonical_value(b)); + if(detail::is_unordered_comparison(t, b)) return false; + return t > b; } template inline typename enable_if::result_type, typename detail::expression::result_type>, bool>::type @@ -282,13 +334,15 @@ inline typename enable_if::result_type t(a); typename detail::expression::result_type t2(b); - return eval_gt(t.backend(), t2.backend()); + if(detail::is_unordered_comparison(t, t2)) return false; + return t > t2; } template inline bool operator <= (const number& a, const number& b) { using default_ops::eval_gt; + if(detail::is_unordered_comparison(a, b)) return false; return !eval_gt(a.backend(), b.backend()); } template @@ -296,6 +350,7 @@ inline typename enable_if_c& a, const Arithmetic& b) { using default_ops::eval_gt; + if(detail::is_unordered_comparison(a, b)) return false; return !eval_gt(a.backend(), number::canonical_value(b)); } template @@ -303,6 +358,7 @@ inline typename enable_if_c& b) { using default_ops::eval_lt; + if(detail::is_unordered_comparison(a, b)) return false; return !eval_lt(b.backend(), number::canonical_value(a)); } template @@ -311,7 +367,10 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_lt; + if(detail::is_unordered_value(a) || detail::is_unordered_value(b)) + return false; result_type t(b); + if(detail::is_unordered_comparison(a, t)) return false; return !eval_lt(t.backend(), result_type::canonical_value(a)); } template @@ -321,6 +380,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_gt; result_type t(a); + if(detail::is_unordered_comparison(t, b)) return false; return !eval_gt(t.backend(), result_type::canonical_value(b)); } template @@ -330,6 +390,7 @@ inline typename enable_if::result_type t(a); typename detail::expression::result_type t2(b); + if(detail::is_unordered_comparison(t, t2)) return false; return !eval_gt(t.backend(), t2.backend()); } @@ -337,6 +398,7 @@ template = (const number& a, const number& b) { using default_ops::eval_lt; + if(detail::is_unordered_comparison(a, b)) return false; return !eval_lt(a.backend(), b.backend()); } template @@ -344,6 +406,7 @@ inline typename enable_if_c= (const number& a, const Arithmetic& b) { using default_ops::eval_lt; + if(detail::is_unordered_comparison(a, b)) return false; return !eval_lt(a.backend(), number::canonical_value(b)); } template @@ -351,6 +414,7 @@ inline typename enable_if_c= (const Arithmetic& a, const number& b) { using default_ops::eval_gt; + if(detail::is_unordered_comparison(a, b)) return false; return !eval_gt(b.backend(), number::canonical_value(a)); } template @@ -360,6 +424,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_gt; result_type t(b); + if(detail::is_unordered_comparison(a, t)) return false; return !eval_gt(t.backend(), result_type::canonical_value(a)); } template @@ -369,6 +434,7 @@ inline typename enable_if_c::result_type result_type; using default_ops::eval_lt; result_type t(a); + if(detail::is_unordered_comparison(t, b)) return false; return !eval_lt(t.backend(), result_type::canonical_value(b)); } template @@ -378,6 +444,7 @@ inline typename enable_if::result_type t(a); typename detail::expression::result_type t2(b); + if(detail::is_unordered_comparison(t, t2)) return false; return !eval_lt(t.backend(), t2.backend()); } diff --git a/test/test_arithmetic.hpp b/test/test_arithmetic.hpp index e6d1fdd9..b060c4b2 100644 --- a/test/test_arithmetic.hpp +++ b/test/test_arithmetic.hpp @@ -757,6 +757,44 @@ void test_float_funcs(const boost::mpl::true_&) BOOST_CHECK_CLOSE_FRACTION(b, Real(atan2(Real(4), Real(2))), tol); } +template +void compare_NaNs(const T& a, const U& b) +{ + BOOST_CHECK_EQUAL(a == b, false); + BOOST_CHECK_EQUAL(a != b, true); + BOOST_CHECK_EQUAL(a <= b, false); + BOOST_CHECK_EQUAL(a >= b, false); + BOOST_CHECK_EQUAL(a > b, false); + BOOST_CHECK_EQUAL(a < b, false); + // + // Again where LHS may be an expression template: + // + BOOST_CHECK_EQUAL(1 * a == b, false); + BOOST_CHECK_EQUAL(1 * a != b, true); + BOOST_CHECK_EQUAL(1 * a <= b, false); + BOOST_CHECK_EQUAL(1 * a >= b, false); + BOOST_CHECK_EQUAL(1 * a > b, false); + BOOST_CHECK_EQUAL(1 * a < b, false); + // + // Again where RHS may be an expression template: + // + BOOST_CHECK_EQUAL(a == b * 1, false); + BOOST_CHECK_EQUAL(a != b * 1, true); + BOOST_CHECK_EQUAL(a <= b * 1, false); + BOOST_CHECK_EQUAL(a >= b * 1, false); + BOOST_CHECK_EQUAL(a > b * 1, false); + BOOST_CHECK_EQUAL(a < b * 1, false); + // + // Again where LHS and RHS may be an expression templates: + // + BOOST_CHECK_EQUAL(1 * a == b * 1, false); + BOOST_CHECK_EQUAL(1 * a != b * 1, true); + BOOST_CHECK_EQUAL(1 * a <= b * 1, false); + BOOST_CHECK_EQUAL(1 * a >= b * 1, false); + BOOST_CHECK_EQUAL(1 * a > b * 1, false); + BOOST_CHECK_EQUAL(1 * a < b * 1, false); +} + template void test_float_ops(const T&){} @@ -861,6 +899,33 @@ void test_float_ops(const boost::mpl::int_::has_quiet_NaN) + { + r = v = std::numeric_limits::quiet_NaN(); + compare_NaNs(r, v); + v = 0; + compare_NaNs(r, v); + r.swap(v); + compare_NaNs(r, v); + // + // Conmpare NaN to int: + // + compare_NaNs(v, 0); + compare_NaNs(0, v); + // + // Compare to floats: + // + compare_NaNs(v, 0.5); + compare_NaNs(0.5, v); + if(std::numeric_limits::has_quiet_NaN) + { + compare_NaNs(r, std::numeric_limits::quiet_NaN()); + compare_NaNs(std::numeric_limits::quiet_NaN(), r); + } + } test_float_funcs(boost::mpl::bool_::is_specialized>()); }