今天,我在看一些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?


当前回答

你不能把这个应用到0,因为有尾数和指数部分。 由于指数可以存储很小的数,小于, 但是当你尝试做一些类似(1.0 -“非常小的数字”)的事情时,你会得到1.0。 Epsilon不是值的指示器,而是值精度的指示器,值精度是尾数。 它显示了我们可以存储多少个正确的十进制数字。

其他回答

你不能把这个应用到0,因为有尾数和指数部分。 由于指数可以存储很小的数,小于, 但是当你尝试做一些类似(1.0 -“非常小的数字”)的事情时,你会得到1.0。 Epsilon不是值的指示器,而是值精度的指示器,值精度是尾数。 它显示了我们可以存储多少个正确的十进制数字。

“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;
} 

该测试当然与someValue == 0不同。浮点数的全部思想是存储一个指数和一个显著值。因此,它们表示具有一定数量的精度二进制有效位数的值(在IEEE双精度的情况下为53)。可表示值在0附近比在1附近密集得多。

为了使用更熟悉的十进制系统,假设您使用exponent存储一个“4位有效数字”的十进制值。那么下一个大于1的可表示值是1.001 * 10^0,是1.000 * 10^ 3。但是1.000 * 10^-4也是可以表示的,假设指数可以存储-4。你可以相信我的话,IEEE double可以存储小于的指数。

你不能仅仅从这段代码中判断用作为边界是否有意义,你需要看一下上下文。可能是对产生someValue的计算错误的合理估计,也可能不是。

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。

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