Can anyone explain why NAN
and a variable equal to NAN
behave differently depending on the version of PHP?
Consider the following code:
$nan = NAN;
print "PHP Version: " . phpversion(). "\n" .
'0 < NAN ? ' . ( 0 < NAN ? 'TRUE' : 'FALSE' ) . "\n" .
'0 > NAN ? ' . ( 0 > NAN ? 'TRUE' : 'FALSE' ) . "\n" .
'0 == NAN ? ' . ( 0 == NAN ? 'TRUE' : 'FALSE' ) . "\n" .
'0 < $nan ? ' . ( 0 < $nan ? 'TRUE' : 'FALSE' ) . "\n" .
'0 > $nan ? ' . ( 0 > $nan ? 'TRUE' : 'FALSE' ) . "\n" .
'0 == $nan ? ' . ( 0 == $nan ? 'TRUE' : 'FALSE' ) . "\n" .
'is_nan(NAN) ' . ( is_nan(NAN) ? 'TRUE' : 'FALSE' ) . "\n" .
'is_nan($nan) ' . ( is_nan($nan) ? 'TRUE' : 'FALSE' ) . "\n" .
'gettype(NAN) is ' . gettype(NAN) . "\n" .
'gettype($nan) is ' . gettype($nan) . "\n";
Now, if I run this code against many versions of PHP (using MAMP) the following are results:
PHP Version: 5.3.5
0 < NAN ? TRUE
0 > NAN ? TRUE
0 == NAN ? FALSE
0 < $nan ? TRUE
0 > $nan ? TRUE
0 == $nan ? FALSE
is_nan(NAN) TRUE
is_nan($nan) TRUE
gettype(NAN) is double
gettype($nan) is double
PHP Version: 5.6.30 (and 5.5.30, 5.4.45)
0 < NAN ? FALSE
0 > NAN ? FALSE
0 == NAN ? FALSE
0 < $nan ? FALSE
0 > $nan ? FALSE
0 == $nan ? FALSE
is_nan(NAN) TRUE
is_nan($nan) TRUE
gettype(NAN) is double
gettype($nan) is double
PHP Version: 7.1.1 (and 7.0.15)
0 < NAN ? TRUE
0 > NAN ? TRUE
0 == NAN ? FALSE
0 < $nan ? FALSE
0 > $nan ? FALSE
0 == $nan ? FALSE
is_nan(NAN) TRUE
is_nan($nan) TRUE
gettype(NAN) is double
gettype($nan) is double
Can a function in PHP rely on a comparison to NAN or should NAN only be used with is_nan()
?
This bug has now been fixed by this commit. The explanation bellow shows what was causing it.
About your first question, it seems that since PHP7 you get different results depending on whether the expression is evaluated during compilation or during runtime.
First off, it is important to note that according to IEEE754 all comparisions where one of the elements is a NAN
, should return false
. You can find some details in this answer. Taking that into account, the behavior of 5.6 seems to be the correct one.
So, for the case of runtime evaluation(0 < $nan
), that comparison is done in the VM by this code:
result = ((double)Z_LVAL_P(op1) < Z_DVAL_P(op2));
That operation will assign 0 to result
which is eventually returned. This will give us the expected output of FALSE
.
In the case of compile time evaluation(0 < NAN
), the comparison ir done during compilation by this code:
case TYPE_PAIR(IS_LONG, IS_DOUBLE):
Z_DVAL_P(result) = (double)Z_LVAL_P(op1) - Z_DVAL_P(op2);
ZVAL_LONG(result, ZEND_NORMALIZE_BOOL(Z_DVAL_P(result)));
return SUCCESS;
Here there are some other things happening, the result of the subtraction 0 - NAN = NAN
that value then goes through the ZEND_NORMALIZE_BOOL
macro that reads as follows:
#define ZEND_NORMALIZE_BOOL(n) \
((n) ? (((n)>0) ? 1 : -1) : 0)
As you can see, if (n) > 0
is false
(which is the case for NAN
), the macro will return -1
.
That value is then passed to the is_smaller_function
where you'll find:
ZVAL_BOOL(result, (Z_LVAL_P(result) < 0));
Given that at this point result
holds -1
, this will be evaluated to TRUE
.
About your second question, I would suggest you should never rely on comparisons with NAN
and stick to using is_nan()
. Note that even NAN == NAN
evaluates to false
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With