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 all of these work, and they are pretty intuitive. Each of these operators evaluates to the boolean value true (1), or false (0).
Keep in mind that comparing floating point values using any of these operators is dangerous. This is because small rounding errors in the floating point operands may cause an unexpected result. See the section on floating point numbers for more details.
However, sometimes the need to do floating point comparisons is unavoidable. In this case, the less than and greater than operators (>, >=, <, and <=) are typically used with floating point values as normal. The operators will produce the correct result most of the time, only potentially failing when the two operands are almost identical. Due to the way these operators tends to be used, a wrong result typically only has slight consequences.
The equality operator is much more troublesome since small rounding errors make it almost useless. Consequently, using the == operator on floating point numbers is not advised. The most common method of doing floating point equality involves using a function that calculates how close the two values are to each other. If the two numbers are "close enough", then we call them equal.
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)”:
bool IsEqual(double dX, double dY)
{
const double dEpsilon = 0.000001; // or some other small number
return fabs(dX - dY) <= dEpsilon * fabs(dX);
}
dEpsilon is a very small value (eg. 0.000001) that is used to help define what “close enough” is. fabs() is a function in the standard library (#include <cmath>) that returns the absolute value of it’s double parameter.
Let’s examine how the IsEqual() function works. On the left side of the <= operator, the absolute value of dX - dY tells how close dX and dY are to each other as a positive number.
It is easiest to think of dEpsilon as a percentage. A dEpsilon of 0.01 means that dX and dY have to be within 1% of each other in order to be considered equal. On the right side of the <= operator, we multiply dEpsilon by fabs(dX) to find the largest distance the two numbers can be apart and still be considered equal. For example, if fabs(dX) evaluates to 1000, and dEpsilon is 0.01, the largest distance apart the two numbers can be is 10.
Finally, we compare the distance between dX and dY with the largest distance apart that they can still be considered "close enough". If they are close enough, the function returns true. Otherwise, it returns false.
The value for dEpsilon can be adjusted to whatever is most appropriate for the program. Often, programmers will make dEpsilon a third parameter of IsEqual() so it can be defined on a call-by-call basis.
To do inequality (!=) instead of equality, simply call this function and use the logical NOT operator (!) to flip the result:
if (!IsEqual(dX, dY))
cout << dX << " is not equal to " << dY << endl;
3.6 — Logical operators
|
Index
|
3.4 — Sizeof, comma, and arithmetic if operators
|
3.6 — Logical operators
Index
3.4 — Sizeof, comma, and arithmetic if operators
If a float is generally accurate to 7 decimal places, and a double is at 16, if I were working with figures with 2 or 4 decimal places, would the accuracy ever really be an issue? For example, would I see something like
(.0095 * 36.75) > 34.90
equate to false since it uses less than the 7 decimals that floats tend to be accurate to?
A float is not accurate to 7 decimal places. A float is accurate to approximately 7 significant digits. A significant digits is any digit that is not a placeholder 0, including ones on the left side of the decimal.
For example, .0095 has two placeholder zeros, and so is only 2 significant figures. 34.90 has 4 significant figures.
There are two types of errors we need to watch out for with floating point values: rounding errors, and precision errors.
Rounding errors can happen with numbers of any length, because some numbers have infinite representations in binary (0.1 for example), and those representations will be truncated. Rounding errors typically make your answer wrong by 0.000001, or some small number like that.
The second and more potentially serious error are precision errors, where your number can’t be stored because the floating point representation doesn’t have enough memory. Precision errors are more serious because they can affect your answer by a much larger degree of magnitude than rounding errors.
0.0095 * 36.75 = 0.349125, which is 6 significant figures, so in this case, you’ll probably be fine in terms of precision errors. Consequently, your answer will only be affected by small rounding errors.
But consider a case like: 0.0095 * 36.7513. Even though 36.7513 is 4 decimal places, it’s 6 significant digits. When multiplied by 0.0095, the answer is 0.34913735. A float will truncate this to 0.349137.
Once you get into larger dollar amounts, floats are even less suitable. Consider $100264.75. This is a number with 8 significant digits, even though it only uses 2 decimal places. Already a float is not going to be able to hold this number. As you do mathematical operations on it, it’s going to drift farther and farther from your intended answer.
In short, if accuracy is important, use double. Only use floats when accuracy is not that important (eg. in games, where it doesn’t really matter if your character has 137.24 or 137.25 strength).
Alex, you’ve made an error in your IsEqual function.
With a dEpsilon of 0.1, if dX is 150 and dY is 135, it will evaluate to true; but if dX is 135 and dY is 150 (the other way round), it will evaluate to false.
This is because your dEpsilon is only working on dX; not both dX and dY.
I’ve rewrote your function like this:
bool IsEqual(double dX, double dY)
{
const double dEpsilon = 1; // dX and dY have to be within 1% of eachother
return fabs(dX - dY) <= dEpsilon * ((dX + dY) / 100);
}
I’ve tested this and it works fine. (99 will equal 100, but 98 will not equal 100.)
; )
BTW, I wish my tags would work. Don’t know what the problem is. : (
I didn’t write that function, Donald Knuth did. :) You’re correct in that it isn’t symmetric though. I’m not sure why your tags aren’t working either. Seems to work for some people and not others and I still haven’t isolated why.
Sorry, LOL. I’d been struggling with it for a while trying to fully understand how it works. . . .
PS. I wouldn’t've been able to code that function without your teachings. ;D