2016-01-04 25 views
11

我正在寻找将MIN_SAFE_INTEGERMAX_SAFE_INTEGER范围内的JavaScript编号(53位不包括符号)转换为7位字节的位串,并将其移位两个以允许符号和空标识符。将整数转换为JavaScript中任意排序的字节数组的最快方法?

到目前为止,我想出最好的是:

function toUint8Array(data) { 
    data = data.toString(2); 
    data = new Array(65 - data.length).join('0') + data; 
    var ret = new Uint8Array(data.length/8); 
    for (var i = 0; i < 8; i++) { 
     ret[i] = 0; 
     ret[i] += (data[i * 8] == '1' ? 128 : 0); 
     ret[i] += (data[(i * 8) + 1] == '1' ? 64 : 0); 
     ret[i] += (data[(i * 8) + 2] == '1' ? 32 : 0); 
     ret[i] += (data[(i * 8) + 3] == '1' ? 16 : 0); 
     ret[i] += (data[(i * 8) + 4] == '1' ? 8 : 0); 
     ret[i] += (data[(i * 8) + 5] == '1' ? 4 : 0); 
     ret[i] += (data[(i * 8) + 6] == '1' ? 2 : 0); 
     ret[i] += (data[(i * 8) + 7] == '1' ? 1 : 0); 
    } 
    return (ret); 
} 

Fiddle

正如您所知道马上,这将是可恨慢(和位还没有被移位在所有7个活动字节中有两个地方)。

有什么办法可以更快地做到这一点?理想情况下避免字符串解析?

+0

其实数据视图,**正确使用**即不是你是如何尝试它,可以给一个温和的(3X在Firefox,1.5X在Chrome中,** ** 7.5X在Internet Explorer)的速度提高 - 我可能会做得不是最佳 –

+0

@JaromandaX我很想知道你是如何管理的,以产生我试图得到的输出。 – CoryG

+0

我可以做一个小提琴,但是...输入严格限于MIN_SAFE_INTEGER - > MAX_SAFE_INTEGER - 一个问题......符号/空位应该是第7个字节的LSB还是第一个字节的MSB? –

回答

1

我打了书,还有几个数学方面的CS朋友,我们现在的判断是,这不能按照你的描述来完成。

我认为你坚持使用字符串解析。

5

javascript中的按位操作只有32位宽。但是移位相当于乘以或除以2的幂次,并且这些都以完全浮点精度进行。

所以你想要做什么是直截了当的。转移以获得低阶位的有趣部分,并掩盖其余部分。 例如你有一个很大的数字0x123456789abc(20015998343868)。

0x123456789abc/0x1 = 0x123456789abc。按位AND与0xff给出0xbc。

0x123456789abc/0x100 = 0x123456789a.bc。按位AND与0xff给出0x9a。

0x123456789abc/0x10000 = 0x12345678.9abc。按位AND与0xff给出0x78。

依此类推。代码:

function toUint8Array(d) { 
    var arr = new Uint8Array(7); 
    for (var i=0, j=1; i<7; i++, j *= 0x100) { 
     arr[i] = (d/j) & 0xff; 
    } 
    return arr; 
} 

随着Uint8Array生活更容易:使用0xff屏蔽是隐式的Uint8Arrays只能介于0和255之间的存储整数但我把它放在了清晰,从而使结果与不同的数组类型相同。

该代码产生一个小尾数组,例如, toUint8Array(0x123456789abc)返回 [0xbc,0x9a,0x78,0x56,0x34,0x12,0]。 如果您想要big-endian,即相反顺序的字节,请将arr[i]替换为arr[6-i]

(如果你想在相反的顺序每个数组项这是稍微复杂与bitrev((d/j) & 0xff),其中bitrev看起来是这样的替换(d/j) & 0xff

function bitrev(byte) { 
    var table = [ 0b0000, 0b1000, 0b0100, 0b1100, 0b0010, 0b1010, 0b0110, 0b1110, 
       0b0001, 0b1001, 0b0101, 0b1101, 0b0011, 0b1011, 0b0111, 0b1111 ]; 
    return table[byte >> 4] + (table[byte & 0xf] << 4); 
} 

最后,这只适用于正整数。但是你的两个想法很容易实现。 d向左移两位。而d < 0 ? -d : d(或Math.abs(d))是d的绝对值。所以arr = toUint8Array((d<0) ? 1-d*4 : d*4)返回d左移两位,符号位在最低有效位(LSB)中。

你还可以用isFinite()检查没有号码,但你必须要小心把它仅在数字,如isFinite(null),也就是说,实际上是true由于隐式转换规则(这是固定在ES6):

function toUint8Array_shifted_signed(d) { 
    /* bit 0 is sign bit (0 for +ve); bit 1 is "not-a-number" */ 
    if (typeof d !== 'number' || !isFinite(d)) { 
     d = 2; 
    } else { 
     d = (d<0) ? 1-d*4 : d*4; 
    } 

    return toUint8Array(d); 
} 
+0

想知道,它快吗? – Ross

+0

感谢这很好 - 另外一个问题 - 有没有一种快速的方法来做2位移位,同时保留所有53个原始整数位?如果你对大于Number.MAX_SAFE_INTEGER/4的数字执行'* 4'操作,事情可能会出错。 – CoryG

+1

* 4即使对于大于MAX_SAFE_INTEGER的数字也是安全的。尾数在内部是相同的,指数只增加了2。 MAX_SAFE_INTEGER并不意味着MAX_SAFE_INTEGER以上的* no *整数可以被无损地表示,只有存在的不能。 尽管所有正整数的代码都是正确的,但1-d * 4会导致大*负整数的精度损失。当d> = 2^56时,也没有检查溢出的情况,并且没有防止d不是整数(其中d * 4可以将小数部分泄漏到低2位中)。 – hexwab

相关问题