5.6 — Relational operators and floating point comparisons

Relational operators are operators that let you compare two values. There are 6 relational operators:

Operator Symbol Form Operation
Greater than > x > y true if x is greater than y, false otherwise
Less than < x < y true if x is less than y, false otherwise
Greater than or equals >= x >= y true if x is greater than or equal to y, false otherwise
Less than or equals <= x <= y true if x is less than or equal to y, false otherwise
Equality == x == y true if x equals y, false otherwise
Inequality != x != y true if x does not equal y, false otherwise

You have already seen how most of these work, and they are pretty intuitive. Each of these operators evaluates to the boolean value true (1), or false (0).

Here’s some sample code using these operators with integers:

And the results from a sample run:

Enter an integer: 4
Enter another integer: 5
4 does not equal 5
4 is less than 5
4 is less than or equal to 5

These operators are extremely straightforward to use when comparing integers.

Boolean conditional values

By default, conditions in an if statement or conditional operator (and a few other places) evaluate as Boolean values.

Many new programmers will write statements like this one:

This is redundant, as the == true doesn’t actually add any value to the condition. Instead, we should write:

Similarly, the following:

is better written as:

Best practice

Don’t add unnecessary == or != to conditions. It makes them harder to read without offering any additional value.

Comparison of floating point values

Consider the following program:

Variables d1 and d2 should both have value 0.01. But this program prints an unexpected result:

d1 > d2

If you inspect the value of d1 and d2 in a debugger, you’d likely see that d1 = 0.0100000000000005116 and d2 = 0.0099999999999997868. Both numbers are close to 0.01, but d1 is greater than, and d2 is less than.

If a high level of precision is required, comparing floating point values using any of the relational operators can be dangerous. This is because floating point values are not precise, and small rounding errors in the floating point operands may cause unexpected results. We discussed rounding errors in lesson 4.8 -- Floating point numbers if you need a refresher.

When the less than and greater than operators (<, <=, >, and >=) are used with floating point values, they will usually produce the correct answer (only potentially failing when the operands are almost identical). Because of this, use of these operands with floating point operands can be acceptable, so long as the consequence of getting a wrong answer when the operands are similar is slight.

For example, consider a game (such as Space Invaders) where you want to determine whether two moving objects (such as a missile and an alien) intersect. If the objects are still far apart, these operators will return the correct answer. If the two objects are extremely close together, you might get an answer either way. In such cases, the wrong answer probably wouldn’t even be noticed (it would just look like a near miss, or near hit) and the game would continue.

Floating point equality

The equality operators (== and !=) are much more troublesome. Consider operator=, which returns true only if its operands are exactly equal. Because even the smallest rounding error will cause two floating point numbers to not be equal, operator= is at high risk for returning false when a true might be expected.
Operator!= has the same problem, just in converse.

For this reason, use of these operators with floating point operands should be avoided.


Avoid using operator== and operator!= with floating point operands.

So how can we reasonably compare two floating point operands to see if they are equal?

The most common method of doing floating point equality involves using a function that looks to see if two numbers are almost the same. If they are “close enough”, then we call them equal. The value used to represent “close enough” is traditionally called epsilon. Epsilon is generally defined as a small positive number (e.g. 0.00000001, sometimes written 1e-8).

New developers often try to write their own “close enough” function like this:

fabs() is a function in the <cmath> library that returns the absolute value of its argument. So fabs(a - b) <= epsilon checks if the distance between a and b is less than whatever epsilon value representing "close enough" was passed in. If a and b are close enough, the function returns true to indicate they're equal. Otherwise, it returns false.

While this function can work, it's not great. An epsilon of 0.00001 is good for inputs around 1.0, too big for inputs around 0.0000001, and too small for inputs like 10,000. This means every time we call this function, we have to pick an epsilon that's appropriate for our inputs. If we know we're going to have to scale epsilon in proportion to our inputs, we might as well modify the function to do that for us.

Donald Knuth, a famous computer scientist, suggested the following method in his book “The Art of Computer Programming, Volume II: Seminumerical Algorithms (Addison-Wesley, 1969)”:

