8
$\begingroup$

When does it typically make sense in programming to be testing the equality of two floating point numbers?

i.e.

a == b 

where both a & b are floats.

My naive impression is that one would always test the difference against some tolerance epsilon.

Am I wrong? Can testing the equality of floats be meaningful in certain contexts?

Any examples from the wild? i.e. From real codebases or applications out there on git etc.

PS. I'm implicit assuming that using the equality operator on floats is indeed meaningful in some contexts; otherwise why would most programming languages allow it.

$\endgroup$
  • 1
    $\begingroup$ Since this is about practical issues with numerical algorithms, I'm migrating to Computational Science. $\endgroup$ – Raphael Nov 12 '16 at 18:44
  • $\begingroup$ It might help to know what problem you're trying to solve by asking this particular question? meta.stackexchange.com/q/66377 $\endgroup$ – Kirill Nov 12 '16 at 22:21
  • 1
    $\begingroup$ @Krill The problem is curiosity. I've known the advice about when NOT to use it. But if the operator is still allowed then there must be cases where it is indeed correct to use it. But those ue cases were not obvious. So I wanted to know. And the answers bring out some good examples. $\endgroup$ – curious_cat Nov 13 '16 at 3:31
5
$\begingroup$

My naive impression is that one would always test the difference against some tolerance epsilon.

A not-naive implementation of this idea should probably take advantage of the equality comparison operator to handle the important special cases that the IEEE 754 standard contemplates (infinities, denormalized numbers...).

Take a look at How should I do floating point comparison?:

...

if (a == b)  // shortcut, handles infinities
  return true;

if (a == 0 || b == 0 || diff < Float.MIN_NORMAL) {
  // a or b is zero or both are extremely close to it
  // relative error is less meaningful here

...


Sometimes there really is one answer that is correct and you want exact equality. Testing the correctness of an implementation is a good example (i.e. There are Only Four Billion Floats–So Test Them All!).

$\endgroup$
  • $\begingroup$ Isn't the test for a==0 inside the OR clause made redundant by diff < Float.MIN_NORMAL? $\endgroup$ – curious_cat Nov 13 '16 at 12:20
3
$\begingroup$

An obvious example where == is ok, is when a and b is the same number i.e. a=c; b=c, for example to check if a and b were initialized the same way. Of course, |a-b| < epsilon would also work here. The only problem is, how small is epsilon?

Also, a == b would compile into one instruction while |a-b| < epsilon would take quite a few.

$\endgroup$
  • $\begingroup$ Thanks! When would one, in a typical coding situation, want to know if a and b were initialized the same way? Can you post any code snippets using this? I mean couldn't one just examine the source & tell? $\endgroup$ – curious_cat Nov 13 '16 at 3:32
  • 1
    $\begingroup$ @curious_cat suppose you're making a game like minecraft, with a grid of tiles, suppose these tiles have float coordinates, for whatever reason, and they were initialized in an obvious loop like for x in [0..n] step w: for y in [0..n] step w: add_tile(x, y). In that case t1.x == t2.x is a perfectly safe way to test if the two tiles are in one plane. You only need |a-b|<epsilon when a and b are results of different computations that were supposed to get the same value. But very often you have a result of one computation stored in two variables, or overwrite variables with constants. $\endgroup$ – Karolis Juodelė Nov 13 '16 at 9:10
  • $\begingroup$ Very nice example! Thanks. Makes a lot of sense. The first real sounding use case I got really! You should add that to your answer! $\endgroup$ – curious_cat Nov 13 '16 at 12:19
2
$\begingroup$

An extreme example: IBM was the first company building processors with a fused multiply-add instruction. Using that instruction, they created a very fast method for calculating square roots according to the IEEE-754 standard. This method fails for one single input value 1 ≤ x < 4: If x is the largest number representable as a floating point number that is less than 4, then the result would be rounded incorrectly.

So somewhere in their implementation, they check whether x equals that one specific value. They want to recognise that value, and not any others.

$\endgroup$
  • $\begingroup$ Thanks! What value is that, any idea? I couldn't find any references to read on this. Can you post any links? $\endgroup$ – curious_cat Nov 13 '16 at 3:33
2
$\begingroup$

My naive impression is that one would always test the difference against some tolerance epsilon. Am I wrong? Can testing the equality of floats be meaningful in certain contexts?

There is not a unique recipes. In this article there is an exhaustive treatment, where you can found a complete answer with technical and code.

In summary, there are mainly 3 cases:

  • comparing against zero
  • comparing against a non-zero
  • comparing two arbitrary numbers

Your idea to use a comparison against a tolerance is good for some cases, but there is also a technic based on Unit in the last place (ULP), described inside the article

I'm implicit assuming that using the equality operator on floats is indeed meaningful in some contexts; otherwise why would most programming languages allow it.

As above, there are situations where you can use it, but be warned. For example the gcc compiler has a warning:

warning: comparing floating point with == or != is unsafe

Update

I add some considerations above this argument also they are not strictly related to the case a == b.

Equality with expression

Considering the case:

a + b == c 

with a b c floats. This is what we are coding, but with more pedantic from a math point of view we are doing: $$ \begin{aligned} a \oplus b &== c \\ \mathbf{fl}(\mathbf{fl}(a) + \mathbf{fl}(b)) &== \mathbf{fl}(c)\\ \end{aligned} $$ where

  • $\oplus$ machine add
  • $\mathbf{fl}(x)$ is the floating point representation of the number $x$, i.e. the real machine number.

With this operation we are introducing a possible estimated error (give the bound) from: $$ \left| \frac{a}{a+b}\right|\mathit{err}_a + \left| \frac{b}{a+b}\right|\mathit{err}_b $$ where $$\mathit{err}_x = \frac{\lvert x - \mathbf{fl}(x) \rvert}{\lvert x \rvert}$$

So in this case there the use of == is more delicate.

Porting in different environments

When we port a code in different environments (different machine) we can obtain some different result (for example try to think to unit test). Also in the case the use of == is delicate.

$\endgroup$
  • $\begingroup$ Ah! So it does generate a warning. Thanks. So my impression now is that in 99% coding cases a float equality comparison is an error but it is still allowed for those very few corner cases where it may be making sense. $\endgroup$ – curious_cat Nov 13 '16 at 3:29
  • 1
    $\begingroup$ @curious_cat Yes, I agree with you. I add some note. $\endgroup$ – Mauro Vanzetto Nov 13 '16 at 11:41
0
$\begingroup$

Your "naive impression" is not a "naive impression" - it is the result of reading about floating point arithmetic and only half understanding it. The "naive impression" would be the obvious impression that to find out whether a and b are equal, you ask if they are equal.

There are plenty of situations where all you need to know is whether two floating point numbers are equal or not. There are plenty of situations where you know that there are either no rounding errors, or no variations due to rounding errors. Like converting decimal numbers to floating point, which will be deterministic in any sane implementation.

Here's a good one: Someone claimed that for any two floating point numbers a, b the result of (b + a + b) and (b + b + a) are the same and you want to test that claim. Try doing that without comparing two floating point numbers for equality.

Here's a better one: Try to create a set of floating point numbers.

$\endgroup$
-1
$\begingroup$

See this question: https://cs.stackexchange.com/questions/19013/is-transitivity-required-for-a-sorting-algorithm?rq=1

It explains very nicely how floating point comparison with some epsilon can lead to a complete and total failure of the quicksort algorithm.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.