Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
101 views
in Technique[技术] by (71.8m points)

c++ - 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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...