2014-08-29 79 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