11
11
#include < catch2/catch_tostring.hpp>
12
12
13
13
#include < type_traits>
14
+ #include < limits>
14
15
15
16
namespace Catch {
16
17
17
18
class Approx {
18
19
private:
19
- bool equalityComparisonImpl (double other) const ;
20
- // Sets and validates the new margin (margin >= 0)
21
- void setMargin (double margin);
20
+ // Performs equivalent check of std::fabs(lhs - rhs) <= margin
21
+ // But without the subtraction to allow for INFINITY in comparison
22
+ constexpr bool marginComparison (double lhs, double rhs, double margin) const {
23
+ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
24
+ }
25
+
26
+ constexpr double fabs (double value) const {
27
+ return (value < 0.0 ) ? -value : value;
28
+ }
29
+
30
+ constexpr bool isinf (double value) const {
31
+ return value == std::numeric_limits<double >::infinity () || value == -std::numeric_limits<double >::infinity ();
32
+ }
33
+
34
+ constexpr bool equalityComparisonImpl (double other) const {
35
+ // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
36
+ // Thanks to Richard Harris for his help refining the scaled margin value
37
+ return marginComparison (m_value, other, m_margin)
38
+ || marginComparison (m_value, other, m_epsilon * (m_scale + fabs (isinf (m_value)? 0 : m_value)));
39
+ }
40
+
22
41
// Sets and validates the new epsilon (0 < epsilon < 1)
23
- void setEpsilon (double epsilon);
42
+ constexpr void setEpsilon (double epsilon) {
43
+ // CATCH_ENFORCE(epsilon >= 0 && epsilon <= 1.0,
44
+ // "Invalid Approx::epsilon: " << epsilon << '.'
45
+ // << " Approx::epsilon has to be in [0, 1]");
46
+ m_epsilon = epsilon;
47
+ }
48
+
49
+ // Sets and validates the new margin (margin >= 0)
50
+ constexpr void setMargin (double margin) {
51
+ // CATCH_ENFORCE(margin >= 0,
52
+ // "Invalid Approx::margin: " << margin << '.'
53
+ // << " Approx::Margin has to be non-negative.");
54
+ m_margin = margin;
55
+ }
24
56
25
57
public:
26
- explicit Approx ( double value );
58
+ constexpr inline explicit Approx ( double value )
59
+ : m_epsilon( static_cast <double >(std::numeric_limits<float >::epsilon())*100. ),
60
+ m_margin( 0.0 ),
61
+ m_scale( 0.0 ),
62
+ m_value( value )
63
+ {}
27
64
28
- static Approx custom ();
65
+ static constexpr Approx custom () {
66
+ return Approx ( 0 );
67
+ }
29
68
30
- Approx operator -() const ;
69
+ constexpr Approx operator -() const {
70
+ auto temp (*this );
71
+ temp.m_value = -temp.m_value ;
72
+ return temp;
73
+ }
31
74
32
75
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
33
- Approx operator ()( T const & value ) const {
76
+ constexpr Approx operator ()( T const & value ) const {
34
77
Approx approx ( static_cast <double >(value) );
35
78
approx.m_epsilon = m_epsilon;
36
79
approx.m_margin = m_margin;
@@ -39,67 +82,67 @@ namespace Catch {
39
82
}
40
83
41
84
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
42
- explicit Approx ( T const & value ): Approx(static_cast <double >(value))
85
+ constexpr inline explicit Approx ( T const & value ): Approx(static_cast <double >(value))
43
86
{}
44
87
45
88
46
89
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
47
- friend bool operator == ( const T& lhs, Approx const & rhs ) {
90
+ friend constexpr bool operator == ( const T& lhs, Approx const & rhs ) {
48
91
auto lhs_v = static_cast <double >(lhs);
49
92
return rhs.equalityComparisonImpl (lhs_v);
50
93
}
51
94
52
95
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
53
- friend bool operator == ( Approx const & lhs, const T& rhs ) {
96
+ friend constexpr bool operator == ( Approx const & lhs, const T& rhs ) {
54
97
return operator ==( rhs, lhs );
55
98
}
56
99
57
100
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
58
- friend bool operator != ( T const & lhs, Approx const & rhs ) {
101
+ friend constexpr bool operator != ( T const & lhs, Approx const & rhs ) {
59
102
return !operator ==( lhs, rhs );
60
103
}
61
104
62
105
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
63
- friend bool operator != ( Approx const & lhs, T const & rhs ) {
106
+ friend constexpr bool operator != ( Approx const & lhs, T const & rhs ) {
64
107
return !operator ==( rhs, lhs );
65
108
}
66
109
67
110
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
68
- friend bool operator <= ( T const & lhs, Approx const & rhs ) {
111
+ friend constexpr bool operator <= ( T const & lhs, Approx const & rhs ) {
69
112
return static_cast <double >(lhs) < rhs.m_value || lhs == rhs;
70
113
}
71
114
72
115
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
73
- friend bool operator <= ( Approx const & lhs, T const & rhs ) {
116
+ friend constexpr bool operator <= ( Approx const & lhs, T const & rhs ) {
74
117
return lhs.m_value < static_cast <double >(rhs) || lhs == rhs;
75
118
}
76
119
77
120
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
78
- friend bool operator >= ( T const & lhs, Approx const & rhs ) {
121
+ friend constexpr bool operator >= ( T const & lhs, Approx const & rhs ) {
79
122
return static_cast <double >(lhs) > rhs.m_value || lhs == rhs;
80
123
}
81
124
82
125
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
83
- friend bool operator >= ( Approx const & lhs, T const & rhs ) {
126
+ friend constexpr bool operator >= ( Approx const & lhs, T const & rhs ) {
84
127
return lhs.m_value > static_cast <double >(rhs) || lhs == rhs;
85
128
}
86
129
87
130
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
88
- Approx& epsilon ( T const & newEpsilon ) {
131
+ constexpr Approx& epsilon ( T const & newEpsilon ) {
89
132
const auto epsilonAsDouble = static_cast <double >(newEpsilon);
90
133
setEpsilon (epsilonAsDouble);
91
134
return *this ;
92
135
}
93
136
94
137
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
95
- Approx& margin ( T const & newMargin ) {
138
+ constexpr Approx& margin ( T const & newMargin ) {
96
139
const auto marginAsDouble = static_cast <double >(newMargin);
97
140
setMargin (marginAsDouble);
98
141
return *this ;
99
142
}
100
143
101
144
template <typename T, typename = std::enable_if_t <std::is_constructible<double , T>::value>>
102
- Approx& scale ( T const & newScale ) {
145
+ constexpr Approx& scale ( T const & newScale ) {
103
146
m_scale = static_cast <double >(newScale);
104
147
return *this ;
105
148
}
@@ -114,8 +157,12 @@ namespace Catch {
114
157
};
115
158
116
159
namespace literals {
117
- Approx operator " " _a(long double val);
118
- Approx operator " " _a(unsigned long long val);
160
+ constexpr Approx operator " " _a(long double val) {
161
+ return Approx (val);
162
+ }
163
+ constexpr Approx operator " " _a(unsigned long long val) {
164
+ return Approx (val);
165
+ }
119
166
} // end namespace literals
120
167
121
168
template <>
0 commit comments