"-Weverything" yielding "Comparing floating point with == or != is unsafe"
Solution 1
You do not need to worry about this warning. It is nonsense in a lot of cases, including yours.
The documentation of doubleValue
does not say that it returns something close enough to HUGE_VAL
or -HUGE_VAL
on overflow. It says that it returns exactly these values in case of overflow.
In other words, the value returned by the method in case of overflow compares ==
to HUGE_VAL
or -HUGE_VAL
.
Why does the warning exist in the first place?
Consider the example 0.3 + 0.4 == 0.7
. This example evaluates to false. People, including the authors of the warning you have met, think that floating-point ==
is inaccurate, and that the unexpected result comes from this inaccuracy.
They are all wrong.
Floating-point addition is “inaccurate”, for some sense of inaccurate: it returns the nearest representable floating-point number for the operation you have requested. In the example above, conversions (from decimal to floating-point) and floating-point addition are the causes of the strange behavior.
Floating-point equality, on the other hand, works pretty much exactly as it does for other discrete types. Floating-point equality is exact: except for minor exceptions (the NaN value and the case of +0. and -0.), equality evaluates to true if and only if the two floating-point numbers under consideration have the same representation.
You don't need an epsilon to test if two floating-point values are equal. And, as Dewar says in substance, the warning in the example 0.3 + 0.4 == 0.7
should be on +
, not on ==
, for the warning to make sense.
Lastly, comparing to within an epsilon means that values that aren't equal will look equal, which is not appropriate for all algorithms.
Solution 2
In this case, try using >=
and <=
.
Solution 3
If you are sure about your comparison and you want tell it to clang, surround your code with:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wfloat-equal"
/* My code triggering the warnings */
#pragma clang diagnostic pop
Solution 4
Probably not necessary for this very simple use case, but here is an example of how you would account for the potential discrepancy if you were trying check if a number is equal to -1:
#include <float.h>
#include <math.h>
int main(void) {
float sales = -1;
// basically if sales == -1
if (fabs(1 + sales) < FLT_EPSILON) {
return 0;
}
}
Related videos on Youtube
NoobOverflow
Updated on November 02, 2020Comments
-
NoobOverflow over 3 years
I have a string that I convert to a double like this:
double d = [string doubleValue];
The documentation for
doubleValue
tells us that upon overflow, this method returns eitherHUGE_VAL
or-HUGE_VAL
. This is how I checked for this earlier:if (d == HUGE_VAL || d == -HUGE_VAL) //overflow
Now, since adding the new "-Weverything" warning flag, the compiler now complains that
Comparing floating point with == or != is unsafe
How can I resolve this issue? How should I be doing these comparisons?
I also have the same question about comparing two "normal" floating point numbers (i.e. not "HUGE_VAL" ones). For instance,
double a, b; //... if (a != b) //this will now yield the same warning //...
How should this be resolved?
-
NoobOverflow almost 12 yearsWhat epsilon can be recommended? Will this approach also work with those HUGE_VALs? And, does your answer imply that Pascal's comment above is incorrect?
-
NoobOverflow almost 12 yearsAlso, isn't there any nice built-in function like this that I can use? It's kind of pity that I have to tuck my own custom function in some global place.
-
NoobOverflow almost 12 yearsMakes sense. On the other hand, it does also seem to make sense to warn about this, in some ways. Is there any way to tell the compiler more explicitly that: "Yes, I'm absolutely sure that I'm comparing these floats as being exactly equal to each other, and don't warn me." ? So in cases where I am sure, the warning wouldn't appear. In other cases, I would do the epsilon test.
-
Pascal Cuoq almost 12 years@NoobOverflow If you really wish to make the compiler happy, test whether
d >= HUGE_VAL || d <= -HUGE_VAL
. It computes the same thing without using==
. But I recommend not using the warning. -
Pascal Cuoq almost 12 years@NoobOverflow Actually, my previous recommendation makes the code less readable, but if Objective-C has a function
is_infinity
, using that would make the code more readable. The specification of the function should be that it returns true exactly for the two valuesHUGE_VAL
and-HUGE_VAL
, and I recommend you use it for just that. -
Brainbot almost 12 yearsPascal is right, you probably won't need to worry about this warning. There is a built-in epsilon though, take a look at this
-
NoobOverflow almost 12 yearsI'm starting to think that your ">= and <=" idea could be used to create both "double_equal" and "double_epsilon_equal" functions, which could then be used as appropriate. What do you think?
-
Pascal Cuoq almost 12 years@NoobOverflow I think you had better use -Weverything -Wno-float-equal rather than make your program ugly. thedailywtf.com is full of programs that redefine existing things, from
#define TWELVE 12
toconst float pi = (22.0f/7);
. You don't want to end up there. -
echristopherson almost 12 yearsWell, it doesn't compare for strict equality, so it pacifies the compiler. And since nothing can be greater than HUGE_VAL or less than -HUGE_VAL, it works just the same.
-
echristopherson almost 12 years@Brainbot That's C++. For C, you'd have to
include <float.h>
and useDBL_EPSILON
. -
Nico over 11 yearsThis fails for comparisons to
HUGE_VAL
, which was the original goal. If the number returnsHUGE_VAL
, the subtraction's result is NaN; if it returns a finite value, subtracting it fromHUGE_VAL
evaluates to infinity. -
Adam Kaplan about 6 yearsThe fact that it required 260 words to explain when and how floating point equality operations can be inaccurate in itself justifies the decision of the compiler engineers. This is a warning because while not wrong to do the operation, it’s a hot spot for bugs. This is especially true for junior engineers... I’ve seen quite a few production issues due to exactly the same behavior. The warning is not nonsense, it’s defensive and smart.
-
Pascal Cuoq about 6 years@AdamKaplan It only requires 260 words because 26 million superstitious, harmful words have been written along the lines of “a floating-point value does not represent a precise rational”. If you are going to hold the length of the answer against it, then most of Jon Skeet's recommendations are bollock by the same reasoning.
-
dragonroot almost 6 yearsThis needs
#pragma clang diagnostic push
before the first line -
yageek almost 6 yearsOups. You are completely right! I’ll update It as soon as I am in front of a computer.
-
dmckee --- ex-moderator kitten over 4 yearsUhg. I have a few dozon carefully considered instances of intended exact floating point comparison scattered through moderate sized code. This incantation shows that the clang authors have thought carefully about what an extensible pragma system looks like, but I was hoping for something a little more pithy. Like the old fashioned
/* FALLTHROUGH */
comment. -
SO_fix_the_vote_sorting_bug almost 3 yearsBut a floating-point value doesn't always represent a precise rational. That's simply a hard fact about finite precision. Of course, an imprecise float can still equal another, so I understand your point that addition is the problem here. Also, being lazy about implicit conversion from
double
->float
can cause==
to not give results that the programmer wanted, at least in C (not sure about Objective-C). -
Pascal Cuoq almost 3 years@jdk1.0 A finite floating-point number does represent exactly one rational. If a piece of code sets a
float
variablex
to1.0f
, thenx == 1.0f
is guaranteed to be true after the assignment. I think this was already clear in my answer and I don't see what else I could add.