在我们开始之前
首先,您的链接在声明“任何小于 2 52 [...] 的整数都可以安全地放入 JavaScript 数字方面存在一点错误。”虽然在技术上是正确的,但它并不是一个严格的界限:可以验证无需太多麻烦,javascript 数字可以存储最多 2 53(但不是 2 53 +1)的每个正整数。
一些代码
事不宜迟,您请求的函数将 52 位数字拆分为低 32 位和高 20 位:
function to_int52(hi, lo) {
/* range checking */
if ((lo !== lo|0) && (lo !== (lo|0)+4294967296))
throw new Error ("lo out of range: "+lo);
if (hi !== hi|0 && hi >= 1048576)
throw new Error ("hi out of range: "+hi);
if (lo < 0)
lo += 4294967296;
return hi * 4294967296 + lo;
}
function from_int52(i) {
var lo = i | 0;
if (lo < 0)
lo += 4294967296;
var hi = i - lo;
hi /= 4294967296;
if ((hi < 0) || (hi >= 1048576)
throw new Error ("not an int52: "+i);
return { lo: lo, hi: hi };
}
在哪里拆分
我不建议使用这些。Javascript 位运算是有符号的(@dandavis:JS 没有UInt32s),当我们真正想要正值时,符号位会让人头疼。Plus V8 对可以存储为 31 位的(有符号)整数进行了优化。结合这两个事实,您应该拆分不超过 30 位,这是适合 V8 小整数(“smi”)的最大正大小。
这是将数字拆分为 30 个低位和 22 个高位的代码:
function int52_30_get(i) {
var lo = i & 0x3fffffff;
var hi = (i - lo) / 0x40000000;
return { lo: lo, hi: hi };
}
不过,您可能不想创建对象。这些应该被内联(如果你真的很关心函数):
function int52_30_get_lo(i) {
return i & 0x3fffffff;
}
function int52_30_get_hi(i) {
return (i - (i & 0x3fffffff)) / 0x40000000;
}
并从低部分和高部分创建数字:
function int52_30_new_safe(hi, lo) {
return (hi & 0x3fffff) * 0x40000000 + (lo & 0x3fffffff);
}
如果您真的确定 hi 和 lo 在范围内,则可以跳过屏蔽:
function int52_30_new(hi, lo) {
return hi * 0x40000000 + lo;
}
分别设置高低部分:
/* set high part of i to hi */
i = (hi & 0x3fffff) * 0x40000000 + (i & 0x3fffffff);
/* set low part of i to lo */
i += (lo & 0x3fffffff) - (i & 0x3fffffff);
如果您确定 hi 和 lo 在范围内:
/* set high part of i to hi */
i = hi * 0x40000000 + (i & 0x3fffffff);
/* set low part of i to lo */
i += lo - (i & 0x3fffffff);
(这些不是函数,因为它们修改了i
.)
为了更有趣,一个提取任意位域的函数:
function int52_30_get_bits(i, lsb, nbits) {
while (lsb >= 32) {
i /= 4294967296;
lsb -= 32;
}
return (i / (1<<lsb)) & ((1<<nbits)-1);
}
(nbits 必须 <= 31。当 nbits 为 32 时的故障模式很有趣,这是由于 << 的 rhs 操作数只有 5 个低位是重要的,这是 javascript 规范与 x86 ISA 共享的缺陷。)
超过 52 位?
完全可以使用符号位将 53 位二进制数存储为从 -2 53到 2 53 -1 的整数。我还没有这样做,但它应该很容易。之后它开始变得有点毛茸茸,你最终会遇到这样一个事实,即在你到达 2 64之前没有足够的浮点数来循环(很多是 NaN) 。将 63 个二进制数字打包成浮点数在理论上应该是可行的,但留给读者作为练习:)
其他方法
另一种方法是使用类型化数组并创建一个 Float 视图和一个 Int 视图:这使您可以直接操作浮点的底层二进制表示。但是你必须开始担心字节顺序之类的问题。
所有建议字符串操作的人都疯了。