From c974e3097460a8b47d2db00ce4ee9bbb7a19cef9 Mon Sep 17 00:00:00 2001 From: Chris Thrasher Date: Thu, 16 May 2024 13:23:38 -0600 Subject: [PATCH] Allow for `Catch::Approx` to be used in a `constexpr` context --- CMake/CatchMiscFunctions.cmake | 1 - src/catch2/catch_approx.cpp | 69 ++------------------------ src/catch2/catch_approx.hpp | 89 +++++++++++++++++++++++++--------- 3 files changed, 72 insertions(+), 87 deletions(-) diff --git a/CMake/CatchMiscFunctions.cmake b/CMake/CatchMiscFunctions.cmake index 05bc83c021..38ebf8e20c 100644 --- a/CMake/CatchMiscFunctions.cmake +++ b/CMake/CatchMiscFunctions.cmake @@ -56,7 +56,6 @@ function(add_warnings_to_targets targets) "-Wexit-time-destructors" "-Wextra" "-Wextra-semi" - "-Wfloat-equal" "-Wglobal-constructors" "-Winit-self" "-Wmisleading-indentation" diff --git a/src/catch2/catch_approx.cpp b/src/catch2/catch_approx.cpp index 08c6799b13..5d355efbfe 100644 --- a/src/catch2/catch_approx.cpp +++ b/src/catch2/catch_approx.cpp @@ -10,73 +10,14 @@ #include #include -#include - -namespace { - -// Performs equivalent check of std::fabs(lhs - rhs) <= margin -// But without the subtraction to allow for INFINITY in comparison -bool marginComparison(double lhs, double rhs, double margin) { - return (lhs + margin >= rhs) && (rhs + margin >= lhs); -} - -} namespace Catch { - Approx::Approx ( double value ) - : m_epsilon( static_cast(std::numeric_limits::epsilon())*100. ), - m_margin( 0.0 ), - m_scale( 0.0 ), - m_value( value ) - {} - - Approx Approx::custom() { - return Approx( 0 ); - } - - Approx Approx::operator-() const { - auto temp(*this); - temp.m_value = -temp.m_value; - return temp; - } - - - std::string Approx::toString() const { - ReusableStringStream rss; - rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; - return rss.str(); - } - - bool Approx::equalityComparisonImpl(const 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 + std::fabs(std::isinf(m_value)? 0 : m_value))); - } - - void Approx::setMargin(double newMargin) { - CATCH_ENFORCE(newMargin >= 0, - "Invalid Approx::margin: " << newMargin << '.' - << " Approx::Margin has to be non-negative."); - m_margin = newMargin; - } - - void Approx::setEpsilon(double newEpsilon) { - CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0, - "Invalid Approx::epsilon: " << newEpsilon << '.' - << " Approx::epsilon has to be in [0, 1]"); - m_epsilon = newEpsilon; - } - -namespace literals { - Approx operator ""_a(long double val) { - return Approx(val); - } - Approx operator ""_a(unsigned long long val) { - return Approx(val); - } -} // end namespace literals +std::string Approx::toString() const { + ReusableStringStream rss; + rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )"; + return rss.str(); +} std::string StringMaker::convert(Catch::Approx const& value) { return value.toString(); diff --git a/src/catch2/catch_approx.hpp b/src/catch2/catch_approx.hpp index de4d2ab4ca..831604bc2e 100644 --- a/src/catch2/catch_approx.hpp +++ b/src/catch2/catch_approx.hpp @@ -11,26 +11,67 @@ #include #include +#include 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::infinity() || value == -std::numeric_limits::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(std::numeric_limits::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 ::value>> - Approx operator()( T const& value ) const { + constexpr Approx operator()( T const& value ) const { Approx approx( static_cast(value) ); approx.m_epsilon = m_epsilon; approx.m_margin = m_margin; @@ -39,67 +80,67 @@ namespace Catch { } template ::value>> - explicit Approx( T const& value ): Approx(static_cast(value)) + constexpr inline explicit Approx( T const& value ): Approx(static_cast(value)) {} template ::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(lhs); return rhs.equalityComparisonImpl(lhs_v); } template ::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 ::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 ::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 ::value>> - friend bool operator <= ( T const& lhs, Approx const& rhs ) { + friend constexpr bool operator <= ( T const& lhs, Approx const& rhs ) { return static_cast(lhs) < rhs.m_value || lhs == rhs; } template ::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(rhs) || lhs == rhs; } template ::value>> - friend bool operator >= ( T const& lhs, Approx const& rhs ) { + friend constexpr bool operator >= ( T const& lhs, Approx const& rhs ) { return static_cast(lhs) > rhs.m_value || lhs == rhs; } template ::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(rhs) || lhs == rhs; } template ::value>> - Approx& epsilon( T const& newEpsilon ) { + constexpr Approx& epsilon( T const& newEpsilon ) { const auto epsilonAsDouble = static_cast(newEpsilon); setEpsilon(epsilonAsDouble); return *this; } template ::value>> - Approx& margin( T const& newMargin ) { + constexpr Approx& margin( T const& newMargin ) { const auto marginAsDouble = static_cast(newMargin); setMargin(marginAsDouble); return *this; } template ::value>> - Approx& scale( T const& newScale ) { + constexpr Approx& scale( T const& newScale ) { m_scale = static_cast(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<>