1 //  (C) Copyright Gennadiy Rozental 2001-2008.
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        : $RCSfile$
9 //
10 //  Version     : $Revision: 57992 $
11 //
12 //  Description : defines algoirthms for comparing 2 floating point values
13 // ***************************************************************************
14 
15 #ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
16 #define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
17 
18 // Boost.Test
19 #include <boost/test/detail/global_typedef.hpp>
20 #include <boost/test/utils/class_properties.hpp>
21 #include <boost/test/predicate_result.hpp>
22 
23 // Boost
24 #include <boost/limits.hpp>  // for std::numeric_limits
25 #include <boost/numeric/conversion/conversion_traits.hpp> // for numeric::conversion_traits
26 #include <boost/static_assert.hpp>
27 
28 #include <boost/test/detail/suppress_warnings.hpp>
29 
30 //____________________________________________________________________________//
31 
32 namespace boost {
33 
34 namespace test_tools {
35 
36 using unit_test::readonly_property;
37 
38 // ************************************************************************** //
39 // **************        floating_point_comparison_type        ************** //
40 // ************************************************************************** //
41 
42 enum floating_point_comparison_type {
43     FPC_STRONG, // "Very close"   - equation 1' in docs, the default
44     FPC_WEAK    // "Close enough" - equation 2' in docs.
45 
46 };
47 
48 // ************************************************************************** //
49 // **************                    details                   ************** //
50 // ************************************************************************** //
51 
52 namespace tt_detail {
53 
54 // FPT is Floating-Point Type: float, double, long double or User-Defined.
55 template<typename FPT>
56 inline FPT
fpt_abs(FPT fpv)57 fpt_abs( FPT fpv )
58 {
59     return fpv < static_cast<FPT>(0) ? -fpv : fpv;
60 }
61 
62 //____________________________________________________________________________//
63 
64 template<typename FPT>
65 struct fpt_limits {
min_valueboost::test_tools::tt_detail::fpt_limits66     static FPT    min_value()
67     {
68         return std::numeric_limits<FPT>::is_specialized
69                     ? (std::numeric_limits<FPT>::min)()
70                     : 0;
71     }
max_valueboost::test_tools::tt_detail::fpt_limits72     static FPT    max_value()
73     {
74         return std::numeric_limits<FPT>::is_specialized
75                     ? (std::numeric_limits<FPT>::max)()
76                     : static_cast<FPT>(1000000); // for the our purpuses it doesn't really matter what value is returned here
77     }
78 };
79 
80 //____________________________________________________________________________//
81 
82 // both f1 and f2 are unsigned here
83 template<typename FPT>
84 inline FPT
safe_fpt_division(FPT f1,FPT f2)85 safe_fpt_division( FPT f1, FPT f2 )
86 {
87     // Avoid overflow.
88     if( (f2 < static_cast<FPT>(1))  && (f1 > f2*fpt_limits<FPT>::max_value()) )
89         return fpt_limits<FPT>::max_value();
90 
91     // Avoid underflow.
92     if( (f1 == static_cast<FPT>(0)) ||
93         ((f2 > static_cast<FPT>(1)) && (f1 < f2*fpt_limits<FPT>::min_value())) )
94         return static_cast<FPT>(0);
95 
96     return f1/f2;
97 }
98 
99 //____________________________________________________________________________//
100 
101 } // namespace tt_detail
102 
103 // ************************************************************************** //
104 // **************         tolerance presentation types         ************** //
105 // ************************************************************************** //
106 
107 template<typename FPT>
108 struct percent_tolerance_t {
percent_tolerance_tboost::test_tools::percent_tolerance_t109     explicit    percent_tolerance_t( FPT v ) : m_value( v ) {}
110 
111     FPT m_value;
112 };
113 
114 //____________________________________________________________________________//
115 
116 template<typename Out,typename FPT>
operator <<(Out & out,percent_tolerance_t<FPT> t)117 Out& operator<<( Out& out, percent_tolerance_t<FPT> t )
118 {
119     return out << t.m_value;
120 }
121 
122 //____________________________________________________________________________//
123 
124 template<typename FPT>
125 inline percent_tolerance_t<FPT>
percent_tolerance(FPT v)126 percent_tolerance( FPT v )
127 {
128     return percent_tolerance_t<FPT>( v );
129 }
130 
131 //____________________________________________________________________________//
132 
133 template<typename FPT>
134 struct fraction_tolerance_t {
fraction_tolerance_tboost::test_tools::fraction_tolerance_t135     explicit fraction_tolerance_t( FPT v ) : m_value( v ) {}
136 
137     FPT m_value;
138 };
139 
140 //____________________________________________________________________________//
141 
142 template<typename Out,typename FPT>
operator <<(Out & out,fraction_tolerance_t<FPT> t)143 Out& operator<<( Out& out, fraction_tolerance_t<FPT> t )
144 {
145     return out << t.m_value;
146 }
147 
148 //____________________________________________________________________________//
149 
150 template<typename FPT>
151 inline fraction_tolerance_t<FPT>
fraction_tolerance(FPT v)152 fraction_tolerance( FPT v )
153 {
154     return fraction_tolerance_t<FPT>( v );
155 }
156 
157 //____________________________________________________________________________//
158 
159 // ************************************************************************** //
160 // **************             close_at_tolerance               ************** //
161 // ************************************************************************** //
162 
163 template<typename FPT>
164 class close_at_tolerance {
165 public:
166     // Public typedefs
167     typedef bool result_type;
168 
169     // Constructor
170     template<typename ToleranceBaseType>
close_at_tolerance(percent_tolerance_t<ToleranceBaseType> tolerance,floating_point_comparison_type fpc_type=FPC_STRONG)171     explicit    close_at_tolerance( percent_tolerance_t<ToleranceBaseType>  tolerance,
172                                     floating_point_comparison_type          fpc_type = FPC_STRONG )
173     : p_fraction_tolerance( tt_detail::fpt_abs( static_cast<FPT>(0.01)*tolerance.m_value ) )
174     , p_strong_or_weak( fpc_type ==  FPC_STRONG )
175     , m_report_modifier( 100. )
176     {}
177     template<typename ToleranceBaseType>
close_at_tolerance(fraction_tolerance_t<ToleranceBaseType> tolerance,floating_point_comparison_type fpc_type=FPC_STRONG)178     explicit    close_at_tolerance( fraction_tolerance_t<ToleranceBaseType> tolerance,
179                                     floating_point_comparison_type          fpc_type = FPC_STRONG )
180     : p_fraction_tolerance( tt_detail::fpt_abs( tolerance.m_value ) )
181     , p_strong_or_weak( fpc_type ==  FPC_STRONG )
182     , m_report_modifier( 1. )
183     {}
184 
operator ()(FPT left,FPT right) const185     predicate_result        operator()( FPT left, FPT right ) const
186     {
187         FPT diff = tt_detail::fpt_abs( left - right );
188         FPT d1   = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( right ) );
189         FPT d2   = tt_detail::safe_fpt_division( diff, tt_detail::fpt_abs( left ) );
190 
191         predicate_result res( p_strong_or_weak
192             ? (d1 <= p_fraction_tolerance.get() && d2 <= p_fraction_tolerance.get())
193             : (d1 <= p_fraction_tolerance.get() || d2 <= p_fraction_tolerance.get()) );
194 
195         if( !res )
196             res.message() << (( d1 <= p_fraction_tolerance.get() ? d2 : d1 ) * m_report_modifier);
197 
198         return res;
199     }
200 
201     // Public properties
202     readonly_property<FPT>  p_fraction_tolerance;
203     readonly_property<bool> p_strong_or_weak;
204 private:
205     // Data members
206     FPT                     m_report_modifier;
207 };
208 
209 //____________________________________________________________________________//
210 
211 // ************************************************************************** //
212 // **************               check_is_close                 ************** //
213 // ************************************************************************** //
214 
215 struct BOOST_TEST_DECL check_is_close_t {
216     // Public typedefs
217     typedef bool result_type;
218 
219     template<typename FPT1, typename FPT2, typename ToleranceBaseType>
220     predicate_result
operator ()boost::test_tools::check_is_close_t221     operator()( FPT1 left, FPT2 right, percent_tolerance_t<ToleranceBaseType> tolerance,
222                 floating_point_comparison_type fpc_type = FPC_STRONG ) const
223     {
224         // deduce "better" type from types of arguments being compared
225         // if one type is floating and the second integral we use floating type and
226         // value of integral type is promoted to the floating. The same for float and double
227         // But we don't want to compare two values of integral types using this tool.
228         typedef typename numeric::conversion_traits<FPT1,FPT2>::supertype FPT;
229         BOOST_STATIC_ASSERT( !is_integral<FPT>::value );
230 
231         close_at_tolerance<FPT> pred( tolerance, fpc_type );
232 
233         return pred( left, right );
234     }
235     template<typename FPT1, typename FPT2, typename ToleranceBaseType>
236     predicate_result
operator ()boost::test_tools::check_is_close_t237     operator()( FPT1 left, FPT2 right, fraction_tolerance_t<ToleranceBaseType> tolerance,
238                 floating_point_comparison_type fpc_type = FPC_STRONG ) const
239     {
240         // same as in a comment above
241         typedef typename numeric::conversion_traits<FPT1,FPT2>::supertype FPT;
242         BOOST_STATIC_ASSERT( !is_integral<FPT>::value );
243 
244         close_at_tolerance<FPT> pred( tolerance, fpc_type );
245 
246         return pred( left, right );
247     }
248 };
249 
250 namespace {
251 check_is_close_t const& check_is_close = unit_test::ut_detail::static_constant<check_is_close_t>::value;
252 }
253 
254 //____________________________________________________________________________//
255 
256 // ************************************************************************** //
257 // **************               check_is_small                 ************** //
258 // ************************************************************************** //
259 
260 struct BOOST_TEST_DECL check_is_small_t {
261     // Public typedefs
262     typedef bool result_type;
263 
264     template<typename FPT>
265     bool
operator ()boost::test_tools::check_is_small_t266     operator()( FPT fpv, FPT tolerance ) const
267     {
268         return tt_detail::fpt_abs( fpv ) < tt_detail::fpt_abs( tolerance );
269     }
270 };
271 
272 namespace {
273 check_is_small_t const& check_is_small = unit_test::ut_detail::static_constant<check_is_small_t>::value;
274 }
275 
276 //____________________________________________________________________________//
277 
278 } // namespace test_tools
279 
280 } // namespace boost
281 
282 //____________________________________________________________________________//
283 
284 #include <boost/test/detail/enable_warnings.hpp>
285 
286 #endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER
287