今天,我在看一些c++代码(别人写的),发现了这一部分:

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

我在想这到底说得通不合理。

epsilon()的文档说:

该函数返回1与可[用双精度符号]表示的大于1的最小值之间的差值。

这是否也适用于0,即()的最小值大于0?或者有没有0到0 +之间的数可以用双精度数表示?

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


当前回答

我认为这取决于你电脑的精度。 看一下这张表:你可以看到,如果用double表示,但你的精度更高,比较并不等于

someValue == 0.0

不管怎样,这是个好问题!

其他回答

可以用下面的程序输出一个数(1.0,0.0,…)的近似值(可能的最小差值)。输出如下: 0.0 = 4.940656e-324 1.0的是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;
}

“1和大于1的最小值之间的差值”意味着1 +“机器零”,这大约是10^-8或10^-16,这取决于你是否使用双变量的浮点数。你可以用1除以2,直到计算机看到1 = 1+1/2^p,如下所示:

#include <iostream>
#include "math.h"
using namespace std;

int main() {
    float a = 1;
    int n = 0;
    while(1+a != 1){
        a = a/2;
        n +=1;
    }
    cout << n-1 << endl << pow(2,-n);
    return 0;
} 

假设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

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

Also, a good reason for having such a function is to remove "denormals" (those very small numbers that can no longer use the implied leading "1" and have a special FP representation). Why would you want to do this? Because some machines (in particular, some older Pentium 4s) get really, really slow when processing denormals. Others just get somewhat slower. If your application doesn't really need these very small numbers, flushing them to zero is a good solution. Good places to consider this are the last steps of any IIR filters or decay functions.

请参见:为什么将0.1f更改为0会使性能降低10倍?

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

有些数字存在于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;
}

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

所以这个函数取正/-之间的值并使它们为零。