2014-08-29 82 views
2

我写了一段代码,遇到了一个非常奇怪的问题。即使实际比较结果为真,两个浮点数之间的比较也会返回NO。我甚至用FLT_EPSILON作为安全浮点比较。这是代码:目标C:安全浮点数比较奇怪地失败

//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模拟器上对此进行了测试。

+0

当使用浮点运算时,NSLog不准确。用'printf(“%。10f \ n”,f)替换打印'f'的行可以帮助您进行调试。 – 2014-08-29 07:32:16

回答

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上的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 
+0

这很有道理!检查f是否大于1有效,我用lldb检查了值,并在我的设置上打印1.01999962。谢谢! – JonasG 2014-08-29 07:36:48

+0

注意检查f> 1.0f是否意味着在积累的错误给出的值比所需值小的情况下进行额外的迭代(例如,在我的输出结果中0.999999582应该是1.0,但如果您认识到它> 1.0在额外的迭代上)。另一种解决方案可能是检查f> MAX - INCREMENT/2,以便接受比前一个值更接近所需值的解决方案。 – Jack 2014-08-29 07:39:18

+0

是的,检查是否f> 1.0f并不总是适合我。一个解决方案是使用roundf来实现整个浮点数,但是我可以使用整数并将值增加100,这样我就可以完全摆脱这些问题。再次感谢您的帮助,很好的回答! – JonasG 2014-08-29 07:45:36

1

必须使用浮点数来解决这个问题?另一种方法是扩展到int。如果您需要使用浮点值,则将int除以100.

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

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

那就是我现在正在做的,欢呼! – JonasG 2014-08-29 07:46:06