在php中比较花车
我想在PHP中比较两个浮点数,就像这个示例代码一样:
$a = 0.17; $b = 1 - 0.83; //0.17 if($a == $b ){ echo 'a and b are same'; } else { echo 'a and b are not same'; }
在这段代码中,它返回else
条件的结果,而不是if
条件,即使$a
和$b
是相同的。 有什么特殊的方式来处理/比较PHP中的浮动?
如果是的话请帮我解决这个问题。
或者我的服务器configuration有问题吗?
如果你这样做,他们应该是一样的。 但是请注意,浮点值的一个特征是似乎导致相同值的计算不需要实际上相同。 所以如果$a
是一个文字.17
并且$b
通过一个计算到达那里,它可能是不同的,尽pipe它们显示相同的值。
通常情况下,您从不比较像这样的相等的浮点值,您需要使用最小的可接受的差异:
if (abs(($a-$b)/$b) < 0.00001) { echo "same"; }
就是这样
首先阅读红色警告http://www.php.net/manual/en/language.types.float.php 。 你永远不能比较花车平等。 你应该使用epsilon技术。
例如:
if (abs($a-$b) < EPSILON) { … }
EPSILON
是一个非常小的数字(你必须定义它)
或者尝试使用bcmath函数:
<?php $a = 0.17; $b = 1 - 0.83; //0.17 echo "$a == $b (core comp oper): ", var_dump($a==$b); echo "$a == $b (with bc func) : ", var_dump( bccomp($a, $b)==0 );
结果:
0.17 == 0.17 (core comp oper): bool(false) 0.17 == 0.17 (with bc func) : bool(true)
如前所述,在PHP中进行浮点比较(无论是等于,大于还是小于)时要非常小心。 但是,如果您只对几位有意义的数字感兴趣,您可以执行如下操作:
$a = round(0.17, 2); $b = round(1 - 0.83, 2); //0.17 if($a == $b ){ echo 'a and b are same'; } else { echo 'a and b are not same'; }
舍入到小数点后两位(或3或4)将导致预期的结果。
如果您将浮点值与平等值进行比较,则避免操作系统,语言,处理器等内部舍入策略风险的一种简单方法是比较值的string表示forms ,如:
if(''。$ a ===''。$ b){…}
这样,你可以看到平等,PHP的答案将等于你的愿望。 如果读取这些值时它是相等的 ,那么该语句将如预期的那样是真实的
如果你这样写就可能会起作用,所以我想你已经简化了这个问题。 (保持简单简洁的问题通常是一件好事。)
但在这种情况下,我想一个结果是一个计算,一个结果是一个常数。
这违反了浮点编程的一个基本规则: 永远不要做相等的比较。
其原因有点微妙1,但重要的是要记住,它们通常不起作用(除了具有讽刺意味的是,对于整数值),另一种方法是模糊的比较:
if abs(a - y) < epsilon
其中一个主要问题就是我们在节目中写数字的方式。 我们把它们写成十进制string,结果我们写的大部分分数都没有确切的机器表示。 它们没有确切的有限forms,因为它们以二进制forms重复。 每个机器分数都是x / 2 nforms的有理数。 现在,常量是十进制的,每一个十进制常量都是x /(2 n * 5 m )forms的有理数。 5 米的数字是奇数,所以没有任何一个2 n的因素。 只有当m == 0时,分数的二进制和十进制扩展都有一个有限的表示。 所以,1.25是精确的,因为它是5 /(2 2 * 5 0 ),但是0.1不是因为它是1 /(2 0 * 5 1 )。 事实上,在1.01 … 1.99系列中,只有3个数字可以精确表示:1.25,1.50和1.75。
这是比较浮点数或十进制数的解决scheme
//$fd['someVal'] = 2.9; //$i for loop variable steps 0.1 if((string)$fd['someVal']== (string)$i) { //Same }
将一个decimal
variables转换为string
,你会没事的。
这在PHP 5.3.27上适用于我。
$payments_total = 123.45; $order_total = 123.45; if (round($payments_total, 2) != round($order_total, 2)) { // they don't match }
如果你有一个小的有限数量的小数点是可以接受的,下面的工作很好(尽pipe比epsilon解决scheme的性能更慢):
$a = 0.17; $b = 1 - 0.83; //0.17 if (number_format($a, 3) == number_format($b, 3)) { echo 'a and b are same'; } else { echo 'a and b are not same'; }
比较花车平等有一个天真的O(n)algorithm。
您必须将每个浮点值转换为一个string,然后使用整数比较运算符比较每个浮点数string表示左边的每个数字。 在比较之前,PHP会将每个索引位置的数字自动分配给一个整数。 第一个数字大于另一个将打破循环,并将其所属的浮点数声明为两者中较大的一个。 平均来说,会有1/2 * n的比较。 对于相等的花车,将会有n个比较。 这是该algorithm的最坏情况。 最好的情况是每个浮点数的第一个数字是不同的,只会导致一个比较。
您不能使用INTEGER COMPARISON OPERATORS对原始浮点值进行生成,以产生有用的结果。 这样的操作的结果没有意义,因为你没有比较整数。 您违反了每个生成无意义结果的运算符的域。 这也适用于三angular洲比较。
使用整数比较运算符来devise它们:比较整数。
简化的解决scheme:
<?php function getRand(){ return ( ((float)mt_rand()) / ((float) mt_getrandmax()) ); } $a = 10.0 * getRand(); $b = 10.0 * getRand(); settype($a,'string'); settype($b,'string'); for($idx = 0;$idx<strlen($a);$idx++){ if($a[$idx] > $b[$idx]){ echo "{$a} is greater than {$b}.<br>"; break; } else{ echo "{$b} is greater than {$a}.<br>"; break; } } ?>
我讨厌这样说,但“为我工作”:
Beech:~ adamw$ php -v PHP 5.3.1 (cli) (built: Feb 11 2010 02:32:22) Copyright (c) 1997-2009 The PHP Group Zend Engine v2.3.0, Copyright (c) 1998-2009 Zend Technologies Beech:~ adamw$ php -f test.php a and b are same
现在,浮点比较通常是棘手的 – 你可能认为是相同的东西不是(由于四舍五入错误和/或表示的细微差别)。 你可能想阅读http://floating-point-gui.de/
if( 0.1 + 0.2 == 0.3 ){ echo 'a and b are same'; } else { echo 'a and b are not same'; }
这会导致问题 ,因为IEEE标准浮点运算(有这个问题)。
一个被忽视的陷阱…
如果你正在使用签名花车,你会想做两个比较来检查接近度:
$a - $b < EPSILON && $b - $a < EPSILON
//You can compare if less or more. $parcela='250.23'; //total value $tax = (double) '15.23'; //tax value $taxaPercent=round((100*$tax)/$parcela,2); //tax percent $min=(double) '2.50';// minimum tax percent if($taxaPercent < $min ){ // tax error tax is less than 2.5 }
使用原生PHP比较会更好:
bccomp($a, $b, 3) // Third parameter - the optional scale parameter // is used to set the number of digits after the decimal place // which will be used in the comparison.
如果两个操作数相等,则返回0;如果left_operand大于right_operand,则返回1;否则返回-1。
function floatcmp($f1,$f2,$precision = 10) { $e = pow(10,$precision); return (intval($f1 * $e) == intval($f2 * $e)); }
testing用例
$a = 0.17; $b = 0.17; echo floatcmp($a,$b) ? 'yes' : 'no'; // yes echo floatcmp($a,$b + 0.01) ? 'yes' : 'no'; // no
function compareFloats($a, $b){ list($a_int, $a_dec) = explode('.', strval($a)); list($b_int, $b_dec) = explode('.', strval($b)); if(intval($a_int) == intval($b_int) && intval($a_dec) == intval($b_dec)){ return 'same'; }else{ if((intval($a_int) < intval($b_int)) || (intval($a_int) === intval($b_int) && intval($a_dec) < intval($b_dec))){ return 'smaller'; } if((intval($a_int) > intval($b_int)) || (intval($a_int) === intval($b_int) && intval($a_dec) > intval($b_dec))){ return 'bigger'; } return 'error'; } }