小编典典

如何检查浮点数的依赖关系

algorithm

我想确定(在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;

将输出:“他们是…”,这是正确的。
为什么会这样呢?


阅读 215

收藏
2020-07-28

共1个答案

小编典典

浮点精度问题


您在这里有两个问题,但是都来自同一个根

您无法精确比较浮点数。您不能精确地减去或除以它们。您不能为他们精确地数 任何东西
。对它们的任何操作都可能(并且几乎总是如此)给结果带来一些错误。甚至a=0.2f不是精确的操作。其他答案的作者很好地解释了其深层原因。(我对此表示感谢和投票。)

这是您的第一个也是更简单的错误。您永远都不要, 永远永远 永远永远 永远 不要
对它们使用==或任何语言的等效语言。

代替a==bAbs(a-b)<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 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进行检查。

2020-07-28