PHP开发过程中的小陷阱(持续更新)

浮点数

浮点数计算

$a = 1.1;
$b = 2.2;
$c = $a + $b;
if($c == 3.3)
{
    echo '相等';
} else {
    echo '不相等';
}

上述代码,你觉得会输出什么结果呢?
在没有进行代码运行之前,很多人可能会认为输出的结果是相等,其实最终的结果是不相等。究其原因就是浮点数在计算机内的二进制表示,下面我们对$c和3.3就行格式化输出。

printf('%.20f', $c);//输出3.30000000000000026645
printf('%.20f', 3.3);//输出3.29999999999999982236

这样一来是不是就明白了,为什么$c和3.3不相等了呢。
如何比较两个浮点数呢?
方法一:

if(round($c, 1) == round(3.3, 1))
{
    echo '相等';
} else {
    echo '不相等';
}

方法二:

if(bccomp($c, 3.3) == 0)
{
    echo '相等';
} else {
    echo '不相等';
}

PHP内置有BC Math函数对任意精度的数字进行计算,对于浮点数的运算,我们最好使用它,上述运算可以改为

$a = 1.1;
$b = 2.2;
$c = bcadd($a, $b, 1);//第三个参数为设置结果中小数点后的小数位数,默认为0,一定要根据计算需求进行设置
if(bccomp($c, 3.3) == 0)
{
    echo '相等';
} else {
    echo '不相等';
}

浮点数取整

本来只是想知道浮点数的运算问题,没想到又翻到了浮点数的一个陷阱,那就是取整。关于这个问题,鸟哥也发过一篇博客来讲述,有兴趣的可以去看看,PHP浮点数的一个常见问题的解答。鸟哥认为这不是PHP的BUG,但是经过测试,还是有些说不通的地方,下面具体来看下问题。

$a = 0.18;
$b = 0.21;
$c = 0.57;
$d = 0.58;
printf('%.20f', $a);//输出0.17999999999999999334
printf('%.20f', $b);//输出0.20999999999999999223
printf('%.20f', $c);//输出0.56999999999999995115
printf('%.20f', $d);//输出0.57999999999999996003
var_dump(intval($a * 100));//输出18
var_dump(intval($b * 100));//输出21
var_dump(intval($c * 100));//输出56
var_dump(intval($d * 100));//输出57

按照鸟哥的理论,0.18 * 100 模糊计算下不应该是17.999吗,intval不应该是17吗,但是输出的却是18。经过大量测试,发现虽然有很多类似0.57和0.58的浮点数,但是只有0.57和0.58两个intval出来的值最特殊,与预期的不符合,至于为什么就不得而知了。

运算符

运算符在开发过程中经常会用到,但是又有多少人真的了解它呢?
demo1:

$a = null;
if($a += 0 || $a++)
{

}
var_dump($a);//输出1

解析:+=运算符的优先级低于||,因此,先进行||运算,0 || $a++,因为0为false,所以需要执行$a++,但是在进行比较的时候,$a还是null,比较的双方为0 || null,结果为false,但是null仍然进行了递增操作,变为了1。然后进行+=运算,$a = $a + false,即$a = 1 + false,隐式转换后变为$a = 1 + 0,因此最后结果为1。

demo2 :

$a = null;
if($a += 0 || ++$a)
{

}
var_dump($a);//输出2

解析:跟上述结构是不是很像,唯一不同的是原来的$a++变为了现在的++$a,前面的过程还是一样,只不过在比较的时候,$a已经不是null而是1了,比较的双方变为了0 || 1,结果为true,在进行+=运算的话变为了$a = 1 + true,也就是1 + 1,结果为2。

demo3:

$a = true;
if($a += 0 && $a++)
{

}
var_dump($a);//输出1

解析:&&运算符的优先级高于+=,因此先进行&&比较,0 && $a++,因为0为false,已经触发短路,所以后面的$a++便不在执行,然后进行+=运算,$a = $a + false,即$a = 1 + 0,结果为1。

demo4:

$a = true;
if($a += 1 && $a++)
{

}
var_dump($a);//输出2

解析:比较双方由0 && $a++,变为1 && $a++,没有触发短路,$a++照常执行,但是++对于布尔值没有效果,所以此时$a仍为true,然后$a = true + true,$a = 1 + 1,结果为2。

demo5:

$a = null
if($a += 0 or ++$a)
{

}
var_dump($a);//输出1

解析:看到这个案例是不是觉得似曾相识,不错,它跟demo2很类似,只不过把||换成了or,而||和or都表示逻辑关系或,那么它们的结果是不是也应该相同呢?答案是否定的,它们不相同,因为它们的计算方式不同。or的优先级低于+=,因此先进行+=运算,$a = $a + 0;也就是$a = null + 0,隐式转换为$a = 0 + 0,结果为0,然后再和++$a进行or运算,0自增1,结果为1。

demo6:

$a = true;
if($a += 1 and $a++)
{

}
var_dump($a);//输出3

解析:是不是跟demo4很类似,有了demo5的前车之鉴,这次不会这么快下结论吧,不错这次的结果跟demo4仍然不同。and运算符的优先级低于+=,因此我们需要先计算+=。$a = $a + 1,$a = true + 1, $a = 1 + 1,此时$a为2,然后再和$a++进行and运算,2 and $a++,2为true,执行\$a++,结果为3。

demo7:

$a = 1;
echo 2 + 5 * 3 - $a += 5 + 4 * 2;//输出3

解析:+=运算符为右结合,因此表达式可分为两部分,一部分是2 + 5 * 3,一部分是5 + 4 * 2,+和*的优先级都比+=高,因此左边为17,右边为13,表达式进一步简化为17 - $a += 13,因为+=是右结合,所以表达式变为17 - (1 + 13),结果为3。注意虽然$a += 13的表达式可以写为$a = $a + 13,但是与其他运算符进行运算的时候,必须作为一个整体,不可拆分开来。

运算符知识点

  1. && ||和and or运算符优先级不一样,or 低于and 低于 += = 低于 || 低于 && 低于 ++,等多运算符优先级参考PHP运算符优先级
  2. $a++和++$a是有区别的,在用于比较和赋值的时候,$a++先赋值或者比较,再自增,++$a则是先自增后,在用于赋值和比较。
  3. 递增/递减运算符不影响布尔值,递减null也没有效果,但是递增null是1。
  4. 逻辑运算符&& || and or均有短路特性,短路后的代码均不被执行。
  5. 运算符不但有优先级之分,还有结合方向之分。
  6. 类似++,+=一类的运算符不能直接作用于标量,只能作用于变量。

开发过程中并不建议把运算符都堆砌在一个表达式当中,如果表达式确实复杂,又不好控制逻辑关系,可以使用()来强制改变优先级,但是这并不能成为我们放弃了解运算符优先级的理由。

猜你喜欢

转载自blog.csdn.net/a7442358/article/details/81667818