245 lines
11 KiB
C++
245 lines
11 KiB
C++
|
// (C) Copyright Gennadiy Rozental 2001.
|
||
|
// 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)
|
||
|
|
||
|
// See http://www.boost.org/libs/test for the library home page.
|
||
|
//
|
||
|
//!@file
|
||
|
//!@brief Floating point comparison with enhanced reporting
|
||
|
// ***************************************************************************
|
||
|
|
||
|
#ifndef BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
|
||
|
#define BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
|
||
|
|
||
|
// Boost.Test
|
||
|
#include <boost/test/tools/assertion.hpp>
|
||
|
|
||
|
#include <boost/test/tools/floating_point_comparison.hpp>
|
||
|
#include <boost/test/tools/fpc_tolerance.hpp>
|
||
|
|
||
|
// Boost
|
||
|
#include <boost/type_traits/common_type.hpp>
|
||
|
#include <boost/type_traits/is_arithmetic.hpp>
|
||
|
#include <boost/utility/enable_if.hpp>
|
||
|
|
||
|
#include <boost/test/detail/suppress_warnings.hpp>
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
namespace boost {
|
||
|
namespace test_tools {
|
||
|
namespace assertion {
|
||
|
namespace op {
|
||
|
|
||
|
// ************************************************************************** //
|
||
|
// ************** fpctraits ************** //
|
||
|
// ************************************************************************** //
|
||
|
// set of floating point comparison traits per comparison OP
|
||
|
|
||
|
template<typename OP>
|
||
|
struct fpctraits {
|
||
|
// indicate if we should perform the operation with a "logical OR"
|
||
|
// with the "equality under tolerance".
|
||
|
static const bool equality_logical_disjunction = true;
|
||
|
};
|
||
|
|
||
|
template <typename Lhs, typename Rhs>
|
||
|
struct fpctraits<op::LT<Lhs,Rhs> > {
|
||
|
static const bool equality_logical_disjunction = false;
|
||
|
};
|
||
|
|
||
|
template <typename Lhs, typename Rhs>
|
||
|
struct fpctraits<op::GT<Lhs,Rhs> > {
|
||
|
static const bool equality_logical_disjunction = false;
|
||
|
};
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
// ************************************************************************** //
|
||
|
// ************** set of overloads to select correct fpc algo ************** //
|
||
|
// ************************************************************************** //
|
||
|
// we really only care about EQ vs NE. All other comparisons use direct first
|
||
|
// and then need EQ. For example a <= b (tolerance t) IFF a <= b OR a == b (tolerance t)
|
||
|
|
||
|
template <typename FPT, typename Lhs, typename Rhs, typename OP>
|
||
|
inline assertion_result
|
||
|
compare_fpv( Lhs const& lhs, Rhs const& rhs, OP* cmp_operator)
|
||
|
{
|
||
|
assertion_result result_direct_compare = cmp_operator->eval_direct(lhs, rhs);
|
||
|
if(fpctraits<OP>::equality_logical_disjunction) {
|
||
|
// this look like this can be simplified, but combining result && compare_fpv
|
||
|
// looses the message in the return value of compare_fpv
|
||
|
if( result_direct_compare ) {
|
||
|
result_direct_compare.message() << "operation" << OP::forward() << "on arguments yields 'true'.";
|
||
|
return result_direct_compare;
|
||
|
}
|
||
|
// result || compare_fpv(EQ)
|
||
|
assertion_result result_eq = compare_fpv<FPT>(lhs, rhs, (op::EQ<Lhs, Rhs>*)0);
|
||
|
result_direct_compare = result_direct_compare || result_eq;
|
||
|
if( !result_eq ) {
|
||
|
result_direct_compare.message() << "operation" << op::EQ<Lhs, Rhs>::forward() << "on arguments yields 'false': " << result_eq.message() << ".";
|
||
|
}
|
||
|
return result_direct_compare;
|
||
|
}
|
||
|
if( !result_direct_compare ) {
|
||
|
result_direct_compare.message() << "operation" << OP::forward() << " on arguments yields 'false'.";
|
||
|
return result_direct_compare;
|
||
|
}
|
||
|
// result && compare_fpv(NE)
|
||
|
assertion_result result_neq = compare_fpv<FPT>(lhs, rhs, (op::NE<Lhs, Rhs>*)0);
|
||
|
result_direct_compare = result_direct_compare && result_neq;
|
||
|
if( !result_neq ) {
|
||
|
result_direct_compare.message() << "operation" << op::NE<Lhs, Rhs>::forward() << "on arguments yields 'false': " << result_neq.message() << ".";
|
||
|
}
|
||
|
return result_direct_compare;
|
||
|
}
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
template <typename FPT, typename Lhs, typename Rhs>
|
||
|
inline assertion_result
|
||
|
compare_fpv_near_zero( FPT const& fpv, op::EQ<Lhs,Rhs>* )
|
||
|
{
|
||
|
fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
|
||
|
|
||
|
assertion_result ar( P( fpv ) );
|
||
|
if( !ar )
|
||
|
ar.message() << "absolute value exceeds tolerance [|" << fpv << "| > "<< fpc_tolerance<FPT>() << ']';
|
||
|
|
||
|
return ar;
|
||
|
}
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
template <typename FPT, typename Lhs, typename Rhs>
|
||
|
inline assertion_result
|
||
|
compare_fpv_near_zero( FPT const& fpv, op::NE<Lhs,Rhs>* )
|
||
|
{
|
||
|
fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
|
||
|
|
||
|
assertion_result ar( !P( fpv ) );
|
||
|
if( !ar )
|
||
|
ar.message() << "absolute value is within tolerance [|" << fpv << "| < "<< fpc_tolerance<FPT>() << ']';
|
||
|
return ar;
|
||
|
}
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
template <typename FPT, typename Lhs, typename Rhs>
|
||
|
inline assertion_result
|
||
|
compare_fpv( Lhs const& lhs, Rhs const& rhs, op::EQ<Lhs,Rhs>* )
|
||
|
{
|
||
|
if( lhs == 0 ) {
|
||
|
return compare_fpv_near_zero<FPT>( rhs, (op::EQ<Lhs,Rhs>*)0 );
|
||
|
}
|
||
|
else if( rhs == 0) {
|
||
|
return compare_fpv_near_zero<FPT>( lhs, (op::EQ<Lhs,Rhs>*)0 );
|
||
|
}
|
||
|
else {
|
||
|
fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_STRONG );
|
||
|
|
||
|
assertion_result ar( P( lhs, rhs ) );
|
||
|
if( !ar )
|
||
|
ar.message() << "relative difference exceeds tolerance ["
|
||
|
<< P.tested_rel_diff() << " > " << P.fraction_tolerance() << ']';
|
||
|
return ar;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
template <typename FPT, typename Lhs, typename Rhs>
|
||
|
inline assertion_result
|
||
|
compare_fpv( Lhs const& lhs, Rhs const& rhs, op::NE<Lhs,Rhs>* )
|
||
|
{
|
||
|
if( lhs == 0 ) {
|
||
|
return compare_fpv_near_zero<FPT>( rhs, (op::NE<Lhs,Rhs>*)0 );
|
||
|
}
|
||
|
else if( rhs == 0 ) {
|
||
|
return compare_fpv_near_zero<FPT>( lhs, (op::NE<Lhs,Rhs>*)0 );
|
||
|
}
|
||
|
else {
|
||
|
fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_WEAK );
|
||
|
|
||
|
assertion_result ar( !P( lhs, rhs ) );
|
||
|
if( !ar )
|
||
|
ar.message() << "relative difference is within tolerance ["
|
||
|
<< P.tested_rel_diff() << " < " << fpc_tolerance<FPT>() << ']';
|
||
|
|
||
|
return ar;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
#define DEFINE_FPV_COMPARISON( oper, name, rev, name_inverse ) \
|
||
|
template<typename Lhs,typename Rhs> \
|
||
|
struct name<Lhs,Rhs,typename boost::enable_if_c< \
|
||
|
(fpc::tolerance_based<Lhs>::value && \
|
||
|
fpc::tolerance_based<Rhs>::value) || \
|
||
|
(fpc::tolerance_based<Lhs>::value && \
|
||
|
boost::is_arithmetic<Rhs>::value) || \
|
||
|
(boost::is_arithmetic<Lhs>::value && \
|
||
|
fpc::tolerance_based<Rhs>::value) \
|
||
|
>::type> { \
|
||
|
public: \
|
||
|
typedef typename common_type<Lhs,Rhs>::type FPT; \
|
||
|
typedef name<Lhs,Rhs> OP; \
|
||
|
typedef name_inverse<Lhs, Rhs> inverse; \
|
||
|
\
|
||
|
typedef assertion_result result_type; \
|
||
|
\
|
||
|
static bool \
|
||
|
eval_direct( Lhs const& lhs, Rhs const& rhs ) \
|
||
|
{ \
|
||
|
return lhs oper rhs; \
|
||
|
} \
|
||
|
\
|
||
|
static assertion_result \
|
||
|
eval( Lhs const& lhs, Rhs const& rhs ) \
|
||
|
{ \
|
||
|
if( fpc_tolerance<FPT>() == FPT(0) \
|
||
|
|| (std::numeric_limits<Lhs>::has_infinity \
|
||
|
&& (lhs == std::numeric_limits<Lhs>::infinity())) \
|
||
|
|| (std::numeric_limits<Rhs>::has_infinity \
|
||
|
&& (rhs == std::numeric_limits<Rhs>::infinity()))) \
|
||
|
{ \
|
||
|
return eval_direct( lhs, rhs ); \
|
||
|
} \
|
||
|
\
|
||
|
return compare_fpv<FPT>( lhs, rhs, (OP*)0 ); \
|
||
|
} \
|
||
|
\
|
||
|
template<typename PrevExprType> \
|
||
|
static void \
|
||
|
report( std::ostream& ostr, \
|
||
|
PrevExprType const& lhs, \
|
||
|
Rhs const& rhs ) \
|
||
|
{ \
|
||
|
lhs.report( ostr ); \
|
||
|
ostr << revert() \
|
||
|
<< tt_detail::print_helper( rhs ); \
|
||
|
} \
|
||
|
\
|
||
|
static char const* forward() \
|
||
|
{ return " " #oper " "; } \
|
||
|
static char const* revert() \
|
||
|
{ return " " #rev " "; } \
|
||
|
}; \
|
||
|
/**/
|
||
|
|
||
|
BOOST_TEST_FOR_EACH_COMP_OP( DEFINE_FPV_COMPARISON )
|
||
|
#undef DEFINE_FPV_COMPARISON
|
||
|
|
||
|
//____________________________________________________________________________//
|
||
|
|
||
|
} // namespace op
|
||
|
} // namespace assertion
|
||
|
} // namespace test_tools
|
||
|
} // namespace boost
|
||
|
|
||
|
#include <boost/test/detail/enable_warnings.hpp>
|
||
|
|
||
|
#endif // BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
|