我想确定(在c ++中)一个浮点数是否是另一个浮点数的乘法逆。问题是我必须使用第三个变量来执行此操作。例如此代码:
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;
将输出:“他们不是…”,这是错误的,此代码为:
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;
将输出:“他们是…”,这是正确的。 为什么会这样呢?
您在这里有两个问题,但是都来自同一个根
您无法精确比较浮点数。您不能精确地减去或除以它们。您不能为他们精确地数 任何东西 。对它们的任何操作都可能(并且几乎总是如此)给结果带来一些错误。甚至a=0.2f不是精确的操作。其他答案的作者很好地解释了其深层原因。(我对此表示感谢和投票。)
a=0.2f
这是您的第一个也是更简单的错误。您永远都不要, 永远 , 永远 , 永远 , 永远 , 永远 不要 对它们使用==或任何语言的等效语言。
代替a==b,Abs(a-b)<HighestPossibleError改用。
a==b
Abs(a-b)<HighestPossibleError
但这不是您任务中的唯一问题。
Abs(1/y-x)<HighestPossibleError 也不行。至少,它不会足够频繁地工作。为什么?
Abs(1/y-x)<HighestPossibleError
让我们以x = 1000和y = 0.001为对。让我们以y的“起始”相对误差为10 -6。
(相对误差=误差/值)。
值的相对误差正在相乘和相除。
1 / y约为1000。其相对误差为10 -6。(“ 1”没有错误)
这使得绝对误差= 1000 * 10 -6 = 0.001。当您以后减去x时,该错误将仅存。(绝对误差在相加和相减时相加,而x的误差可忽略不计。)当然,您没有指望那么大的误差,HighestPossibleError肯定会设置得较低,并且您的程序会抛出一对不错的x, ÿ
因此,浮点运算的下两个规则:尽量不要将较大的赋值器除以较小的赋值器,然后上帝会避免您减去此后的平仓值。
有两种简单的方法可以解决此问题。
通过找出x,y的abs值较大,然后用1除以较大的abs,然后再减去较小的ab。
如果要比较1/y against x,则在使用字母而不是值的同时, 并且操作没有错误 ,请将比较的两边乘以y,得到1 against x*y。(通常,您应该在该操作中检查符号,但是这里我们使用abs值,因此很干净。)结果比较根本没有除法。
1/y against x
1 against x*y
简而言之:
1/y V x <=> y*(1/y) V x*y <=> 1 V x*y
我们已经知道1 against x*y应该进行这样的比较:
const float HighestPossibleError=1e-10; if(Abs(x*y-1.0)<HighestPossibleError){...
就这些。
附注:如果您真的需要 全部 一行,请使用:
if(Abs(x*y-1.0)<1e-10){...
但这是不好的风格。我不会建议。
PPS在第二个示例中,编译器对代码进行了优化,以便在运行任何代码之前将z设置为5。因此,即使是浮点数,也要对照5对5进行检查。