Add ULP and margin matcher

Closes #1074
This commit is contained in:
Martin Hořeňovský 2017-11-10 18:14:42 +01:00
parent 24e6d5fa33
commit 0b1f1b1003
11 changed files with 797 additions and 8 deletions

View File

@ -140,6 +140,7 @@ set(INTERNAL_HEADERS
${HEADER_DIR}/internal/catch_leak_detector.h
${HEADER_DIR}/internal/catch_list.h
${HEADER_DIR}/internal/catch_matchers.h
${HEADER_DIR}/internal/catch_matchers_floating.h
${HEADER_DIR}/internal/catch_matchers_string.h
${HEADER_DIR}/internal/catch_matchers_vector.h
${HEADER_DIR}/internal/catch_message.h
@ -206,6 +207,7 @@ set(IMPL_SOURCES
${HEADER_DIR}/internal/catch_list.cpp
${HEADER_DIR}/internal/catch_leak_detector.cpp
${HEADER_DIR}/internal/catch_matchers.cpp
${HEADER_DIR}/internal/catch_matchers_floating.cpp
${HEADER_DIR}/internal/catch_matchers_string.cpp
${HEADER_DIR}/internal/catch_message.cpp
${HEADER_DIR}/internal/catch_registry_hub.cpp

View File

@ -36,10 +36,19 @@ REQUIRE_THAT( str,
```
## Built in matchers
Currently Catch has some string matchers and some vector matchers. They are in the `Catch::Matchers` and `Catch` namespaces.
Catch currently provides some matchers, they are in the `Catch::Matchers` and `Catch` namespaces.
### String matchers
The string matchers are `StartsWith`, `EndsWith`, `Contains` and `Equals`. Each of them also takes an optional second argument, that decides case sensitivity (by-default, they are case sensitive).
### Vector matchers
The vector matchers are `Contains`, `VectorContains` and `Equals`. `VectorContains` looks for a single element in the matched vector, `Contains` looks for a set (vector) of elements inside the matched vector.
### Floating point matchers
The floating point matchers are `WithinULP` and `WithinAbs`. `WithinAbs` accepts floating point numbers that are within a certain margin of target. `WithinULP` performs an [ULP](https://en.wikipedia.org/wiki/Unit_in_the_last_place)-based comparison of two floating point numbers and accepts them if they are less than certain number of ULPs apart.
Do note that ULP-based checks only make sense when both compared numbers are of the same type and `WithinULP` will use type of its argument as the target type. This means that `WithinULP(1.f, 1)` will expect to compare `float`s, but `WithinULP(1., 1)` will expect to compare `double`s.
## Custom matchers
It's easy to provide your own matchers to extend Catch or just to work with your own types.

View File

@ -10,6 +10,7 @@
#include "catch_capture.hpp"
#include "catch_matchers.h"
#include "catch_matchers_floating.h"
#include "catch_matchers_string.h"
#include "catch_matchers_vector.h"

View File

@ -0,0 +1,144 @@
/*
* 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 <cmath>
#include <cstdint>
#include <cstring>
#include <stdexcept>
namespace Catch {
namespace Matchers {
namespace Floating {
enum class FloatingPointKind : uint8_t {
Float,
Double
};
}
}
}
namespace {
union Float_t {
Float_t(float num = 0.0f) : f(num) {}
// Portable extraction of components.
bool Negative() const { return i < 0; }
int32_t RawMantissa() const { return i & ((1 << 23) - 1); }
int32_t RawExponent() const { return (i >> 23) & 0xFF; }
int32_t i;
float f;
};
template <typename T>
struct Converter;
template <>
struct Converter<float> {
static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
Converter(float f) {
std::memcpy(&i, &f, sizeof(f));
}
int32_t i;
};
template <>
struct Converter<double> {
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);
}
template <typename FP>
bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
// Comparison with NaN should always be false.
// This way we can rule it out before getting into the ugly details
if (std::isnan(lhs) || std::isnan(rhs)) {
return false;
}
auto lc = convert(lhs);
auto rc = convert(rhs);
if ((lc.i < 0) != (rc.i < 0)) {
// Potentially we can have +0 and -0
return lhs == rhs;
}
auto ulpDiff = abs(lc.i - rc.i);
return ulpDiff <= maxUlpDiff;
}
}
namespace Catch {
namespace Matchers {
namespace Floating {
WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
:m_target{ target }, m_margin{ margin } {}
// 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 >= m_margin);
}
std::string WithinAbsMatcher::describe() const {
return "is within " + std::to_string(m_margin) + " of " + std::to_string(m_target);
}
WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
:m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
if (m_ulps < 0) {
throw std::domain_error("Expected ulp difference has to be >0");
}
}
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:
throw std::domain_error("Unknown FloatingPointKind value");
}
}
std::string WithinUlpsMatcher::describe() const {
return "is within " + std::to_string(m_ulps) + " ULPs of " + std::to_string(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
}
}// namespace Floating
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
}
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
}
Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
return Floating::WithinAbsMatcher(target, margin);
}
} // namespace Matchers
} // namespace Catch

View File

@ -0,0 +1,59 @@
/*
* 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)
*/
#ifndef TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED
#define TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED
#include "catch_matchers.h"
#include <type_traits>
#include <cmath>
namespace Catch {
namespace Matchers {
namespace Floating {
enum class FloatingPointKind : uint8_t;
struct WithinAbsMatcher : MatcherBase<double> {
WithinAbsMatcher(double target, double margin);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
double m_margin;
};
struct WithinUlpsMatcher : MatcherBase<double> {
WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
bool match(double const& matchee) const override;
std::string describe() const override;
private:
double m_target;
int m_ulps;
FloatingPointKind m_type;
};
} // namespace Floating
Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
Floating::WithinAbsMatcher WithinAbs(double target, double margin);
// The following functions create the actual matcher objects.
// This allows the types to be inferred
// StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
// StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
// StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
// StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
} // namespace Matchers
} // namespace Catch
#endif // TWOBLUECUBES_CATCH_MATCHERS_FLOATING_H_INCLUDED

View File

@ -1003,6 +1003,6 @@ with expansion:
"{?}" == "1"
===============================================================================
test cases: 185 | 134 passed | 47 failed | 4 failed as expected
assertions: 904 | 787 passed | 96 failed | 21 failed as expected
test cases: 187 | 136 passed | 47 failed | 4 failed as expected
assertions: 935 | 818 passed | 96 failed | 21 failed as expected

View File

@ -1717,6 +1717,234 @@ PASSED:
with expansion:
3628800 (0x<hex digits>) == 3628800 (0x<hex digits>)
-------------------------------------------------------------------------------
Floating point matchers: double
Margin
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinAbs(1., 0) )
with expansion:
1.0 is within 0.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0., WithinAbs(1., 1) )
with expansion:
0.0 is within 1.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0., !WithinAbs(1., 0.99) )
with expansion:
0.0 not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0., !WithinAbs(1., 0.99) )
with expansion:
0.0 not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinAbs(NAN, 0) )
with expansion:
nanf not is within 0.000000 of nan
-------------------------------------------------------------------------------
Floating point matchers: double
ULPs
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinULP(1., 0) )
with expansion:
1.0 is within 0 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1., 2.), WithinULP(1., 1) )
with expansion:
1.0 is within 1 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1., 0.), WithinULP(1., 1) )
with expansion:
1.0 is within 1 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1., 2.), !WithinULP(1., 0) )
with expansion:
1.0 not is within 0 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinULP(1., 0) )
with expansion:
1.0 is within 0 ULPs of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( -0., WithinULP(0., 0) )
with expansion:
-0.0 is within 0 ULPs of 0.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinULP(NAN, 123) )
with expansion:
nanf not is within 123 ULPs of nanf
-------------------------------------------------------------------------------
Floating point matchers: double
Composed
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinAbs(1., 0.5) || WithinULP(2., 1) )
with expansion:
1.0 ( is within 0.500000 of 1.000000 or is within 1 ULPs of 2.000000 )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1., WithinAbs(2., 0.5) || WithinULP(1., 0) )
with expansion:
1.0 ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000 )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) )
with expansion:
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
-------------------------------------------------------------------------------
Floating point matchers: float
Margin
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinAbs(1.f, 0) )
with expansion:
1.0f is within 0.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, WithinAbs(1.f, 1) )
with expansion:
0.0f is within 1.000000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) )
with expansion:
0.0f not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) )
with expansion:
0.0f not is within 0.990000 of 1.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 0.f, WithinAbs(-0.f, 0) )
with expansion:
0.0f is within 0.000000 of -0.000000
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinAbs(NAN, 0) )
with expansion:
nanf not is within 0.000000 of nan
-------------------------------------------------------------------------------
Floating point matchers: float
ULPs
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinULP(1.f, 0) )
with expansion:
1.0f is within 0 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1.f, 2.f), WithinULP(1.f, 1) )
with expansion:
1.0f is within 1 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1.f, 0.f), WithinULP(1.f, 1) )
with expansion:
1.0f is within 1 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( std::nextafter(1.f, 2.f), !WithinULP(1.f, 0) )
with expansion:
1.0f not is within 0 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinULP(1.f, 0) )
with expansion:
1.0f is within 0 ULPs of 1.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( -0.f, WithinULP(0.f, 0) )
with expansion:
-0.0f is within 0 ULPs of 0.000000f
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !WithinULP(NAN, 123) )
with expansion:
nanf not is within 123 ULPs of nanf
-------------------------------------------------------------------------------
Floating point matchers: float
Composed
-------------------------------------------------------------------------------
MatchersTests.cpp:<line number>
...............................................................................
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) )
with expansion:
1.0f ( is within 0.500000 of 1.000000 or is within 1 ULPs of 1.000000f )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) )
with expansion:
1.0f ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000f )
MatchersTests.cpp:<line number>:
PASSED:
REQUIRE_THAT( NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)) )
with expansion:
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
-------------------------------------------------------------------------------
Greater-than inequalities with different epsilons
-------------------------------------------------------------------------------
@ -7632,6 +7860,6 @@ MiscTests.cpp:<line number>:
PASSED:
===============================================================================
test cases: 185 | 132 passed | 49 failed | 4 failed as expected
assertions: 903 | 783 passed | 99 failed | 21 failed as expected
test cases: 187 | 134 passed | 49 failed | 4 failed as expected
assertions: 934 | 814 passed | 99 failed | 21 failed as expected

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<testsuitesloose text artifact
>
<testsuite name="<exe-name>" errors="15" failures="85" tests="904" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testsuite name="<exe-name>" errors="15" failures="85" tests="935" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
<testcase classname="<exe-name>.global" name="# A test name that starts with a #" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" time="{duration}"/>
<testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}">
@ -263,6 +263,12 @@ MessageTests.cpp:<line number>
</failure>
</testcase>
<testcase classname="<exe-name>.global" name="Factorials are computed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: double/Margin" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: double/ULPs" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: double/Composed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Margin" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/ULPs" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Floating point matchers: float/Composed" time="{duration}"/>
<testcase classname="<exe-name>.global" name="Greater-than inequalities with different epsilons" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO and WARN do not abort tests" time="{duration}"/>
<testcase classname="<exe-name>.global" name="INFO gets logged on failure" time="{duration}">

View File

@ -1945,6 +1945,278 @@
</Expression>
<OverallResult success="true"/>
</TestCase>
<TestCase name="Floating point matchers: double" tags="[floating-point][matchers]" filename="projects/<exe-name>/MatchersTests.cpp" >
<Section name="Margin" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinAbs(1., 0)
</Original>
<Expanded>
1.0 is within 0.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0., WithinAbs(1., 1)
</Original>
<Expanded>
0.0 is within 1.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0., !WithinAbs(1., 0.99)
</Original>
<Expanded>
0.0 not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0., !WithinAbs(1., 0.99)
</Original>
<Expanded>
0.0 not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinAbs(NAN, 0)
</Original>
<Expanded>
nanf not is within 0.000000 of nan
</Expanded>
</Expression>
<OverallResults successes="5" failures="0" expectedFailures="0"/>
</Section>
<Section name="ULPs" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinULP(1., 0)
</Original>
<Expanded>
1.0 is within 0 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1., 2.), WithinULP(1., 1)
</Original>
<Expanded>
1.0 is within 1 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1., 0.), WithinULP(1., 1)
</Original>
<Expanded>
1.0 is within 1 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1., 2.), !WithinULP(1., 0)
</Original>
<Expanded>
1.0 not is within 0 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinULP(1., 0)
</Original>
<Expanded>
1.0 is within 0 ULPs of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
-0., WithinULP(0., 0)
</Original>
<Expanded>
-0.0 is within 0 ULPs of 0.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinULP(NAN, 123)
</Original>
<Expanded>
nanf not is within 123 ULPs of nanf
</Expanded>
</Expression>
<OverallResults successes="7" failures="0" expectedFailures="0"/>
</Section>
<Section name="Composed" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinAbs(1., 0.5) || WithinULP(2., 1)
</Original>
<Expanded>
1.0 ( is within 0.500000 of 1.000000 or is within 1 ULPs of 2.000000 )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1., WithinAbs(2., 0.5) || WithinULP(1., 0)
</Original>
<Expanded>
1.0 ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000 )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))
</Original>
<Expanded>
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
</Expanded>
</Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section>
<OverallResult success="true"/>
</TestCase>
<TestCase name="Floating point matchers: float" tags="[floating-point][matchers]" filename="projects/<exe-name>/MatchersTests.cpp" >
<Section name="Margin" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinAbs(1.f, 0)
</Original>
<Expanded>
1.0f is within 0.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, WithinAbs(1.f, 1)
</Original>
<Expanded>
0.0f is within 1.000000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, !WithinAbs(1.f, 0.99f)
</Original>
<Expanded>
0.0f not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, !WithinAbs(1.f, 0.99f)
</Original>
<Expanded>
0.0f not is within 0.990000 of 1.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
0.f, WithinAbs(-0.f, 0)
</Original>
<Expanded>
0.0f is within 0.000000 of -0.000000
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinAbs(NAN, 0)
</Original>
<Expanded>
nanf not is within 0.000000 of nan
</Expanded>
</Expression>
<OverallResults successes="6" failures="0" expectedFailures="0"/>
</Section>
<Section name="ULPs" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f is within 0 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1.f, 2.f), WithinULP(1.f, 1)
</Original>
<Expanded>
1.0f is within 1 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1.f, 0.f), WithinULP(1.f, 1)
</Original>
<Expanded>
1.0f is within 1 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
std::nextafter(1.f, 2.f), !WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f not is within 0 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f is within 0 ULPs of 1.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
-0.f, WithinULP(0.f, 0)
</Original>
<Expanded>
-0.0f is within 0 ULPs of 0.000000f
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !WithinULP(NAN, 123)
</Original>
<Expanded>
nanf not is within 123 ULPs of nanf
</Expanded>
</Expression>
<OverallResults successes="7" failures="0" expectedFailures="0"/>
</Section>
<Section name="Composed" filename="projects/<exe-name>/MatchersTests.cpp" >
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1)
</Original>
<Expanded>
1.0f ( is within 0.500000 of 1.000000 or is within 1 ULPs of 1.000000f )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0)
</Original>
<Expanded>
1.0f ( is within 0.500000 of 2.000000 or is within 0 ULPs of 1.000000f )
</Expanded>
</Expression>
<Expression success="true" type="REQUIRE_THAT" filename="projects/<exe-name>/MatchersTests.cpp" >
<Original>
NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123))
</Original>
<Expanded>
nanf not ( is within 100.000000 of nan or is within 123 ULPs of nanf )
</Expanded>
</Expression>
<OverallResults successes="3" failures="0" expectedFailures="0"/>
</Section>
<OverallResult success="true"/>
</TestCase>
<TestCase name="Greater-than inequalities with different epsilons" tags="[Approx]" filename="projects/<exe-name>/ApproxTests.cpp" >
<Expression success="true" type="REQUIRE" filename="projects/<exe-name>/ApproxTests.cpp" >
<Original>
@ -8446,7 +8718,7 @@ loose text artifact
</Section>
<OverallResult success="true"/>
</TestCase>
<OverallResults successes="783" failures="100" expectedFailures="21"/>
<OverallResults successes="814" failures="100" expectedFailures="21"/>
</Group>
<OverallResults successes="783" failures="99" expectedFailures="21"/>
<OverallResults successes="814" failures="99" expectedFailures="21"/>
</Catch>

View File

@ -226,6 +226,67 @@ TEST_CASE("Exception matchers that fail", "[matchers][exceptions][!throws][.fail
}
}
TEST_CASE("Floating point matchers: float", "[matchers][floating-point]") {
SECTION("Margin") {
REQUIRE_THAT(1.f, WithinAbs(1.f, 0));
REQUIRE_THAT(0.f, WithinAbs(1.f, 1));
REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f));
REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f));
REQUIRE_THAT(0.f, WithinAbs(-0.f, 0));
REQUIRE_THAT(NAN, !WithinAbs(NAN, 0));
}
SECTION("ULPs") {
REQUIRE_THAT(1.f, WithinULP(1.f, 0));
REQUIRE_THAT(std::nextafter(1.f, 2.f), WithinULP(1.f, 1));
REQUIRE_THAT(std::nextafter(1.f, 0.f), WithinULP(1.f, 1));
REQUIRE_THAT(std::nextafter(1.f, 2.f), !WithinULP(1.f, 0));
REQUIRE_THAT(1.f, WithinULP(1.f, 0));
REQUIRE_THAT(-0.f, WithinULP(0.f, 0));
REQUIRE_THAT(NAN, !WithinULP(NAN, 123));
}
SECTION("Composed") {
REQUIRE_THAT(1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1));
REQUIRE_THAT(1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0));
REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)));
}
}
TEST_CASE("Floating point matchers: double", "[matchers][floating-point]") {
SECTION("Margin") {
REQUIRE_THAT(1., WithinAbs(1., 0));
REQUIRE_THAT(0., WithinAbs(1., 1));
REQUIRE_THAT(0., !WithinAbs(1., 0.99));
REQUIRE_THAT(0., !WithinAbs(1., 0.99));
REQUIRE_THAT(NAN, !WithinAbs(NAN, 0));
}
SECTION("ULPs") {
REQUIRE_THAT(1., WithinULP(1., 0));
REQUIRE_THAT(std::nextafter(1., 2.), WithinULP(1., 1));
REQUIRE_THAT(std::nextafter(1., 0.), WithinULP(1., 1));
REQUIRE_THAT(std::nextafter(1., 2.), !WithinULP(1., 0));
REQUIRE_THAT(1., WithinULP(1., 0));
REQUIRE_THAT(-0., WithinULP(0., 0));
REQUIRE_THAT(NAN, !WithinULP(NAN, 123));
}
SECTION("Composed") {
REQUIRE_THAT(1., WithinAbs(1., 0.5) || WithinULP(2., 1));
REQUIRE_THAT(1., WithinAbs(2., 0.5) || WithinULP(1., 0));
REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)));
}
}
#endif // CATCH_CONFIG_DISABLE_MATCHERS
#ifdef __clang__

View File

@ -52,6 +52,12 @@ infParser = re.compile(r'''
|
__builtin_huge_valf\(\) # OSX macro
''', re.VERBOSE)
nanParser = re.compile(r'''
\(\(float\)\(\(\(float\)\(1e\+300\ \*\ 1e\+300\)\)\ \*\ 0\.0F\)\) # MSVC NAN macro
|
\(__builtin_nanf\ \(""\)\) # Linux (ubuntu) NAN macro
''', re.VERBOSE)
if len(sys.argv) == 2:
cmdPath = sys.argv[1]
@ -110,6 +116,7 @@ def filterLine(line):
line = errnoParser.sub('errno', line)
line = sinceEpochParser.sub('{since-epoch-report}', line)
line = infParser.sub('INFINITY', line)
line = nanParser.sub('NAN', line)
return line