JavaScript:将52位整数转换为20位和32位整数

tre*_*der 5 javascript bit-manipulation

在其他可以代表64位整数的语言中,可以很容易地做到这一点......

如何在两个32位整数中存储64位整数并再次转换回来

如何在Ruby中以两个32位整数存储64位整数

// convert 64-bit n to two 32-bit x and y
x = (n & 0xFFFFFFFF00000000) >> 32
y =  n & 0xFFFFFFFF
Run Code Online (Sandbox Code Playgroud)

但JavaScript不能代表64位整数.它只能代表52位整数而不会出现问题.

现在这意味着无法将64位整数转换为两个32位整数,因为它甚至不可能首先具有64位整数.

但是,我们还有52位.我的问题是:如何在JavaScript中将这个52位整数分成两个32位整数(20个高位和32个低位)

有人建议像上面的位操作代码在JavaScript中进行20位和32位拆分吗?

相关: 由位操作产生的32位JavaScript数如何转换回64位数

hex*_*wab 11

在我们开始之前

首先,您的链接包含一个小的不准确性,声明"任何小于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 };
}
Run Code Online (Sandbox Code Playgroud)

在哪里拆分

我不建议使用这些.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 };
}
Run Code Online (Sandbox Code Playgroud)

您可能不想创建对象.这些应该被内联(如果你真的在烦扰函数):

function int52_30_get_lo(i) {
    return i & 0x3fffffff;
}

function int52_30_get_hi(i) {
    return (i - (i & 0x3fffffff)) / 0x40000000;
}
Run Code Online (Sandbox Code Playgroud)

并从低和高部分创建数字:

function int52_30_new_safe(hi, lo) {
    return (hi & 0x3fffff) * 0x40000000 + (lo & 0x3fffffff);
}
Run Code Online (Sandbox Code Playgroud)

如果你真的确定hi和lo在范围内,你可以跳过屏蔽:

function int52_30_new(hi, lo) {
    return hi * 0x40000000 + lo;
}
Run Code Online (Sandbox Code Playgroud)

分别设置高低部件:

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

如果你确定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);
Run Code Online (Sandbox Code Playgroud)

(这些不是函数,因为它们会修改i.)

为了额外的乐趣,一个拉出任意位域的功能:

function int52_30_get_bits(i, lsb, nbits) {
    while (lsb >= 32) {
        i /= 4294967296;
        lsb -= 32;
    }
    return (i / (1<<lsb)) & ((1<<nbits)-1);
}
Run Code Online (Sandbox Code Playgroud)

(nbits必须<= 31.当nbits为32时的失败模式很有意思,并且仅由于<<很重要的js操作数的5个低位,javascript规范与x86 ISA共享的缺陷).

超过52位?

完全可以使用符号位将53位二进制数存储为-2 53到2 53 -1的整数.我没有这样做,但它应该很容易.之后它开始变得有点毛茸茸,你最终会遇到这样一个事实,即在你达到2 64之前没有足够的浮点数(很多都是NaN).将63个二进制数字打包到一个浮点数应该在理论上是可行的,但留给读者的练习:)

其他方法

另一种方法是使用类型化数组并创建一个Float视图和一个Int视图:这使您可以直接操作浮点数的基础二进制表示.但是你必须开始担心字节序等.

所有建议字符串操作的人都疯了.