How to use floating point tolerances in the Catch framework?
Asked Answered
A

4

30

I'm using the Catch test framework.

In the introductory blog post the author mentions the following feature:

  • Floating point tolerances supported in an easy to use way

I couldn't find any documentation on how to do this. How is this done in Catch?

Alvie answered 16/7, 2011 at 15:27 Comment(0)
A
35

It's simple. There is a class called Approx that lets you do this test in a very readable manner:

#include <limits>
TEST_CASE("demo/approx", "Approx demo") {
    double a = 1.0;
    double b = a + std::numeric_limits<double>::epsilon();
    REQUIRE_FALSE(b == a);
    REQUIRE(b == Approx(a));
}

The tolerance can be changed by using the member functions epsilon() and scale() of the Approx object, like so: Approx(a).epsilon(e).

Alvie answered 16/7, 2011 at 15:28 Comment(4)
If you are going to down-vote explain why. To me this looks fine.Stogner
Related meta discussion: meta.stackexchange.com/questions/98766/…Ectomere
@Martin that was the sort of thing one would write in a comment, not in an edit.Alvie
@Martin no problem; you made me aware of the other answer which I had missed. Thanks.Alvie
N
16

The tolerance is has been customizable since Apr 2011. Approx has two member functions for this: epsilon() and scale(). For example:

REQUIRE(a == Approx(b).epsilon(my_eps));

The tolerance is ε × (scale+max(|a|, |b|)), where scale defaults to 1, so this will pass:

REQUIRE((2+2) == Approx(5).epsilon(0.17));
Nucleon answered 6/4, 2014 at 19:5 Comment(0)
F
2

It is important to note that Approx is now considered deprecated [source]:

The downside to Approx is that it has a couple of issues that we cannot fix without breaking backwards compatibility. Because Catch2 also provides complete set of matchers that implement different floating point comparison methods, Approx is left as-is, is considered deprecated, and should not be used in new code.

As of version 2.10, one should switch to using Matchers.

    #include <limits>
    #include <catch2/matchers/catch_matchers_floating_point.hpp>

    TEST_CASE("demo/matchers", "Matchers demo") {
        double a = 1.0;
        double b = a + std::numeric_limits<double>::epsilon();
        REQUIRE_FALSE(b == a);
        REQUIRE_THAT(b, Catch::Matchers::WithinRel(a, std::numeric_limits<double>::epsilon()));
    }
Forewing answered 24/1, 2023 at 9:35 Comment(0)
O
1

I know this is an old question, but I just stumbled upon the same problem and found a simple solution. In the Catch.hpp header file where the Approx class is defined (line 2045 at the time of writing), just add the following constructor:

class Approx {
public:
    explicit Approx( double value )
    :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
        m_scale( 1.0 ),
        m_value( value )
    {}

    explicit Approx( double value, double epsilon ) // <- New constructor
    :   m_epsilon( epsilon ),
        m_scale( 1.0 ),
        m_value( value )
    {}

Now you can do this:

TEST_CASE("demo/approx", "Approx demo") {
    double myTol = 0.1;
    double a = 1.0;
    double b = a + myTol;
    REQUIRE_FALSE(a == b);
    REQUIRE(a == Approx(b, myTol)); 
}
Oswell answered 5/1, 2014 at 0:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.