使用epsilon将double比较为零

Seb*_*ski 209 c++ double

今天,我正在查看一些C++代码(由其他人编写)并找到了这一部分:

double someValue = ...
if (someValue <  std::numeric_limits<double>::epsilon() && 
    someValue > -std::numeric_limits<double>::epsilon()) {
  someValue = 0.0;
}
Run Code Online (Sandbox Code Playgroud)

我试图弄清楚这是否有意义.

文档epsilon()说:

该函数返回1和大于1的最小值之间的差值,可以表示[乘双].

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

如果没有,那么比较不等于someValue == 0.0

ybu*_*ill 190

假设64位IEEE双,则有52位尾数和11位指数.看看下面的数字:

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^0 = 1
Run Code Online (Sandbox Code Playgroud)

可表示的最小数字大于1:

1.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^0 = 1 + 2^-52
Run Code Online (Sandbox Code Playgroud)

因此:

epsilon = (1 + 2^-52) - 1 = 2^-52
Run Code Online (Sandbox Code Playgroud)

0和epsilon之间有数字吗?很多......例如,最小正可表示(正常)数字是:

1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^-1022 = 2^-1022
Run Code Online (Sandbox Code Playgroud)

实际上有大约(1022 - 52 + 1)×2^52 = 43729952381767516160到epsilon之间的数字,大约是所有可表示数字的47%......

  • 很奇怪,你可以说"47%的正数":) (27认同)
  • @configurator:不,你不能说(没有'自然'有限度量存在).但你可以说"47%的正面*代表*数字". (13认同)
  • @PavloDyban:仅仅因为指数*没有符号位.它们被编码为偏移量:如果编码的指数是"0 <= e <2048",那么尾数乘以2得到"e - 1023"的幂.例如,"2 ^ 0"的指数被编码为"e = 1023","2 ^ 1"被编码为"e = 1024"并且"2 ^ -1022"被编码为"e = 1"."e = 0"的值保留给次正规和实零. (2认同)
  • @PavloDyban:同样`2 ^ -1022`是最小的*正常*数字.最小的数字实际上是"0.0000 00000000 00000000 00000000 00000000 00000000 00000001×2 ^ -1022 = 2 ^ -1074`.这是次正规的,意味着尾数部分小于1,因此它用指数"e = 0"编码. (2认同)

Ste*_*sop 17

测试当然不一样someValue == 0.浮点数的整个概念是它们存储指数和有效数.因此,它们表示具有一定数量的二进制有效精度数值的值(在IEEE双精度的情况下为53).可表示的值在0附近比在1附近密集得多.

要使用更熟悉的十进制系统,假设您使用指数存储"4个有效数字"的十进制值.那么接下来的表示值大于11.001 * 10^0,和epsilon1.000 * 10^-3.但1.000 * 10^-4也可以表示,假设指数可以存储-4.您可以接受我的说法,IEEE双倍可以存储小于指数的指数epsilon.

你无法单独从这段代码中看出是否有意义地使用它epsilon作为界限,你需要查看上下文.这可能是对epsilon所产生的计算中的误差的合理估计someValue,并且可能不是.

  • 好的,但即使是这种情况,更好的做法是将错误限制在一个合理命名的变量中,并在比较中使用它.就目前而言,它与魔法常数没有什么不同. (2认同)

Ski*_*izz 13

在0和epsilon之间存在数字,因为epsilon是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;
}
Run Code Online (Sandbox Code Playgroud)

使用调试器,在main结束时停止程序并查看结果,您将看到epsilon/2与epsilon,0和1不同.

因此,此函数采用+/- epsilon之间的值并使它们为零.


pbh*_*bhd 5

可以使用以下程序打印数字(1.0,0.0,...)附近的epsilon(最小可能差异)的近似值.它打印出以下输出:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
一点思考清楚地表明,epsilon越小,我们用来查看其epsilon值的数字越小,因为指数可以调整到该数字的大小.

#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;
}
Run Code Online (Sandbox Code Playgroud)

  • 你检查了哪些实现?GCC 4.7 绝对不是这种情况。 (2认同)