2012-11-07 80 views
3

當我運行:PHP異常/怪異的行爲

for($o=1;$o<=655;$o++){ $r = $r+0.01; echo $r." ";}

在某些時候,我得到:

...4.29 4.3 4.31 4.32 4.33 4.34 4.35 4.36 4.37 4.38 4.39 4.4 
4.41 4.42 4.4299999999999 4.4399999999999 4.4499999999999 
4.4599999999999 4.4699999999999 ... 

但是當我運行:

for($o=1;$o<=5;$o+=0.01){ echo $o." "; }

異常啓動at:

4.34 4.35 4.36 4.37 4.38 4.3899999999999 4.3999999999999 

爲什麼添加是for循環的一部分,還是在for循環中有區別?

+0

標準浮點不準確(谷歌它,或閱讀以下線程:http://stackoverflow.com/questions/2100490/floating-point-inaccuracy-examples) – ChristopheD

+0

http://php.net/language.types。浮動 - 見大紅色框 - [每個計算機科學家應該知道的浮點算術](http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – hakre

+0

我完全瞭解意識到這一點,但爲什麼它在for循環中定義和在它之後定義時有所不同? 如果我使用$ o ++和$ r = $ r + 0.01,它會在使用$ o + = 0.01時顯示不同的結果。這是真正的問題 –

回答

5

歡迎來到四捨五入錯誤的奇蹟。

0.01不能精確地表示爲有限二進制小數,因此如果將其重複添加到另一個浮點數,最終會選取捨入誤差。

您看到舍入誤差的具體點取決於所涉及數字的二進制展開如何產生舍入誤差。

+0

我完全意識到這一點,但爲什麼它在for循環中定義和在它之後有所作用? –

+0

我不知道你什麼時候設置'$ r'的值,或者你設置了它的值。您可以在循環中使用它,但不會發佈設置初始值的代碼。那可能是那裏的區別。它可能與循環無關。 – John

+0

這是我設置值的實際位置,在此之前沒有$ r或$ o的其他init –

1

浮點數學不能準確地表示值。見here

0

當數字「0.01」被轉換爲64位IEEE 754二進制浮點,結果是0.010000000000000000208166817117216851329430937767028809。當$ r被設置爲0,並且將該近似值0.01添加到100時,結果是1.0000000000000006661338147750939242541790008544921875,這是由於在添加期間附加的舍入錯誤。

現在考慮$ r從這個值進一步增加到略大於1,而$ o從它的起始值恰好爲1增加。首先,由於$ r和$ o以不同的值開始,它們通常會有不同的值後面的值。其次,因爲他們是不同的,隨着增加到每個人中,他們會遇到不同的錯誤。這些錯誤可能會發生,以加強或取消,導致$ r或$ o或多或少有所不同。

最後,PHP有一些標準來決定是否打印一個有兩位小數或更多的數字。據推測,在某些時候,$ r或$ o的值離最近的數字足夠遠,並且有兩個小數點,PHP決定它應該打印更多的數字。如上所述,這發生在$ r和$ o的不同時間。