Fix NaN comparisons.

Add tests to verify comparisons of NaN's are unordered.
Fix operators to check for unordered comparisons.
Fixes: https://svn.boost.org/trac/boost/ticket/11159
This commit is contained in:
jzmaddock
2015-04-03 09:35:58 +01:00
parent d79401c61c
commit 4f16c26457
2 changed files with 135 additions and 3 deletions

View File

@@ -91,12 +91,42 @@ template <class tag, class Arg1, class Arg2, class Arg3, class Arg4, class B, ex
struct is_valid_mixed_compare<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >
: public mpl::bool_<is_convertible<expression<tag, Arg1, Arg2, Arg3, Arg4>, number<B, ET> >::value> {};
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value != number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>&)
{
return false;
}
template <class Backend, expression_template_option ExpressionTemplates>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Backend>::value == number_kind_floating_point, bool>::type is_unordered_value(const number<Backend, ExpressionTemplates>& a)
{
using default_ops::eval_fpclassify;
return eval_fpclassify(a.backend()) == FP_NAN;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value != number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic&)
{
return false;
}
template <class Arithmetic>
inline BOOST_CONSTEXPR typename boost::enable_if_c<number_category<Arithmetic>::value == number_kind_floating_point, bool>::type is_unordered_value(const Arithmetic& a)
{
return (boost::math::isnan)(a);
}
template <class T, class U>
inline BOOST_CONSTEXPR bool is_unordered_comparison(const T& a, const U& b)
{
return is_unordered_value(a) || is_unordered_value(b);
}
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator == (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
@@ -104,6 +134,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator == (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
@@ -111,6 +142,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator == (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_eq(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
@@ -120,6 +152,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
@@ -129,6 +162,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
@@ -138,6 +172,7 @@ inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::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 <class Backend, expression_template_option ExpressionTemplates, class B
inline bool operator != (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
@@ -152,6 +188,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator != (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(a.backend(), number<Backend, et_on>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
@@ -159,6 +196,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator != (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_eq;
if(detail::is_unordered_comparison(a, b)) return true;
return !eval_eq(b.backend(), number<Backend, et_on>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
@@ -168,6 +206,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
@@ -177,6 +216,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
@@ -186,6 +226,7 @@ inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A
using default_ops::eval_eq;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::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 <class Backend, expression_template_option ExpressionTemplates, class B
inline bool operator < (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
@@ -200,6 +242,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator < (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
@@ -207,6 +250,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator < (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
@@ -216,6 +260,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
@@ -225,6 +270,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
@@ -234,6 +280,7 @@ inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::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 <class Backend, expression_template_option ExpressionTemplates, class B
inline bool operator > (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
@@ -248,6 +296,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator > (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
@@ -255,6 +304,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator > (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
@@ -264,7 +314,8 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, Arithmetic>::value, bool>::type
@@ -273,7 +324,8 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A4>::result_type, typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type>, bool>::type
@@ -282,13 +334,15 @@ inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
return eval_gt(t.backend(), t2.backend());
if(detail::is_unordered_comparison(t, t2)) return false;
return t > t2;
}
template <class Backend, expression_template_option ExpressionTemplates, class Backend2, expression_template_option ExpressionTemplates2>
inline bool operator <= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
@@ -296,6 +350,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator <= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
@@ -303,6 +358,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator <= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
@@ -311,7 +367,10 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
{
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
@@ -321,6 +380,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
@@ -330,6 +390,7 @@ inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A
using default_ops::eval_gt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::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 <class Backend, expression_template_option ExpressionTemplates, class B
inline bool operator >= (const number<Backend, ExpressionTemplates>& a, const number<Backend2, ExpressionTemplates2>& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), b.backend());
}
template <class Backend, expression_template_option ExpressionTemplates, class Arithmetic>
@@ -344,6 +406,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator >= (const number<Backend, ExpressionTemplates>& a, const Arithmetic& b)
{
using default_ops::eval_lt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_lt(a.backend(), number<Backend, ExpressionTemplates>::canonical_value(b));
}
template <class Arithmetic, class Backend, expression_template_option ExpressionTemplates>
@@ -351,6 +414,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<number<Backend, Expre
operator >= (const Arithmetic& a, const number<Backend, ExpressionTemplates>& b)
{
using default_ops::eval_gt;
if(detail::is_unordered_comparison(a, b)) return false;
return !eval_gt(b.backend(), number<Backend, ExpressionTemplates>::canonical_value(a));
}
template <class Arithmetic, class Tag, class A1, class A2, class A3, class A4>
@@ -360,6 +424,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Arithmetic>
@@ -369,6 +434,7 @@ inline typename enable_if_c<detail::is_valid_mixed_compare<typename detail::expr
typedef typename detail::expression<Tag, A1, A2, A3, A4>::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 <class Tag, class A1, class A2, class A3, class A4, class Tagb, class A1b, class A2b, class A3b, class A4b>
@@ -378,6 +444,7 @@ inline typename enable_if<is_same<typename detail::expression<Tag, A1, A2, A3, A
using default_ops::eval_lt;
typename detail::expression<Tag, A1, A2, A3, A4>::result_type t(a);
typename detail::expression<Tagb, A1b, A2b, A3b, A4b>::result_type t2(b);
if(detail::is_unordered_comparison(t, t2)) return false;
return !eval_lt(t.backend(), t2.backend());
}

View File

@@ -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 <class T, class U>
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 <class Real, class T>
void test_float_ops(const T&){}
@@ -861,6 +899,33 @@ void test_float_ops(const boost::mpl::int_<boost::multiprecision::number_kind_fl
BOOST_CHECK_THROW(Real(Real(20) / 0u), std::overflow_error);
}
}
//
// Comparisons of NaN's should always fail:
//
if(std::numeric_limits<Real>::has_quiet_NaN)
{
r = v = std::numeric_limits<Real>::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<double>::has_quiet_NaN)
{
compare_NaNs(r, std::numeric_limits<double>::quiet_NaN());
compare_NaNs(std::numeric_limits<double>::quiet_NaN(), r);
}
}
test_float_funcs<Real>(boost::mpl::bool_<std::numeric_limits<Real>::is_specialized>());
}