tags:

views:

427

answers:

6

When comparing for equality is it okay to use ==?

For example:

int a = 3;
int b = 4;

If checking for equality should you use:

if (a == b)
{
     . . .
}

Would the situation change if floating point numbers were used?

+14  A: 

'==' is perfectly good for integer values.

You should not compare floats for equality; use an tolerance approach:

if (fabs(a - b) < tolerance)
{
   // a and b are equal to within tolerance
}
Mitch Wheat
Please use fabs! comparing with abs converts the arguments to int first... which equals very inaccurate comparisons.
Michael Anderson
@Michael `abs` is overloaded for floating point types in `cmath`, there is no problem in Mitch's code.
AraK
One needs to use std::abs, because only that is overloaded. The C function ::abs isn't. I would always explicitly state the namespace to avoid hard-to-debug bugs in case it accidentally picks the non-overloaded C function.
Tronic
If someone does a #include <math.h> or removes your "using namespace std" things can go very bad. It's likely you wont even get a compiler warning. Now you might be happy with code that fragile... I certainly would not be.
Michael Anderson
updated to fabs(); BTW my 'code' was meant as pseudocode!
Mitch Wheat
personally, I go with cmath, but qualify most floating point calls explicitly, like `std::abs()` or `std::ceil()`. I need this as in some of my apps I use a real, which is a typedef for things like float, double.
phresnel
+4  A: 

Re floating points: yes. Don't use == for floats (or know EXACTLY what you're doing if you do). Rather use something like

if (fabs(a - b) < SOME_DELTA) {
  ...
}

EDIT: changed abs() to fabs()

