2010-01-03 57 views
5
$onethird = 1.0/3; 
$fivethirds = 1.0/3+1.0/3+1.0/3+1.0/3+1.0/3; 
$half = 1.0/2; 
$threehalf = 1.0/2+1.0/2+1.0/2; 
var_dump($onethird + $fivethirds == $half + $threehalf); 

其輸出false,但大家都知道:5/3+1/3=2=3/2+1/2如何在PHP中解決這個問題?

如何解決這個問題?

回答

5

這是IEEE 754表示浮點數的問題之一;這些表示不夠精確,無法表示所有有理數。

做到這一點的方法是比較反對的親密極少數的差異,而不是平等:

abs(($onethird + $fivethirds) - ($half + $threehalf)) < 1e-8 
+1

這是浮點數的一般性質,所以它與IEEE 754標準有什麼關係? – 2010-01-03 15:15:33

+0

PHP是用C編寫的,因此得到了它對FP的支持,而且大多數C運行時使用IEEE 754. 另外,IEEE 854使用可變基數,因此可以以某些處理速度爲代價支持某些數字的更高精度與他們一起計算。 – 2010-01-03 15:17:47

+0

這是一般浮點數的本質。 epsilon(這裏是0.000000001)的值取決於表示浮點數的位數。 – slebetman 2010-01-03 15:18:25

4

問題來自Floating Point arithmetic引入的小錯誤。這不是特定於PHP的。

您可以通過引入一個小的「容差」因子來解決這個問題,即通過檢查比較中的第一個值> =第二個值減去容差並且第二個值加上容差。

1
var_dump(abs($onethird + $fivethirds - $half + $threehalf) < 0.00001); 

見:http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm 也:http://docs.sun.com/source/806-3568/ncg_goldberg.html

在所有編程語言中都是如此。

我還沒有來到任何語言,當程序員想要浮點數相等時,這是自動完成的。應該有人想出了一個新的運營商,或許=〜=浮法平等這將自動完成DIFF與小量的比較:

if ($float1 =~= $float2) {... 

很是煩人,每年,因爲我畢業,2000年,在這段時間一些新手會在一些新聞組,論壇或郵件列表上提出這個問題。就在上個月,我在comp.lang.tcl上回答了這個問題。這不僅僅是新手,兩個月前,我必須向我的同事解釋這一點,他已經開發了5年以上的軟件,問我爲什麼他的Perl代碼不起作用。

1

如何解決這個問題?

什麼實際問題你想解決?你的代碼只是表明浮點數的準確性有限 - 這並不是什麼新鮮事。

對於大多數真實世界的應用,無論如何輸入不是100%準確的,結果只需要精確到小數點後兩位。平等比較根本不是你大部分時間需要的。如果你這樣做,你可以通過查看結果是否在距離給定數字的某個預定義的小距離內來進行擬合。

如果您需要具有特定精度的小數運算,請使用BC Math函數 - 但要意識到,如果用於複雜計算,則這些函數非常慢。