Skip to content

Commit

Permalink
Fix mixed mode comparison operators.
Browse files Browse the repository at this point in the history
See https://svn.boost.org/trac/boost/ticket/11328.
And added tests for mixed mode comparisons and other operators.
  • Loading branch information
jzmaddock committed Jun 1, 2015
1 parent 7bf2e83 commit d711ce2
Show file tree
Hide file tree
Showing 8 changed files with 424 additions and 30 deletions.
60 changes: 30 additions & 30 deletions include/boost/multiprecision/detail/number_compare.hpp
Expand Up @@ -6,6 +6,8 @@
#ifndef BOOST_MP_COMPARE_HPP
#define BOOST_MP_COMPARE_HPP

#include <boost/multiprecision/traits/is_backend.hpp>

//
// Comparison operators for number.
//
Expand All @@ -19,56 +21,54 @@ inline bool eval_eq(const B& a, const B& b)
{
return a.compare(b) == 0;
}
//
// For the default version which compares to some arbitrary type convertible to
// our number type, we don't know what value the ExpressionTemplates parameter to
// class number should be. We generally prefer ExpressionTemplates to be enabled
// in case type A is itself an expression template, but we need to test both options
// with is_convertible in case A has an implicit conversion operator to number<B,something>.
// This is the case with many uBlas types for example.
//
template <class B, class A>
inline bool eval_eq(const B& a, const A& b)
{
typedef typename mpl::if_c<
is_convertible<A, number<B, et_on> >::value,
number<B, et_on>,
number<B, et_off> >::type mp_type;
mp_type t(b);
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_eq(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_eq(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_eq(t.backend(), b);
}

template <class B>
inline bool eval_lt(const B& a, const B& b)
{
return a.compare(b) < 0;
}
template <class B, class A>
inline bool eval_lt(const B& a, const A& b)
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typedef typename mpl::if_c<
is_convertible<A, number<B, et_on> >::value,
number<B, et_on>,
number<B, et_off> >::type mp_type;
mp_type t(b);
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_lt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_lt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_lt(t.backend(), b);
}

template <class B>
inline bool eval_gt(const B& a, const B& b)
{
return a.compare(b) > 0;
}
template <class B, class A>
inline bool eval_gt(const B& a, const A& b)
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_first_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typedef typename mpl::if_c<
is_convertible<A, number<B, et_on> >::value,
number<B, et_on>,
number<B, et_off> >::type mp_type;
mp_type t(b);
typename boost::multiprecision::detail::number_from_backend<T, U>::type t(b);
return eval_gt(a, t.backend());
}
template <class T, class U>
inline typename enable_if_c<boost::multiprecision::detail::is_second_backend<T, U>::value, bool>::type eval_gt(const T& a, const U& b)
{
typename boost::multiprecision::detail::number_from_backend<U, T>::type t(a);
return eval_gt(t.backend(), b);
}

} // namespace default_ops

Expand Down
62 changes: 62 additions & 0 deletions include/boost/multiprecision/traits/is_backend.hpp
@@ -0,0 +1,62 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright 2015 John Maddock. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_MP_IS_BACKEND_HPP
#define BOOST_MP_IS_BACKEND_HPP

#include <boost/mpl/has_xxx.hpp>
#include <boost/type_traits/is_convertible.hpp>
#include <boost/multiprecision/detail/number_base.hpp>

namespace boost{ namespace multiprecision{ namespace detail{

BOOST_MPL_HAS_XXX_TRAIT_DEF(signed_types);
BOOST_MPL_HAS_XXX_TRAIT_DEF(unsigned_types);
BOOST_MPL_HAS_XXX_TRAIT_DEF(float_types);

template <class T>
struct is_backend
{
static const bool value = has_signed_types<T>::value && has_unsigned_types<T>::value && has_float_types<T>::value;
};

template <class Backend>
struct other_backend
{
typedef typename boost::conditional<
boost::is_same<number<Backend>, number<Backend, et_on> >::value,
number<Backend, et_off>, number<Backend, et_on> >::type type;
};

template <class B, class V>
struct number_from_backend
{
typedef typename boost::conditional <
boost::is_convertible<V, number<B> >::value,
number<B>,
typename other_backend<B>::type > ::type type;
};

template <bool b, class T, class U>
struct is_first_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_first_backend_imp<true, T, U>{ static const bool value = is_convertible<U, number<T, et_on> >::value || is_convertible<U, number<T, et_off> >::value; };

template <class T, class U>
struct is_first_backend : is_first_backend_imp<is_backend<T>::value, T, U> {};

template <bool b, class T, class U>
struct is_second_backend_imp{ static const bool value = false; };
template <class T, class U>
struct is_second_backend_imp<true, T, U>{ static const bool value = is_convertible<T, number<U> >::value || is_convertible<T, number<U, et_off> >::value; };

template <class T, class U>
struct is_second_backend : is_second_backend_imp<is_backend<U>::value, T, U> {};

}
}
}

