php 浮點數比較方法

浮點數運算精度問題

 
首先看一個例子:

<?php
$a = 0.1;
$b = 0.9;
$c = 1;

var_dump(($a+$b)==$c);
var_dump(($c-$b)==$a);
?>

 
$a+$b==$c 返回true,正確
$c-$b==$a 返回false,錯誤
 
爲什麼會這樣呢?
運算後,精度爲20位時實際返回的內容如下:

<?php
$a = 0.1;
$b = 0.9;
$c = 1;

printf("%.20f", $a+$b); // 1.00000000000000000000
printf("%.20f", $c-$b); // 0.09999999999999997780
?>

 
$c-$b 爲 0.09999999999999997780,因此與0.1比較返回false
 
出現這個問題是因爲浮點數計算涉及精度,當浮點數轉爲二進制時有可能會造成精度丟失。
 

浮點數轉二進制方法

 
整數部分採用除以2取餘方法
小數部分採用乘以2取整方法
 
例如:把數字8.5轉爲二進制
整數部分是8
8/2=4 8%2=0
4/2=2 4%2=0
2/2=1 2%2=0
1比2小,因此不需要計算下去,整數8的二進制爲 1000
 
小數部分是0.5
0.5x2 = 1.0
因取整後小數部分爲0,因此不需要再計算下去
小數0.5的二進制爲 0.1
 
8.5的二進制爲1000.1
 
計算數字0.9的二進制
0.9x2=1.8
0.8x2=1.6
0.6x2=1.2
0.2x2=0.4
0.4x2=0.8
0.8x2=1.6
…. 之後不斷循環下去,當截取精度爲N時,N後的數會被捨去,導致精度丟失。
上例中0.9在轉爲二進制時精度丟失,導致比較時出現錯誤。
 
所以永遠不要相信浮點數已精確到最後一位,也永遠不要比較兩個浮點數是否相等。
 
 

正確比較浮點數的方法

 

1.使用round方法處理後再比較

例子:

<?php
$a = 0.1;
$b = 0.9;
$c = 1;

var_dump(($c-$b)==$a);                   // false
var_dump(round(($c-$b),1)==round($a,1)); // true
?>

 

2.使用高精度運算方法

首先進行運算時,使用高精度的運算方法,這樣可以保證精度不丟失。
 
高精度運算的方法如下:
bcadd 將兩個高精度數字相加
bccomp 比較兩個高精度數字,返回-1,0,1
bcdiv 將兩個高精度數字相除
bcmod 求高精度數字餘數
bcmul 將兩個高精度數字相乘
bcpow 求高精度數字乘方
bcpowmod 求高精度數字乘方求模
bcscale 配置默認小數點位數,相當於Linux bc中的”scale=”
bcsqrt 求高精度數字平方根
bcsub 將兩個高精度數字相減
 
例子:

<?php
$a = 0.1;
$b = 0.9;
$c = 1;

var_dump(($c-$b)==$a);          // false
var_dump(bcsub($c, $b, 1)==$a); // true
?>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章