電商或財務系統計算錢精度的問題
在財務系統中經常,國內最小是分,比如說一件東西0.58元,如果買一百件那應該是58元吧? 用php表示計算
<?php
var_dump(0.58*100) ;//58
?>
結果正確,但是在看一下,下面的結果,怎麼就變成57了呢?
<?php
var_dump(intval(0.58*100));//57
?>
是php的bug嗎?
那讓我們再看一下,javascript,執行下面的程式碼,你會發現第一個輸出怎麼不是0.3?
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 == 0.3);
因此php 表示這個鍋,我不背,搞懂這些就要了解浮點數在計算機中表示的方法.
浮點數, 以64位的長度(雙精度)為例, 會採用1位符號位(E), 11指數位(Q), 52位尾數(M)表示(一共64位).
0.58的二進位制表示基本上(52位)是: 0010100011110101110000101000111101011100001010001111, 如果只是通過這52位計算的話是 0.58 -> 0.57999999999999996,至於0.58 * 100的具體浮點數乘法, 我們不考慮那麼細,我們就模糊的以心算來看… 0.58 * 100 = 57.999999999
那你intval一下, 自然就是57了….
因此浮點數的計算和比較是有誤差的.
在php中如何來解決這個問題呢?可以有兩種方法
方法1:PHP 為任意精度數學計算提供了二進位制計算器(Binary Calculator),它支援任意大小和精度的數字,以字串(非浮點數)形式描述
bcadd — 加法
bccomp — 比較
bcdiv — 相除
bcmod — 求餘數
bcmul — 乘法
bcpow — 次方
bcpowmod — 先次方然後求餘數
bcscale — 給所有函式設定小數位精度
bcsqrt — 求平方根
bcsub — 減法
方法2: 如果需要轉成整形轉換成多少分錢取整,最後第三個100是換回成多少元.
還是上面的 就是 round(0.58*100)*100/100;
那前面挖的坑,在javascript也有對應的函式,Math.round,可以通過Math.round(0.58*100),將小數轉換成整數,最後再除100,轉換成元計算.