今天,我在看一些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.000000000000000000000和1.00000000000000001。这是1.0和1.0 + 1e-20。你认为在-1e-20和+1e-20之间还有一些值可以表示吗?

其他回答

可以用下面的程序输出一个数(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;
}

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

someValue == 0.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的计算错误的合理估计,也可能不是。

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

假设系统无法区分1.000000000000000000000和1.00000000000000001。这是1.0和1.0 + 1e-20。你认为在-1e-20和+1e-20之间还有一些值可以表示吗?