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


当前回答

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

其他回答

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

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

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

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

X和X的下一个值之间的差值根据X而变化。 Epsilon()只是1和下一个1的值之间的差。 0和下一个0值之间的差不是()。

相反,你可以使用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;
}

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