今天,我在看一些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?
使用IEEE浮点,在最小的非零正数和最小的非零负数之间,存在两个值:正零和负零。测试一个值是否在最小的非零值之间等价于测试与零相等;然而,赋值可能会产生影响,因为它会将负0变为正0。
It would be conceivable that a floating-point format might have three values between the smallest finite positive and negative values: positive infinitesimal, unsigned zero, and negative infinitesimal. I am not familiar with any floating-point formats that in fact work that way, but such a behavior would be perfectly reasonable and arguably better than that of IEEE (perhaps not enough better to be worth adding extra hardware to support it, but mathematically 1/(1/INF), 1/(-1/INF), and 1/(1-1) should represent three distinct cases illustrating three different zeroes). I don't know whether any C standard would mandate that signed infinitesimals, if they exist, would have to compare equal to zero. If they do not, code like the above could usefully ensure that e.g. dividing a number repeatedly by two would eventually yield zero rather than being stuck on "infinitesimal".
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;
}
可以用下面的程序输出一个数(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;
}