| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 | /* *  Created by Martin on 07/11/2017. * * Distributed under the Boost Software License, Version 1.0. (See accompanying * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) */#include "catch_matchers_floating.h"#include "catch_enforce.h"#include "catch_polyfills.hpp"#include "catch_to_string.hpp"#include "catch_tostring.h"#include <algorithm>#include <cmath>#include <cstdlib>#include <cstdint>#include <cstring>#include <sstream>#include <type_traits>#include <iomanip>#include <limits>namespace Catch {namespace {    int32_t convert(float f) {        static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");        int32_t i;        std::memcpy(&i, &f, sizeof(f));        return i;    }    int64_t convert(double d) {        static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");        int64_t i;        std::memcpy(&i, &d, sizeof(d));        return i;    }    template <typename FP>    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)) {            return false;        }        auto lc = convert(lhs);        auto rc = convert(rhs);        if ((lc < 0) != (rc < 0)) {            // Potentially we can have +0 and -0            return lhs == rhs;        }        // static cast as a workaround for IBM XLC        auto ulpDiff = std::abs(static_cast<FP>(lc - rc));        return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;    }#if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)    float nextafter(float x, float y) {        return ::nextafterf(x, y);    }    double nextafter(double x, double y) {        return ::nextafter(x, y);    }#endif // ^^^ CATCH_CONFIG_GLOBAL_NEXTAFTER ^^^template <typename FP>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        start = std::nextafter(start, direction);#endif    }    return start;}// Performs equivalent check of std::fabs(lhs - rhs) <= margin// But without the subtraction to allow for INFINITY in comparisonbool marginComparison(double lhs, double rhs, double margin) {    return (lhs + margin >= rhs) && (rhs + margin >= lhs);}template <typename FloatingPoint>void write(std::ostream& out, FloatingPoint num) {    out << std::scientific        << std::setprecision(std::numeric_limits<FloatingPoint>::max_digits10 - 1)        << num;}} // end anonymous namespacenamespace Matchers {namespace Floating {    enum class FloatingPointKind : uint8_t {        Float,        Double    };    WithinAbsMatcher::WithinAbsMatcher(double target, double margin)        :m_target{ target }, m_margin{ margin } {        CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'            << " Margin has to be non-negative.");    }    // Performs equivalent check of std::fabs(lhs - rhs) <= margin    // But without the subtraction to allow for INFINITY in comparison    bool WithinAbsMatcher::match(double const& matchee) const {        return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);    }    std::string WithinAbsMatcher::describe() const {        return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);    }    WithinUlpsMatcher::WithinUlpsMatcher(double target, uint64_t ulps, FloatingPointKind baseType)        :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {        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__)#pragma clang diagnostic push// Clang <3.5 reports on the default branch in the switch below#pragma clang diagnostic ignored "-Wunreachable-code"#endif    bool WithinUlpsMatcher::match(double const& matchee) const {        switch (m_type) {        case FloatingPointKind::Float:            return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);        case FloatingPointKind::Double:            return almostEqualUlps<double>(matchee, m_target, m_ulps);        default:            CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );        }    }#if defined(__clang__)#pragma clang diagnostic pop#endif    std::string WithinUlpsMatcher::describe() const {        std::stringstream ret;        ret << "is within " << m_ulps << " ULPs of ";        if (m_type == FloatingPointKind::Float) {            write(ret, static_cast<float>(m_target));            ret << 'f';        } else {            write(ret, m_target);        }        ret << " ([";        if (m_type == FloatingPointKind::Double) {            write(ret, step(m_target, static_cast<double>(-INFINITY), m_ulps));            ret << ", ";            write(ret, step(m_target, static_cast<double>( INFINITY), m_ulps));        } else {            // We have to cast INFINITY to float because of MinGW, see #1782            write(ret, step(static_cast<float>(m_target), static_cast<float>(-INFINITY), m_ulps));            ret << ", ";            write(ret, step(static_cast<float>(m_target), static_cast<float>( INFINITY), m_ulps));        }        ret << "])";        return ret.str();    }    WithinRelMatcher::WithinRelMatcher(double target, double epsilon):        m_target(target),        m_epsilon(epsilon){        CATCH_ENFORCE(m_epsilon >= 0., "Relative comparison with epsilon <  0 does not make sense.");        CATCH_ENFORCE(m_epsilon  < 1., "Relative comparison with epsilon >= 1 does not make sense.");    }    bool WithinRelMatcher::match(double const& matchee) const {        const auto relMargin = m_epsilon * (std::max)(std::fabs(matchee), std::fabs(m_target));        return marginComparison(matchee, m_target,                                std::isinf(relMargin)? 0 : relMargin);    }    std::string WithinRelMatcher::describe() const {        Catch::ReusableStringStream sstr;        sstr << "and " << m_target << " are within " << m_epsilon * 100. << "% of each other";        return sstr.str();    }}// namespace FloatingFloating::WithinUlpsMatcher WithinULP(double target, uint64_t maxUlpDiff) {    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);}Floating::WithinUlpsMatcher WithinULP(float target, uint64_t maxUlpDiff) {    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);}Floating::WithinAbsMatcher WithinAbs(double target, double margin) {    return Floating::WithinAbsMatcher(target, margin);}Floating::WithinRelMatcher WithinRel(double target, double eps) {    return Floating::WithinRelMatcher(target, eps);}Floating::WithinRelMatcher WithinRel(double target) {    return Floating::WithinRelMatcher(target, std::numeric_limits<double>::epsilon() * 100);}Floating::WithinRelMatcher WithinRel(float target, float eps) {    return Floating::WithinRelMatcher(target, eps);}Floating::WithinRelMatcher WithinRel(float target) {    return Floating::WithinRelMatcher(target, std::numeric_limits<float>::epsilon() * 100);}} // namespace Matchers} // namespace Catch
 |