I want to determine (in c++) if one float number is the multiplicative inverse of another float number. The problem is that i have to use a third variable to do it. For instance this code:
float x=5,y=0.2;
if(x==(1/y)) cout<<"They are the multiplicative inverse of eachother"<<endl;
else cout<<"They are NOT the multiplicative inverse of eachother"<<endl;
will output: "they are not..." which is wrong and this code:
float x=5,y=0.2,z;
z=1/y;
if(x==z) cout<<"They are the multiplicative inverse of eachother"<<endl;
else cout<<"They are NOT the multiplicative inverse of eachother"<<endl;
will output: "they are..." which is right.
why is this happening?
1.2 is a double (8 bytes). 1.2f is a float (4 bytes). Save this answer.
And the reason the comparison succeeds with 1.5 is that 1.5 can be represented exactly as a float and as a double ; it has a bunch of zeros in its low bits, so when the promotion adds zeros the result is the same as the double representation.
You have two problems here, but both come from the same root
You can't compare floats precisely. You can't subtract or divide them precisely. You can't count anything for them precisely. Any operation with them could (and almost always does) bring some error into the result. Even a=0.2f
is not a precise operation. The deeper reasons of that are very well explained by the authors of the other answers here. (My thanks and votes to them for that.)
Here comes your first and more simple error. You should never, never, never, never, NEVER use on them == or its equivalent in any language.
Instead of a==b
, use Abs(a-b)<HighestPossibleError
instead.
But this is not the sole problem in your task.
Abs(1/y-x)<HighestPossibleError
won't work, either. At least, it won't work often enough. Why?
Let's take pair x=1000 and y=0.001. Let's take the "starting" relative error of y for 10-6.
(Relative error = error/value).
Relative errors of values are adding to at multiplication and division.
1/y is about 1000. Its relative error is the same 10-6. ("1" hasn't errors)
That makes absolute error =1000*10-6=0.001. When you subtract x later, that error will be all that remains. (Absolute errors are adding to at adding and subtracting, and the error of x is negligibly small.) Surely, you are not counting on so large errors, HighestPossibleError would be surely set lower and your program would throw off a good pair of x,y
So, the next two rule for float operations: try not to divide greater valuer by lesser one and God save you from subtracting the close values after that.
There are two simple ways to escape this problem.
By founding what of x,y has the greater abs value and divide 1 by the greater one and only later to subtract the lesser one.
If you want to compare 1/y against x
, while you are working yet with letters, not values, and your operations make no errors, multiply the both sides of comparison by y
and you have 1 against x*y
. (Usually you should check signs in that operation, but here we use abs values, so, it is clean.) The result comparison has no division at all.
In a shorter way:
1/y V x <=> y*(1/y) V x*y <=> 1 V x*y
We already know that such comparison as 1 against x*y
should be done so:
const float HighestPossibleError=1e-10;
if(Abs(x*y-1.0)<HighestPossibleError){...
That is all.
P.S. If you really need it all on one line, use:
if(Abs(x*y-1.0)<1e-10){...
But it is bad style. I wouldn't advise it.
P.P.S. In your second example the compiler optimizes the code so, that it sets z to 5 before running any code. So, checking 5 against 5 works even for floats.
The problem is that 0.2
cannot be represented exactly in binary, because its binary expansion has an infinite number of digits:
1/5: 0.0011001100110011001100110011001100110011...
This is similar to how 1/3
cannot be represented exactly in decimal. Since x
is stored in a float
which has a finite number of bits, these digits will get cut off at some point, for example:
x: 0.0011001100110011001100110011001
The problem arises because CPUs often use a higher precision internally, so when you've just calculated 1/y
, the result will have more digits, and when you load x
to compare them, x
will get extended to match the internal precision of the CPU.
1/y: 0.0011001100110011001100110011001100110011001100110011
x: 0.0011001100110011001100110011001000000000000000000000
So when you do a direct bit-by-bit comparison, they are different.
In your second example, however, storing the result into a variable means it gets truncated before doing the comparison, so comparing them at this precision, they're equal:
x: 0.0011001100110011001100110011001
z: 0.0011001100110011001100110011001
Many compilers have switches you can enable to force intermediate values to be truncated at every step for consistency, however the usual advice is to avoid doing direct comparisons between floating-point values and instead check if they differ by less than some epsilon value, which is what Gangnus is suggesting.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With