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).
doesn’t 34.90 have 3 significant digits?
I know math teachers usually say that the last zeros don’t matter after a decimal, but when you’re talking about precision they do. In sciences when you’re taking measurements a .39 may be the same value as .3900000 mathematically speaking, but the latter is more precise to 7 significant figures. With the .39 value you cannot be assured the number in the thousandths place is 0.
[...] 2007 Prev/Next Posts « 3.5 — Relational operators (comparisons) | Home | 3.7 — Converting between binary and decimal » Friday, June 15th, 2007 at [...]
[...] 3.5 — Relational operators (comparisons) [...]
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. : (
When I saw the function I thought it was incorect. The way I use it is like this:
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
To be totally accurate, it is needed to say that the given function with dEpsilon == 1.0 gives precision of 2.0202…% (left side equals to right side of equation when x={+-}101y/99 ~= {+-}1.0202…).
To be symmetric, the function should compare the distance between dX and dY with some percent to an average between dX and dY:
In this case, dEpsilon will be the precision factor. Just to optimize the function, it better should be re-written into:
bool isEqual(double dX, double dY, double dPrecision = 0.01) { return fabs(dX-dY) <= fabs(dX+dY)/2 * dPrecision; };On the other hand, it may be not fully correct to compare distance between dX and dY with an average on them. In some very precise calculation the comparing value should not affect the testing function. In this case it may be more correct to use the function given by Donald Knuth, I guess:
bool isEqual(double a, double b, double precision = 0.000001) { return fabs(a - b) <= precision * fabs(a); };and remember that the comparing value should be the second argument to this function when the first argument must be the value which one that is compared to.
I don’t really understand what is happening with fabs(). For example if dX = 2 and dY = 3 how would I figure what the result of fabs(dX), or fabs(dX – dY) would/should be. The sentence that states that fabs() is a function that returns the absolute value of it’s double parameter leads me to believe that dX would evaluate to 4 and dY to 6 in this case, but when compiled and ran with the above values and sending fabs(dX) or fabs(dY) to the screen using cout I get whatever the value of dX or dY was to begin with. When doing the same with fabs(dX – dY) however I get a value of 1, which would be the same as (dX – dY) anyway. (scratches head)
Heh, that’s not what he meant. :) The double parameter he was referring to was the double float point variable that is provided to fabs as a parameter. So fabs doesn’t return double the value put into it, it just returns the absolute value of the number sent to it. You can think of fabs doing something similar to this, though this is REALLY simplified:
double fabs(double x) { if (x < 0) { return -x; //The value must be negative, therefore return that value times -1. } else { return x; //No changes needed, the value is positive. } }And remember, when you’re using functions, the mathematical expressions inside their parameters are evaluated before the function call is, for example using “fabs(-4 – 6)” is the same as “fabs(-10)”.
can anyone please tell me why this returns a syntax error in line 12 , the return stataement?
thanx in advance
Auldy66
Ps: Well done Alex , made me start hammering the keys after a lot of years..
#include "stdafx.h" #include "cmath" #include <iostream> //tests to see if 2 numbers are close enough to call equal bool IsEqual(double dX, double dY); const double dEpsilon = 0.000001; // or some other small number return fabs(dX - dY) <= dEpsilon * fabs(dX) //testing function above int main() { using namespace std; int dX = 0, dY = 0; cout << "Enter a value for X: " << endl; cin >> dX; cout << "Enter a value for Y: " << endl; cin >> dY; if (IsEqual(dX, dY)) { cout << dX << " is equal to " << dY << endl; } system("pause"); return 0; }Hi auldy66, try this one. I hope I helped you.
#include<iostream> #include<cmath> using namespace std; bool IsEqual(double x,double y); int main() { double w,z; cout<<"Enter a value for w: "; cin>>w; cout<<"Enter a value for z: "; cin>>z; bool answer=IsEqual(w,z); if(answer) cout<<w<<" is equal to "<<z<<endl; else cout<<w<<" is not equal to "<<z<<endl; return 0; } bool IsEqual(double x,double y) { const double Epsilon=0.000001; return fabs(x-y)<=Epsilon*fabs(x); }Hi Auldy66,
it looks like you did not make your IsEqual function correctly. It is missing brackets, it should not have a semicolon at the end of the fucntion name line number 1 , and the return needs a semicolon. Not sure if they were left out of your code or just a result of entering into the web page. I changed your function as shown below and it worked just fine.
Regards,
Scott
{I dont get it.
If we have this for example
And we test with x=20.111 and y=20.1101 (close enought) we get this:
that equals true.
But lets use smaller numbers, that are close to each other as on the first example: x=0.001, y=0.0001, we get this:
that equals false.
For me personaly, in both examples the numbers are satisfying close.
If i would need to compare two close floating point values, i would go for this:
If i need more precission i would increase the nFloatPPrecis depending on how much digits i would need.
What do you think?
I find it very curious that “absolute value” is a function in the standard library, but not a built-in operator in C++. You would think that the (otherwise useless) unary “+” operator would be assigned as the operator for the absolute value function. In other words, it seems logical that the expression +(x – y) should evaluate to the absolute value of (x – y).
In any case Alex, I think it would be better to introduce the absolute value function in section 3.2 with the Operators.
Keep up the good work.
Guys, I’ll tell you what I think, if I may. The function that Knuth made has one problem to my mind: it takes only the first value passed in into consideration when calculating the relative distance between the variables. As a mathematician, I think it should take both of them but – attention! – instead of using (x+y) it should make use of max(|x|,|y|). Therefore, you look for this piece of code, I believe:
bool IsEqual( double x,double y, double epsilon = 1e-6 ) { return fabs( x - y ) <= epsilon * max( fabs(x), fabs(y) ); }By the way, I’m sure there must be some kind of max function in C++ but don’t know in which library. I have not analysed the function thoroughly as it should be but I think it’s the best version so far. Will gladly see a better one.
This seems to be best function.
I think there is max function somewhere nearby, probabaly in stdlib.h
but it can be easily written off:
float max (float dx, float dy) { return ( (dx>dy) ? dx:dy ); }[...] 3.5 Relational operators (comparisons) [...]
Due to the way these operators tends to be used, a wrong result typically only has slight consequences.
tend
Please can anyone specify why we multiply dEpsilon with dX? What is the purpose?
Thanks. Note: Even if you see this message has been sent out quiet long time ago, your answer will be ok for me.
bool IsEqual(double dX, double dY)
{
const double dEpsilon = 0.000001; // or some other small number
return fabs(dX - dY) <= dEpsilon * fabs(dX);
}
[...] section on relational operators has more detail on comparing floating point [...]
this is probably a silly question, but i keep reading this thinking i’m missing something really obvious. when you use maths in your program, after some point 2+2 will no longer equal 4? .. i like to think of a computer as a pretty dependable calculator, so this discussion of two numbers being “close enough” within a margin of 1% is blowing my mind.