2010-03-24 54 views
7

這種感覺就像只能在原地失敗的那種代碼,但我會嘗試將它調整爲代表我所看到的代碼片段。爲什麼我的號碼被錯誤地舍入?

float f = myFloat * myConstInt; /* Where myFloat==13.45, and myConstInt==20 */ 
int i = (int)f; 
int i2 = (int)(myFloat * myConstInt); 

步進代碼後,i == 269和i2 == 268。這裏發生了什麼事情來解釋差異?

+3

一個邪惡面試問題的潛力:i和i2的價值是什麼? – 2010-03-24 16:48:20

+0

...這是一本IEEE手冊和一些機器寄存器信息。和一個白板筆。走! – 2010-03-24 16:49:50

+2

參見http://stackoverflow.com/questions/2342396和http://stackoverflow.com/questions/2345534和http://stackoverflow.com/questions/2225503和http://stackoverflow.com/questions/2494724 – 2010-03-24 17:03:36

回答

15

浮點數學運算可以比公佈的精度高。但是,只要您將其存儲在浮點f中,那麼額外的精度就會丟失。在第二種方法中,你不會失去這種精度,當然,直到你將結果轉換爲int。

編輯:看到這個問題Why differs floating-point precision in C# when separated by parantheses and when separated by statements?比我大概提供了一個更好的解釋。

+0

+1很好的解釋。 – technophile 2010-03-24 16:47:54

+0

第二個結果是不正確的!所以在那裏失去了精確度。但是哪裏? – Andrey 2010-03-24 16:52:30

+1

看到這個問題:http://stackoverflow.com/questions/2491161/why-differs-floating-point-precision-in-c-when-separated-by-parantheses-and-when/2494724#2494724更好的解釋我可能提供。 – 2010-03-24 16:53:06

4

因爲浮點變量不是infinitely accurate。如果您需要這種精確度,請使用小數點。

不同的rounding modes也可能會出現這個問題,但準確性問題是你在這裏遇到的問題,AFAIK。

+0

+1有趣的鏈接 – Robusto 2010-03-24 16:52:35

+1

恩,小數也不是無限精確的。 – 2010-03-24 16:56:42

+1

@丹尼爾:小數不是無限精確的,但它們*一貫*精確。十進制算術實際上全部用整數完成,而不是用浮點數完成,所以它不具有基於芯片質量的精度。 – 2010-03-24 16:58:12

1

替換

double f = myFloat * myConstInt; 

,看看你是否得到相同的答案。

+0

儘管在這種情況下它可能會給出「正確」結果,但在某些情況下,雙打仍然會出現相同的問題。你需要一個任意的精度類型來避免這個問題。 – technophile 2010-03-24 16:50:43

2

浮點精度有限,基於二進制而不是十進制。十進制數13.45不能用二進制浮點精確表示,因此向下舍入。乘以20進一步誇大了精度的損失。在這一點上,你有268.999 ... - 不是269 - 因此轉換爲整數截斷爲268.

要轉換爲最接近的整數,可以嘗試在轉換回整數之前加0.5。

對於「完美」算術,您可以嘗試使用Decimal或Rational數值類型 - 我相信C#具有兩個庫,但我不確定。但是,這些會變慢。

編輯 - 我已經找到了一個「十進制」類型,但不是一個理性 - 我可能是錯誤的可用。十進制浮點數是不準確的,就像二進制一樣,但這是我們習慣的那種不準確性,所以它的結果並不令人意外。

1

我想提供不同的解釋。

下面的代碼,我已經註釋(我看着記憶解剖彩車):

 
float myFloat = 13.45; //In binary is 1101.01110011001100110011 
int myConstInt = 20; 
float f = myFloat * myConstInt; //In binary is exactly 100001101 (269 decimal) 
int i = (int)f; // Turns float 269 into int 269 -- no surprises 
int i2 = (int)(myFloat * myConstInt);//"Extra precision" causes round to 268 

讓我們看一下計算接近:

  • F = 1101.01110011001100110011 * 10100 = 100001100.111111111111111 111

    空格之後的部分是位25-27,它使位24四捨五入,因此整個值四捨五入爲269

  • INT I2 =(int)的(myFloat * myConstInt)

    myfloat延伸到雙精度進行計算(0被附加):1101。0111001100110011001100000000000000000000000000000

    myfloat * 20 = 100001100.11111111111111111100000000000000000000000000

    位54及以後是0,所以沒有舍入完成:鑄造導致整數268.

    (如果使用擴展精度的類似的解釋將工作。)

更新:我改進我的答案,並寫了一個全面的一篇名爲When Floats Don’t Behave Like Floats

相關問題