If all you care about is equality for the whole container, I would recommend the ::std::equal
algorithm. Here's how:
const double tolerance = 0.01;
bool equal = m1.size() == m2.size() &&
::std::equal(m1.begin(), m1.end(), m2.begin(),
[tolerance](const decltype(m1)::value_type &a,
const decltype(m2)::value_type &b) {
return (a.first == b.first) &&
(::std::abs(a.second - b.second) < tolerance);
});
If you care about a 'less than' relationship, then ::std::lexicographical_compare
is what you want. This requires the C++11 lambda feature to work.
If what you really want are data values that compare equal in a fuzzy way, I present to you a bit of a hack (and something that also requires a couple of C++11 features) fuzzy-double.cpp
. I disable ordering comparisons because that would tempt you to stuff these things in ordering containers, and since (2.0 == 2.1) && (2.1 == 2.2)
, but (2.0 != 2.2)
, they are not suitable for this purpose.
#include <cmath>
#include <iostream>
template <const double &tolerance>
class fuzzy_double {
public:
fuzzy_double(double x) : x_(x) {
static_assert(tolerance >= 0, "tolerance must be >= 0");
}
operator double() const { return x_; }
const fuzzy_double &operator =(double x) { x_ = x; }
bool equals(const fuzzy_double &b) const {
return ::std::abs(x_ - b.x_) <= tolerance;
}
// This cannot be used as the basis of a 'less than' comparison operator for
// the purposes of other algorithms because it's possible for a transitive
// equality relationship to exit that equates all fuzzy_double's to
// eachother. There is no strict ordering that makes sense.
bool fuzzy_less(const fuzzy_double &b) const {
return (b.x_ - x_) > tolerance;
}
private:
double x_;
};
template <const double &tolerance>
bool operator ==(const fuzzy_double<tolerance> &a,
const fuzzy_double<tolerance> &b)
{
return a.equals(b);
}
template <const double &tolerance>
bool operator !=(const fuzzy_double<tolerance> &a,
const fuzzy_double<tolerance> &b)
{
return !a.equals(b);
}
template <const double &tolerance>
bool operator <(const fuzzy_double<tolerance> &a,
const fuzzy_double<tolerance> &b)
{
// tolerance < 0 should be an impossible condition and always be false, but
// it's dependent on the template parameter and so only evaluated when the
// template is instantiated.
static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
return false;
}
template <const double &tolerance>
bool operator >=(const fuzzy_double<tolerance> &a,
const fuzzy_double<tolerance> &b)
{
// tolerance < 0 should be an impossible condition and always be false, but
// it's dependent on the template parameter and so only evaluated when the
// template is instantiated.
static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
return false;
}
template <const double &tolerance>
bool operator >(const fuzzy_double<tolerance> &a,
const fuzzy_double<tolerance> &b)
{
// tolerance < 0 should be an impossible condition and always be false, but
// it's dependent on the template parameter and so only evaluated when the
// template is instantiated.
static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
return false;
}
template <const double &tolerance>
bool operator <=(const fuzzy_double<tolerance> &a,
const fuzzy_double<tolerance> &b)
{
// tolerance < 0 should be an impossible condition and always be false, but
// it's dependent on the template parameter and so only evaluated when the
// template is instantiated.
static_assert(tolerance < 0, "fuzzy_doubles cannot be ordered.");
return false;
}
extern constexpr double ten_e_minus_2 = 0.01;
int main()
{
fuzzy_double<ten_e_minus_2> a(3);
fuzzy_double<ten_e_minus_2> b(3.009);
fuzzy_double<ten_e_minus_2> c(2.991);
fuzzy_double<ten_e_minus_2> d(3.011);
fuzzy_double<ten_e_minus_2> e(2.989);
using ::std::cout;
cout << "a == a: " << (a == a) << '\n';
cout << "a == b: " << (a == b) << '\n';
cout << "a == c: " << (a == c) << '\n';
cout << "a == d: " << (a == d) << '\n';
cout << "a == e: " << (a == e) << '\n';
return 0;
}
C++11 does not allow a double
, not even const
one, to be a template parameter. It does, OTOH, allow pointers and references to objects with external linkage to be template parameters. So if you declare your tolerance as an extern constexpr double
you can than use the named tolerance as a template parameter.