Practical PHP Programming

比较操作符

比较操作符,如<>==,可以根据比较对象返回1或0(或空),使得PHP根据其返回值来做出决定。看一下下面的代码:

<?php
    if ($foo < 10) {
        // do stuff
    }
?>

小于操作符<$foo同10进行比较,如果$foo小于10,则返回1使得第一行代码成为if(1) {。之前说过,PHP将0或空视为false,其他的值视为true,也就是说,那行代码相当于if(true) {。自然,true就意味着后面的代码块可以执行。

知道了比较操作符是怎么操作的,你就应该能够通过范围检查避免一些常识性的错误。来看一下下面的代码:

<?php
    if ($a <= $b <= $c) {
        // do stuff
    }
?>

这里,我们好像是想要$b大于等于$a,同时要小于等于$c,但是实际情况不是这样的!PHP首先会比较$a$b的大小,返回0或1,然后再用0或1同$c进行大小比较——这明显不是我们想要的,可以将上述代码重写为:

<?php
    if ($a <= $b && $b <= $c) {
        // do stuff
    }
?>

在修改版中,我们将两个分别进行检查,相对来说会好一点。

接下来,我们要问一下你,等号==的作用是什么?如果你的回答是“你连这个都不知道吗?两个值相等的时候返回真啊”的话,你就和大多数人一样了。

来看一下这个代码:

if (31415926535897932 == 31415926535897933) {
    echo "Numbers are the same!\n";
} else {
    echo "Numbers are not the same!\n";
}

不用费劲去数两个数字了:第一个是π取17位数,第二个是π取17位数加1。两个数都把小数点去掉了。想一想,上面的代码会输出什么。提示一点:这个问题有点坏!

在阅读答案之前,我强烈建议你们在PHP中运行一下,这样可能解释起来更加容易一些。

尽管两个数不一样,PHP还是会输出“Numbers are the same!”。这是因为PHP对大数的支持不好,当超过一个特定的数值的时候,用==来判断数字相等就不靠谱了。

在内部,PHP默认使用整型(int)来存储数字。当你用一个很大的整型加上另一个很大的整型,PHP知道结果超过整型的范围,就会将结果保存成浮点型(float)。浮点型的数一般都很大,当超过16位数字时,PHP会舍弃掉后面的部分。

下面这段代码简单的阐释了一下:

$foo =  2147483647;
var_dump($foo);

++$foo;
var_dump($foo);

$bar = 31415926535897932;
$baz = 31415926535897933;
var_dump($bar);
var_dump($baz);

上面的代码输出为:

int(2147483647)
float(2147483648)
float(31415926535898000)
float(31415926535898000)

上面第一个数字是整型的,加一之后超过了整型的范围,PHP将其转换为浮点型。再看一下后面两个数字,都是浮点型,默认精度是14位数字,所以前面的部分都成了31415926535898。所以在进行大数比较的时候,结果通常都是相等。

有两种解决方案,但是两种都不完美。第一种,你可以将两个数字作为字符串进行比较。PHP进行字符串比较的时候是逐字进行比较字符的大小的,因此可以比较数字的大小。但是,这并不是简单的用双引号将数字引起来就可以解决的问题。PHP是一种弱类型语言,字符串里面都是数字的时候将会自动将其转换为整型,因此还会出现相同的问题。

这里的解决方案是将等号换为绝对等号===,用来比较两个值是不是相等且同类型。所以,如果我们用引号将数字引起来将其变为字符串,再用===进行比较,PHP为了确保类型相同就不会自动将其转换为整型了。这样,才会将两个数字作为字符串进行比较,得到我们想要的结果。下面是代码:

if ("31415926535897932" === "31415926535897933") {
    echo "Numbers are the same!\n";
} else {
    echo "Numbers are not the same!\n";
}

Delbert说明:经过本人在5.3.29版本的PHP当中测试,确实是存在这样的问题,但是在5.5.9版中已经不存在这样的问题了。(未测试更老版本)

var_dump的结果如下:

int(2147483647)
int(2147483648)
int(31415926535897932)
int(31415926535897933)

直接测试输出的是“Numbers are not the same!”。