3 回答

TA貢獻1811條經(jīng)驗 獲得超4個贊
只要迭代次數(shù)少于十億次,事情就會很容易崩潰。問題是,通過使用浮點數(shù)和算術(shù),您可以很容易地發(fā)現(xiàn)自己得到意想不到的結(jié)果,即使數(shù)字表面看起來不錯,細微的不精確性也可能導致應用程序出錯。
讓我們嘗試一下答案中示例的變體:
$total = 0.0;
for ($i = 0; $i < 10; $i++) {
? ? $total += 0.1;
}
echo "added ten cents, ten times\n";
// since we added 0.1 € x 10 times, we now have 1€ in total, right?
if ($total == 1) {
? ? echo "I have 1€. All is good in the realm.";
}
else {
? ? echo "WTF? Where is my money? I only have {$total}€!!!!\n";
? ? echo "\$total holds: ";
? ? var_dump($total);
}
上述的輸出是:
added ten cents
WTF? Where is my money? I only have 1€!!!!
$total holds: float(1)
即使$total看起來是這樣float(1),代碼也會遵循“錯誤”的執(zhí)行分支,從而破壞了我們的應用程序。
如果我們在 PHP8(目前為測試版)中執(zhí)行相同的代碼,您將得到更容易理解的結(jié)果:
added ten cents
WTF? Where is my money? I only have 1€!!!!
$total holds: float(0.9999999999999999)
另一個簡單的例子:
$balance? ? ? ? ? = 50.03;
$debit? ? ? ? ? ? = 45.42;
$expected_balance = 4.61;
$real_balance? ? ?= $balance - $debit;
if ($real_balance !== $expected_balance) {
? ? echo "problems: ";
? ? var_dump($real_balance);
}
上述的輸出是:
balance mismatch: float(4.61)
或者,在 PHP8 中:
balance mismatch: float(4.609999999999999)
上述兩個例子都表明,實際上,使用浮點數(shù)進行(特別是)貨幣算術(shù)可能會出現(xiàn)問題。由于結(jié)果不再符合您的期望,因此它不僅可能導致明顯錯誤的結(jié)果,而且細微的不同結(jié)果可能使整個應用程序以意想不到的方式運行。

TA貢獻1868條經(jīng)驗 獲得超4個贊
這個問題沒有什么意義,因為這不是浮點錯誤的工作原理。
誤差很小。它們以遠程小數(shù)形式出現(xiàn),只有當您需要非常高的精度水平時它們才會被注意到。畢竟,IEEE 754 為絕大多數(shù)計算機系統(tǒng)提供支持,并且它提供了出色的精度。具體來說,0.1 公里以浮點數(shù)表示為 0.100000001490116119384765625,如果我沒有算錯的話,精確度可達 1/10 μm。
可能沒有一組精心挑選的數(shù)字和現(xiàn)實生活中的計算需要您使用 PHP 進行(發(fā)票、證券交易所指數(shù)...),無論您多么小心地精確,都會呈現(xiàn)不正確的結(jié)果水平。因為那不是問題。
浮點數(shù)學的問題在于,它迫使您在每一步上都非常小心,并且很容易出現(xiàn)錯誤。
對于精度很重要的應用程序,您可以使用浮點數(shù)編寫正確的軟件,但它不會那么簡單、可維護或健壯。
原答案:
這是迄今為止我得到的最好的:
// Set-up and display settings (shouldn't affect internal calculations or final result)
set_time_limit(0);
ini_set('precision', -1);
// Expected accuracy: 2 decimal positions
$total = 0;
for ($i = 0; $i < 1e9; $i++) {
? ? $total += 0.01;
? ? // It's important to NOT round inside the loop, e.g.: $total = round($total + 0.01, 2);
}
var_dump($total, number_format($total, 2));
float(9999999.825158669)
string(12) "9,999,999.83" // Correct value would be "10,000,000.00"
不幸的是,它依賴于大量精度誤差的累積(它需要大約 1,000,000,000 個誤差才會發(fā)生,并且在我的 PC 上運行需要超過 4 分鐘),因此它并不像我想象的那樣真實喜歡,但這確實說明了根本問題。

TA貢獻1777條經(jīng)驗 獲得超10個贊
echo floor((0.1 + 0.7) * 10);
預期結(jié)果:0.1+0.7=0.8;0.8*10=8;
結(jié)果:7
在 PHP 7.2.12 上測試
- 3 回答
- 0 關(guān)注
- 160 瀏覽
添加回答
舉報