在 PHP 版本中正确处理 NAN

And*_*ndy 5 php nan

任何人都可以解释为什么NAN和一个变量等于NAN根据 PHP 的版本而表现不同?

考虑以下代码:

$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";
Run Code Online (Sandbox Code Playgroud)

现在,如果我针对许多版本的 PHP(使用 MAMP)运行此代码,结果如下:

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
Run Code Online (Sandbox Code Playgroud)

PHP 中的函数可以依赖于与 NAN 的比较还是应该 NAN 只与 一起使用is_nan()

pmm*_*aga 3

此错误现已通过此提交修复。下面的解释说明了造成这种情况的原因。

关于你的第一个问题,似乎从 PHP7 开始,你会得到不同的结果,具体取决于表达式是在编译期间还是在运行时评估。

首先,值得注意的是,根据 IEEE754,其中一个元素是NAN, 的所有比较都应该返回false您可以在此答案中找到一些详细信息。考虑到这一点,5.6 的行为似乎是正确的。


因此,对于运行时评估()的情况,该比较是通过以下代码0 < $nan在VM中完成的:

result = ((double)Z_LVAL_P(op1) < Z_DVAL_P(op2));
Run Code Online (Sandbox Code Playgroud)

该操作将分配 0,result最终返回。这将为我们提供 的预期输出FALSE


在编译时评估()的情况下0 < NAN,比较是在编译期间通过以下代码完成的:

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;
Run Code Online (Sandbox Code Playgroud)

这里还发生了一些其他事情,该值的减法结果0 - NAN = NAN然后通过ZEND_NORMALIZE_BOOL宏,如下所示:

#define ZEND_NORMALIZE_BOOL(n)          \
    ((n) ? (((n)>0) ? 1 : -1) : 0)
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,如果(n) > 0false(这是 的情况NAN),宏将返回-1。然后将该值传递到您将找到的is_smaller_function位置:

ZVAL_BOOL(result, (Z_LVAL_P(result) < 0));
Run Code Online (Sandbox Code Playgroud)

鉴于此时result成立-1,这将被评估为TRUE


关于你的第二个问题,我建议你永远不要依赖与的比较NAN并坚持使用is_nan(). 请注意,even 的NAN == NAN计算结果为false