In this case, instead of epsilon being an absolute number, epsilon is now relative to the magnitude of a or b.

Let's examine in more detail how this crazy looking function works. On the left side of the <= operator, fabs(a - b) tells us the distance between a and b as a positive number.

On the right side of the <= operator, we need to calculate the largest value of "close enough" we're willing to accept. To do this, the algorithm chooses the larger of a and b (as a rough indicator of the overall magnitude of the numbers), and then multiplies it by epsilon. In this function, epsilon represents a percentage. For example, if we want to say "close enough" means a and b are within 1% of the larger of a and b, we pass in an epsilon of 0.01 (1% = 1/100 = 0.01). The value for epsilon can be adjusted to whatever is most appropriate for the circumstances (e.g. an epsilon of 0.002 means within 0.2%).

To do inequality (!=) instead of equality, simply call this function and use the logical NOT operator (!) to flip the result:

Note that while the approximatelyEqual() function will work for most cases, it is not perfect, especially as the numbers approach zero:

Perhaps surprisingly, this returns:


The second call didn't perform as expected. The math simply breaks down close to zero.

One way to avoid this is to use both an absolute epsilon (as we did in the first approach) and a relative epsilon (as we did in Knuth's approach):

In this algorithm, we first check if a and b are close together in absolute terms, which handles the case where a and b are both close to zero. The absEpsilon parameter should be set to something very small (e.g. 1e-12). If that fails, then we fall back to Knuth's algorithm, using the relative epsilon.

Here's our previous code testing both algorithms:


You can see that with an appropriately picked absEpsilon, approximatelyEqualAbsRel() handles the small inputs correctly.

Comparison of floating point numbers is a difficult topic, and there's no "one size fits all" algorithm that works for every case. However, the approximatelyEqualAbsRel() should be good enough to handle most cases you'll encounter.

5.7 -- Logical operators
5.5 -- Comma and conditional operators

51 comments to 5.6 — Relational operators and floating point comparisons

  • Aryan

    Hey Alex!
    I made a similar 'close enough' program using Knuth's algorithm and every thing was working fine but I was curious to know the value of the epilson and I can't use debugger (I haven't figured it out in xcode of how to use the debugger). The program was :

    The value of epilson came out to be 4.94066e-324! Now 3 questions

    1 Is this epilson default value ?
    2 I use d1 and d2 values to be the same(-2), then how is the conditional statement suppose to work?
    3 I also checked Smokeycow program and your reply to it so I thought shouldn't his version and knuth version work the same if both d1 and d2 values were to be same (say -2).

    • Alex

      Epsilon is something that needs to be set by you, based on how much tolerance you need. Your epsilon is uninitialized, which means whatever garbage was in the memory allocated to epsilon is now being interpreted as a double. That's why you're getting 4.94066e-324.

      Try initializing epsilon to something small, like 1.0e-12.

  • Tony

    wow man starting to lose hope with all the math examples. ):
    How much math do you think is needed for Isometric 2D games?

    • Alex

      > How much math do you think is needed for Isometric 2D games?

      Totally depends on what type of isometric game you're interested in creating. For a simple tile-based game, probably not so much. For a shooter or other physics-based game, probably a lot.

      Isometric games aside, you'll use relational operators in almost every program you write, so make sure you understand them. :)

  • You said that the d1evaluates to 0.0099999999999997868 and d2 to 0.0100000000000005116 in the above program. But when I use cout to print the value of d1 and d2, the program gives me 0.01 as output for both variables. Please let me know if I m missing something.

    I m not sure but I think you missed your else statement in the following program:

    If the code is okay, then what will happen if the if's expression evaluates to true. will return true. But what after that. Does the compiler then Ignores the line 10 where we are returning Knuth's method.

    • Alex

      std::cout prints variables to 6 significant digits by default. You can see their actual values by using std::setprecision() to ask std::cout to show more precision, or by looking at them in a debugger.

      There's no need for an else statement in approximatelyEqualAbsRel. If the return statement is executed, the function returns a value to the caller right then and there, and the rest of the function does not execute. So effectively, everything after the if statement is essentially the else case, just implicitly.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">