# Float Precision
# Date: 2019/April/15
# 60 seconds
# 問題: 0.1 + 0.2 != 0.3 ?
由於計算機底層使用0與1來代表數字的緣故, 你看見的0.1跟0.2並不是真正的0.1+0.2.
讓電腦實際上去跑一次就知道了:
$a = 0.1 + 0.2;
$b = 0.3;
var_dump(number_format($a, 20, '.', ' '));
var_dump(number_format($b, 20, '.', ' '));
var_dump($a == $b);
2
3
4
5
6
實際輸出:
string(22) "0.30000000000000004441"
string(22) "0.29999999999999998890"
bool(false)
2
3
# 解決方案
THE FLOATING-POINT GUIDE在php cheat sheet (opens new window)提到了可以使用 bcadd()來做運算,
也可以使用 number_format() 來印出資料.
# 60 minutes
# 為什麼?
THE FLOATING-POINT GUIDE - Binary Fractions (opens new window)有一個非常精美的表格,
解釋了表面上看到的數字, 實際上在電腦裡是怎麼儲存的:
能用二進制表示的不是問題, 可是小數通常都不會是只靠二進制就能解決的.
如圖所示, 可以看到從十進位轉換成二進位時的誤差有多嚴重.
作者提到有三種方式可以解決這種問題:
- Limited-Precision Decimal
以PHP來說, 有善心人士寫成的ext-decimal (opens new window), 可以嘗試使用看看.
- Arbitrary-Precision Decimal
在PHP裡面就有BCMath與GMP兩種選擇了. 詳細的比較可以參考這則StackOverflow (opens new window).
- Symbolic calculations
使用符號來表示數據, 而不是算出數字. 一樣有善心人士提供了套件 (opens new window)使用,
而且還是composer, 最低需求只需要PHP 7!
# References
# 其他參考資料
PHP Manual (2019). Floating point numbers (opens new window).
PHP Manual (2019). BC Math Functions (opens new window).
PHP Manual (2019). number_format — Format a number with grouped thousands (opens new window).
Borgwardt, M. (a.k.a. brazzy) (2010). Basic Answers (opens new window). THE FLOATING-POINT GUIDE.