c++ - 如何检查 float 的依赖关系

标签 c++ algorithm floating-point floating-accuracy inverse

我想确定(在 C++ 中)一个 float 是否是另一个 float 的乘法逆数。问题是我必须使用第三个变量来做到这一点。例如这段代码:

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;

会输出:“他们是……”这是对的。
为什么会这样?

最佳答案

浮点精度问题

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

您无法精确比较 float 。您不能精确地减去或除以它们。你不能精确地为他们计算任何东西。对它们进行的任何操作都可能(并且几乎总是会)给结果带来一些错误。偶a=0.2f不是一个精确的操作。此处其他答案的作者很好地解释了其更深层次的原因。 (我对此表示感谢并投票给他们。)

这是您的第一个也是更简单的错误。你不应该,neverneverneverNEVER在他们身上使用= = 或任何语言的等价物。

而不是 a==b , 使用 Abs(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 的绝对值较大,然后将 1 除以较大的值,然后再减去较小的值。

  • 如果要比较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){...

就是这样。


附:如果你真的需要all一行,请使用:

if(Abs(x*y-1.0)<1e-10){...

但这是一种糟糕的风格。我不建议这样做。

附言在您的第二个示例中,编译器会优化代码,以便在运行任何代码之前将 z 设置为 5。因此,即使是 float ,检查 5 对 5 也有效。

关于c++ - 如何检查 float 的依赖关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9136860/

相关文章:

c++ - 模板功能的小烦恼

c++ - 跨 x 轴反射点后获取象限值

c# - 实现用于检测自相交多边形的蛮力算法

algorithm - rBST 的计算概率

java - 如何打印数字的 float 部分的最后一位?

c++ - 在现代 C++ 中比较 double/float 是否相等的现代实践

c++ - 访问对象层次结构的子集(不是子树)

c++ - 重载 'operator++' 必须是一元或二元运算符(有 3 个参数)

algorithm - 通过仅将元素移动到开头或结尾来对数组进行排序

Java double