为什么使用float.Epsilon而不是零?

48
在下面的代码中,为什么要与float.Epsilon进行比较而不是0?
// Coroutine to move elements
protected IEnumerator SmoothMovement (Vector3 end)
{
    // Distance computation
    float sqrRemainingDistance = (transform.position - end).sqrMagnitude;

    while(sqrRemainingDistance > float.Epsilon)
    {
        Vector3 newPostion = Vector3.MoveTowards(
            rb2D.position, end, inverseMoveTime * Time.deltaTime
        );
        rb2D.MovePosition (newPostion);
        sqrRemainingDistance = (transform.position - end).sqrMagnitude;
        yield return null;
    }
}

2
因为float.Epsilon不等于0。http://en.wikipedia.org/wiki/Machine_epsilon - Tim Schmelter
https://dev59.com/4HE95IYBdhLWcg3wMrEB - ken2k
11
这段话的意思是:这要么是不必要的,要么是错误的。如果作者试图避免舍入误差,那么float.Epsilon通常是一个太小的值。如果他们不试图避免舍入误差,那么他们正在做非常奇怪(很可能也是错误的)或不必要的事情。 - Matthew Watson
4
为什么会被踩?在我看来这是一个合理的问题。 - MeanGreen
1
我正在观看“Unity 2D Roguelike”教程,同时也在思考“float.Epsilon”,刚好找到了这个相同的代码 :) - Kim
Unity 2D地牢冒险教程也带我来到这里 :) 感谢您的提问! - Michaël Randria
2个回答

50

实际上,在这里使用float.Epsilon可能没有任何显著的差异。 float.Epsilon是大于零的最小可能float(大约为1.401298E-45),这并不意味着它是任意两个float之间最小的差异。由于浮点数计算是不精确的,两个看似相等的数字之间的差异可能比float.Epsilon大得多。例如:

float f1 = 1.0f / 3.0f;
float f = 1.0f;

(f1 * 3).Dump();  // 1
(f1 * 3 - f).Dump();  // 2.980232E-08
比较浮点数时,更好的做法是选择一个“合理”的值来确定两个浮点数是否“足够接近”相等。这是一种上下文定义——例如对于距离,1毫米“足够接近”吗?也许在建造狗屋时可以,但在电路板上不行。你不会一直削减一块木材,直到它的长度与目标长度相差不超过1.401298E-45米。你会选择一个“足够接近”的差异来称它们为相等。

对于精灵移动(我假设这就是样本中所做的),也许更合理的“epsilon”是高分辨率监视器上可表示的最小距离(或者至少会被人眼注意到的距离)。

所有这些都是说,sqrRemainingDistance > 0 在这里可能同样合理,因为0到float.Epsilon之间没有其他数字,但更好的选择可能是一些比Epsilon大得多的数字,以确定何时停止循环。程序可能会循环比必要的次数更多,以获得“合理”的结果。

实际上,这在MSDN上有记录:

如果您创建了一个自定义算法来确定两个浮点数是否可以被视为相等,则必须使用大于Epsilon常量的值来建立可接受的绝对差距范围,使得这两个值被视为相等。(通常,那种差距范围要比Epsilon大很多。)


0

因为浮点数计算不精确。我链接的文章很长,很详细,所以让我用一个简单的例子来解释:

43.65+61.11=104.75999999999999

这背后的原因在于浮点数实际上是如何保存的。如果你不想深入了解,只需记住浮点算术的一般限制,并不要期望它们按照数学应该是什么来精确计算——包括在这种情况下的0。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接