1 //  (C) Copyright Gennadiy Rozental 2001-2014.
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 algorithms for comparing floating point values
10 // ***************************************************************************
11 
12 #ifndef BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
13 #define BOOST_TEST_FLOATING_POINT_COMPARISON_HPP_071894GER
14 
15 // Boost.Test
16 #include <boost/test/detail/global_typedef.hpp>
17 #include <boost/test/tools/assertion_result.hpp>
18 
19 // Boost
20 #include <boost/limits.hpp>  // for std::numeric_limits
21 #include <boost/static_assert.hpp>
22 #include <boost/assert.hpp>
23 #include <boost/mpl/bool.hpp>
24 #include <boost/type_traits/is_floating_point.hpp>
25 #include <boost/type_traits/is_array.hpp>
26 #include <boost/type_traits/conditional.hpp>
27 #include <boost/utility/enable_if.hpp>
28 
29 // STL
30 #include <iosfwd>
31 
32 #include <boost/test/detail/suppress_warnings.hpp>
33 
34 //____________________________________________________________________________//
35 
36 namespace boost {
37 namespace math {
38 namespace fpc {
39 
40 // ************************************************************************** //
41 // **************              fpc::tolerance_based            ************** //
42 // ************************************************************************** //
43 
44 
45 //! @internal
46 //! Protects the instanciation of std::numeric_limits from non-supported types (eg. T=array)
47 template <typename T, bool enabled>
48 struct tolerance_based_delegate;
49 
50 template <typename T>
51 struct tolerance_based_delegate<T, false> : mpl::false_ {};
52 
53 template <typename T>
54 struct tolerance_based_delegate<T, true>
55 : mpl::bool_<
56     is_floating_point<T>::value ||
57     (!std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_specialized && !std::numeric_limits<T>::is_exact)>
58 {};
59 
60 
61 /*!@brief Indicates if a type can be compared using a tolerance scheme
62  *
63  * This is a metafunction that should evaluate to @c mpl::true_ if the type
64  * @c T can be compared using a tolerance based method, typically for floating point
65  * types.
66  *
67  * This metafunction can be specialized further to declare user types that are
68  * floating point (eg. boost.multiprecision).
69  */
70 template <typename T>
71 struct tolerance_based : tolerance_based_delegate<T, !is_array<T>::value >::type {};
72 
73 // ************************************************************************** //
74 // **************                 fpc::strength                ************** //
75 // ************************************************************************** //
76 
77 //! Method for comparing floating point numbers
78 enum strength {
79     FPC_STRONG, //!< "Very close"   - equation 2' in docs, the default
80     FPC_WEAK    //!< "Close enough" - equation 3' in docs.
81 };
82 
83 
84 // ************************************************************************** //
85 // **************         tolerance presentation types         ************** //
86 // ************************************************************************** //
87 
88 template<typename FPT>
89 struct percent_tolerance_t {
percent_tolerance_tboost::math::fpc::percent_tolerance_t90     explicit    percent_tolerance_t( FPT v ) : m_value( v ) {}
91 
92     FPT m_value;
93 };
94 
95 //____________________________________________________________________________//
96 
97 template<typename FPT>
operator <<(std::ostream & out,percent_tolerance_t<FPT> t)98 inline std::ostream& operator<<( std::ostream& out, percent_tolerance_t<FPT> t )
99 {
100     return out << t.m_value;
101 }
102 
103 //____________________________________________________________________________//
104 
105 template<typename FPT>
106 inline percent_tolerance_t<FPT>
percent_tolerance(FPT v)107 percent_tolerance( FPT v )
108 {
109     return percent_tolerance_t<FPT>( v );
110 }
111 
112 //____________________________________________________________________________//
113 
114 // ************************************************************************** //
115 // **************                    details                   ************** //
116 // ************************************************************************** //
117 
118 namespace fpc_detail {
119 
120 // FPT is Floating-Point Type: float, double, long double or User-Defined.
121 template<typename FPT>
122 inline FPT
fpt_abs(FPT fpv)123 fpt_abs( FPT fpv )
124 {
125     return fpv < static_cast<FPT>(0) ? -fpv : fpv;
126 }
127 
128 //____________________________________________________________________________//
129 
130 template<typename FPT>
131 struct fpt_specialized_limits
132 {
min_valueboost::math::fpc::fpc_detail::fpt_specialized_limits133   static FPT    min_value() { return (std::numeric_limits<FPT>::min)(); }
max_valueboost::math::fpc::fpc_detail::fpt_specialized_limits134   static FPT    max_value() { return (std::numeric_limits<FPT>::max)(); }
135 };
136 
137 template<typename FPT>
138 struct fpt_non_specialized_limits
139 {
min_valueboost::math::fpc::fpc_detail::fpt_non_specialized_limits140   static FPT    min_value() { return static_cast<FPT>(0); }
max_valueboost::math::fpc::fpc_detail::fpt_non_specialized_limits141   static FPT    max_value() { return static_cast<FPT>(1000000); } // for our purposes it doesn't really matter what value is returned here
142 };
143 
144 template<typename FPT>
145 struct fpt_limits : boost::conditional<std::numeric_limits<FPT>::is_specialized,
146                                        fpt_specialized_limits<FPT>,
147                                        fpt_non_specialized_limits<FPT>
148                                       >::type
149 {};
150 
151 //____________________________________________________________________________//
152 
153 // both f1 and f2 are unsigned here
154 template<typename FPT>
155 inline FPT
safe_fpt_division(FPT f1,FPT f2)156 safe_fpt_division( FPT f1, FPT f2 )
157 {
158     // Avoid overflow.
159     if( (f2 < static_cast<FPT>(1))  && (f1 > f2*fpt_limits<FPT>::max_value()) )
160         return fpt_limits<FPT>::max_value();
161 
162     // Avoid underflow.
163     if( (f1 == static_cast<FPT>(0)) ||
164         ((f2 > static_cast<FPT>(1)) && (f1 < f2*fpt_limits<FPT>::min_value())) )
165         return static_cast<FPT>(0);
166 
167     return f1/f2;
168 }
169 
170 //____________________________________________________________________________//
171 
172 template<typename FPT, typename ToleranceType>
173 inline FPT
fraction_tolerance(ToleranceType tolerance)174 fraction_tolerance( ToleranceType tolerance )
175 {
176   return static_cast<FPT>(tolerance);
177 }
178 
179 //____________________________________________________________________________//
180 
181 template<typename FPT2, typename FPT>
182 inline FPT2
fraction_tolerance(percent_tolerance_t<FPT> tolerance)183 fraction_tolerance( percent_tolerance_t<FPT> tolerance )
184 {
185     return FPT2(tolerance.m_value)*FPT2(0.01);
186 }
187 
188 //____________________________________________________________________________//
189 
190 } // namespace fpc_detail
191 
192 // ************************************************************************** //
193 // **************             close_at_tolerance               ************** //
194 // ************************************************************************** //
195 
196 
197 /*!@brief Predicate for comparing floating point numbers
198  *
199  * This predicate is used to compare floating point numbers. In addition the comparison produces maximum
200  * related differnce, which can be used to generate detailed error message
201  * The methods for comparing floating points are detailed in the documentation. The method is chosen
202  * by the @ref boost::math::fpc::strength given at construction.
203  */
204 template<typename FPT>
205 class close_at_tolerance {
206 public:
207     // Public typedefs
208     typedef bool result_type;
209 
210     // Constructor
211     template<typename ToleranceType>
close_at_tolerance(ToleranceType tolerance,fpc::strength fpc_strength=FPC_STRONG)212     explicit    close_at_tolerance( ToleranceType tolerance, fpc::strength fpc_strength = FPC_STRONG )
213     : m_fraction_tolerance( fpc_detail::fraction_tolerance<FPT>( tolerance ) )
214     , m_strength( fpc_strength )
215     , m_tested_rel_diff( 0 )
216     {
217         BOOST_ASSERT_MSG( m_fraction_tolerance >= FPT(0), "tolerance must not be negative!" ); // no reason for tolerance to be negative
218     }
219 
220     // Access methods
221     //! Returns the tolerance
fraction_tolerance() const222     FPT                 fraction_tolerance() const  { return m_fraction_tolerance; }
223 
224     //! Returns the comparison method
strength() const225     fpc::strength       strength() const            { return m_strength; }
226 
227     //! Returns the failing fraction
tested_rel_diff() const228     FPT                 tested_rel_diff() const     { return m_tested_rel_diff; }
229 
230     /*! Compares two floating point numbers a and b such that their "left" relative difference |a-b|/a and/or
231      * "right" relative difference |a-b|/b does not exceed specified relative (fraction) tolerance.
232      *
233      *  @param[in] left first floating point number to be compared
234      *  @param[in] right second floating point number to be compared
235      *
236      * What is reported by @c tested_rel_diff in case of failure depends on the comparison method:
237      * - for @c FPC_STRONG: the max of the two fractions
238      * - for @c FPC_WEAK: the min of the two fractions
239      * The rationale behind is to report the tolerance to set in order to make a test pass.
240      */
operator ()(FPT left,FPT right) const241     bool                operator()( FPT left, FPT right ) const
242     {
243         FPT diff              = fpc_detail::fpt_abs<FPT>( left - right );
244         FPT fraction_of_right = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( right ) );
245         FPT fraction_of_left  = fpc_detail::safe_fpt_division( diff, fpc_detail::fpt_abs( left ) );
246 
247         FPT max_rel_diff = (std::max)( fraction_of_left, fraction_of_right );
248         FPT min_rel_diff = (std::min)( fraction_of_left, fraction_of_right );
249 
250         m_tested_rel_diff = m_strength == FPC_STRONG ? max_rel_diff : min_rel_diff;
251 
252         return m_tested_rel_diff <= m_fraction_tolerance;
253     }
254 
255 private:
256     // Data members
257     FPT                 m_fraction_tolerance;
258     fpc::strength       m_strength;
259     mutable FPT         m_tested_rel_diff;
260 };
261 
262 // ************************************************************************** //
263 // **************            small_with_tolerance              ************** //
264 // ************************************************************************** //
265 
266 
267 /*!@brief Predicate for comparing floating point numbers against 0
268  *
269  * Serves the same purpose as boost::math::fpc::close_at_tolerance, but used when one
270  * of the operand is null.
271  */
272 template<typename FPT>
273 class small_with_tolerance {
274 public:
275     // Public typedefs
276     typedef bool result_type;
277 
278     // Constructor
small_with_tolerance(FPT tolerance)279     explicit    small_with_tolerance( FPT tolerance ) // <= absolute tolerance
280     : m_tolerance( tolerance )
281     {
282         BOOST_ASSERT( m_tolerance >= FPT(0) ); // no reason for the tolerance to be negative
283     }
284 
285     // Action method
operator ()(FPT fpv) const286     bool        operator()( FPT fpv ) const
287     {
288         return fpc::fpc_detail::fpt_abs( fpv ) < m_tolerance;
289     }
290 
291 private:
292     // Data members
293     FPT         m_tolerance;
294 };
295 
296 // ************************************************************************** //
297 // **************                  is_small                    ************** //
298 // ************************************************************************** //
299 
300 template<typename FPT>
301 inline bool
is_small(FPT fpv,FPT tolerance)302 is_small( FPT fpv, FPT tolerance )
303 {
304     return small_with_tolerance<FPT>( tolerance )( fpv );
305 }
306 
307 //____________________________________________________________________________//
308 
309 } // namespace fpc
310 } // namespace math
311 } // namespace boost
312 
313 #include <boost/test/detail/enable_warnings.hpp>
314 
315 #endif // BOOST_FLOATING_POINT_COMAPARISON_HPP_071894GER
316