Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compare floating point containers #760

Closed
miqelm opened this issue Dec 16, 2016 · 7 comments
Closed

Compare floating point containers #760

miqelm opened this issue Dec 16, 2016 · 7 comments

Comments

@miqelm
Copy link

miqelm commented Dec 16, 2016

Will there be nice way to use Approx method for comparing floating point values stored in containers, e.g. std::vector, as it is for integral types?

For integral types I can write:

REQUIRE(computed == expected);

But for double value container I have to write loop for use Approx:

REQUIRE(computed.size() == expected.size());
for (size_t i = 0; i < computed.size(); ++i)
    REQUIRE(computed[i] == Approx(expected[i]));

or make ugly looking comparison with std::equal:

REQUIRE(computed.size() == expected.size());
REQUIRE(std::equal(computed.begin(),computed.end(),expected.‌​begin(), [](double x, double y)->bool { return x==Approx(y); }));

The best it would be to do:

REQUIRE(computed == Approx(expected));
@everythingfunctional
Copy link

I would like to second this request.

Any thoughts on how to implement it?

@lightmare
Copy link
Contributor

I once used something like this; not with Approx, but known expected values of any custom class work the same:

auto expected = { Approx(1.), 2., 3., 4., ... }; // initializer_list<Approx>
CHECK(computed == std::vector<Approx>(expected));

I also wanted to avoid the construction of the temporary vector, so I wrote a function template comparing two sequences:

template <typename C1, typename C2>
size_t count_differences(C1& c1, C2& c2);

CHECK(0 == count_differences(computed, expected));

@everythingfunctional
Copy link

For the time being I've resorted to a kind of workaround. I created the following two functions to be used wherever you want to check two vectors. (i.e. REQUIRE(compareVectors(vec1, vec2)); )

bool compareVectors(std::vector<double> a, std::vector<double> b) {
    if (a.size() != b.size()) return false;
    for (size_t i = 0; i < a.size(); i++) {
        if (a[i] != Approx(b[i])) {
            std::cout << a[i] << " Should == " << b[i] << std::endl;
            return false;
        }
    }
    return true;
}

bool compare2dVectors(std::vector<std::vector<double>> a,
                      std::vector<std::vector<double>> b) {
    if (a.size() != b.size()) return false;
    for (size_t i = 0; i < a.size(); i++) {
        if (! compareVectors(a[i], b[i])) return false;
    }
    return true;
}

That way you'll at least be able to see the name of the vector that failed, and the first values that differed.

It's not the ideal solution, so I'm still hoping someone will be able to come up with something better, but I thought I'd at least share what I've come up with so far in case it helps somebody.

@tgnottingham
Copy link

I'd like this functionality for comparing vectors and matrices from the glm library. If this issue gets resolved, I hope that the solution is general enough to handle container types such as these as well, even if requires some work from the user to make it work for their particular containers.

@horenmar
Copy link
Member

@tgnottingham I might at some point create a convenience wrapper that would use iterators to try and compare containers, but Catch already contains a generic mechanism for smarter assertions in matchers.

@schoetbi
Copy link

schoetbi commented Nov 21, 2018

I also had the problem especially in conjunction with the command line flag --success. The flag prints out every successful REQUIRE statement. When using REQUIRE inside of a loop over a vector of many elements this can lead to a long console output.

I created this helper function for vectors of T:

template <typename T>
void RequireVectorEquals(const vector<T>& a, const vector<T>& b,
                         T max_error = 0.000001) {
  REQUIRE(a.size() == b.size());
  const auto first_mismatch = std::mismatch(
      a.begin(), a.end(), b.begin(), [max_error](T val_a, T val_b) {
        return val_a == val_b || std::abs(val_a - val_b) < max_error;
      });
  if (first_mismatch.first != a.end()) {
    FAIL("Difference at index " +
         std::to_string(std::distance(a.begin(), first_mismatch.first)) +
         " values=(" + std::to_string(*first_mismatch.first) + "," +
         std::to_string(*first_mismatch.second) + ")");
  }
}

and this for arrays

template <typename T>
void RequireArrayEquals(const T* a, const T* b, size_t len,
                        T max_error = 0.000001) {
  size_t i = 0;
  while (i < len) {
    const auto val_a = a[i];
    const auto val_b = b[i];
    if (val_a == val_b || std::abs(val_a - val_b) < max_error) {
      ++i;
      continue;
    }

    FAIL("Difference at index " + std::to_string(i) + " values=(" +
         std::to_string(val_a) + "," + std::to_string(val_b) + ")");
    return;
  }
}

@horenmar
Copy link
Member

This has been implemented in #1499

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants