我想知道JS_CANONICALIZE_NAN的目标是什么,是否在所有平台上都需要它?
答案 0 :(得分:7)
这是一个有趣的!因此,SpiderMonkey在内部使用标记值表示来表示JavScript的“无类型值” - 这允许VM确定“a
中存储的变量是一个数字,以及存储在b
中的值是一个数字,因此运行a + b
会进行数字加法“。
有很多不同的价值标记方案,SpiderMonkey使用一种称为“NaN拳击”的方案。这意味着引擎中的所有无类型值都由64位值表示,这些值可以是:
这里真正的技巧是现代系统通常使用单个位模式来表示NaN,您可以通过math.h的sqrt(-1)
或log(0)
来观察。但是根据IEEE浮点规范,有一些很多的位模式也被认为是NaN。
double由子字段组成:
{sign: 1, exponent: 11, significand: 52}
通过用1s填充指数字段并在有效数字中放置非零值来表示NaN。
如果您运行这样的小程序来查看平台的NaN值:
#include <stdio.h>
#include <math.h>
#include <limits>
static unsigned long long
DoubleAsULL(double d) {
return *((unsigned long long *) &d);
}
int main() {
double sqrtNaN = sqrt(-1);
printf("%5f 0x%llx\n", sqrtNaN, DoubleAsULL(sqrtNaN));
double logNaN = log(-1);
printf("%5f 0x%llx\n", logNaN, DoubleAsULL(logNaN));
double compilerNaN = NAN;
printf("%5f 0x%llx\n", compilerNaN, DoubleAsULL(compilerNaN));
double compilerSNAN = std::numeric_limits<double>::signaling_NaN();
printf("%5f 0x%llx\n", compilerSNAN, DoubleAsULL(compilerSNAN));
return 0;
}
你会看到这样的输出:
-nan 0xfff8000000000000 // Canonical qNaNs...
nan 0x7ff8000000000000
nan 0x7ff8000000000000
nan 0x7ff4000000000000 // sNaN (signaling)
请注意,安静NaN的唯一区别在于符号位,后面跟着12位1,满足上面提到的NaN要求。最后一个,即信号NaN,清除第12个(is_quiet)NaN位并使第13个保持上面提到的NaN不变量。
除此之外,NaN空间可以自由播放--11位来填充指数,确保signficand非零,并且你还有很多空间。在x64上,我们使用47位虚拟地址假设,留下64 - 47 - 11 = 6
位用于注释值类型。在x86上,所有对象指针都适合低32位。
然而,我们仍然需要确保非规范的NaN,如果它们通过像js-ctypes这样的东西进行爬行,就不会产生类似于标记非双值的东西,因为这可能导致可利用的行为VM。 (将数字视为对象是非常多的坏消息。)因此,当我们形成双精度数(如在DOUBLE_TO_JSVAL中)时,我们确保规范化d != d
到规范NaN形式的所有双精度数。
更多信息位于bug 584168。