Comparison ==! = In templates

What is the correct way to do the == and! = Operators in template classes? Suppose this code:

template<typename T>
class C {
        T x, y;

        public:
        C(T a, T b) : x(a), y(b) {}

        bool cmp() {
                return x == y;
        }

};

int main()
{
        // OK
        C<int> i(1,2);
        i.cmp();

        // not OK
        C<double> d(1.0, 2.0);
        d.cmp();

        return 0;
}

      

If you build it with g ++ -Wfloat-equal you get

warning: comparing floating point with == or! = unsafe [-Wfloat-equal]

because you can't just compare floating variables.


Update

I solved the problem using type_traits and enable_if like this (thanks to @Andrew and @OMGtechy):

#include <type_traits>
#include <limits>
#include <cmath>
#include <iostream>

using namespace std;

template <typename IntegralType>
typename std::enable_if<std::is_integral<IntegralType>::value, bool>::type
equal(const IntegralType& a, const IntegralType& b) {
        return a == b;
}

template <typename FloatingType>
typename std::enable_if<std::is_floating_point<FloatingType>::value, bool>::type
equal(const FloatingType& a, const FloatingType& b) {
        return std::fabs(a-b) < std::numeric_limits<FloatingType>::epsilon();
}

template<typename T>
class C {
    T x, y;

    public:
    C(T a, T b) : x(a), y(b) {}

    bool cmp() {
        return equal(x, y);
    }

};

int main()
{
    // OK
    C<int> i(1,2);
    cout << i.cmp() << endl;

    // not OK
    C<double> d(1.0, 1.0);
    cout << d.cmp() << endl;

    return 0;
}

      

+3


source to share


3 answers


This question seems to be asking two things:

  • How to do floating point comparisons without using the == operator and
  • How to change the behavior of the template depending on the type passed to it.

One answer to the second question is the use of type traits. The code below demonstrates this for your situation, providing compare_traits for generic types (using ==) and specialization for doubles using tolerance (which also answers the first question).



#include <cmath>

template <typename T> struct comparison_traits {
  bool equal(const T& a, const T& b) {
    return a == b;
  }

  // etc.
};

template<> struct comparison_traits<double> {
  bool equal(const double& a, const double& b) {
    return fabs(a - b) < 1e-15; // or whatever...
  }
};

template <typename T>
class C {
  T x, y;

  public:
    C(const T& a, const T& b) : x(a), y(b) {}

    bool cmp() {
      return comparison_traits<T>::equal(x, y);
    }
};

int main() {
  // OK
  C<int> i(1, 2);
  i.cmp();

  // Now OK too...
  C<double> d(1.0, 2.0);
  d.cmp();

  return 0;
}

      

Other options:

  • Providing a template parameter that allows you to specify a comparison function, the default is std :: equal_to

  • Specialize your template for double so you can write another cmp () implementation

+6


source


It depends on how it will be used. Comparing floats properly depends on the context.

I would recommend what @Niall says: add a comparator template parameter, the default is std::equal_to

. This will allow callers to control how values ​​are compared. See the docs on std::sort

, for example, to learn how the comparator parameter is used. The downside to this is that it is the caller's responsibility to compare float

s. If they forget, then they get a compiler warning.



Another option is to specialize the template class. Make a specialization for your class to deal with types float

or double

, and compare them differently using whatever logic you prefer. This is probably not the best solution. The advantage of this is that callers no longer need to memorize to specify a comparator.

+2


source


If you ask why you are getting this warning:

here's an example:

double a,b;
a=10.0/13.0;
b = a/3;
b*=3;

std::cout<<"a="<<a<<std::endl;
std::cout<<"b="<<b<<std::endl;
if(a!=b){
    std::cout<<"NOT  equal!!"<<std::endl;
    std::cout<<"a-b="<<a-b<<std::endl;
}
else
    std::cout<<"equal"<<std::endl;

      

if you do the math a and b are clearly equal. but this is the result I have:

a=0.769231
b=0.769231
NOT  equal!!
a-b=-1.11022e-016

      

because it's not so accurate, a correct comparison for a double should define some validity:

for example (permissibility may vary according to needs):

int compare(double a, double b)
{
    double tolerancy = 0.000001;
    if (abs(a-b) < tolerancy) return 0;
    else if (a>b) return 1;
    else /*if (a<b)*/ return -1;
} 

      

and if i use this comparison i get:

a=0.769231
b=0.769231
equal

      

0


source







All Articles