"-Weverything" yielding "Comparing floating point with == or != is unsafe"

23,931

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;
  }
}
Share:
23,931

Related videos on Youtube

NoobOverflow
Author by

NoobOverflow

Updated on November 02, 2020

Comments

  • NoobOverflow
    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 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
    NoobOverflow almost 12 years
    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
    NoobOverflow almost 12 years
    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
    NoobOverflow almost 12 years
    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.
  • Pascal Cuoq
    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
    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 values HUGE_VAL and -HUGE_VAL, and I recommend you use it for just that.
  • Brainbot
    Brainbot almost 12 years
    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
  • NoobOverflow
    NoobOverflow almost 12 years
    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?
  • Pascal Cuoq
    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 to const float pi = (22.0f/7);. You don't want to end up there.
  • echristopherson
    echristopherson almost 12 years
    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
    echristopherson almost 12 years
    @Brainbot That's C++. For C, you'd have to include <float.h> and use DBL_EPSILON.
  • Nico
    Nico over 11 years
    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.
  • Adam Kaplan
    Adam Kaplan about 6 years
    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.
  • Pascal Cuoq
    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
    dragonroot almost 6 years
    This needs #pragma clang diagnostic push before the first line
  • yageek
    yageek almost 6 years
    Oups. You are completely right! I’ll update It as soon as I am in front of a computer.
  • dmckee --- ex-moderator kitten
    dmckee --- ex-moderator kitten over 4 years
    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.
  • SO_fix_the_vote_sorting_bug
    SO_fix_the_vote_sorting_bug almost 3 years
    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).
  • Pascal Cuoq
    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 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.