19

为了使问题简短,假设我想计算sa / (b - c)上的表达式float

为了确保结果有意义,我可以检查是否b相等c

float EPS = std::numeric_limits<float>::epsilon();
if ((b - c) > EPS || (c - b) > EPS)
{
    return a / (b - c);
}

但我的测试表明,如果可能的话,仅仅保证有意义的结果或不提供结果是不够的。

情况1:

a = 1.0f;
b = 0.00000003f;
c = 0.00000002f;

结果:不满足 if 条件,但表达式会产生正确的结果 100000008(至于浮点数的精度)。

案例二:

a = 1e33f;
b = 0.000003;
c = 0.000002;

结果:满足 if 条件,但表达式产生的结果没有意义+1.#INF00

我发现检查结果而不是参数更可靠:

const float INF = numeric_limits<float>::infinity();
float x = a / (b - c);
if (-INF < x && x < INF)
{
     return x;
}

但是 epsilon 是为了什么,为什么每个人都说 epsilon 好用呢?

4

2 回答 2

23

“在处理浮点数时必须使用 epsilon”是对浮点计算有肤浅理解的程序员的下意识反应,用于一般比较(不仅是零)。

这通常是没有帮助的,因为它没有告诉你如何最小化舍入误差的传播,它没有告诉你如何避免取消或吸收问题,甚至当你的问题确实与两个浮点数的比较有关时,它没有告诉您 epsilon 的什么值适合您正在做的事情

如果您还没有阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic,这是一个很好的起点。此外,如果您对示例中除法结果的精度感兴趣,则必须估计先前b-c舍入误差造成的不精确程度,因为确实如果b-c小,一个小的绝对误差对应一个大的结果绝对误差。如果您只关心除法不应该溢出,那么您的测试(对结果)是正确的。没有理由用浮点数测试空除数,您只需测试结果是否溢出,这会捕获除数为空和除数太小以致结果无法表示的两种情况任何精度。

关于舍入误差的传播,有专门的分析器可以帮助你估计它,因为手工做是一件乏味的事情。

于 2010-04-28T13:21:49.710 回答
3

Epsilon 用于确定存在舍入误差的两个数字是否足够接近以被视为“相等”。请注意,由于 IEEE 浮点数的设计,测试fabs(b/c - 1) < EPS比更好fabs(b-c) < EPS,甚至更好abs(*(int*)&b - *(int*)&c) < EPSI(其中 EPSI 是一些小整数)。

您的问题具有不同的性质,可能需要测试结果而不是输入。

于 2010-04-28T13:21:07.127 回答