blob: 211f29be38ee23cfaa8d38d7a2e453d45351278f [file] [log] [blame]
Jeff Thompsonef2d5a42013-08-22 19:09:24 -07001// (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 <ndnboost/test/detail/global_typedef.hpp>
20#include <ndnboost/test/utils/class_properties.hpp>
21#include <ndnboost/test/predicate_result.hpp>
22
23// Boost
24#include <ndnboost/limits.hpp> // for std::numeric_limits
25#include <ndnboost/numeric/conversion/conversion_traits.hpp> // for numeric::conversion_traits
26#include <ndnboost/static_assert.hpp>
27
28#include <ndnboost/test/detail/suppress_warnings.hpp>
29
30//____________________________________________________________________________//
31
32namespace ndnboost {
33
34namespace test_tools {
35
36using unit_test::readonly_property;
37
38// ************************************************************************** //
39// ************** floating_point_comparison_type ************** //
40// ************************************************************************** //
41
42enum 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
52namespace tt_detail {
53
54// FPT is Floating-Point Type: float, double, long double or User-Defined.
55template<typename FPT>
56inline FPT
57fpt_abs( FPT fpv )
58{
59 return fpv < static_cast<FPT>(0) ? -fpv : fpv;
60}
61
62//____________________________________________________________________________//
63
64template<typename FPT>
65struct fpt_limits {
66 static FPT min_value()
67 {
68 return std::numeric_limits<FPT>::is_specialized
69 ? (std::numeric_limits<FPT>::min)()
70 : 0;
71 }
72 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
83template<typename FPT>
84inline FPT
85safe_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
107template<typename FPT>
108struct percent_tolerance_t {
109 explicit percent_tolerance_t( FPT v ) : m_value( v ) {}
110
111 FPT m_value;
112};
113
114//____________________________________________________________________________//
115
116template<typename Out,typename FPT>
117Out& operator<<( Out& out, percent_tolerance_t<FPT> t )
118{
119 return out << t.m_value;
120}
121
122//____________________________________________________________________________//
123
124template<typename FPT>
125inline percent_tolerance_t<FPT>
126percent_tolerance( FPT v )
127{
128 return percent_tolerance_t<FPT>( v );
129}
130
131//____________________________________________________________________________//
132
133template<typename FPT>
134struct fraction_tolerance_t {
135 explicit fraction_tolerance_t( FPT v ) : m_value( v ) {}
136
137 FPT m_value;
138};
139
140//____________________________________________________________________________//
141
142template<typename Out,typename FPT>
143Out& operator<<( Out& out, fraction_tolerance_t<FPT> t )
144{
145 return out << t.m_value;
146}
147
148//____________________________________________________________________________//
149
150template<typename FPT>
151inline fraction_tolerance_t<FPT>
152fraction_tolerance( FPT v )
153{
154 return fraction_tolerance_t<FPT>( v );
155}
156
157//____________________________________________________________________________//
158
159// ************************************************************************** //
160// ************** close_at_tolerance ************** //
161// ************************************************************************** //
162
163template<typename FPT>
164class close_at_tolerance {
165public:
166 // Public typedefs
167 typedef bool result_type;
168
169 // Constructor
170 template<typename ToleranceBaseType>
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>
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
185 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;
204private:
205 // Data members
206 FPT m_report_modifier;
207};
208
209//____________________________________________________________________________//
210
211// ************************************************************************** //
212// ************** check_is_close ************** //
213// ************************************************************************** //
214
215struct 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
221 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
237 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
250namespace {
251check_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
260struct BOOST_TEST_DECL check_is_small_t {
261 // Public typedefs
262 typedef bool result_type;
263
264 template<typename FPT>
265 bool
266 operator()( FPT fpv, FPT tolerance ) const
267 {
268 return tt_detail::fpt_abs( fpv ) < tt_detail::fpt_abs( tolerance );
269 }
270};
271
272namespace {
273check_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 ndnboost
281
282//____________________________________________________________________________//
283
284#include <ndnboost/test/detail/enable_warnings.hpp>
285
286#endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER