Comparing floating point with == or != is unsafe

I updated from JUCE 7.0.3 to 7.0.5 develop today.

I am now getting dozens of warnings I’ve never seen before:

“Comparing floating point with == or != is unsafe”

1 - Where is this new warning coming from? I use Projucer; is it a new “recommended compiler warning flag” that was added?

2 - if you have something like this, where minPoint and maxPoint are floats:

if (minPoint.x == maxPoint.x)  //"WARNING: Comparing floating point with == or != is unsafe"

OR

if (minPoint.x != maxPoint.x)  //"WARNING: Comparing floating point with == or != is unsafe"

… is the fix just to do this everywhere?

if (approximatelyEqual(minPoint.x, maxPoint.x))

OR

if (! approximatelyEqual(minPoint.x, maxPoint.x))

Just want to verify this approach before I change everything. Thanks.

That’s exactly it.

As you pointed out use approximatelyEqual() to silence the error. You can also use exactlyEqual() if you are absolutely sure you want an exact match.

3 Likes

I mean it depends what you are doing?

If you do something like this it’ll be fine and I think this is fine code:

float x { 0.0f };

[....] // stuff happens

if (x != 0.0f) // checking to see if it's changed
{

}

But code like this is probably an infinitely long loop waiting to happen:

float a = b / 100.0f;
float c = b;

while (c != b * 2.0f)
  c += a;

As long as nobody comes up with:

if (x === oldX)

:stuck_out_tongue:

2 Likes

I think comparing to 0 is the only safe comparison to do as it will have the same bit representation no matter what register it is in.

1 Like

I’ve had situations where I’ve had a floating point value moving towards a target. Clamping is applied so it never exceeds the target value. In this case comparing a float to the target value is fine and you wouldn’t want an approximate check because it’d fall short.

However, arbitrarily doing arithmetic on a float then depending on it hitting a specific value is asking for trouble. Although on the basis that programmers should know how floats work I’m not sure why anyone would do this.

Wanted to share two really useful resources on this topic that I’ve run into lately:

  1. Martin Hořeňovský, the maintainer of Catch2, has a great writeup on float comparison:
  1. Julia Evans released a cool zine that goes into detail and visualizes floating point and integer representations How Integers and Floats Work
6 Likes

Yeah, from reading the first article I think that’s why a ULP-based approximatelyEqual method argument in JUCE would be useful, though I guess one could argue that the “disadvantage” discussed in the article might present itself if developers didn’t understand how to use it properly. Used properly however, it could allow for some very powerful approaches to signal output analysis, especially if you could leverage higher precision comparisons in double-based and long double-based signal computations vs float. It might make it hard to provide a single appropriate ULP default that would make sense for all templated types however.

The most surprising thing to me about float comparisons is that the FPU x87 registers have 80-bits so can store a higher precisions. IIUC this means that a float that has been processed in x87 registers, then stored back to memory effectively looses precision (though not in terms of what would be expected).

But, if you then do the same calculation with a new set of numbers, all in x87 registers, then compare this with your previous result, as the previously stored number doesn’t magically regain its precision, they could be different bit patterns.
en.wikipedia.org/wiki/X87

1 Like