目标C:安全浮动比较奇怪地失败

时间:2014-08-29 07:19:00

标签: 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(@"STOP");
    }
    else {
        NSLog(@"F %f", f);
        [self increment:f];
    }
}

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

2 个答案:

答案 0 :(得分:3)

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

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

如果你需要在1.0停止,你应该直接检查if (f > 1.0f),或使用更宽松的约束。请注意,如果值在所需值之前稍微增加,则使用f > 1.0f可能会产生额外的迭代,因此如果迭代量必须精确,则可能不合适。像f > 1.0 - 0.02f/2这样的东西应该更精确。

0.98            1.0-0.02/2              1.0
 |                 |      ACCEPTABLE     |   ACCEPTABLE...   

lldb on Xcode 5.1

(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

答案 1 :(得分:1)

你必须使用花车来解决问题吗?另一种方法是扩展到int。如果需要使用浮点值,则将int除以100。

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

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