“epsilon”是否是?真的能保证浮点计算中的任何东西吗?
为了简化问题,假设我想计算 float
上的表达式 a / (b - c)
。
为了确保结果有意义,我可以检查 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(对于浮点数的精度)。
情况 2:
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 很好用?
To make the problem short let's say I want to compute the expression a / (b - c)
on float
s.
To make sure the result is meaningful, I can check if b
and c
are in equal:
float EPS = std::numeric_limits<float>::epsilon();
if ((b - c) > EPS || (c - b) > EPS)
{
return a / (b - c);
}
but my tests show it is not enough to guarantee either meaningful results nor not failing to provide a result if it is possible.
Case 1:
a = 1.0f;
b = 0.00000003f;
c = 0.00000002f;
Result: The if condition is NOT met, but the expression would produce a correct result 100000008 (as for the floats' precision).
Case 2:
a = 1e33f;
b = 0.000003;
c = 0.000002;
Result: The if condition is met, but the expression produces not a meaningful result +1.#INF00
.
I found it much more reliable to check the result, not the arguments:
const float INF = numeric_limits<float>::infinity();
float x = a / (b - c);
if (-INF < x && x < INF)
{
return x;
}
But what for is the epsilon then and why is everyone saying epsilon is good to use?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
“处理浮点数时必须使用 epsilon”是对浮点计算了解肤浅的程序员的下意识反应,用于一般比较(不仅仅是与零)。
这通常没有帮助,因为它不会告诉您如何最小化舍入误差的传播,它不会告诉您如何避免取消或吸收问题,即使您的问题确实与两个浮点数的比较有关,< strong>它不会告诉您 epsilon 的值适合您正在做的事情。
如果您还没有阅读每个计算机科学家应该了解的浮点知识算术,这是一个很好的起点。除此之外,如果您对示例中除法结果的精度感兴趣,则必须估计之前的舍入误差导致
bc
的不精确程度,因为事实上,如果 bc 很小,则较小的绝对误差对应于结果的较大绝对误差。如果您只关心除法不应该溢出,那么您的测试(对结果)是正确的。没有理由测试浮点数的空除数,您只需测试结果的溢出,它捕获除数为空和除数太小以致结果无法用以下表示的情况任何精度。关于舍入误差的传播,存在专门的分析仪可以帮助您估计它,因为手动操作是一件乏味的事情。
"you must use an epsilon when dealing with floats" is a knee-jerk reaction of programmers with a superficial understanding of floating-point computations, for comparisons in general (not only to zero).
This is usually unhelpful because it doesn't tell you how to minimize the propagation of rounding errors, it doesn't tell you how to avoid cancellation or absorption problems, and even when your problem is indeed related to the comparison of two floats, it doesn't tell you what value of epsilon is right for what you are doing.
If you have not read What Every Computer Scientist Should Know About Floating-Point Arithmetic, it's a good starting point. Further than that, if you are interested in the precision of the result of the division in your example, you have to estimate how imprecise
b-c
was made by previous rounding errors, because indeed ifb-c
is small, a small absolute error corresponds to a large absolute error on the result. If your concern is only that the division should not overflow, then your test (on the result) is right. There is no reason to test for a null divisor with floating-point numbers, you just test for overflow of the result, which captures both the cases where the divisor is null and where the divisor is so small as to make the result not representable with any precision.Regarding the propagation of rounding errors, there exists specialized analyzers that can help you estimate it, because it is a tedious thing to do by hand.
Epsilon 用于确定两个存在舍入误差的数字是否足够接近以被视为“相等”。请注意,最好测试 fabs(b/c - 1)
fabs(b/c - 1)
fabs(b/c - 1) < EPS
比 fabs(bc)EPS
,甚至更好 - 由于 IEEE 浮点的设计 - 测试abs(*(int*)&b - *(int*)&c)
abs(*(int*)&b - *(int*)&c)
EPSI
(其中 EPSI 是一些小整数)。您的问题具有不同的性质,并且可能需要测试结果而不是输入。
Epsilon is used to determine whether two numbers subject to rounding error are close enough to be considered "equal". Note that it is better to test
fabs(b/c - 1) < EPS
thanfabs(b-c) < EPS
, and even better — thanks to the design of IEEE floats — to testabs(*(int*)&b - *(int*)&c) < EPSI
(where EPSI is some small integer).Your problem is of a different nature, and probably warrants testing the result rather than the inputs.