在 PHP 个版本中正确处理 NAN
Correctly handing NAN in PHP versions
谁能解释为什么 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()
一起使用?
This bug has now been fixed by this commit. The explanation bellow shows what was causing it.
关于你的第一个问题,似乎自从 PHP7 以来,你会得到不同的结果,具体取决于表达式是在编译期间还是在运行时求值。
首先,重要的是要注意,根据 IEEE754,其中一个元素是 NAN
的所有比较都应该 return false
。您可以在 this answer 中找到一些详细信息。考虑到这一点,5.6 的行为似乎是正确的。
因此,对于运行时评估 (0 < $nan
),比较是在 VM 中通过 this code:
result = ((double)Z_LVAL_P(op1) < Z_DVAL_P(op2));
该操作会将 0 分配给 result
,最终 returned。这将为我们提供 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) > 0
是 false
(NAN
就是这种情况),宏将 return -1
。
然后将该值传递给 is_smaller_function
,其中 you'll find:
ZVAL_BOOL(result, (Z_LVAL_P(result) < 0));
鉴于此时 result
成立 -1
,这将被评估为 TRUE
。
关于你的第二个问题,我建议你永远不要依赖与 NAN
的比较并坚持使用 is_nan()
。请注意,即使 NAN == NAN
的计算结果也是 false
.
这就是 documentation 关于 NaN
的说法:
NaN
Some numeric operations can result in a value represented by the constant NAN
. This result represents an undefined or unrepresentable value in floating-point calculations. Any loose or strict comparisons of this value against any other value, including itself, but except TRUE
, will have a result of FALSE
.
Because NAN
represents any number of different values, NAN
should not be compared to other values, including itself, and instead should be checked for using 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
谁能解释为什么 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()
一起使用?
This bug has now been fixed by this commit. The explanation bellow shows what was causing it.
关于你的第一个问题,似乎自从 PHP7 以来,你会得到不同的结果,具体取决于表达式是在编译期间还是在运行时求值。
首先,重要的是要注意,根据 IEEE754,其中一个元素是 NAN
的所有比较都应该 return false
。您可以在 this answer 中找到一些详细信息。考虑到这一点,5.6 的行为似乎是正确的。
因此,对于运行时评估 (0 < $nan
),比较是在 VM 中通过 this code:
result = ((double)Z_LVAL_P(op1) < Z_DVAL_P(op2));
该操作会将 0 分配给 result
,最终 returned。这将为我们提供 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) > 0
是 false
(NAN
就是这种情况),宏将 return -1
。
然后将该值传递给 is_smaller_function
,其中 you'll find:
ZVAL_BOOL(result, (Z_LVAL_P(result) < 0));
鉴于此时 result
成立 -1
,这将被评估为 TRUE
。
关于你的第二个问题,我建议你永远不要依赖与 NAN
的比较并坚持使用 is_nan()
。请注意,即使 NAN == NAN
的计算结果也是 false
.
这就是 documentation 关于 NaN
的说法:
NaN
Some numeric operations can result in a value represented by the constant
NAN
. This result represents an undefined or unrepresentable value in floating-point calculations. Any loose or strict comparisons of this value against any other value, including itself, but exceptTRUE
, will have a result ofFALSE
.Because
NAN
represents any number of different values,NAN
should not be compared to other values, including itself, and instead should be checked for usingis_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