1 //  (C) Copyright Gennadiy Rozental 2001.
2 //  Distributed under the Boost Software License, Version 1.0.
3 //  (See accompanying file LICENSE_1_0.txt or copy at
4 //  http://www.boost.org/LICENSE_1_0.txt)
5 
6 //  See http://www.boost.org/libs/test for the library home page.
7 //
8 //!@file
9 //!@brief Floating point comparison with enhanced reporting
10 // ***************************************************************************
11 
12 #ifndef BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
13 #define BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
14 
15 // Boost.Test
16 #include <boost/test/tools/assertion.hpp>
17 
18 #include <boost/test/tools/floating_point_comparison.hpp>
19 #include <boost/test/tools/fpc_tolerance.hpp>
20 
21 // Boost
22 #include <boost/type_traits/common_type.hpp>
23 #include <boost/type_traits/is_arithmetic.hpp>
24 #include <boost/utility/enable_if.hpp>
25 
26 #include <boost/test/detail/suppress_warnings.hpp>
27 
28 //____________________________________________________________________________//
29 
30 namespace boost {
31 namespace test_tools {
32 namespace assertion {
33 namespace op {
34 
35 // ************************************************************************** //
36 // **************                   fpctraits                  ************** //
37 // ************************************************************************** //
38 // set of floating point comparison traits per comparison OP
39 
40 template<typename OP>
41 struct fpctraits {
42     // indicate if we should perform the operation with a "logical OR"
43     // with the "equality under tolerance".
44     static const bool equality_logical_disjunction = true;
45 };
46 
47 template <typename Lhs, typename Rhs>
48 struct fpctraits<op::LT<Lhs,Rhs> > {
49     static const bool equality_logical_disjunction = false;
50 };
51 
52 template <typename Lhs, typename Rhs>
53 struct fpctraits<op::GT<Lhs,Rhs> > {
54     static const bool equality_logical_disjunction = false;
55 };
56 
57 //____________________________________________________________________________//
58 
59 // ************************************************************************** //
60 // ************** set of overloads to select correct fpc algo  ************** //
61 // ************************************************************************** //
62 // we really only care about EQ vs NE. All other comparisons use direct first
63 // and then need EQ. For example a <= b (tolerance t) IFF a <= b OR a == b (tolerance t)
64 
65 template <typename FPT, typename Lhs, typename Rhs, typename OP>
66 inline assertion_result
compare_fpv(Lhs const & lhs,Rhs const & rhs,OP * cmp_operator)67 compare_fpv( Lhs const& lhs, Rhs const& rhs, OP* cmp_operator)
68 {
69     bool result = cmp_operator->eval_direct(lhs, rhs);
70     if(fpctraits<OP>::equality_logical_disjunction) {
71         return result || compare_fpv<FPT>(lhs, rhs, (op::EQ<Lhs, Rhs>*)0);
72     }
73     return result && compare_fpv<FPT>(lhs, rhs, (op::NE<Lhs, Rhs>*)0);
74 }
75 
76 //____________________________________________________________________________//
77 
78 template <typename FPT, typename Lhs, typename Rhs>
79 inline assertion_result
compare_fpv_near_zero(FPT const & fpv,op::EQ<Lhs,Rhs> *)80 compare_fpv_near_zero( FPT const& fpv, op::EQ<Lhs,Rhs>* )
81 {
82     fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
83 
84     assertion_result ar( P( fpv ) );
85     if( !ar )
86         ar.message() << "Absolute value exceeds tolerance [|" << fpv << "| > "<< fpc_tolerance<FPT>() << ']';
87 
88     return ar;
89 }
90 
91 //____________________________________________________________________________//
92 
93 template <typename FPT, typename Lhs, typename Rhs>
94 inline assertion_result
compare_fpv_near_zero(FPT const & fpv,op::NE<Lhs,Rhs> *)95 compare_fpv_near_zero( FPT const& fpv, op::NE<Lhs,Rhs>* )
96 {
97     fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
98 
99     assertion_result ar( !P( fpv ) );
100     if( !ar )
101         ar.message() << "Absolute value is within tolerance [|" << fpv << "| < "<< fpc_tolerance<FPT>() << ']';
102     return ar;
103 }
104 
105 //____________________________________________________________________________//
106 
107 template <typename FPT, typename Lhs, typename Rhs>
108 inline assertion_result
compare_fpv(Lhs const & lhs,Rhs const & rhs,op::EQ<Lhs,Rhs> *)109 compare_fpv( Lhs const& lhs, Rhs const& rhs, op::EQ<Lhs,Rhs>* )
110 {
111     if( lhs == 0 ) {
112         return compare_fpv_near_zero<FPT>( rhs, (op::EQ<Lhs,Rhs>*)0 );
113     }
114     else if( rhs == 0) {
115         return compare_fpv_near_zero<FPT>( lhs, (op::EQ<Lhs,Rhs>*)0 );
116     }
117     else {
118         fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_STRONG );
119 
120         assertion_result ar( P( lhs, rhs ) );
121         if( !ar )
122             ar.message() << "Relative difference exceeds tolerance ["
123                          << P.tested_rel_diff() << " > " << P.fraction_tolerance() << ']';
124         return ar;
125     }
126 }
127 
128 //____________________________________________________________________________//
129 
130 template <typename FPT, typename Lhs, typename Rhs>
131 inline assertion_result
compare_fpv(Lhs const & lhs,Rhs const & rhs,op::NE<Lhs,Rhs> *)132 compare_fpv( Lhs const& lhs, Rhs const& rhs, op::NE<Lhs,Rhs>* )
133 {
134     if( lhs == 0 ) {
135         return compare_fpv_near_zero<FPT>( rhs, (op::NE<Lhs,Rhs>*)0 );
136     }
137     else if( rhs == 0 ) {
138         return compare_fpv_near_zero<FPT>( lhs, (op::NE<Lhs,Rhs>*)0 );
139     }
140     else {
141         fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_WEAK );
142 
143         assertion_result ar( !P( lhs, rhs ) );
144         if( !ar )
145             ar.message() << "Relative difference is within tolerance ["
146                          << P.tested_rel_diff() << " < " << fpc_tolerance<FPT>() << ']';
147 
148         return ar;
149     }
150 }
151 
152 //____________________________________________________________________________//
153 
154 #define DEFINE_FPV_COMPARISON( oper, name, rev )                        \
155 template<typename Lhs,typename Rhs>                                     \
156 struct name<Lhs,Rhs,typename boost::enable_if_c<                        \
157     (fpc::tolerance_based<Lhs>::value &&                                \
158      fpc::tolerance_based<Rhs>::value) ||                               \
159     (fpc::tolerance_based<Lhs>::value &&                                \
160      boost::is_arithmetic<Rhs>::value) ||                               \
161     (boost::is_arithmetic<Lhs>::value &&                                \
162      fpc::tolerance_based<Rhs>::value)                                  \
163      >::type> {                                                         \
164 public:                                                                 \
165     typedef typename common_type<Lhs,Rhs>::type FPT;                    \
166     typedef name<Lhs,Rhs> OP;                                           \
167                                                                         \
168     typedef assertion_result result_type;                               \
169                                                                         \
170     static bool                                                         \
171     eval_direct( Lhs const& lhs, Rhs const& rhs )                       \
172     {                                                                   \
173         return lhs oper rhs;                                            \
174     }                                                                   \
175                                                                         \
176     static assertion_result                                             \
177     eval( Lhs const& lhs, Rhs const& rhs )                              \
178     {                                                                   \
179         if( fpc_tolerance<FPT>() == FPT(0)                              \
180             || (std::numeric_limits<Lhs>::has_infinity                  \
181                 && (lhs == std::numeric_limits<Lhs>::infinity()))       \
182             || (std::numeric_limits<Rhs>::has_infinity                  \
183                 && (rhs == std::numeric_limits<Rhs>::infinity())))      \
184         {                                                               \
185             return eval_direct( lhs, rhs );                             \
186         }                                                               \
187                                                                         \
188         return compare_fpv<FPT>( lhs, rhs, (OP*)0 );                    \
189     }                                                                   \
190                                                                         \
191     template<typename PrevExprType>                                     \
192     static void                                                         \
193     report( std::ostream&       ostr,                                   \
194             PrevExprType const& lhs,                                    \
195             Rhs const&          rhs )                                   \
196     {                                                                   \
197         lhs.report( ostr );                                             \
198         ostr << revert()                                                \
199              << tt_detail::print_helper( rhs );                         \
200     }                                                                   \
201                                                                         \
202     static char const* revert()                                         \
203     { return " " #rev " "; }                                            \
204 };                                                                      \
205 /**/
206 
207 BOOST_TEST_FOR_EACH_COMP_OP( DEFINE_FPV_COMPARISON )
208 #undef DEFINE_FPV_COMPARISON
209 
210 //____________________________________________________________________________//
211 
212 } // namespace op
213 } // namespace assertion
214 } // namespace test_tools
215 } // namespace boost
216 
217 #include <boost/test/detail/enable_warnings.hpp>
218 
219 #endif // BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
220