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

时间:2013-10-06 19:44:16

标签: javascript bit-manipulation

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

How to store a 64 bit integer in two 32 bit integers and convert back again

How to store a 64 bit integer in two 32 bit integers in Ruby

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

但JavaScript不能代表64位整数。它can only represent 52-bit integers没有问题。

现在这意味着无法将64位整数转换为两个32位整数,因为首先是it is not even possible to have a 64-bit integer

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

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

相关: How are 32 bit JavaScript numbers resulting from a bit-wise operation converted back to 64 bit numbers

3 个答案:

答案 0 :(得分:11)

开始之前

首先,您的link包含一个轻微的不准确声明 “任何小于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);
}

如果你真的确定你好,你可以跳过掩蔽:

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必须是&lt; = 31。当nbits是32时的失败模式很有趣,并且仅由于&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&gt; x86 ISA。)

超过52位?

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

其他方法

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

所有提出字符串操作的人都很疯狂。

答案 1 :(得分:4)

嗯,你可以在数字上这样做:

function numeric(n) {
    return { 
        hi: Math.floor(n / 4294967296), 
        lo: (n & 0xFFFFFFFF) >>> 0
    }
}

或字符串版本可以是:

function strings(n) { 
    s = n.toString(16);

    if (s.length > 8) { 

        return { 
            hi: parseInt( s.toString(16).slice(0, s.length - 8), 16),
            lo: parseInt( s.toString(16).slice(s.length - 8), 16)
        }
    } else { 
        return { hi: 0, lo: n } 
    }

}

或者也许......

function stringPad(n) { 
    s = "00000000000"+n.toString(16);
    return { 
        hi: parseInt( s.toString(16).slice(0, s.length - 8), 16),
        lo: parseInt( s.toString(16).slice(s.length - 8), 16)
    }
}

现在,哪个更快。为了找到我在这里设置一个测试床:http://jsfiddle.net/SpaceDog/ZTJ2p/(你也可以使用你最喜欢的JS探查器)。

结果(100000个电话):

Function: numeric completed in 146 ms
Function: strings completed in 379 ms
Function: stringPad completed in 459 ms

我原本认为字符串更快,并且想知道它是否是parseInt调用,但是没有:

Function: stringPadNoParse completed in 386 ms

现在,这不是很精确,因为它取决于很多其他的东西(同样,分析器可能会更好)但它看起来似乎数字版本更快,我已经运行了几次测试。

但也许有人会来提供另一种方式。

答案 2 :(得分:0)

Typed arrays可用于获取double值的两半

var buf = new ArrayBuffer(8);
(new Float64Array(buf))[0] = f;
fl = (new Uint32Array(buf))[0];
fh = (new Uint32Array(buf))[1];

现在您有了尾数。如果需要,只需提取并移动以获得整数部分

var exp = ((fh >> 20) & 0x7FF) - 1023;
var mant_h = (fh & 0xFFFFF) | (1 << 20);
var mant_l = fl;
if (exp > 52)
    throw new Error ("overflow int53 range");
else if (exp >= 32)
{
    L = mant_h >> (exp - 32);
    H = 0;
}
else if (exp >= 0)
{
    L = (mant_l >> exp) | (mant_h << (32 - exp));
    H = mant_h >> exp;
}

反方向,您可以在JavaScript变量中存储64位int

请注意,您可以使用BigInt64ArrayBigUint64Array

在Javascript中使用64位int