Frank Shearar
I would never use == for comparing floats.
Mitch Wheat
Please use fabs! comparing with abs converts the arguments to int first... which equals very inaccurate comparisons.
Michael Anderson
@Mitch Wheat: This is why Frank Shearar mentioned that you should know what you do. If you do, using == is okay. See Goldbergs classy "What every computer scientist should know about floating point math" (http://docs.sun.com/source/806-3568/ncg_goldberg.html)
phresnel
Thanks. I've read that article and others like it (used to work in numerical processing area). I can think of few situations where using == for floats is applicable.
Mitch Wheat
Same here. Some might include: * testing a random number generator on different platforms for same results * testing for NaN ((x==x)==false) * testing for infinity .
phresnel
@Michael, @Frank: In C++, when `using namespace std;`, `abs` applied to fp types will call through a template to perform the correct operation. `fabs` can cause unnecessary casts to `double`, and generally makes code less generic.
Potatoswatter
http://kpreid.livejournal.com/24684.html has an interesting example of when you WOULD want to use == with floats.
Frank Shearar
+1  A: 

While comparing ints, use ==. Using "<" and ">" at the same time to check equality on a int results in slower code because it takes two comparisons instead of one, taking the double amount of time. (altough probably the compiler will fix it for you, but you should not get used to writing bad code).

Remember, early optimization is bad, but early inefficient code is just as bad.

EDIT: Fixed some english...

speeder
Downvoters care to explain what I said wrong?
speeder
Ok, so we have people that downvote others to promote their awnsers? I've got 2 downvotes and no explanation, if you are a actual critic, you should see that I am newbie, and should then know that I have no idea of what I did wrong, but if you are not a actual critic, you should not go around casting random downvotes.
speeder
I didn't downvote, but the statement of two comparisons producing slower code is very much false, as compilers (as you say) optimize it anyway.
Tronic
So it is not false. It is true, but PROBABLY (remember that we don't know what compiler and how much optimization the person is using) it will be optmized out. What if for example the guy is using a early DOS C compiler? I know that people still use those (today someone asked a question and mentioned using one of those)
speeder
@speeder: According to the vote splitup (available to users with a rep over 1000) you got one up and 0 down. I guess either SO is buggy or they took the downvotes back.
Fredrik
After complaining on meta people pointed that my english is not clear enough... That should explain the downvote. (but yes, I am sure that people downvoted me, so they removed the downvote... I pressed F5 and saw the number decreasing...)
speeder
In some cases customers request that compiler optimizations be turned off (usually safety critical, and/or high reliability embedded systems) so the compiler is not allowed to optimize this particular situation. What you write, is exactly what will be processed, and in that case `==` is more correct. But even if `==` was slower, I'd recommend it simply for readability. Decrease readability for performance and watch your bug rate climb, and time to debug explode.
Adam Davis
I agree with Adam :)
speeder
Ok, not I got MORE not commented downvotes... That is getting annoying.
speeder
+2  A: 

In many classes, operator== is typically implemented as (!(a < b || b < a)), so you should go ahead and use ==. Except for floats, as Mitch Wheat said above.

rlbond
Duncan
I've never seen a compiler generating code for `==` use anything other than a single compare instruction. If I understand correctly, you're saying it does two compares and checks not for "equality" but a pair of less thans?
wallyk
rlbond is not talking about built-in types, but user classes. Writing operator== that way is indeed typical, because the "standard" comparison opearator in C++ is operator<, if only because that's what the STL uses. So writing other comparison operators in terms of operator< is good practise.
Gorpik
MSalters
Gorpik
Doh! That's what I get for coding when tired. You guys are correct, it's `||`.MSalters: In most cases, the overhead of the extra operations is miniscule in comparison to the amount of time saved. In fact, you can derive your class from `boost::totally_ordered<T>` to implement all comparison operators just from your `operator<`.
rlbond
+1  A: 

For integers, == does just what you expect. If they're equal, they're equal.

For floats, it's another story. Operations produce imprecise results and errors accumulate. You need to be a little fuzzy when dealing with numbers. I use

if ( std::abs( a - b )
    < std::abs( a ) * ( std::numeric_limits<float_t>::epsilon() * error_margin ) )

where float_t is a typedef; this gives me as much precision as possible (assuming error_margin was calculated correctly) and allows easy adjustment to another type.

Furthermore, some floating-point values are not numbers: there's infinity, minus infinity, and of course not-a-number. == does funny things with those. Infinity equals infinity, but not-a-number does not equal not-a-number.

Finally, there are positive and negative zero, which are distinct but equal to each other! To separate them, you need to do something like check whether the inverse is positive or negative infinity. (Just make sure you won't get a divide-by-zero exception.)

So, unless you have a more specific question, I hope that handles it…

Potatoswatter
Not a good idea if a is negative.try std::abs(a-b) < ( std::abs(a)+std::abs(b) ) * ....
Michael Anderson
@Michael: Thanks, fixed. There's no need to add them on the RHS, since the comparison would be false if it made a difference. The difference between epsilon*(a+a) and epsilon*(a+b) is less than the precision of a-b, or else error_margin must be chosen low enough for the comparison to fail. Maybe adding a+b is important if you are filtering out more than half the mantissa bits… in any case some analysis of the actual calculation is required.
Potatoswatter
The problem I have with your updated comparison method is that its not symmetric.i.e. you can have compare(a,b) != compare(b,a) when b-a is close to the scaled tolerance. I don't like that possibility.
Michael Anderson
@Michael: I don't think that can happen because the difference between eps*(2*a) and eps*(a+b) is only in bits beyond the precision of a-b. (a-b) is going to have a small amount of data in the mantissa, as many bits as error_margin, followed by a long string of zeroes, if there's a chance the equality will hold. Adding a+b only changes what those zeroes are compared to. See my previous response. Perhaps this doesn't hold true for very large error_margins. Do you have a specific example?
Potatoswatter
@Potatoswatter I've looked through the maths a bit deeper and for small error_margin I"d agree with you. I think I used my version with values calculated from noisy, and thus needed a large tolerance.
Michael Anderson
+2  A: 

Doing < and > comparisons doesn't really help you with rounding errors. Use the solution given by Mark Shearar. Direct equality comparisons for floats are not always bad, though. You can use them if some specific value (e.g. 0.0 or 1.0) is directly assigned to a variable, to check if the variable still has that value. It is only after calculations where the rounding errors screw up equality checks.

Notice that comparing a NaN value to anything (also another NaN) with <, >, <=, >= or == returns false. != returns true.

Tronic
Mark Shearar did not participate in this thread.
phresnel
@phresnel: if you mean that he mistook "Mark" for "Frank", OK, let the guy live...
Gorpik
@Gorpik: of course, this was not intended as an insult or so :)
phresnel