2012-03-26 96 views
6

雙重價值的比較,我嘗試創建單元測試用例來檢查我的表值是正確的或錯誤的。問題與PHP

這是我的代碼

echo $a = 2/9; 
echo "<br>".$b=0.22222222222222; 

echo "<br>".gettype($a); 
echo "<br>".gettype($b); 
if($a==$b){ 
    echo "<br>". "equal"; 
}else echo "<br>". "Not equal"; 


if((string)$a==(string)$b){ 
    echo "<br>". "equal"; 
}else echo "<br>". "Not equal"; 

爲什麼我的第一個,如果條件不工作?我找不到原因。請幫幫我。

+2

可能的重複http://stackoverflow.com/questions/3148937/compare-floats-in-php永遠不會比較浮動與'=='。另外'2/9'不是'0.22222222222222'。小數部分無限變化,因此即使PHP以符號方式計算浮點數,您的比較也會失敗。 – Basti 2012-03-26 05:43:29

回答

10

試驗違反了浮點編程的基本規則:永遠不會做相等比較。

有許多問題源於浮點部分具有大而有限的位數的事實。這些問題通常被稱爲「舍入誤差」雖然在大多數情況下,他們都沒有錯誤,但格式限制。

例如,由於的方式編程...爲十進制,當我們寫號......大多數的數字,我們可以寫出不具備浮點格式的相應的表示,如果他們有一個小數。小數部分在基數2中重複。

這在很大程度上排除了準確,比較浮點數,除了具有諷刺意味的積分值。 您需要實現一個比較模糊,如abs(a - b) < epsilon.

而實際上,你2/9是不具有有限的表示形式要麼十進制字符串二進制字符串中獎情況!

爲了比較2/9與常數的平等成功,對程序,解釋器和庫的完美性提出了更多的要求。

例如,您需要輸入比您需要的更多的2,並且解釋器必須以比格式更精確的知識對常數的低位進行舍入。在執行操作時,機器實際上有一些額外的知識,但解釋器在轉換常量時可能不會。此外,運行四捨五入是各種選項和像PHP語言甚至可以不指定常量究竟是如何不可表示的是從源代碼內部形式四捨五入。

而實際上它是更糟比,因爲個體0。2/10 n組件中的十進制字符串沒有確切的二進制等值。所以,極有可能的是,0.22222222222222的一個真正完美和忠實的轉換不是實際上等於實際2/9的盡力而爲表示。您無法將有限的十進制字符串表示爲在任何特定(有限)位數中最接近2/9的確切基數2分數。

(我們必須有地方對沒有做相等比較浮點數一個標準答案。)


1. 每臺機器分數形式X/2 ñ的有理數。現在,常量是十進制的,每一個十進制常量是一個有理數的形式x /(2 n * 5 m)。這些數字是奇數,因此對於它們中的任何一個都不存在因數。只有當m == 0時,分數的二進制和十進制擴展都有一個有限表示。例如,1.25是精確的,因爲它是5 /(2 * 5 ),但0.1不是因爲它的1 /(2 0 * 5 )。並且對於有理數2/9,既沒有一個因子,也沒有一個因子。

3

如果您看看floating point numbers(其中包括雙打)的PHP文檔,您會很快發現由於浮點數的性質而難以比較難以比較。

因此,不要將浮點數結果信任到最後一位數字,也不要直接比較浮點數是否相等。

的文檔提供了一個例子,以及:

<?php 

$a = 1.23456789; 
$b = 1.23456780; 
$epsilon = 0.00001; 

if(abs($a-$b) < $epsilon) { 
    echo "true"; 
} 
4

浮動很棘手,您需要限制小數點的數量。

$a = 2/9; 
$b=0.22222222222222; 

$a = number_format($a, 9); 
$b = number_format($b, 9); 

echo "a = " . $a . " and b = " . $b; 

echo "<br>".gettype($a); 
echo "<br>".gettype($b); 
if($a==$b){ 
    echo "<br>". "equal"; 
}else echo "<br>". "Not equal"; 


if((string)$a==(string)$b){ 
    echo "<br>". "equal"; 
}else echo "<br>". "Not equal";