mirror of
				https://github.com/catchorg/Catch2.git
				synced 2025-11-04 05:59:32 +01:00 
			
		
		
		
	Allow full range of target ULP values for the ULPMatcher
Previously it was limited to roughly 2 billion ULPs, rather than the roughly 2^64 possible ones.
This commit is contained in:
		@@ -34,34 +34,22 @@ enum class FloatingPointKind : uint8_t {
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct Converter;
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct Converter<float> {
 | 
			
		||||
int32_t convert(float f) {
 | 
			
		||||
    static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
 | 
			
		||||
    Converter(float f) {
 | 
			
		||||
        std::memcpy(&i, &f, sizeof(f));
 | 
			
		||||
    }
 | 
			
		||||
    int32_t i;
 | 
			
		||||
};
 | 
			
		||||
    std::memcpy(&i, &f, sizeof(f));
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct Converter<double> {
 | 
			
		||||
int64_t convert(double d) {
 | 
			
		||||
    static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
 | 
			
		||||
    Converter(double d) {
 | 
			
		||||
        std::memcpy(&i, &d, sizeof(d));
 | 
			
		||||
    }
 | 
			
		||||
    int64_t i;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
auto convert(T t) -> Converter<T> {
 | 
			
		||||
    return Converter<T>(t);
 | 
			
		||||
    std::memcpy(&i, &d, sizeof(d));
 | 
			
		||||
    return i;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename FP>
 | 
			
		||||
bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
 | 
			
		||||
bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
 | 
			
		||||
    // Comparison with NaN should always be false.
 | 
			
		||||
    // This way we can rule it out before getting into the ugly details
 | 
			
		||||
    if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
 | 
			
		||||
@@ -71,13 +59,13 @@ bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
 | 
			
		||||
    auto lc = convert(lhs);
 | 
			
		||||
    auto rc = convert(rhs);
 | 
			
		||||
 | 
			
		||||
    if ((lc.i < 0) != (rc.i < 0)) {
 | 
			
		||||
    if ((lc < 0) != (rc < 0)) {
 | 
			
		||||
        // Potentially we can have +0 and -0
 | 
			
		||||
        return lhs == rhs;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    auto ulpDiff = std::abs(lc.i - rc.i);
 | 
			
		||||
    return ulpDiff <= maxUlpDiff;
 | 
			
		||||
    auto ulpDiff = std::abs(lc - rc);
 | 
			
		||||
    return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
 | 
			
		||||
@@ -99,8 +87,8 @@ namespace Catch {
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
template <typename FP>
 | 
			
		||||
FP step(FP start, FP direction, int steps) {
 | 
			
		||||
    for (int i = 0; i < steps; ++i) {
 | 
			
		||||
FP step(FP start, FP direction, uint64_t steps) {
 | 
			
		||||
    for (uint64_t i = 0; i < steps; ++i) {
 | 
			
		||||
#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
 | 
			
		||||
        start = Catch::nextafter(start, direction);
 | 
			
		||||
#else
 | 
			
		||||
@@ -133,10 +121,11 @@ namespace Floating {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
 | 
			
		||||
    WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)
 | 
			
		||||
        :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
 | 
			
		||||
        CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
 | 
			
		||||
            << " ULPs have to be non-negative.");
 | 
			
		||||
        CATCH_ENFORCE(m_type == FloatingPointKind::Double
 | 
			
		||||
                   || m_ulps < std::numeric_limits<uint32_t>::max(),
 | 
			
		||||
            "Provided ULP is impossibly large for a float comparison.");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#if defined(__clang__)
 | 
			
		||||
@@ -190,11 +179,11 @@ namespace Floating {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
 | 
			
		||||
Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {
 | 
			
		||||
    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
 | 
			
		||||
Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {
 | 
			
		||||
    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -26,12 +26,12 @@ namespace Matchers {
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        struct WithinUlpsMatcher : MatcherBase<double> {
 | 
			
		||||
            WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
 | 
			
		||||
            WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType);
 | 
			
		||||
            bool match(double const& matchee) const override;
 | 
			
		||||
            std::string describe() const override;
 | 
			
		||||
        private:
 | 
			
		||||
            double m_target;
 | 
			
		||||
            int m_ulps;
 | 
			
		||||
            uint64_t m_ulps;
 | 
			
		||||
            FloatingPointKind m_type;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
@@ -40,8 +40,8 @@ namespace Matchers {
 | 
			
		||||
 | 
			
		||||
    // The following functions create the actual matcher objects.
 | 
			
		||||
    // This allows the types to be inferred
 | 
			
		||||
    Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
 | 
			
		||||
    Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
 | 
			
		||||
    Floating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff);
 | 
			
		||||
    Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff);
 | 
			
		||||
    Floating::WithinAbsMatcher WithinAbs(double target, double margin);
 | 
			
		||||
 | 
			
		||||
} // namespace Matchers
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user