Ios 目标C:安全浮动比较异常失败

Ios 目标C:安全浮动比较异常失败,ios,objective-c,floating-point,comparison,epsilon,Ios,Objective C,Floating Point,Comparison,Epsilon,我写了一段代码,遇到了一个非常奇怪的问题。两个浮点数之间的比较即使在实际比较为真时也返回NO。我甚至通过与FLT_EPSILON进行比较来使用安全浮点比较。代码如下: //To start the process run this: [self increment:0.0f]; - (void)increment:(float)f { f += 0.02f; if ((fabs(f - 1.0f) < FLT_EPSILON)) { NSLog(@

我写了一段代码,遇到了一个非常奇怪的问题。两个浮点数之间的比较即使在实际比较为真时也返回NO。我甚至通过与FLT_EPSILON进行比较来使用安全浮点比较。代码如下:

//To start the process run this:
[self increment:0.0f];




- (void)increment:(float)f {
    f += 0.02f;

    if ((fabs(f - 1.0f) < FLT_EPSILON)) {
        NSLog(@"STOP");
    }
    else {
        NSLog(@"F %f", f);
        [self increment:f];
    }
}

比较总是会失败,代码会进入无限循环。我已经在iOS 7上的32位设备和iOS 8上的iPhone 5S模拟器上对此进行了测试。

问题在于您正在累积不精确的值。FLT_EPSILON应定义为最小值,即1.0f+FLT_EPSILON!=1.0f

所发生的情况是,在每一步中,您将一个有限精度值添加到另一个有限精度值,并累积一个小误差。由于您正在精确检查一个值,该值足够接近1.0f,无法与1.0f区分,因此检查总是失败

如果需要在1.0处停止,则应直接检查f>1.0f,或使用更宽松的约束。请注意,如果值比所需值稍早一点,则使用f>1.0f可能会产生额外的迭代,因此如果迭代量必须精确,则可能不合适。f>1.0-0.02f/2应该更精确

0.98            1.0-0.02/2              1.0
 |                 |      ACCEPTABLE     |   ACCEPTABLE...   
Xcode 5.1上的lldb

(lldb) p f
(float) $0 = 0.999999582
(lldb) p -(f - 1.0f)
(float) $1 = 0.000000417232513
(lldb) p __FLT_EPSILON__
(float) $2 = 0.00000011920929
(lldb) p (-(f - 1.0)) < __FLT_EPSILON__
(bool) $3 = false

你必须使用浮动来解决这个问题吗?另一种方法是放大到整数。如果需要使用浮点值,请将整数除以100

- (void)increment:(NSUInteger)f {
    f += 2;

    if (f > 200) {
        NSLog(@"STOP");
    }
    else {
        [self increment:f];
    }
}

使用浮动时,NSLog不准确。将行打印f替换为printf%.10f\n,f;可以帮助您进行调试。这很有意义!检查f是否大于1有效,我已经用lldb检查了该值,它在我的设置中打印了1.01999962。谢谢请注意,检查f>1.0f意味着在累积误差给出的值小于所需值的情况下进行额外迭代,例如,在我的输出中,0.99999582应为1.0,但当f>1.0f时,您仅在额外迭代中实现。另一个解决方案是检查f>MAX-INCREMENT/2,这样你就可以接受一个比上一个更接近期望值的解决方案。是的,检查f>1.0f是否对我来说并不总是有效。一个解决方案是对整个浮点使用roundf,但是我可以只使用整数并将值放大100,这样我就可以完全消除这些问题。再次感谢你的帮助,很好的回答!更准确地说,FLT_EPSILON应定义为1.0f与其后继值之间的距离。尽管如此,答案还是很好。