比较浮点值有多危险?
我知道UIKit
使用CGFloat
是因为独立于坐标系的分辨率。
但每次我想检查是否例如frame.origin.x
是0
它使我感到恶心:
if (theView.frame.origin.x == 0) {
// do important operation
}
与==
, <=
, >=
, <
, >
比较时, CGFloat
是否容易受到误报? 这是一个浮点,他们有无法解决的问题:例如, 0.0000000000041
。
Objective-C
是在比较内部还是在内部处理这种情况,或者可能会发生这样的情况: origin.x
读数为零并不与0
比较为真?
首先,浮点值在其行为中不是“随机的”。 精确的比较在大量现实世界的使用中可以并且确实有意义。 但是如果你要使用浮点数,你需要知道它是如何工作的。 在假定浮点工作像真实数字这一方面会让你的代码很快崩溃。 在假设浮点结果的方面存在较大的随机模糊(就像这里提供的大多数答案一样)会让你看起来起作用的代码,但最终会出现大幅度错误和破损的角落案例。
首先,如果你想用浮点编程,你应该阅读这个:
每位计算机科学家应该了解的浮点算法
是的,请阅读所有内容。 如果负担过重,则应使用整数/固定点进行计算,直到有时间阅读为止。 :-)
现在,就这样说,精确浮点比较的最大问题归结为:
事实上,您可能会在源代码中写入很多值,或者使用scanf
或strtod
读入值,这些值不会以浮点值的形式存在,而会悄然转换为最接近的近似值。 这就是demon9733的答案所谈论的。
由于没有足够的精度来表示实际结果,所以许多结果变得圆整。 一个简单的例子,你可以看到这是添加x = 0x1fffffe
和y = 1
作为浮点数。 这里, x
在尾数(ok)中有24位精度, y
只有1位,但是当你添加它们时,它们的位不在重叠位置,结果需要25位精度。 相反,它会变成四舍五入(在默认舍入模式下为0x2000000
)。
由于需要无限多的位置以获得正确的值,所以许多结果变得圆整。 这包括1/3的理性结果(你从十进制中所熟悉的无限多的地方),还有1/10(它也需要二进制无限多的地方,因为5不是2的幂),以及不合理的结果,比如任何不是完美广场的平方根。
双舍入。 在某些系统(特别是x86)上,浮点表达式的评估精度要高于其标称类型。 这意味着,当上述类型的舍入之一发生时,您会得到两个舍入步骤,首先将结果舍入为高精度类型,然后舍入到最终类型。 作为一个例子,如果将1.49舍入到整数(1),考虑如果将第一个舍入到小数点后一位(1.5),然后将结果舍入到整数(2),会发生什么。 这实际上是浮点处理的最难处理的领域之一,因为编译器的行为(特别是对于像GCC这样的错误的,不合规的编译器)是不可预知的。
超越函数( trig
, exp
, log
等)未被指定为具有正确舍入的结果; 结果在最后一个精确位置(通常称为1ulp )的一个单位内被指定为正确。
当你编写浮点代码时,你需要记住你正在处理的数字可能导致结果不准确,并相应地进行比较。 通常与“epsilon”进行比较是有意义的,但是这个epsilon应该基于你所比较的数量的大小,而不是绝对常数。 (如果绝对常数epsilon可以工作,这强烈表明固定点,而不是浮点,是工作的正确工具!)
编辑:特别是,一个幅度相对的epsilon检查应该看起来像这样:
if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y))
其中FLT_EPSILON
是float.h
的常量(将其DBL_EPSILON
为double
s的LDBL_EPSILON
或long double
s的LDBL_EPSILON
), K
是您选择的常数,这样您的计算的累积误差肯定受到最后K
单位的限制如果你不确定是否有误差计算的权利,请将K
比计算所说的要大几倍)。
最后,请注意,如果您使用此功能,则可能需要特别小心,因为FLT_EPSILON
对于非规范化无效。 一个快速解决办法是使它:
if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y) || fabs(x-y) < FLT_MIN)
如果使用双打,也可以替换DBL_MIN
。
由于0可以完全表示为IEEE754浮点数(或者使用任何其他我曾经使用过的fp数的实现),所以与0进行比较可能是安全的。 然而,如果你的程序计算出一个值(例如theView.frame.origin.x
),你有理由相信它应该是0,但是你的计算不能保证为0,你可能会被咬。
为了澄清一点,计算例如:
areal = 0.0
将(除非你的语言或系统被破坏)创建一个值,使得(areal == 0.0)返回true,但另一个计算如
areal = 1.386 - 2.1*(0.66)
不得。
如果你可以向自己保证你的计算产生的值是0(而不只是它们产生的值应该是0),那么你可以继续前进,并将fp值与0进行比较。如果你不能保证达到要求的程度,最好坚持“容忍平等”的通常做法。
在最坏的情况下,对fp值的粗心比较可能是非常危险的:认为航空电子设备,武器指导,发电厂运营,车辆导航,几乎所有计算符合现实世界的应用。
对于愤怒的小鸟来说,并不那么危险。
我想给出一些不同于其他人的答案。 他们非常适合回答你所提到的问题,但可能不是你需要知道的或你真正的问题所在。
在图形中的浮点很好! 但几乎没有必要直接比较花车。 你为什么需要这样做? 图形使用浮动来定义间隔。 并且比较浮动是否也在浮动定义的时间间隔内总是被很好地定义,并且只需要一致,不准确或不精确! 只要一个像素(这也是一个间隔!)可以分配这是所有图形需求。
所以,如果你想测试你的观点是否超出了[0..width [范围这就好了。 只要确保你一致地定义包含。 例如,总是定义里面是(x> = 0 && x <宽度)。 交集或命中测试也是如此。
但是,如果您滥用图形坐标作为某种标志,例如查看窗口是否停靠,则不应该这样做。 改为使用与图形表示层分离的布尔标志。
链接地址: http://www.djcxy.com/p/78635.html