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
30namespace boost {
31namespace test_tools {
32namespace assertion {
33namespace op {
34
35// ************************************************************************** //
36// ************** fpctraits ************** //
37// ************************************************************************** //
38// set of floating point comparison traits per comparison OP
39
40template<typename OP>
41struct 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
47template <typename Lhs, typename Rhs>
48struct fpctraits<op::LT<Lhs,Rhs> > {
49 static const bool equality_logical_disjunction = false;
50};
51
52template <typename Lhs, typename Rhs>
53struct 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
65template <typename FPT, typename Lhs, typename Rhs, typename OP>
66inline assertion_result
67compare_fpv( Lhs const& lhs, Rhs const& rhs, OP* cmp_operator)
68{
69 assertion_result result_direct_compare = cmp_operator->eval_direct(lhs, rhs);
70 if(fpctraits<OP>::equality_logical_disjunction) {
71 // this look like this can be simplified, but combining result && compare_fpv
72 // looses the message in the return value of compare_fpv
73 if( result_direct_compare ) {
74 result_direct_compare.message() << "operation" << OP::forward() << "on arguments yields 'true'.";
75 return result_direct_compare;
76 }
77 // result || compare_fpv(EQ)
78 assertion_result result_eq = compare_fpv<FPT>(lhs, rhs, (op::EQ<Lhs, Rhs>*)0);
79 result_direct_compare = result_direct_compare || result_eq;
80 if( !result_eq ) {
81 result_direct_compare.message() << "operation" << op::EQ<Lhs, Rhs>::forward() << "on arguments yields 'false': " << result_eq.message() << ".";
82 }
83 return result_direct_compare;
84 }
85 if( !result_direct_compare ) {
86 result_direct_compare.message() << "operation" << OP::forward() << " on arguments yields 'false'.";
87 return result_direct_compare;
88 }
89 // result && compare_fpv(NE)
90 assertion_result result_neq = compare_fpv<FPT>(lhs, rhs, (op::NE<Lhs, Rhs>*)0);
91 result_direct_compare = result_direct_compare && result_neq;
92 if( !result_neq ) {
93 result_direct_compare.message() << "operation" << op::NE<Lhs, Rhs>::forward() << "on arguments yields 'false': " << result_neq.message() << ".";
94 }
95 return result_direct_compare;
96}
97
98//____________________________________________________________________________//
99
100template <typename FPT, typename Lhs, typename Rhs>
101inline assertion_result
102compare_fpv_near_zero( FPT const& fpv, op::EQ<Lhs,Rhs>* )
103{
104 fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
105
106 assertion_result ar( P( fpv ) );
107 if( !ar )
108 ar.message() << "absolute value exceeds tolerance [|" << fpv << "| > "<< fpc_tolerance<FPT>() << ']';
109
110 return ar;
111}
112
113//____________________________________________________________________________//
114
115template <typename FPT, typename Lhs, typename Rhs>
116inline assertion_result
117compare_fpv_near_zero( FPT const& fpv, op::NE<Lhs,Rhs>* )
118{
119 fpc::small_with_tolerance<FPT> P( fpc_tolerance<FPT>() );
120
121 assertion_result ar( !P( fpv ) );
122 if( !ar )
123 ar.message() << "absolute value is within tolerance [|" << fpv << "| < "<< fpc_tolerance<FPT>() << ']';
124 return ar;
125}
126
127//____________________________________________________________________________//
128
129template <typename FPT, typename Lhs, typename Rhs>
130inline assertion_result
131compare_fpv( Lhs const& lhs, Rhs const& rhs, op::EQ<Lhs,Rhs>* )
132{
133 if( lhs == 0 ) {
134 return compare_fpv_near_zero<FPT>( rhs, (op::EQ<Lhs,Rhs>*)0 );
135 }
136 else if( rhs == 0) {
137 return compare_fpv_near_zero<FPT>( lhs, (op::EQ<Lhs,Rhs>*)0 );
138 }
139 else {
140 fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_STRONG );
141
142 assertion_result ar( P( lhs, rhs ) );
143 if( !ar )
144 ar.message() << "relative difference exceeds tolerance ["
145 << P.tested_rel_diff() << " > " << P.fraction_tolerance() << ']';
146 return ar;
147 }
148}
149
150//____________________________________________________________________________//
151
152template <typename FPT, typename Lhs, typename Rhs>
153inline assertion_result
154compare_fpv( Lhs const& lhs, Rhs const& rhs, op::NE<Lhs,Rhs>* )
155{
156 if( lhs == 0 ) {
157 return compare_fpv_near_zero<FPT>( rhs, (op::NE<Lhs,Rhs>*)0 );
158 }
159 else if( rhs == 0 ) {
160 return compare_fpv_near_zero<FPT>( lhs, (op::NE<Lhs,Rhs>*)0 );
161 }
162 else {
163 fpc::close_at_tolerance<FPT> P( fpc_tolerance<FPT>(), fpc::FPC_WEAK );
164
165 assertion_result ar( !P( lhs, rhs ) );
166 if( !ar )
167 ar.message() << "relative difference is within tolerance ["
168 << P.tested_rel_diff() << " < " << fpc_tolerance<FPT>() << ']';
169
170 return ar;
171 }
172}
173
174//____________________________________________________________________________//
175
176#define DEFINE_FPV_COMPARISON( oper, name, rev, name_inverse ) \
177template<typename Lhs,typename Rhs> \
178struct name<Lhs,Rhs,typename boost::enable_if_c< \
179 (fpc::tolerance_based<Lhs>::value && \
180 fpc::tolerance_based<Rhs>::value) || \
181 (fpc::tolerance_based<Lhs>::value && \
182 boost::is_arithmetic<Rhs>::value) || \
183 (boost::is_arithmetic<Lhs>::value && \
184 fpc::tolerance_based<Rhs>::value) \
185 >::type> { \
186public: \
187 typedef typename common_type<Lhs,Rhs>::type FPT; \
188 typedef name<Lhs,Rhs> OP; \
189 typedef name_inverse<Lhs, Rhs> inverse; \
190 \
191 typedef assertion_result result_type; \
192 \
193 static bool \
194 eval_direct( Lhs const& lhs, Rhs const& rhs ) \
195 { \
196 return lhs oper rhs; \
197 } \
198 \
199 static assertion_result \
200 eval( Lhs const& lhs, Rhs const& rhs ) \
201 { \
202 if( fpc_tolerance<FPT>() == FPT(0) \
203 || (std::numeric_limits<Lhs>::has_infinity \
204 && (lhs == std::numeric_limits<Lhs>::infinity())) \
205 || (std::numeric_limits<Rhs>::has_infinity \
206 && (rhs == std::numeric_limits<Rhs>::infinity()))) \
207 { \
208 return eval_direct( lhs, rhs ); \
209 } \
210 \
211 return compare_fpv<FPT>( lhs, rhs, (OP*)0 ); \
212 } \
213 \
214 template<typename PrevExprType> \
215 static void \
216 report( std::ostream& ostr, \
217 PrevExprType const& lhs, \
218 Rhs const& rhs ) \
219 { \
220 lhs.report( ostr ); \
221 ostr << revert() \
222 << tt_detail::print_helper( rhs ); \
223 } \
224 \
225 static char const* forward() \
226 { return " " #oper " "; } \
227 static char const* revert() \
228 { return " " #rev " "; } \
229}; \
230/**/
231
232BOOST_TEST_FOR_EACH_COMP_OP( DEFINE_FPV_COMPARISON )
233#undef DEFINE_FPV_COMPARISON
234
235//____________________________________________________________________________//
236
237} // namespace op
238} // namespace assertion
239} // namespace test_tools
240} // namespace boost
241
242#include <boost/test/detail/enable_warnings.hpp>
243
244#endif // BOOST_TEST_TOOLS_FPC_OP_HPP_050915GER
245

source code of include/boost/test/tools/fpc_op.hpp