#endif // BOOST_MP_IS_BACKEND_HPP
9 changes: 9 additions & 0 deletions test/Jamfile.v2
Expand Up @@ -670,6 +670,15 @@ run test_float128_serial.cpp ../../serialization/build//boost_serialization quad
run test_cpp_bin_float_serial.cpp ../../serialization/build//boost_serialization : : : release <define>TEST1 : test_bin_dec_float_serial_1 ;
run test_cpp_bin_float_serial.cpp ../../serialization/build//boost_serialization : : : release <define>TEST2 : test_bin_dec_float_serial_2 ;

#
# Mixed mode comparison tests, see: https://svn.boost.org/trac/boost/ticket/11328
#
run test_checked_mixed_cpp_int.cpp ;
run test_mixed_cpp_bin_float.cpp ;
run test_mixed_cpp_dec_float.cpp ;
run test_mixed_mpf_float.cpp gmp : : : [ check-target-builds ../config//has_gmp : : <build>no ] ;
run test_mixed_mpfr_float.cpp mpfr gmp : : : [ check-target-builds ../config//has_mpfr : : <build>no ] ;


if $(enable-specfun)
{
Expand Down
191 changes: 191 additions & 0 deletions test/test_mixed.hpp
@@ -0,0 +1,191 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright 2015 John Maddock. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_MATH_TEST_MIXED_HPP
#define BOOST_MATH_TEST_MIXED_HPP

#include "test.hpp"

template <class Big, class Small>
void test()
{
Big big_val = 1;
big_val += std::numeric_limits<Big>::epsilon();
Small small_val = 1;

BOOST_CHECK_EQUAL(big_val == small_val, false);
BOOST_CHECK_EQUAL(big_val <= small_val, false);
BOOST_CHECK_EQUAL(big_val >= small_val, true);
BOOST_CHECK_EQUAL(big_val < small_val, false);
BOOST_CHECK_EQUAL(big_val > small_val, true);
BOOST_CHECK_EQUAL(big_val != small_val, true);
BOOST_CHECK_EQUAL(small_val == big_val, false);
BOOST_CHECK_EQUAL(small_val <= big_val, true);
BOOST_CHECK_EQUAL(small_val >= big_val, false);
BOOST_CHECK_EQUAL(small_val < big_val, true);
BOOST_CHECK_EQUAL(small_val > big_val, false);
BOOST_CHECK_EQUAL(small_val != big_val, true);
// Again with expression templates, on one the other, or both args:
BOOST_CHECK_EQUAL(big_val == small_val * 1, false);
BOOST_CHECK_EQUAL(big_val <= small_val * 1, false);
BOOST_CHECK_EQUAL(big_val >= small_val * 1, true);
BOOST_CHECK_EQUAL(big_val < small_val * 1, false);
BOOST_CHECK_EQUAL(big_val > small_val * 1, true);
BOOST_CHECK_EQUAL(big_val != small_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 == big_val, false);
BOOST_CHECK_EQUAL(small_val * 1 <= big_val, true);
BOOST_CHECK_EQUAL(small_val * 1 >= big_val, false);
BOOST_CHECK_EQUAL(small_val * 1 < big_val, true);
BOOST_CHECK_EQUAL(small_val * 1 > big_val, false);
BOOST_CHECK_EQUAL(small_val * 1 != big_val, true);
BOOST_CHECK_EQUAL(big_val * 1 == small_val, false);
BOOST_CHECK_EQUAL(big_val * 1 <= small_val, false);
BOOST_CHECK_EQUAL(big_val * 1 >= small_val, true);
BOOST_CHECK_EQUAL(big_val * 1 < small_val, false);
BOOST_CHECK_EQUAL(big_val * 1 > small_val, true);
BOOST_CHECK_EQUAL(big_val * 1 != small_val, true);
BOOST_CHECK_EQUAL(small_val == big_val * 1, false);
BOOST_CHECK_EQUAL(small_val <= big_val * 1, true);
BOOST_CHECK_EQUAL(small_val >= big_val * 1, false);
BOOST_CHECK_EQUAL(small_val < big_val * 1, true);
BOOST_CHECK_EQUAL(small_val > big_val * 1, false);
BOOST_CHECK_EQUAL(small_val != big_val * 1, true);
BOOST_CHECK_EQUAL(big_val * 1 == small_val * 1, false);
BOOST_CHECK_EQUAL(big_val * 1 <= small_val * 1, false);
BOOST_CHECK_EQUAL(big_val * 1 >= small_val * 1, true);
BOOST_CHECK_EQUAL(big_val * 1 < small_val * 1, false);
BOOST_CHECK_EQUAL(big_val * 1 > small_val * 1, true);
BOOST_CHECK_EQUAL(big_val * 1 != small_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 == big_val * 1, false);
BOOST_CHECK_EQUAL(small_val * 1 <= big_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 >= big_val * 1, false);
BOOST_CHECK_EQUAL(small_val * 1 < big_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 > big_val * 1, false);
BOOST_CHECK_EQUAL(small_val * 1 != big_val * 1, true);


BOOST_CHECK_EQUAL(small_val + big_val, Big(small_val) + big_val);
BOOST_CHECK_EQUAL(small_val - big_val, Big(small_val) - big_val);
BOOST_CHECK_EQUAL(small_val * big_val, Big(small_val) * big_val);
BOOST_CHECK_EQUAL(small_val / big_val, Big(small_val) / big_val);
BOOST_CHECK_EQUAL(big_val + small_val, big_val + Big(small_val));
BOOST_CHECK_EQUAL(big_val - small_val, big_val - Big(small_val));
BOOST_CHECK_EQUAL(big_val * small_val, big_val * Big(small_val));
BOOST_CHECK_EQUAL(big_val / small_val, big_val / Big(small_val));
// Again with expression templates, on one the other, or both args:
BOOST_CHECK_EQUAL(small_val + (big_val * 1), Big(small_val) + (big_val * 1));
BOOST_CHECK_EQUAL(small_val - (big_val * 1), Big(small_val) - (big_val * 1));
BOOST_CHECK_EQUAL(small_val * (big_val * 1), Big(small_val) * (big_val * 1));
BOOST_CHECK_EQUAL(small_val / (big_val * 1), Big(small_val) / (big_val * 1));
BOOST_CHECK_EQUAL((big_val * 1) + small_val, (big_val * 1) + Big(small_val));
BOOST_CHECK_EQUAL((big_val * 1) - small_val, (big_val * 1) - Big(small_val));
BOOST_CHECK_EQUAL((big_val * 1) * small_val, (big_val * 1) * Big(small_val));
BOOST_CHECK_EQUAL((big_val * 1) / small_val, (big_val * 1) / Big(small_val));
BOOST_CHECK_EQUAL((small_val * 1) + big_val, Big((small_val * 1)) + big_val);
BOOST_CHECK_EQUAL((small_val * 1) - big_val, Big((small_val * 1)) - big_val);
BOOST_CHECK_EQUAL((small_val * 1) * big_val, Big((small_val * 1)) * big_val);
BOOST_CHECK_EQUAL((small_val * 1) / big_val, Big((small_val * 1)) / big_val);
BOOST_CHECK_EQUAL(big_val + (small_val * 1), big_val + Big((small_val * 1)));
BOOST_CHECK_EQUAL(big_val - (small_val * 1), big_val - Big((small_val * 1)));
BOOST_CHECK_EQUAL(big_val * (small_val * 1), big_val * Big((small_val * 1)));
BOOST_CHECK_EQUAL(big_val / (small_val * 1), big_val / Big((small_val * 1)));
BOOST_CHECK_EQUAL((small_val * 1) + (big_val * 1), Big((small_val * 1)) + (big_val * 1));
BOOST_CHECK_EQUAL((small_val * 1) - (big_val * 1), Big((small_val * 1)) - (big_val * 1));
BOOST_CHECK_EQUAL((small_val * 1) * (big_val * 1), Big((small_val * 1)) * (big_val * 1));
BOOST_CHECK_EQUAL((small_val * 1) / (big_val * 1), Big((small_val * 1)) / (big_val * 1));
BOOST_CHECK_EQUAL((big_val * 1) + (small_val * 1), (big_val * 1) + Big((small_val * 1)));
BOOST_CHECK_EQUAL((big_val * 1) - (small_val * 1), (big_val * 1) - Big((small_val * 1)));
BOOST_CHECK_EQUAL((big_val * 1) * (small_val * 1), (big_val * 1) * Big((small_val * 1)));
BOOST_CHECK_EQUAL((big_val * 1) / (small_val * 1), (big_val * 1) / Big((small_val * 1)));

big_val = 1;
big_val -= std::numeric_limits<Big>::epsilon();

BOOST_CHECK_EQUAL(big_val == small_val, false);
BOOST_CHECK_EQUAL(big_val <= small_val, true);
BOOST_CHECK_EQUAL(big_val >= small_val, false);
BOOST_CHECK_EQUAL(big_val < small_val, true);
BOOST_CHECK_EQUAL(big_val > small_val, false);
BOOST_CHECK_EQUAL(big_val != small_val, true);
BOOST_CHECK_EQUAL(small_val == big_val, false);
BOOST_CHECK_EQUAL(small_val <= big_val, false);
BOOST_CHECK_EQUAL(small_val >= big_val, true);
BOOST_CHECK_EQUAL(small_val < big_val, false);
BOOST_CHECK_EQUAL(small_val > big_val, true);
BOOST_CHECK_EQUAL(small_val != big_val, true);
// Again with expression templates, on one the other, or both args:
BOOST_CHECK_EQUAL(big_val == small_val * 1, false);
BOOST_CHECK_EQUAL(big_val <= small_val * 1, true);
BOOST_CHECK_EQUAL(big_val >= small_val * 1, false);
BOOST_CHECK_EQUAL(big_val < small_val * 1, true);
BOOST_CHECK_EQUAL(big_val > small_val * 1, false);
BOOST_CHECK_EQUAL(big_val != small_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 == big_val, false);
BOOST_CHECK_EQUAL(small_val * 1 <= big_val, false);
BOOST_CHECK_EQUAL(small_val * 1 >= big_val, true);
BOOST_CHECK_EQUAL(small_val * 1 < big_val, false);
BOOST_CHECK_EQUAL(small_val * 1 > big_val, true);
BOOST_CHECK_EQUAL(small_val * 1 != big_val, true);
BOOST_CHECK_EQUAL(big_val * 1 == small_val, false);
BOOST_CHECK_EQUAL(big_val * 1 <= small_val, true);
BOOST_CHECK_EQUAL(big_val * 1 >= small_val, false);
BOOST_CHECK_EQUAL(big_val * 1 < small_val, true);
BOOST_CHECK_EQUAL(big_val * 1 > small_val, false);
BOOST_CHECK_EQUAL(big_val * 1 != small_val, true);
BOOST_CHECK_EQUAL(small_val == big_val * 1, false);
BOOST_CHECK_EQUAL(small_val <= big_val * 1, false);
BOOST_CHECK_EQUAL(small_val >= big_val * 1, true);
BOOST_CHECK_EQUAL(small_val < big_val * 1, false);
BOOST_CHECK_EQUAL(small_val > big_val * 1, true);
BOOST_CHECK_EQUAL(small_val != big_val * 1, true);
BOOST_CHECK_EQUAL(big_val * 1 == small_val * 1, false);
BOOST_CHECK_EQUAL(big_val * 1 <= small_val * 1, true);
BOOST_CHECK_EQUAL(big_val * 1 >= small_val * 1, false);
BOOST_CHECK_EQUAL(big_val * 1 < small_val * 1, true);
BOOST_CHECK_EQUAL(big_val * 1 > small_val * 1, false);
BOOST_CHECK_EQUAL(big_val * 1 != small_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 == big_val * 1, false);
BOOST_CHECK_EQUAL(small_val * 1 <= big_val * 1, false);
BOOST_CHECK_EQUAL(small_val * 1 >= big_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 < big_val * 1, false);
BOOST_CHECK_EQUAL(small_val * 1 > big_val * 1, true);
BOOST_CHECK_EQUAL(small_val * 1 != big_val * 1, true);

BOOST_CHECK_EQUAL(small_val + big_val, Big(small_val) + big_val);
BOOST_CHECK_EQUAL(small_val - big_val, Big(small_val) - big_val);
BOOST_CHECK_EQUAL(small_val * big_val, Big(small_val) * big_val);
BOOST_CHECK_EQUAL(small_val / big_val, Big(small_val) / big_val);
BOOST_CHECK_EQUAL(big_val + small_val, big_val + Big(small_val));
BOOST_CHECK_EQUAL(big_val - small_val, big_val - Big(small_val));
BOOST_CHECK_EQUAL(big_val * small_val, big_val * Big(small_val));
BOOST_CHECK_EQUAL(big_val / small_val, big_val / Big(small_val));
// Again with expression templates, on one the other, or both args:
BOOST_CHECK_EQUAL(small_val + (big_val * 1), Big(small_val) + (big_val * 1));
BOOST_CHECK_EQUAL(small_val - (big_val * 1), Big(small_val) - (big_val * 1));
BOOST_CHECK_EQUAL(small_val * (big_val * 1), Big(small_val) * (big_val * 1));
BOOST_CHECK_EQUAL(small_val / (big_val * 1), Big(small_val) / (big_val * 1));
BOOST_CHECK_EQUAL((big_val * 1) + small_val, (big_val * 1) + Big(small_val));
BOOST_CHECK_EQUAL((big_val * 1) - small_val, (big_val * 1) - Big(small_val));
BOOST_CHECK_EQUAL((big_val * 1) * small_val, (big_val * 1) * Big(small_val));
BOOST_CHECK_EQUAL((big_val * 1) / small_val, (big_val * 1) / Big(small_val));
BOOST_CHECK_EQUAL((small_val * 1) + big_val, Big((small_val * 1)) + big_val);
BOOST_CHECK_EQUAL((small_val * 1) - big_val, Big((small_val * 1)) - big_val);
BOOST_CHECK_EQUAL((small_val * 1) * big_val, Big((small_val * 1)) * big_val);
BOOST_CHECK_EQUAL((small_val * 1) / big_val, Big((small_val * 1)) / big_val);
BOOST_CHECK_EQUAL(big_val + (small_val * 1), big_val + Big((small_val * 1)));
BOOST_CHECK_EQUAL(big_val - (small_val * 1), big_val - Big((small_val * 1)));
BOOST_CHECK_EQUAL(big_val * (small_val * 1), big_val * Big((small_val * 1)));
BOOST_CHECK_EQUAL(big_val / (small_val * 1), big_val / Big((small_val * 1)));
BOOST_CHECK_EQUAL((small_val * 1) + (big_val * 1), Big((small_val * 1)) + (big_val * 1));
BOOST_CHECK_EQUAL((small_val * 1) - (big_val * 1), Big((small_val * 1)) - (big_val * 1));
BOOST_CHECK_EQUAL((small_val * 1) * (big_val * 1), Big((small_val * 1)) * (big_val * 1));
BOOST_CHECK_EQUAL((small_val * 1) / (big_val * 1), Big((small_val * 1)) / (big_val * 1));
BOOST_CHECK_EQUAL((big_val * 1) + (small_val * 1), (big_val * 1) + Big((small_val * 1)));
BOOST_CHECK_EQUAL((big_val * 1) - (small_val * 1), (big_val * 1) - Big((small_val * 1)));
BOOST_CHECK_EQUAL((big_val * 1) * (small_val * 1), (big_val * 1) * Big((small_val * 1)));
BOOST_CHECK_EQUAL((big_val * 1) / (small_val * 1), (big_val * 1) / Big((small_val * 1)));
}

#endif // BOOST_MATH_TEST_MIXED_HPP
33 changes: 33 additions & 0 deletions test/test_mixed_cpp_bin_float.cpp
@@ -0,0 +1,33 @@
///////////////////////////////////////////////////////////////
// Copyright 2012 John Maddock. Distributed under the Boost
// Software License, Version 1.0. (See accompanying file
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_

#ifdef _MSC_VER
# define _SCL_SECURE_NO_WARNINGS
#endif

#include <boost/multiprecision/cpp_bin_float.hpp>
#include "test_mixed.hpp"

int main()
{
try{
typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<100>, boost::multiprecision::et_on> big_type1;
typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<50>, boost::multiprecision::et_on> small_type1;
typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<100>, boost::multiprecision::et_off> big_type2;
typedef boost::multiprecision::number<boost::multiprecision::cpp_bin_float<50>, boost::multiprecision::et_off> small_type2;

test<big_type1, small_type1>();
test<big_type2, small_type2>();
test<big_type1, small_type2>();
test<big_type2, small_type1>();
}
catch(const std::exception& e)
{
std::cout << "Failed with unexpected exception: " << e.what() << std::endl;
return 1;
}
return boost::report_errors();
}

0 comments on commit d711ce2

Please sign in to comment.