比较浮点值有多危险?

我知道UIKit使用CGFloat是因为独立于坐标系的分辨率。

但每次我想检查是否例如frame.origin.x0它使我感到恶心:

if (theView.frame.origin.x == 0) {
    // do important operation
}

==<=>=<>比较时, CGFloat是否容易受到误报? 这是一个浮点,他们有无法解决的问题:例如, 0.0000000000041

Objective-C是在比较内部还是在内部处理这种情况,或者可能会发生这样的情况: origin.x读数为零并不与0比较为真?


首先,浮点值在其行为中不是“随机的”。 精确的比较在大量现实世界的使用中可以并且确实有意义。 但是如果你要使用浮点数,你需要知道它是如何工作的。 在假定浮点工作像真实数字这一方面会让你的代码很快崩溃。 在假设浮点结果的方面存在较大的随机模糊(就像这里提供的大多数答案一样)会让你看起来起作用的代码,但最终会出现大幅度错误和破损的角落案例。

首先,如果你想用浮点编程,你应该阅读这个:

每位计算机科学家应该了解的浮点算法

是的,请阅读所有内容。 如果负担过重,则应使用整数/固定点进行计算,直到有时间阅读为止。 :-)

现在,就这样说,精确浮点比较的最大问题归结为:

  • 事实上,您可能会在源代码中写入很多值,或者使用scanfstrtod读入值,这些值不会以浮点值的形式存在,而会悄然转换为最接近的近似值。 这就是demon9733的答案所谈论的。

  • 由于没有足够的精度来表示实际结果,所以许多结果变得圆整。 一个简单的例子,你可以看到这是添加x = 0x1fffffey = 1作为浮点数。 这里, x在尾数(ok)中有24位精度, y只有1位,但是当你添加它们时,它们的位不在重叠位置,结果需要25位精度。 相反,它会变成四舍五入(在默认舍入模式下为0x2000000 )。

  • 由于需要无限多的位置以获得正确的值,所以许多结果变得圆整。 这包括1/3的理性结果(你从十进制中所熟悉的无限多的地方),还有1/10(它也需要二进制无限多的地方,因为5不是2的幂),以及不合理的结果,比如任何不是完美广场的平方根。

  • 双舍入。 在某些系统(特别是x86)上,浮点表达式的评估精度要高于其标称类型。 这意味着,当上述类型的舍入之一发生时,您会得到两个舍入步骤,首先将结果舍入为高精度类型,然后舍入到最终类型。 作为一个例子,如果将1.49舍入到整数(1),考虑如果将第一个舍入到小数点后一位(1.5),然后将结果舍入到整数(2),会发生什么。 这实际上是浮点处理的最难处理的领域之一,因为编译器的行为(特别是对于像GCC这样的错误的,不合规的编译器)是不可预知的。

  • 超越函数( trigexplog等)未被指定为具有正确舍入的结果; 结果在最后一个精确位置(通常称为1ulp )的一个单位内被指定为正确。

  • 当你编写浮点代码时,你需要记住你正在处理的数字可能导致结果不准确,并相应地进行比较。 通常与“epsilon”进行比较是有意义的,但是这个epsilon应该基于你所比较的数量的大小,而不是绝对常数。 (如果绝对常数epsilon可以工作,这强烈表明固定点,而不是浮点,是工作的正确工具!)

    编辑:特别是,一个幅度相对的epsilon检查应该看起来像这样:

    if (fabs(x-y) < K * FLT_EPSILON * fabs(x+y))
    

    其中FLT_EPSILONfloat.h的常量(将其DBL_EPSILONdouble s的LDBL_EPSILONlong 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

    上一篇: How dangerous is it to compare floating point values?

    下一篇: choosing data types and how to understand them