mirror of
https://github.com/catchorg/Catch2.git
synced 2024-12-25 12:33:29 +01:00
Allow for Catch::Approx
to be used in a constexpr
context
This commit is contained in:
parent
4e8d92bf02
commit
c974e30974
@ -56,7 +56,6 @@ function(add_warnings_to_targets targets)
|
|||||||
"-Wexit-time-destructors"
|
"-Wexit-time-destructors"
|
||||||
"-Wextra"
|
"-Wextra"
|
||||||
"-Wextra-semi"
|
"-Wextra-semi"
|
||||||
"-Wfloat-equal"
|
|
||||||
"-Wglobal-constructors"
|
"-Wglobal-constructors"
|
||||||
"-Winit-self"
|
"-Winit-self"
|
||||||
"-Wmisleading-indentation"
|
"-Wmisleading-indentation"
|
||||||
|
@ -10,73 +10,14 @@
|
|||||||
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
#include <catch2/internal/catch_reusable_string_stream.hpp>
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
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 {
|
namespace Catch {
|
||||||
|
|
||||||
Approx::Approx ( double value )
|
std::string Approx::toString() const {
|
||||||
: m_epsilon( static_cast<double>(std::numeric_limits<float>::epsilon())*100. ),
|
ReusableStringStream rss;
|
||||||
m_margin( 0.0 ),
|
rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
|
||||||
m_scale( 0.0 ),
|
return rss.str();
|
||||||
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 StringMaker<Catch::Approx>::convert(Catch::Approx const& value) {
|
std::string StringMaker<Catch::Approx>::convert(Catch::Approx const& value) {
|
||||||
return value.toString();
|
return value.toString();
|
||||||
|
@ -11,26 +11,67 @@
|
|||||||
#include <catch2/catch_tostring.hpp>
|
#include <catch2/catch_tostring.hpp>
|
||||||
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace Catch {
|
namespace Catch {
|
||||||
|
|
||||||
class Approx {
|
class Approx {
|
||||||
private:
|
private:
|
||||||
bool equalityComparisonImpl(double other) const;
|
// Performs equivalent check of std::fabs(lhs - rhs) <= margin
|
||||||
// Sets and validates the new margin (margin >= 0)
|
// But without the subtraction to allow for INFINITY in comparison
|
||||||
void setMargin(double margin);
|
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)
|
// 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:
|
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>>
|
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 approx( static_cast<double>(value) );
|
||||||
approx.m_epsilon = m_epsilon;
|
approx.m_epsilon = m_epsilon;
|
||||||
approx.m_margin = m_margin;
|
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>>
|
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>>
|
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);
|
auto lhs_v = static_cast<double>(lhs);
|
||||||
return rhs.equalityComparisonImpl(lhs_v);
|
return rhs.equalityComparisonImpl(lhs_v);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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 );
|
return operator==( rhs, lhs );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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 );
|
return !operator==( lhs, rhs );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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 );
|
return !operator==( rhs, lhs );
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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;
|
return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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;
|
return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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;
|
return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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;
|
return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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);
|
const auto epsilonAsDouble = static_cast<double>(newEpsilon);
|
||||||
setEpsilon(epsilonAsDouble);
|
setEpsilon(epsilonAsDouble);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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);
|
const auto marginAsDouble = static_cast<double>(newMargin);
|
||||||
setMargin(marginAsDouble);
|
setMargin(marginAsDouble);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
|
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);
|
m_scale = static_cast<double>(newScale);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -114,8 +155,12 @@ namespace Catch {
|
|||||||
};
|
};
|
||||||
|
|
||||||
namespace literals {
|
namespace literals {
|
||||||
Approx operator ""_a(long double val);
|
constexpr Approx operator ""_a(long double val) {
|
||||||
Approx operator ""_a(unsigned long long val);
|
return Approx(val);
|
||||||
|
}
|
||||||
|
constexpr Approx operator ""_a(unsigned long long val) {
|
||||||
|
return Approx(val);
|
||||||
|
}
|
||||||
} // end namespace literals
|
} // end namespace literals
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
|
Loading…
Reference in New Issue
Block a user