比较双用小量零(Compare double to zero using epsilon)

2019-06-21 13:12发布

今天,我一直在寻找通过一些C ++代码(由别人写的),发现本节:

double someValue = ...
if (someValue <  std::numeric_limits<double>::epsilon() && 
    someValue > -std::numeric_limits<double>::epsilon()) {
  someValue = 0.0;
}

我想弄清楚这是否甚至是有道理的。

对于文档epsilon()说:

该函数返回[通过双] 1和最小的大于1的值即可表示之间的差。

这是否适用于0为好,即epsilon()是最小的值大于0? 或者有没有之间的数字00 + epsilon可以由代表double

如果不是,那么是不是等同于比较someValue == 0.0

Answer 1:

假设64位IEEE双,有一个52位尾数和11位指数。 让我们来打破它位:

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^0 = 1

最小可表示数大于1:

1.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^0 = 1 + 2^-52

因此:

epsilon = (1 + 2^-52) - 1 = 2^-52

是否有0型和戊型之间的任意数字? 大量...如最小正表示的(普通)号码为:

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^-1022 = 2^-1022

事实上,有(1022 - 52 + 1)×2^52 = 4372995238176751616 0和ε,这是所有正表示数的47%之间的数...



Answer 2:

测试肯定是不一样的someValue == 0 。 浮点数的整体思路是,它们存储指数和尾数。 因此,它们代表具有(在IEEE双的情况下53)一定数目的精度的二进制数字显著的值。 该表示的值更密集接近0比接近1。

要使用更熟悉的十进制,假设你存储一个数值“4级显著人物”与指数。 然后下一个可表示值大于11.001 * 10^0 ,和epsilon1.000 * 10^-3 。 不过1.000 * 10^-4也可表示,假设指数可以存储-4。 你可以把我的话是比指数更低的IEEE双CAN店指数epsilon

你不能从这个代码单独告诉它是否有道理,还是不要用epsilon专门为界,你需要看看上下文。 这可能是因为epsilon是在所产生的计算误差的合理估计someValue ,并且它可以是它不是。



Answer 3:

有迹象表明,0和小量之间存在因为小量是1之间,并且可以在上面1表示下一个最高数和不为0之间的差值,并且可以大于0来表示(如果它是下一个最大的数目的差数,即代码会很少做): -

#include <limits>

int main ()
{
  struct Doubles
  {
      double one;
      double epsilon;
      double half_epsilon;
  } values;

  values.one = 1.0;
  values.epsilon = std::numeric_limits<double>::epsilon();
  values.half_epsilon = values.epsilon / 2.0;
}

在主要结束使用调试器,停止该程序并查看结果,你会发现小量/ 2为ε,零和一个截然不同的。

因此,该功能只+/-小量之间的值,使它们为零。



Answer 4:

围绕一个数字(1.0,0.0,...)的ε-的aproximation(最小可能差)可以被印有下面的程序。 它打印的输出如下:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
小算盘清楚,即小量变小的更小的数字是我们用来查看其小量价值,因为该指数能适应这个数字的大小。

#include <stdio.h>
#include <assert.h>
double getEps (double m) {
  double approx=1.0;
  double lastApprox=0.0;
  while (m+approx!=m) {
    lastApprox=approx;
    approx/=2.0;
  }
  assert (lastApprox!=0);
  return lastApprox;
}
int main () {
  printf ("epsilon for 0.0 is %e\n", getEps (0.0));
  printf ("epsilon for 1.0 is %e\n", getEps (1.0));
  return 0;
}


Answer 5:

假设我们正在与适合在一个16位的寄存器玩具浮点数工作。 有一个符号位,5位指数和10位尾数。

这浮点数的值是尾数,解释为一个二进制十进制值,时间的两个给幂的。

大约1的指数等于零。 所以尾数的最小数字是在1024的一部分。

近1/2的指数是负一,所以尾数的最小部分的一半。 有了一个5位的指数可以达到负16,在这一点尾数的最小部分是值得32米一个组成部分。 而在负16指数,该值是围绕一个部分在32K,比一个绕小量我们上面计算更接近于零!

现在,这是一个玩具浮点模型,并不能反映真实的浮点系统的所有怪癖,而是要体现价值比小量小的能力与实际浮点值相当类似。



Answer 6:

之间的差X和的下一个值X而变化,根据X
epsilon()是仅之间的差1和的下一个值1
之间的差0和的下一个值0epsilon()

相反,你可以使用std::nextafter与之比较的双精度值0 ,如下所示:

bool same(double a, double b)
{
  return std::nextafter(a, std::numeric_limits<double>::lowest()) <= b
    && std::nextafter(a, std::numeric_limits<double>::max()) >= b;
}

double someValue = ...
if (same (someValue, 0.0)) {
  someValue = 0.0;
}


Answer 7:

我认为这取决于精确您的计算机。 就这个一看表 :你可以看到,如果你的小量由双表示,但你的精度更高,比较不等同于

someValue == 0.0

好问题呢!



Answer 8:

您不能将此应用到0,因为尾数和指数部分。 由于指数可以存储非常少的数字,这比小量小,但是当你试图做类似(1.0 - “极少数”),你会得到1.0。 小量为不值的指标,但值的精度,这是在尾数。 它展示了许多许多正确随之而来的十进制数字,我们可以存储。



Answer 9:

所以我们说系统不能区分1.000000000000000000000和1.000000000000000000001。 即1.0和1.0 + 1E-20。 你觉得还有是可以-1e-20和+ 1E-20之间代表的一些价值观?



Answer 10:

与IEEE浮点,最小非零正值和最小非零负值之间,存在两个值:正零和负零。 测试一个值是否是最小的非零值之间相当于测试与零的相等; 分配,但是,可能有效果,因为它会改变一个负零到正零。

这将能想到,一个浮点格式可能具有最小的有限的正负值之间的三个值:正无穷,无符号零和负无穷。 我不熟悉任何浮点格式,其实工作的方式,但是这样的行为是完全合理的,可以说是比IEEE(更好的也许不够好,值得增加额外的硬件来支持它,但数学1 /(1 / INF),1 /( - 1 / INF)和1 /(1-1)应该表示三种不同的情况示出了三个不同的零)。 我不知道任何C标准是否将要求签署无穷,如果存在,就不得不比较等于零。 如果他们不这样做,像上面的代码可以有效保证如反复划分数由两个最终产量为零,而不是停留在“无穷小”。



Answer 11:

此外,一个很好的原因具有这样的功能是消除“非规格化”(那些非常小的数字,可以不再使用隐含的前导“1”,并有专门的FP表示)。 你为什么想做这个? 由于某些机器(尤其是一些老的Pentium 4)得到真正的,非正规处理时很慢。 其他人只是拿到有点慢。 如果您的应用程序并不真正需要这些非常小的数字,他们冲洗至零是一个很好的解决方案。 好的地方考虑这是任何IIR滤波器或衰减函数的最后一个步骤。

另请参阅: 为什么改变0.1F到了10倍,0降低性能?

和http://en.wikipedia.org/wiki/Denormal_number



文章来源: Compare double to zero using epsilon
标签: c++ double