在PHP版本中正确处理NAN

时间:2017-07-21 23:15:04

标签: 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";

现在,如果我针对许多版本的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

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

2 个答案:

答案 0 :(得分:3)

  

此错误现已由this commit修复。下面的解释说明了导致它的原因。

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

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

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

result = ((double)Z_LVAL_P(op1) < Z_DVAL_P(op2));

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

在编译时评估(0 < NAN)的情况下,编译期间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;

此处还有其他一些事情发生,减法0 - NAN = NAN的结果是该值经过ZEND_NORMALIZE_BOOL宏,其内容如下:

#define ZEND_NORMALIZE_BOOL(n)          \
    ((n) ? (((n)>0) ? 1 : -1) : 0)

如您所见,如果(n) > 0falseNAN就是这种情况),则宏将返回-1。 然后,该值会传递到is_smaller_function,其中you'll find

ZVAL_BOOL(result, (Z_LVAL_P(result) < 0));

鉴于此时result持有-1,我们会将其评估为TRUE

关于你的第二个问题,我建议你不要依赖与NAN的比较,并坚持使用is_nan()。请注意,即使NAN == NAN评估为false

答案 1 :(得分:1)

这是documentation关于NaN所说的内容:

  

<强>的NaN

     

某些数值运算可能会产生由常量NAN表示的值。此结果表示浮点计算中未定义或不可表示的值。除了TRUE之外,对此值与任何其他值(包括其自身)进行任何宽松或严格的比较,都会得到FALSE的结果。

     

由于NAN表示任意数量的不同值,因此NAN不应与其他值(包括其自身)进行比较,而应使用is_nan()进行检查。

简单来说,NaN不是一个值。它是一种表示法,表示使用浮点标准使用实数的内部表示无法计算或存储的结果。

NaN不等于其他任何东西。 NaN即使与另一个NaN不相等,因为许多不同的计算都可以产生NaN

检查结果是NaN的唯一可靠方法是使用is_nan()函数。

只有代码中的最后四个测试有效:

is_nan(NAN) TRUE
is_nan($nan) TRUE
gettype(NAN) is double
gettype($nan) is double