简体   繁体   中英

Overloaded comparison operator fails [C++11]

I have an RGB color class with two overloaded comparison operators (operator==). One for the self type and one for int (HEX).

// this one assigns the value correctly
RGB     RGB::operator=(const int hex)
{
    this->r = (hex>>16 & 0xFF) / 255.0f;
    this->g = (hex>>8  & 0xFF) / 255.0f;
    this->b = (hex     & 0xFF) / 255.0f;
    return *this;
}
//--------------------------------------------------------------------------------------
// also works
bool    RGB::operator==(const RGB &color)
{
    return (r == color.r && g == color.g && b == color.b);
}
// this is evil
bool    RGB::operator==(const int hex)
{
    float rr = (hex>>16 & 0xFF) / 255.0f;
    float gg = (hex>>8  & 0xFF) / 255.0f;
    float bb = (hex     & 0xFF) / 255.0f;

    // if i uncomment these lines then everything is fine
    //std::cout<<r<<" "<<rr<<std::endl;
    //std::cout<<g<<" "<<gg<<std::endl;
    //std::cout<<b<<" "<<bb<<std::endl;

    return (r == rr &&
            g == gg &&
            b == bb);
}

RGB::RGB(int hex) 
{ 
  setHex(hex); 
} 

inline void RGB::setHex(unsigned hex) 
{ 
  r = (float)(hex >> 16 & 0xFF) / 255.0f; 
  g = (float)(hex >> 8 & 0xFF) / 255.0f; 
  b = (float)(hex & 0xFF) / 255.0f; 
}

...then I compare in main.cpp like:

RGB a = 0x555555;
bool equals = (a == 0x555555); // returns false

I don't know what happens. The comparison returns with false, but if I uncomment the std::cout lines in the definition then the function works as expected and returns true.

This works also without problem:

RGB a = 0x555555;
RGB b = 0x555555;
bool equals = (a == b); // returns true

Anybody has an idea?

You should not get floating point comparison effects without optimization. This is because you have the same function in both cases.

Without optimization, the following is true:

float func(float);
float a = ...;
func(a) == func(a); //< always true

This is what you have, where your function is the shift and divide by 255.

However - with optimization it's a different story. GCC has optimizations (see eg -freciprocal-math, -fassociative-math) that can rearrange your expressions.

In your case, you have:

float rr = (X) / 255.0f;
...
r == rr

For example, it could do something that amounts to this upon optimization:

255.0f * r == (X)

This now does suffer from floating point comparison effects. However, by introducing the stdout in the middle, you are forcing it to evaluate the expressions closer to the way they are written, which again gets you back to the sanity of evaluating truth on the same function against itself.

You could either change the definition of the class to store the values as integers and convert to float only when you need floats, or store both the hex representation and the floats and using the hex for comparison. Or you could do comparisons testing whether the two floating point values are within 1/255 of each other using greater than/less than instead of double-equals. Eg something like this:

return (abs(r - rr) < 1/255.0f && ...);

Are you aware of the fact that your RGB::operator=() never gets called?

RGB a = 0x555555;

invokes RGB's constructor which takes an int . Without having any such constructor defined, your code won't compile and thus, the snippet given is incomplete. However,

RGB a;
a = 0x555555;

default constructs an RGB instance and invokes your RGB::operator=(int). I tried your code with both, clang++ and g++ and the comparison always evaluates to RGB::operator=(int). I tried your code with both, clang++ and g++ and the comparison always evaluates to true`.

The point that the code behaves differently with the std::cout lines commented in or out is pretty strange. It might be some optimization issue together with the floating point compares which are evil: google for "floating point comparison" to see why. In order to verify this, I would attach a debugger to it and have a look at the actual (hex) values of r , rr , g , gg , b and bb .

Please note that your assignment operator should return a reference to *this rather than a copy.

Thanks guys, the problem was the optimization as JoshG79 stated. To fix the problem, first I tried to store the calculations in volatile variables, which could prevent them from optimization, but they do some other stuff and cause overhead. So I decided to use GCC function attributes .

So the function declaration in the header looks like this:

bool operator==(const unsigned int hex) __attribute__((optimize("O0")));

Now everything's fine and work like a charm.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM