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:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> int main() { std::cout << "Enter an integer: "; int x{}; std::cin >> x; std::cout << "Enter another integer: "; int y{}; std::cin >> y; if (x == y) std::cout << x << " equals " << y << "\n"; if (x != y) std::cout << x << " does not equal " << y << "\n"; if (x > y) std::cout << x << " is greater than " << y << "\n"; if (x < y) std::cout << x << " is less than " << y << "\n"; if (x >= y) std::cout << x << " is greater than or equal to " << y << "\n"; if (x <= y) std::cout << x << " is less than or equal to " << y << "\n"; return 0; } |

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:

1 |
if (b1 == true) ... |

This is redundant, as the `== true`

doesn’t actually add any value to the condition. Instead, we should write:

1 |
if (b1) ... |

Similarly, the following:

1 |
if (b1 == false) ... |

is better written as:

1 |
if (!b1) ... |

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:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#include <iostream> int main() { double d1(100 - 99.99); // should equal 0.01 double d2(10 - 9.99); // should equal 0.01 if (d1 == d2) std::cout << "d1 == d2" << "\n"; else if (d1 > d2) std::cout << "d1 > d2" << "\n"; else if (d1 < d2) std::cout << "d1 < d2" << "\n"; return 0; } |

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 kind of problem.

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

Warning

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:

1 2 3 4 5 6 7 8 |
#include <cmath> // for fabs() // epsilon is an absolute value bool isAlmostEqual(double a, double b, double epsilon) { // if the distance between a and b is less than epsilon, then a and b are "close enough" return fabs(a - b) <= epsilon; } |

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)”:

1 2 3 4 5 6 7 |
#include <cmath> // for fabs() // return true if the difference between a and b is within epsilon percent of the larger of a and b bool approximatelyEqual(double a, double b, double epsilon) { return fabs(a - b) <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); } |

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:

1 2 |
if (!approximatelyEqual(a, b, 0.001)) std::cout << a << " is not equal to " << b << "\n"; |

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

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <iostream> #include <cmath> // for fabs() // return true if the difference between a and b is within epsilon percent of the larger of a and b bool approximatelyEqual(double a, double b, double epsilon) { return fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); } int main() { // a is really close to 1.0, but has rounding errors, so it's slightly smaller than 1.0 double a = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1; // First, let's compare a (almost 1.0) to 1.0. std::cout << approximatelyEqual(a, 1.0, 1e-8) << "\n"; // Second, let's compare a-1.0 (almost 0.0) to 0.0 std::cout << approximatelyEqual(a-1.0, 0.0, 1e-8) << "\n"; } |

Perhaps surprisingly, this returns:

1 0

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):

1 2 3 4 5 6 7 8 9 10 11 |
// return true if the difference between a and b is less than absEpsilon, or within relEpsilon percent of the larger of a and b bool approximatelyEqualAbsRel(double a, double b, double absEpsilon, double relEpsilon) { // Check if the numbers are really close -- needed when comparing numbers near zero. double diff{ fabs(a - b) }; if (diff <= absEpsilon) return true; // Otherwise fall back to Knuth's algorithm return diff <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * relEpsilon); } |

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:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
#include <iostream> #include <cmath> // for fabs() // return true if the difference between a and b is within epsilon percent of the larger of a and b bool approximatelyEqual(double a, double b, double epsilon) { return fabs(a - b) <= ((fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon); } bool approximatelyEqualAbsRel(double a, double b, double absEpsilon, double relEpsilon) { // Check if the numbers are really close -- needed when comparing numbers near zero. double diff{ fabs(a - b) }; if (diff <= absEpsilon) return true; // Otherwise fall back to Knuth's algorithm return diff <= ( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * relEpsilon); } int main() { // a is really close to 1.0, but has rounding errors double a = 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1; std::cout << approximatelyEqual(a, 1.0, 1e-8) << "\n"; // compare "almost 1.0" to 1.0 std::cout << approximatelyEqual(a-1.0, 0.0, 1e-8) << "\n"; // compare "almost 0.0" to 0.0 std::cout << approximatelyEqualAbsRel(a-1.0, 0.0, 1e-12, 1e-8) << "\n"; // compare "almost 0.0" to 0.0 } |

1 0 1

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 |

Index |

5.5 -- Comma and conditional operators |

In the first paragraph under "Floating point equality" you use

where you mean

.

Thanks! Fixed.

In the beginning of the floating point equality paragraph there seems to be a typo, operator = instead of ==

Fixed. Thanks!

In this example, do we not need 'else' before the second expression?

No. If the if's condition is true, the function returns. A return is the last thing a function does.

"This handles the case where a and b are both close to zero"

Nope. Or at least not complete.

This handles the case where a and b are very close one to each other.

LE: I understand that this case is there because Knuth's algorithm fails when both numbers are near 0. But this case will also manage every 'very close a and b case', even when a and be are very large. The answer will be true, same answer that Knuth's algorithm would do anyway, so there is no conflict here.

Hi Alex!

Why not

?

The original algorithm was written in C, not C++, and I didn't convert it. I'll flag this lesson for an C++ overhaul.

Quick question:

Am I right to assume fabs(a) < fabs(b) is used instead of a < b to work around negative numbers?

EDIT: So, fabs are like absolute markers I've had in math? I guess the symbols I used were: |number|

Is abs() a function as well?

And why is the double diff created? Why not just use fabs(a - b). I'm guessing I'm missing something?

Correct, `std::fabs` (Or `std::abs`) returns the [abs]olute value of a number, ie. turns - into +.

`abs` is a function too, but it's the C-version which only handles int. Use `std::abs`, it works for all numbers.

> And why is the double diff created?

Do you mean in `approximatelyEqualAbsRel`? Knuth's method fails for very small numbers, an absolute epsilon solves this.

Hi, thank you for responding!

What I meant with the diff variable:

*, why not if (fabs(a - b) <= absEpsilon)?

And if I had to choose std::fabs or std::abs.

And why is with std:: beter than without std::?

edit: the code thing fails, must have done something wrong but can't spot it. sorry for that...

Because we then we'd need to call `fabs` again in line 9. If nothing changes in-between 2 calls to a function, 1 call is enough.

ok thanks!

If you are new to programming, it might be helpful to see a rewrite of Knuth's algorithm:

#include <cmath>

bool approximatelyEqual(double a, double b, double epsilon)

{

double fa = fabs(a);

double fb = fabs(b);

double denom = (fa > fb) ? fa : fb;

return ( fabs(a-b)/denom <= epsilon );

}

The original in the text (above) is better. The rule is: whenever possible, use multiplication instead of division.

Also, uniform initialization is better practice than copy initialization:

double fa { fabs(a) };

...

Hi,

I have not learned.

How does the 'fabs()' function work .

Whereas 'fabs()' gets one argument, how can it notice what we mean which numbers ?

"fabs() is a function in the <cmath> library that returns the absolute value of its parameter"

https://en.cppreference.com/w/cpp/numeric/math/fabs

I'm having a problem with this code here and I don't know why...

So the output is unexpected. For example, if x is less than or greater than y, the program does not output that it is not equal. And no matter what the integer for x is , I always end up getting "0 is less than [y]"

doesn't do what you were expecting. This parses as x = (!y).

You actually want:

Thank you kindly.

We discuss rounding errors in detail in section 2.5 -- floating point numbers.

discuss should be discussed.

Why in the last code, the second function call returns 0?

The browser javascript console returns 0.9999999999999999 for (0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1), and returns -1.1102230246251565e-16 for (0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1)-1.0.

According to https://www.calculatorsoup.com/calculators/math/scientific-notation-converter.php, -1.1102230246251565e-16 is -0.00000000000000011102230246251565.

Isn't it close enough to 0?

Yes, but as noted in the lesson, the math for approximatelyEqual() breaks down close to 0. If you're curious as to why, you can assign

`fabs(a - b)`

and`( (fabs(a) < fabs(b) ? fabs(b) : fabs(a)) * epsilon)`

to doubles and inspect them in a debugger.That's the impetus for the improved version.

I am really confused with the ? and : alternative to if and else. I think that if and else is soooooooo much easier to read. Could you change the AppproximatelyEqual function to if and else format please in this comment section?

If/else might be easier to read in isolation, but sometimes it can be much less concise, which can actually make it harder to read.

Here's the requested function using an if/else. It went from 1 line to 6!

hi,

can you please explain this funktion here

I understand that if b is smaller than a that we do fab(a) * relEpsilon but that if a is smaller than b then we just return b?

shoudnt it be like this?

(also, from where did your website get this pic from? I cant remember which site I gave it to and would like to deactivate it, thx)

> shoudnt it be like this?

Without knowing what you the code is supposed to achieve, there's no way to answer this.

> where did your website get this pic from?

gravatar

thx for the reply, I was just confused by the function that I posted, its from this site. I thought the idea behind the function from Donald Knuth, was that it first checks which number (a or b) is smaller and then multiply that with relEpsilon. with how the function is written

I dont see how it would do a*relEpsilon, I only see that funtion doing b*relEpsilon if b is smaller than a.

> I thought the idea behind the function from Donald Knuth [...]

If that's the case the multiplication has to be moved out of the conditional statement

ahhh now I understand it, thank you very much!!!

Wouldn't it be possible to use the floating point standard to just compare a specified amount of significant digits (bitwise)? While that may be less intuitive, and possibly not as precise, it could be a much quicker way. Or am I wrong?

Hi DecSco!

In order for this to be more efficient it'd need to be implemented on CPU-level.

If you're doing this through software the overhead by extracting the bits would be higher than the performance gain.

I thought if you use bitwise operators, it is in fact on that level - I mean, that's why you'd use C/C++ rather than say Python. Or do you mean you'd need specific hardware for that like FPUs?

Hardware is what I meant. You wouldn't need a new component, it could be added to the FPU. Shrinking date types isn't usually done for performance gain but for network traffic reduction. For example ARMA, or PUPG I think is using a custom data type to transmit radar information.

Dear Teacher, please let me add to your comment: "The math simply breaks down close to zero", that the machines break down after 64th binary digit, and sometimes before that. Regards.

I try the floating point comparison problem. I made some changes to the code to figure out what are the value of d1 and d2. I using codeblocks 17.12 IDE and sets it to C++14 compiler. It prints d1 > d2 but the output of d1 is 0.01 and d2 is 0.01 too. Why it happen? the code is below.

Hi Samira!

@std::cout by default rounds floating point numbers. You can change this with @std::setprecision.

Output

d1 > d2

d1 = 0.0100000000000051

d2 = 0.00999999999999979

Thank you nascardriver!

"First, we check to see if the distance between a and b is less than our absEpsilon, which should be set at something very small (e.g. 1e-12). This handles the case where a and b are both close to zero."

I'm not sure I understand this. Isn't it possible for a and b to be very close to each other and still be both large? Shouldn't we rather check e.g. "((a>b)?a:b)<absEpsilon"?

If I understand correctly, the problem here is that for very small numbers, the relative epsilon turns out to be exactly zero - very small a or b multiplied by very small percentage exceeds float/double precision (smaller than smallest possible float/double value). Do I get it right?

Hi Alex!,

Thank you so much for the updated, It's helping me a lot!

updated tutorials*

Hi, Alex! Thank you very much for all of the effort! Writing the last function in order to test if it works, I wondered if it is possible to create my own library (I think that's how it is called), something similar to the libraries C++ uses. In this library I can put some useful functions, such as proxEquelAbsRel() function, and use the library when I need those functions, instead of writing them every time.

Hi Cosmin!

You can have classes/functions that you know you'll be using in other projects in a separate directory and tell your compiler to make those files available in your project.

You could also write those functions/classes and compile them into a library, this way you won't need to compile them every time you start a new project.

How this is done depends on the compiler/IDE your using so I won't go into detail.

In my experience option 1 is better to work with, because you'll find yourself making changes to the shared code while working on projects. This is easier without pre-compiled libraries.

Thank you for helping me, nascardriver!

Yes! There are two options:

1) Put the code you want to share in a central directory somewhere (e.g. c:\cpplibrary\). This includes both the .cpp files and the .h files. Then you'll need to do two things. First, your compiler settings should have an "include path" that will instruct the compiler where to look for #included files. Add the location of this central directory to the include path. That way, when you #include "somefile.h", the compiler will know where to find it. Second, make sure you add any .cpp files you use directly into your project. This will cause the compiler to compile the function definitions into your program.

2) Create a precompiled library. I don't recommend this because it's more work and not particularly necessary if you're not distributing your code. But it is an option.

In the approximatelyEqual function you used:

You say "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)..." Doesn't the algorithm returns the smaller one?

Hi Vitor!

The syntax of conditional operator is as follows:

Let's look at an example similar to the code in approcimatelyEqual

Reduce

Reduce

Reduce

If you're still not quite certain how this works you might wanna have a another look at lesson 3.4 (Sizeof, comma, and conditional operators).

Hi!

Thanks for these great updated tutorials :).

Why instead of Knuth solution don't you just do rounding to a specific decimal place? For example, I updated your code to:

That may work for this specific example but I'm guessing it won't work for all combinations of numbers.

Hi, I tried this example with floats instead of doubles, and I got d1 == d2. I thought that floats had less precision than doubles, so I am confused. Thanks!

Yes, floats have less precision. This means they may represent numbers slightly differently than doubles (e.g. round at a different place). As a result, something like this may express differently with floats and doubles. Which is yet another reason to avoid direct comparisons!

Here:

Where i pointed out wouldn't have an 'else'?

It could, but in this case, it's not necessary. If the if statement's conditional is true, then the function returns true immediately. That means the bottom code will only execute when the conditional was false, which is what we want anyway.

"In this algorithm, we’ve added a new parameter: absEpsilon. First, we check to see if the a and b are less than our absEpsilon, which should be set at something very small (e.g. 1e-12). This handles the case where a and b are both close to zero. If that fails, then we fall back to Knuth’s algorithm."

Hello, Alex, I'm a bit confused with the line "First, we check to see if the a and b are less than our absEpsilon", do you mean to first check if the "difference" between a and b are less than our absEpsilon instead?

Yes, I meant the distance between a and b. Article updated. Thanks for pointing that out.

Hi Alex,

1) I read this whole part, but I don't understood the Knuth's method, can you please explain me in details.

2) I searched in the web about best way for comparing floating point numbers and I saw on StackOverflow Relative error, absolute, and percentage error, can you explain me what do they mean?

I'm not sure I can explain it in any more detail than what's already in the article. In short:

1) In order to determine if two floating point numbers are equal, we take the approach that they're equal if they're "close enough" (to account for precision issues).

2) We get to determine what "close enough" means. We do this by defining a value called epsilon.

3) If the two numbers within epsilon of each other, we consider them equal.

Then the question becomes, how do we pick epsilon?

1) An absolute epsilon just uses a number, like 0.01, which means the numbers are considered the same if they're within 0.01 of each other. This is simple, but doesn't work well for both small and large numbers (0.01 is huge if you're trying to compare two very small numbers).

2) A relative epsilon scales your epsilon by one of the input numbers, so instead of comparing against some absolute value, you're comparing against a number that is scaled appropriately for your inputs. In this context, the epsilon functions as a percentage of your input rather than an absolute number. This is typically done by multiplying your epsilon by one of the input numbers. Knuth's method multiplies it by the larger of the two numbers.

Can the relative epsilon value be chosen the same as the absolute epsilon value? In case not, why?

Sure. In our example, we pick 1e-12 for absolute and 1e-8 for relative, but you could use 1e-8 for both if you wanted. It just depends on what your tolerance for considering two numbers "close enough" is.

why don't we use an approxEqual function which checks whether or not the two numbers are within a certain percentage of each other?

for example something like:

this is how we calculate percent difference in intro physics classes and will produce a reasonable result regardless of the relative closeness to zero of either a or b, and requires fewer lines of code than the suggested isAlmostEqual functions. I suppose I'm not sure why I would use the longer code you suggest rather than the simpler (to me) percent difference function used in the sciences.

a=1, b=-1. Code explodes.

Is there some assumption in your physics class that both numbers are positive?

Ahh, yes, you are correct, thank you for pointing that out. It is only used for comparing numbers with the same sign. Thanks!

There is a small misprint:

"Both numbers are close to 0.1 ..." -> should be "close to 0.01"

Thanks, fixed.