Read-only mirrors. Development, issues and pull requests are on GitHub.

summaryrefslogtreecommitdiffhomepage
path: root/src/catch2/catch_approx.hpp
diff options
context:
space:
mode:
authorChris Thrasher2024-05-16 13:23:38 -0600
committerChris Thrasher2024-05-18 09:42:42 -0600
commitc974e3097460a8b47d2db00ce4ee9bbb7a19cef9 (patch)
treeb0eeb90ba10c7118042ded4a3f5a75c87364beee /src/catch2/catch_approx.hpp
parent4e8d92bf02f7d1c8006a0e7a5ecabd8e62d98502 (diff)
downloadcatch3-constexpr_approx.tar.gz
Allow for `Catch::Approx` to be used in a `constexpr` contextconstexpr_approx
Diffstat (limited to 'src/catch2/catch_approx.hpp')
-rw-r--r--src/catch2/catch_approx.hpp89
1 files changed, 67 insertions, 22 deletions
diff --git a/src/catch2/catch_approx.hpp b/src/catch2/catch_approx.hpp
index de4d2ab4..831604bc 100644
--- a/src/catch2/catch_approx.hpp
+++ b/src/catch2/catch_approx.hpp
@@ -11,26 +11,67 @@
#include <catch2/catch_tostring.hpp>
#include <type_traits>
+#include <limits>
namespace Catch {
class Approx {
private:
- bool equalityComparisonImpl(double other) const;
- // Sets and validates the new margin (margin >= 0)
- void setMargin(double margin);
+ // Performs equivalent check of std::fabs(lhs - rhs) <= margin
+ // But without the subtraction to allow for INFINITY in comparison
+ constexpr bool marginComparison (double lhs, double rhs, double margin) const {
+ return (lhs + margin >= rhs) && (rhs + margin >= lhs);
+ }
+
+ constexpr double fabs(double value) const {
+ return (value < 0.0) ? -value : value;
+ }
+
+ constexpr bool isinf(double value) const {
+ return value == std::numeric_limits<double>::infinity() || value == -std::numeric_limits<double>::infinity();
+ }
+
+ constexpr bool equalityComparisonImpl(double other) const {
+ // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
+ // Thanks to Richard Harris for his help refining the scaled margin value
+ return marginComparison(m_value, other, m_margin)
+ || marginComparison(m_value, other, m_epsilon * (m_scale + fabs(isinf(m_value)? 0 : m_value)));
+ }
+
// Sets and validates the new epsilon (0 < epsilon < 1)
- void setEpsilon(double epsilon);
+ constexpr void setEpsilon(double epsilon) {
+ if(epsilon < 0)
+ throw std::domain_error("Invalid Approx::epsilon. Approx::epsilon has to be in [0, 1]");
+ m_epsilon = epsilon;
+ }
+
+ // Sets and validates the new margin (margin >= 0)
+ constexpr void setMargin(double margin) {
+ if(margin < 0)
+ throw std::domain_error("Invalid Approx::margin. Approx::Margin has to be non-negative.");
+ m_margin = margin;
+ }
public:
- explicit Approx ( double value );
+ constexpr inline explicit Approx ( double value )
+ : m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ),
+ m_margin( 0.0 ),
+ m_scale( 0.0 ),
+ m_value( value )
+ {}
- static Approx custom();
+ static constexpr Approx custom() {
+ return Approx( 0.0 );
+ }
- Approx operator-() const;
+ constexpr Approx operator-() const {
+ auto temp(*this);
+ temp.m_value = -temp.m_value;
+ return temp;
+ }
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- Approx operator()( T const& value ) const {
+ constexpr Approx operator()( T const& value ) const {
Approx approx( static_cast<double>(value) );
approx.m_epsilon = m_epsilon;
approx.m_margin = m_margin;
@@ -39,67 +80,67 @@ namespace Catch {
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- explicit Approx( T const& value ): Approx(static_cast<double>(value))
+ constexpr inline explicit Approx( T const& value ): Approx(static_cast<double>(value))
{}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator == ( const T& lhs, Approx const& rhs ) {
+ friend constexpr bool operator == ( const T& lhs, Approx const& rhs ) {
auto lhs_v = static_cast<double>(lhs);
return rhs.equalityComparisonImpl(lhs_v);
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator == ( Approx const& lhs, const T& rhs ) {
+ friend constexpr bool operator == ( Approx const& lhs, const T& rhs ) {
return operator==( rhs, lhs );
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator != ( T const& lhs, Approx const& rhs ) {
+ friend constexpr bool operator != ( T const& lhs, Approx const& rhs ) {
return !operator==( lhs, rhs );
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator != ( Approx const& lhs, T const& rhs ) {
+ friend constexpr bool operator != ( Approx const& lhs, T const& rhs ) {
return !operator==( rhs, lhs );
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator <= ( T const& lhs, Approx const& rhs ) {
+ friend constexpr bool operator <= ( T const& lhs, Approx const& rhs ) {
return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator <= ( Approx const& lhs, T const& rhs ) {
+ friend constexpr bool operator <= ( Approx const& lhs, T const& rhs ) {
return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator >= ( T const& lhs, Approx const& rhs ) {
+ friend constexpr bool operator >= ( T const& lhs, Approx const& rhs ) {
return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- friend bool operator >= ( Approx const& lhs, T const& rhs ) {
+ friend constexpr bool operator >= ( Approx const& lhs, T const& rhs ) {
return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- Approx& epsilon( T const& newEpsilon ) {
+ constexpr Approx& epsilon( T const& newEpsilon ) {
const auto epsilonAsDouble = static_cast<double>(newEpsilon);
setEpsilon(epsilonAsDouble);
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- Approx& margin( T const& newMargin ) {
+ constexpr Approx& margin( T const& newMargin ) {
const auto marginAsDouble = static_cast<double>(newMargin);
setMargin(marginAsDouble);
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
- Approx& scale( T const& newScale ) {
+ constexpr Approx& scale( T const& newScale ) {
m_scale = static_cast<double>(newScale);
return *this;
}
@@ -114,8 +155,12 @@ namespace Catch {
};
namespace literals {
- Approx operator ""_a(long double val);
- Approx operator ""_a(unsigned long long val);
+ constexpr Approx operator ""_a(long double val) {
+ return Approx(val);
+ }
+ constexpr Approx operator ""_a(unsigned long long val) {
+ return Approx(val);
+ }
} // end namespace literals
template<>