29

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 either HUGE_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
  • 1,208
  • 3
  • 14
  • 19

5 Answers5

46

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.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • Makes 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. – NoobOverflow Jul 10 '12 at 21:37
  • 2
    @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 Jul 10 '12 at 21:47
  • @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 values `HUGE_VAL` and `-HUGE_VAL`, and I recommend you use it for just that. – Pascal Cuoq Jul 10 '12 at 21:58
  • I'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? – NoobOverflow Jul 10 '12 at 22:26
  • @NoobOverflow I think you had better use -Weverything -Wno-float-equal rather than make your program ugly. http://thedailywtf.com/ is full of programs that redefine existing things, from `#define TWELVE 12` to `const float pi = (22.0f/7);`. You don't want to end up there. – Pascal Cuoq Jul 10 '12 at 22:33
  • 2
    The 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. – Adam Kaplan Jan 31 '18 at 20:15
  • 4
    @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. – Pascal Cuoq Feb 01 '18 at 23:07
  • But 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). – SO_fix_the_vote_sorting_bug Jun 05 '21 at 19:37
  • 1
    @jdk1.0 A finite floating-point number does represent exactly one rational. If a piece of code sets a `float` variable `x` to `1.0f`, then `x == 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. – Pascal Cuoq Jun 10 '21 at 08:59
12

In this case, try using >= and <=.

echristopherson
  • 6,974
  • 2
  • 21
  • 31
  • 3
    Well, 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 Jul 11 '12 at 18:31
7

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
yageek
  • 4,115
  • 3
  • 30
  • 48
  • 1
    This needs `#pragma clang diagnostic push` before the first line – dragonroot Jul 05 '18 at 02:31
  • Oups. You are completely right! I’ll update It as soon as I am in front of a computer. – yageek Jul 05 '18 at 05:49
  • Uhg. 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. – dmckee --- ex-moderator kitten Oct 24 '19 at 22:19
1

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;
  }
}
ericcurtin
  • 1,499
  • 17
  • 20
-2

Floats should not be compared with == or != due to inaccuracy of the float type, which could result in unexpected errors when using these operators. You should test if the floats lie within a distance of each other instead ( called "Epsilon" most of the time ).

It could look like this:

const float EPSILON = 1.0f; // use a really small number instead of this

bool closeEnough( float f1, float f2)
{
    return fabs(f1-f2)<EPSILON; 
    // test if the floats are so close together that they can be considered equal
}
Brainbot
  • 365
  • 1
  • 8
  • 1
    What 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 Jul 10 '12 at 21:09
  • 1
    Also, 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 Jul 10 '12 at 21:12
  • Pascal is right, you probably won't need to worry about this warning. There is a built-in epsilon though, take a look at [this](http://www.cplusplus.com/reference/std/limits/numeric_limits/) – Brainbot Jul 10 '12 at 21:58
  • 2
    @Brainbot That's C++. For C, you'd have to `include ` and use `DBL_EPSILON`. – echristopherson Jul 11 '12 at 18:34
  • 3
    This fails for comparisons to `HUGE_VAL`, which was the original goal. If the number returns `HUGE_VAL`, the subtraction's result is NaN; if it returns a finite value, subtracting it from `HUGE_VAL` evaluates to infinity. – Peter Hosey Jan 20 '13 at 23:50