为了解决问题,请假设我想在a / (b - c)
s上计算表达式float
。
为确保结果有意义,我可以检查b
和c
是否相同:
float EPS = std::numeric_limits<float>::epsilon();
if ((b - c) > EPS || (c - b) > EPS)
{
return a / (b - c);
}
但是我的测试显示,如果可能的话,这不足以保证有意义的结果,也不能不提供结果。
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很好用?
答案 0 :(得分:19)
“处理浮点数时必须使用epsilon”是程序员对浮点计算的表面理解的一种下意识反应,一般用于比较(不仅是零)。
这通常没有用,因为它没有告诉你如何最小化舍入错误的传播,它没有告诉你如何避免取消或吸收问题,甚至当你的问题确实与两个比较时有关浮动,它没有告诉你epsilon对你正在做什么的正确价值。
如果您还没有阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic,那么这是一个很好的起点。除此之外,如果您对示例中除法结果的精确度感兴趣,则必须估计之前的舍入错误对b-c
的不精确程度,因为确实{ {1}}很小,一个小的绝对误差对应于结果上的大绝对误差。如果你的担心只是分裂不应该溢出,那么你的测试(在结果上)是正确的。没有理由测试带有浮点数的空除数,你只需要测试结果的溢出,它会捕获除数为空的情况和除数非常小以致结果无法表示的情况。任何精确度。
关于舍入错误的传播,存在specialized analyzers可以帮助您估算它,因为手动执行是一件单调乏味的事情。
答案 1 :(得分:2)
Epsilon用于确定两个受舍入误差影响的数字是否足够接近被认为是“相等”。请注意,测试fabs(b/c - 1) < EPS
比fabs(b-c) < EPS
更好,甚至更好 - 由于IEEE浮点数的设计 - 来测试abs(*(int*)&b - *(int*)&c) < EPSI
(其中EPSI是一些小整数)。
您的问题具有不同的性质,可能需要测试结果而不是输入。