Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to check dependencies of floats

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?

like image 909
user1006274 Avatar asked Feb 03 '12 23:02

user1006274


People also ask

Is 1.2 float or double?

1.2 is a double (8 bytes). 1.2f is a float (4 bytes). Save this answer.

Is 1.5 float or double?

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.


2 Answers

The Float Precision Problem

    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.

like image 83
Gangnus Avatar answered Sep 21 '22 07:09

Gangnus


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.

like image 27
hammar Avatar answered Sep 21 '22 07:09

hammar