PHP 浮點數高精度運算

2021-10-23 19:46:38 字數 3347 閱讀 1022

記錄下,工作中遇到的坑 ...

關於 php 浮點數運算,特別是金融行業、電子商務訂單管理、資料包表等相關業務,利用浮點數進行加減乘除時,稍不留神運算結果就會出現偏差,輕則損失幾十萬,重則會有信譽損失,甚至吃上官司,我們一定要引起高度重視!

//加

$a = 0.1;

$b = 0.7;

$c = intval(($a + $b) * 10);

echo $c."

";//輸出:7

//減$a = 100;

$b = 99.98;

$c = $a - $b;

echo $c."

";//輸出:0.019999999999996

//乘$a = 0.58;

$b = 100;

$c = intval($a * $b);

echo $c."

";//輸出:57

//除$a = 0.7;

$b = 0.1;

$c = intval($a / $b);

echo $c."

";//輸出:6

上面的結果,顯然不是我們想要的!

php 官方手冊解釋如下:

浮點數的精度有限。儘管取決於系統,php 通常使用 ieee 754 雙精度格式,則由於取整而導致的最大相對誤差為 1.11e-16。非基本數**算可能會給出更大誤差,並且要考慮到進行復合運算時的誤差傳遞。永遠不要相信浮點數結果精確到了最後一位,也永遠不要比較兩個浮點數是否相等。如果確實需要更高的精度,應該使用任意精度數學函式或者gmp 函式
這裡的關鍵在於,浮點數的小數用二進位制的表示,過程如下:

例:0.58

我們會得到乙個無限迴圈的二進位制小數:

0.1001010001...

小數部分出現迴圈,有限的二進位制位無法準確的表示乙個小數,這也就是小數運算出現誤差的原因。

接下來給大家介紹任意精度數學函式

對於任意精度的數學,php 提供了支援用字串表示的任意大小和精度的數字的二進位制計算。

bcmath:bc 是 binary calculator 的縮寫。

官方手冊:

大家在使用前,請先確認是否已安裝 bcmath。

//加

$a = 0.1;

$b = 0.7;

$c = intval(bcadd($a, $b, 1) * 10);

echo $c."

";//輸出:8

//減$a = 100;

$b = 99.98;

$c = bcsub($a, $b, 2);

echo $c."

";//輸出:0.02

//乘$a = 0.58;

$b = 100;

$c = intval(bcmul($a, $b));

echo $c."

";//輸出:58

//除$a = 0.7;

$b = 0.1;

$c = intval(bcdiv($a, $b));

echo $c."

";//輸出:7

除了加減乘除,bcmath 還提供了以下方法:

捨去法取整(向下取整)

echo floor(5.1);

//輸出:5

echo floor(8.8);

//輸出:8

進一法取整(向上取整)

echo ceil(5.1);

//輸出:6

echo ceil(8.8);

//輸出:9

普通四捨五入法

echo round(5.1);

//輸出:5

echo round(8.8);

//輸出:9

//保留兩位小數並且進行四捨五入

echo round(5.123, 2);

//輸出:5.12

echo round(8.888, 2);

//輸出:8.89

//保留兩位小數並且不進行四捨五入

echo substr(round(5.12345, 3), 0, -1);

//輸出:5.12

echo substr(round(8.88888, 3), 0, -1);

//輸出:8.88

銀行家捨入法

四捨六入五考慮,五後非空就進一,五後為空看奇偶,五前為偶應捨去,五前為奇要進一。

保留兩位小數,例:

實現**如下:

echo round(1.2849, 2, php_round_half_even);

//輸出:1.28

echo round(1.2866, 2, php_round_half_even);

//輸出:1.29

echo round(1.2851, 2, php_round_half_even);

//輸出:1.29

echo round(1.2850, 2, php_round_half_even);

//輸出:1.28

echo round(1.2750, 2, php_round_half_even);

//輸出:1.28

數值格式化(千位分組)

應用於金額的展示,比如我們經常會看的銀行卡餘額。

echo number_format('10000.98', 2, '.', ',');

//輸出:10,000.98

echo number_format('340888999', 2, '.', ',');

//輸出:340,888,999.00

mysql 浮點型字段

在 mysql 中,建立表字段時也有浮點數型別。

浮點數型別包括單精度浮點數(float)和雙精度浮點數(double)。

同理,不建議使用浮點數型別!!!

浮點數存在誤差,當我們使用精度敏感的資料時,應該使用定點數(decimal)進行儲存。

通過浮點數精度的問題,了解到浮點數的小數用二進位制的表示。

分享了用php 任意精度數學函式,來進行高精度運算。

同時分享了常用數值處理方案,比如捨去法、進一法、四捨五入法、銀行家捨入法、數值格式化 等。

最後,通過 php 的 float 聯想到 mysql 的 float。

以後,在使用浮點數運算的時候,一定要慎之又慎,細節決定成敗。

js高精度浮點數運算

貼 自定義高精度浮點數運算 物件格式寫法 var float calculator catch e m math.pow 10,math.max r1,r2 計算因子 return arg1 m arg2 m m minus function arg1,arg2 mul function arg1,...

系統的講解 PHP 浮點數高精度運算

加 a 0.1 b 0.7 c intval a b 10 echo c.輸出 7 減 a 100 b 99.98 c a b echo c.輸出 0.019999999999996 乘 a 0.58 b 100 c intval a b echo c.輸出 57 除 a 0.7 b 0.1 c i...

PHP浮點數運算精度問題

最近有客戶反應 訂單金額總是不準確,總是相隔一分錢。檢查相關 邏輯都是正確的,就是運用了四則運算。大概推測問題可能出在浮點計算丟失精度。在 php程式設計師雷雪松的部落格 中寫過一篇關於js精度不準確的文章 js中浮點數運算不精準 下面php程式設計師雷雪松詳細的介紹一下如何解決php浮點數運算精度...