php關於金額比較引發的問題(轉)
做電子商務的時候一般會涉及到金額的比較,按正常的思路來看用><=這些個符號就可以了。可是要是到程式上來搞這個的話就出大事了。現在看下這段程式碼:
$f = 0.07; var_dump($f * 100 == 7);//輸出false
輸出結果會出乎大家意料,輸出false,為什麼會這樣呢?其實這個和電腦中儲存小數的原理有關。大家都知道計算機只能儲存0和1,我們日常生活習慣使用的是10進位制的資料,像0.07這個小數在計算機中儲存時會有精度損失,以至於計算出來的結果會有偏差。
那麼怎麼解決這個問題?雖然計算機儲存小數有偏差,但是偏差還是非常小,像上例中0.07 * 100如果顯示出小數點後面20位的話,最終的值如下
$f
= 0.07;
//輸出7.00000000000000088818
echo
number_format(
$f
* 100, 20)
可以看到已經在小數點10多位之後了。在實際中我們通常也不需要精確到後面這麼多位數字,在金額方面通常精確到後面3位就好了。如果精確到小數點後面三位的話,0.07*100和7就會相等了。在php中提供了一個bccomp函式用來處理這方面的比較。
$f
= 0.07;
var_dump(
$f
* 100 == 7);
//輸出0,表示兩個數字精度為小數點後3位的時候相等
var_dump(
bccomp
(
$f
* 100, 7, 3));
雖然最終解決了問題,但是還是想搞明白為什麼0.07這樣的浮點數會有精度損失,經過一段時間的研究,發現產生誤差的原因:就在於浮點數的小數位在轉換成二進位制的時候產生的。
浮點數小數部分轉換成二進位制規則:乘2取整法,即每一步將十進位制小數部分乘以2,所得積的小數點左邊的數字(0或1)作為二進位制表示法中的數字,直到滿足精確度為止。
經過計算發現0.07即使計算到60位後,依然還沒有結束。在計算機中,32位的計算機中浮點數尾數部分是23位,64位的是52位。所以後面多出來的部分就會被捨棄掉。
測試都是在64位機器上進行的。
$bin
=
""
;
$int
= 7;
$base
= 100;
echo
"<table border=`1`>"
;
echo
"<td width=`50`>位數</td>"
;
echo
"<td width=`50`>x2</td>"
;
echo
"<td width=`50`>位值</td>"
;
for
(
$i
= 0;
$i
<= 60;
$i
++) {
echo
"<tr>"
;
echo
"<td>$i</td>"
;
$int
=
$int
* 2;
echo
"<td>$int</td>"
;
if
(
$int
== 100) {
$bin
.=
"1"
;
echo
"<td>1</td>"
;
break
;
}
if
(
$int
> 100) {
$bin
.=
"1"
;
$int
=
$int
-
$base
;
echo
"<td>1</td>"
;
}
else
{
$bin
.=
"0"
;
echo
"<td>0</td>"
;
}
echo
"</td>"
;
echo
"</tr>"
;
}
echo
"</table>"
;
echo
$bin
;
對上例轉換的二進位制進行反推:
/*
輸出內容
0.070000000000000006661338147751
0.070000000000000006661338147751
*/
$f
= 0.0;
$bin
=
"0001000111101011100001010001111010111000010100011110101110000"
;
$l
=
strlen
(
$bin
);
for
(
$i
= 0;
$i
<
$l
;
$i
++) {
if
(
$bin
[
$i
] > 0) {
$f
=
$f
+ pow(2, -(
$i
+ 1));
}
}
echo
number_format(
$f
, 30);
$f
= 0.07;
echo
"<br />"
;
echo
number_format(
$f
, 30);
bcadd — 將兩個高精度數字相加
bccomp — 比較兩個高精度數字,返回-1, 0, 1
bcdiv — 將兩個高精度數字相除
bcmod — 求高精度數字餘數
bcmul — 將兩個高精度數字相乘
bcpow — 求高精度數字乘方
bcpowmod — 求高精度數字乘方求模,數論裡非常常用
bcscale — 配置預設小數點位數,相當於就是Linux bc中的”scale=”
bcsqrt — 求高精度數字平方根
bcsub — 將兩個高精度數字相減
整理了一些例項
php BC高精確度函式庫包含了:相加,比較,相除,相減,求餘,相乘,n次方,配置預設小數點數目,求平方。這些函式在涉及到有關金錢計算時比較有用,比如電商的價格計算。
/** * 兩個高精度數比較 * * @access global * @param float $left * @param float $right * @param int $scale 精確到的小數點位數 * * @return int $left==$right 返回 0 | $left<$right 返回 -1 | $left>$right 返回 1 */ var_dump(bccomp($left=4.45, $right=5.54, 2)); // -1 /** * 兩個高精度數相加 * * @access global * @param float $left * @param float $right * @param int $scale 精確到的小數點位數 * * @return string */ var_dump(bcadd($left=1.0321456, $right=0.0243456, 2)); //1.04 /** * 兩個高精度數相減 * * @access global * @param float $left * @param float $right * @param int $scale 精確到的小數點位數 * * @return string */ var_dump(bcsub($left=1.0321456, $right=3.0123456, 2)); //-1.98 /** * 兩個高精度數相除 * * @access global * @param float $left * @param float $right * @param int $scale 精確到的小數點位數 * * @return string */ var_dump(bcdiv($left=6, $right=5, 2)); //1.20 /** * 兩個高精度數相乘 * * @access global * @param float $left * @param float $right * @param int $scale 精確到的小數點位數 * * @return string */ var_dump(bcmul($left=3.1415926, $right=2.4569874566, 2)); //7.71 /** * 設定bc函式的小數點位數 * * @access global * @param int $scale 精確到的小數點位數 * * @return void */ bcscale(3); var_dump(bcdiv(`105`, `6.55957`)); // 16.007
相關文章
- 關於 PHP 不同資料型別在比較時該如何轉化問題PHP資料型別
- php比較運算子的安全問題PHP
- 新手問題,關於LINUX的引導!(轉)Linux
- php關於session的問題PHPSession
- 對於“前端狀態”相關問題,如何思考比較全面前端
- 關於jsp中轉發的問題JS
- 關於PHP佇列的問題PHP佇列
- PHP 微信支付通知金額不一致的問題PHP
- JAVA中文比較問題的分析和解決 (轉)Java
- 關於PHP的OpenSSL的加密問題PHP加密
- 關於轉義符 在php正則中的匹配問題PHP
- 轉一篇關於JAVA 和 .NET的文章的比較Java
- 一個有關多域名session的問題,比較棘手Session
- PHP物件的比較PHP物件
- 關於 http cache 的一個小問題以及引發的思考HTTP
- 關於php生成靜態問題PHP
- 關於PHP 時區錯誤的問題PHP
- 關於Oracle字元型別的比較Oracle字元型別
- 關於 PHP artisan config:cache 引發的 bugPHP
- 關於Go tools的比較有用的flagsGo
- 關於 RocketMQ ClientID 相同引發的訊息堆積的問題MQclient
- 版本號比較大小問題
- Java 字串比較、拼接問題Java字串
- PHP比較字串PHP字串
- php,asp,jsp,c,速度比較 (轉)PHPJS
- 關於PHP記憶體洩漏的問題PHP記憶體
- 令人比較失落的IT圈子-關於華為裁員
- 關於日期計算的問題 (轉)
- PHP array_column 引發的一個小問題PHP
- 關於幾個MySQL環境問題的對比MySql
- 金額轉換
- 關於 PHP Session ID 改變的問題解決PHPSession
- MySQL中NULL欄位的比較問題MySqlNull
- 關於plsql,crt登入比較慢SQL
- 關於c、c++之前比較模糊的概念C++
- EJBQL中關於日期型資料的比較
- JavaScript 與 Java、PHP 的比較JavaScriptPHP
- PHP中的比較運算子PHP