1 /*
2  *  Created by Phil on 28/04/2011.
3  *  Copyright 2010 Two Blue Cubes Ltd. All rights reserved.
4  *
5  *  Distributed under the Boost Software License, Version 1.0. (See accompanying
6  *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
7  */
8 #ifndef TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
9 #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
10 
11 #include "catch_tostring.h"
12 
13 #include <cmath>
14 #include <limits>
15 
16 #if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
17 #include <type_traits>
18 #endif
19 
20 namespace Catch {
21 namespace Detail {
22 
23     class Approx {
24     public:
Approx(double value)25         explicit Approx ( double value )
26         :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
27             m_margin( 0.0 ),
28             m_scale( 1.0 ),
29             m_value( value )
30         {}
31 
custom()32         static Approx custom() {
33             return Approx( 0 );
34         }
35 
36 #if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
37 
38         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator ()(T value)39         Approx operator()( T value ) {
40             Approx approx( static_cast<double>(value) );
41             approx.epsilon( m_epsilon );
42             approx.margin( m_margin );
43             approx.scale( m_scale );
44             return approx;
45         }
46 
47         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
Approx(T value)48         explicit Approx( T value ): Approx(static_cast<double>(value))
49         {}
50 
51 
52         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator ==(const T & lhs,Approx const & rhs)53         friend bool operator == ( const T& lhs, Approx const& rhs ) {
54             // Thanks to Richard Harris for his help refining this formula
55             auto lhs_v = double(lhs);
56             bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
57             if (relativeOK) {
58                 return true;
59             }
60 
61             return std::fabs(lhs_v - rhs.m_value) <= rhs.m_margin;
62         }
63 
64         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator ==(Approx const & lhs,const T & rhs)65         friend bool operator == ( Approx const& lhs, const T& rhs ) {
66             return operator==( rhs, lhs );
67         }
68 
69         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator !=(T lhs,Approx const & rhs)70         friend bool operator != ( T lhs, Approx const& rhs ) {
71             return !operator==( lhs, rhs );
72         }
73 
74         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator !=(Approx const & lhs,T rhs)75         friend bool operator != ( Approx const& lhs, T rhs ) {
76             return !operator==( rhs, lhs );
77         }
78 
79         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator <=(T lhs,Approx const & rhs)80         friend bool operator <= ( T lhs, Approx const& rhs ) {
81             return double(lhs) < rhs.m_value || lhs == rhs;
82         }
83 
84         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator <=(Approx const & lhs,T rhs)85         friend bool operator <= ( Approx const& lhs, T rhs ) {
86             return lhs.m_value < double(rhs) || lhs == rhs;
87         }
88 
89         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator >=(T lhs,Approx const & rhs)90         friend bool operator >= ( T lhs, Approx const& rhs ) {
91             return double(lhs) > rhs.m_value || lhs == rhs;
92         }
93 
94         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
operator >=(Approx const & lhs,T rhs)95         friend bool operator >= ( Approx const& lhs, T rhs ) {
96             return lhs.m_value > double(rhs) || lhs == rhs;
97         }
98 
99         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
epsilon(T newEpsilon)100         Approx& epsilon( T newEpsilon ) {
101             m_epsilon = double(newEpsilon);
102             return *this;
103         }
104 
105         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
margin(T newMargin)106         Approx& margin( T newMargin ) {
107             m_margin = double(newMargin);
108             return *this;
109         }
110 
111         template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
scale(T newScale)112         Approx& scale( T newScale ) {
113             m_scale = double(newScale);
114             return *this;
115         }
116 
117 #else
118 
operator ()(double value)119         Approx operator()( double value ) {
120             Approx approx( value );
121             approx.epsilon( m_epsilon );
122             approx.margin( m_margin );
123             approx.scale( m_scale );
124             return approx;
125         }
126 
127 
operator ==(double lhs,Approx const & rhs)128         friend bool operator == ( double lhs, Approx const& rhs ) {
129             // Thanks to Richard Harris for his help refining this formula
130             bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
131             if (relativeOK) {
132                 return true;
133             }
134             return std::fabs(lhs - rhs.m_value) <= rhs.m_margin;
135         }
136 
operator ==(Approx const & lhs,double rhs)137         friend bool operator == ( Approx const& lhs, double rhs ) {
138             return operator==( rhs, lhs );
139         }
140 
operator !=(double lhs,Approx const & rhs)141         friend bool operator != ( double lhs, Approx const& rhs ) {
142             return !operator==( lhs, rhs );
143         }
144 
operator !=(Approx const & lhs,double rhs)145         friend bool operator != ( Approx const& lhs, double rhs ) {
146             return !operator==( rhs, lhs );
147         }
148 
operator <=(double lhs,Approx const & rhs)149         friend bool operator <= ( double lhs, Approx const& rhs ) {
150             return lhs < rhs.m_value || lhs == rhs;
151         }
152 
operator <=(Approx const & lhs,double rhs)153         friend bool operator <= ( Approx const& lhs, double rhs ) {
154             return lhs.m_value < rhs || lhs == rhs;
155         }
156 
operator >=(double lhs,Approx const & rhs)157         friend bool operator >= ( double lhs, Approx const& rhs ) {
158             return lhs > rhs.m_value || lhs == rhs;
159         }
160 
operator >=(Approx const & lhs,double rhs)161         friend bool operator >= ( Approx const& lhs, double rhs ) {
162             return lhs.m_value > rhs || lhs == rhs;
163         }
164 
epsilon(double newEpsilon)165         Approx& epsilon( double newEpsilon ) {
166             m_epsilon = newEpsilon;
167             return *this;
168         }
169 
margin(double newMargin)170         Approx& margin( double newMargin ) {
171             m_margin = newMargin;
172             return *this;
173         }
174 
scale(double newScale)175         Approx& scale( double newScale ) {
176             m_scale = newScale;
177             return *this;
178         }
179 #endif
180 
toString() const181         std::string toString() const {
182             std::ostringstream oss;
183             oss << "Approx( " << Catch::toString( m_value ) << " )";
184             return oss.str();
185         }
186 
187     private:
188         double m_epsilon;
189         double m_margin;
190         double m_scale;
191         double m_value;
192     };
193 }
194 
195 template<>
toString(Detail::Approx const & value)196 inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
197     return value.toString();
198 }
199 
200 } // end namespace Catch
201 
202 #endif // TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
203