123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- // (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
|