2013-03-13 72 views
3

我需要一個函數來將一個正整數加到最接近的整數。潛伏aorund我發現這非常優雅的方式floor()表現奇怪

int x = floor(y + 0.5); 

我寫了一個簡單的測試程序:

double a = 10.0; 

for (int i = 0; i < 10; i++) { 
    cout << a << "\t" << a + 0.5 << "\t" << floor(a + 0.5) << endl; 
    a += 0.1; 
} 

,但我收到一些奇怪的輸出

10  10.5 10 
10.1 10.6 10 
10.2 10.7 10 
10.3 10.8 10 
10.4 10.9 10 
10.5 11  10 <--- should be 11! 
10.6 11.1 11 
10.7 11.2 11 
10.8 11.3 11 
10.9 11.4 11 

whay是什麼?

感謝 問候 盧卡

+2

雙打和浮點(變量存儲的是在浮點標準中)只是近似值。精度不會與精確的十進制值綁定。 – suspectus 2013-03-13 09:19:52

+0

http://stackoverflow.com/questions/5562492/strange-results-with-c-ceiling-function – uba 2013-03-13 09:26:27

+1

有關浮點計算的大量細節:http://docs.oracle.com/cd/E19957-01/806 -3568/ncg_goldberg.html - 網上還有不錯的PDF版本。但是glglgl的回答已經說過了。 – Arne 2013-03-13 09:29:05

回答

2

這裏的輸出,它使用printf代替:

printf("%.15f\t%.15f\t%.15f\n", a, a + 0.5, floor(a + 0.5));

不精確現在很清楚:

10.000000000000000 10.500000000000000 10.000000000000000 
10.100000000000000 10.600000000000000 10.000000000000000 
10.199999999999999 10.699999999999999 10.000000000000000 
10.299999999999999 10.799999999999999 10.000000000000000 
10.399999999999999 10.899999999999999 10.000000000000000 
10.499999999999998 10.999999999999998 10.000000000000000 
10.599999999999998 11.099999999999998 11.000000000000000 
10.699999999999998 11.199999999999998 11.000000000000000 
10.799999999999997 11.299999999999997 11.000000000000000 
10.899999999999997 11.399999999999997 11.000000000000000 
+0

沒有必要將問題與'printf'混淆。只需在'std :: cout'上設置精度即可。 – 2013-03-13 09:50:12

+0

@JamesKanze - 之前的問題是用C標記的。它應該仍然清楚發生了什麼。 – teppic 2013-03-13 09:53:27

3

加入0.1,你確實添加稍低於0.1的值。

所以加入0.1 5次與添加0.5次不一樣;你沒有完全達到那個值。再次加入.5,你不會超過11,這會產生你觀察到的行爲。


AC程序,如

#include <stdio.h> 
#include <math.h> 

int main() 
{ 
    double a = 10.0; 
    int i; 
    for (i = 0; i < 11; i++) { 
     printf("%4.19f\t%4.19f\t%4.19f\n", a, a+.5, floor(a + 0.5)); 
     a += 0.1; 
    } 
    printf("\n"); 
    for (i = 0; i < 11; i++) { 
     a = 10.0 + i/10.0; 
     printf("%4.19f\t%4.19f\t%4.19f\n", a, a+.5, floor(a + 0.5)); 
    } 
} 

顯示了它的輸出

10.0000000000000000000 10.5000000000000000000 10.0000000000000000000 
10.0999999999999996447 10.5999999999999996447 10.0000000000000000000 
10.1999999999999992895 10.6999999999999992895 10.0000000000000000000 
10.2999999999999989342 10.7999999999999989342 10.0000000000000000000 
10.3999999999999985789 10.8999999999999985789 10.0000000000000000000 
10.4999999999999982236 10.9999999999999982236 10.0000000000000000000 
10.5999999999999978684 11.0999999999999978684 11.0000000000000000000 
10.6999999999999975131 11.1999999999999975131 11.0000000000000000000 
10.7999999999999971578 11.2999999999999971578 11.0000000000000000000 
10.8999999999999968026 11.3999999999999968026 11.0000000000000000000 
10.9999999999999964473 11.4999999999999964473 11.0000000000000000000 

10.0000000000000000000 10.5000000000000000000 10.0000000000000000000 
10.0999999999999996447 10.5999999999999996447 10.0000000000000000000 
10.1999999999999992895 10.6999999999999992895 10.0000000000000000000 
10.3000000000000007105 10.8000000000000007105 10.0000000000000000000 
10.4000000000000003553 10.9000000000000003553 10.0000000000000000000 
10.5000000000000000000 11.0000000000000000000 11.0000000000000000000 
10.5999999999999996447 11.0999999999999996447 11.0000000000000000000 
10.6999999999999992895 11.1999999999999992895 11.0000000000000000000 
10.8000000000000007105 11.3000000000000007105 11.0000000000000000000 
10.9000000000000003553 11.4000000000000003553 11.0000000000000000000 
11.0000000000000000000 11.5000000000000000000 11.0000000000000000000 

的區別:第一運行與累加錯誤並與0.0999999999999996447步驟你的方法,而第二次運行會盡可能地重新計算,從而可以準確地達到10.5和11.0。

+0

如果OP增加「0.125」,他們會更好,因爲「1/8」可以精確表示。 – 2013-03-13 09:41:50

+1

@PeterWood如果OP增加了0.125,他很可能沒有意識到可能存在問題。 – 2013-03-13 09:52:28

+0

@JamesKanze對不起,是的,OP需要明白有一個問題。我的意思是幫助他們思考爲什麼有些數字比其他數字更好。 – 2013-03-13 09:55:59

0

該問題是由於四捨五入錯誤的積累。浮點數在內部不是整數,它們的值大多是近似值。所以你每次執行時都會累積舍入誤差a + = .1a + .5操作,結果就是你得到的結果。

您可以嘗試在增量修改看看不行,但在使用下面的表達式(通常它使大規模更好的結果):

a = 10. + .1 * i; 
+1

這是朝着正確方向邁出的一步,但這裏的問題是'0.1'不能完全表示。使用'10。 +(i/10)'應該給出正確的結果,但是:所有涉及的數字都可以精確地表示,'i'爲'5'時的結果也可以。 (當'i'是'1'時,結果仍然不是'10.1',但是對於舍入,我們只關心結果是否接近'something.5'。) – 2013-03-13 09:49:09

+0

需要注意的是舍入錯誤在'.1'的**轉換**中爲浮點值。一個粗略的類比將是'int i = 0; i + = 3/2;我+ = 3/2;'。 「i」的結果值是2,而不是3. – 2013-03-13 12:09:15

0

問題是,正如其他人指出的,你從來沒有 實際上有10.5;你只有非常接近的東西 到10.5(但非常小)。

作爲一般規則,對於這種事情,你應該而不是是 增加一個增量到浮點值。你應該 只使用積分增量,它的規模每次去 浮點值:

for (int i = 0; i != 10; ++ i) { 
    double aa = a + (i/10.); 
    std::cout << aa << '\t' << aa + 0.5 << '\t' << floor(aa + 0.5) << std::endl; 
} 

這應該給你想要的結果。

當然,如果你的例子只是一個測試...很大程度上取決於 如何計算四捨五入的值。您正在使用的實際舍入 可能是合適的。或者,如果你知道 值應該是0.1倍數,你可以嘗試做 算術10縮放,然後輪結果,然後輪 10的倍數