【问题标题】:Fastest way to convert a integer to arbitrarily ordered byte arrays in JavaScript?在 JavaScript 中将整数转换为任意顺序字节数组的最快方法?
【发布时间】:2016-01-04 00:45:43
【问题描述】:

我希望将 JavaScript 数字的 MIN_SAFE_INTEGERMAX_SAFE_INTEGER 范围(不包括符号的 53 位)转换为分布在 7 个字节上的位串,移位两个以允许符号和空标识符。

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

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 个活动字节中,这些位仍未移动两位。)

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

【问题讨论】:

  • 实际上 DataView,正确使用,即不是您尝试过的方式,可以提供适度的(Firefox 中为 3X,Chrome 中为 1.5X,7.5X b> 在 Internet Explorer 中)速度提高 - 我可能做得不够理想
  • @JaromandaX 我很想知道您是如何管理它以产生我想要获得的输出的。
  • 我可以做一个小提琴,但是......输入严格限制为 MIN_SAFE_INTEGER -> MAX_SAFE_INTEGER - 一个问题......符号/空位应该是第 7 个字节的 LSB 还是 MSB第一个字节?
  • @JaromandaX 符号位是第一个字节的第 1 位,空位是第一个字节 like in this fiddle that only goes to 32 bits 的第 2 位。我实际上对MAX_SAFE_INTEGER 允许的 53 位非常满意,因为我使用的是bignumber.js,除此之外,我真的很想避免对33 位进行字符串解析,尽管53 如果可能的话。

标签: javascript numbers bitmask


【解决方案1】:

javascript 中的位运算只有 32 位宽。但移位等价于乘以或除以 2 的幂,并且这些操作以全浮点精度发生。

所以你想做的很简单。移位以获得低位中有趣的部分,并屏蔽其余部分。 例如。你有一个大数字 0x123456789abc (20015998343868)。

0x123456789abc / 0x1 = 0x123456789abc。按位与 0xff 得到 0xbc。

0x123456789abc / 0x100 = 0x123456789a.bc。按位与 0xff 得到 0x9a。

0x123456789abc / 0x10000 = 0x12345678.9abc。按位与 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]。 如果您想要大端,即相反顺序的字节,请将arr[i] 替换为arr[6-i]

(如果您希望每个数组条目中的 bits 以相反的顺序排列,这会稍微复杂一些。将 (d / j) &amp; 0xff 替换为 bitrev((d / j) &amp; 0xff),其中 bitrev 看起来像这样:

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*4 左移两位。而d &lt; 0 ? -d : d(或Math.abs(d))是d的绝对值。所以arr = toUint8Array((d&lt;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);
}

【讨论】:

  • 谢谢,这太好了——还有一个问题——有没有一种快速的方法来进行 2 位移位,同时保留所有 53 个原始整数位?如果您对大于Number.MAX_SAFE_INTEGER / 4 的数字执行* 4 操作,则可能会出现错误。
  • * 4 实际上是安全的,即使对于大于 MAX_SAFE_INTEGER 的数字也是如此。在内部,尾数是相同的,指数只是增加了 2。 MAX_SAFE_INTEGER 并不意味着 no 大于 MAX_SAFE_INTEGER 的整数可以无损表示,只是存在不能表示的整数。但是,虽然代码对于所有正整数都是正确的,但1-d*4 可能会导致大 整数的精度损失。当 d>=2^56 时,也不会检查溢出,并且没有防止 d 不是整数(其中 d*4 可以将小数部分泄漏到低 2 位)。
  • 在这些情况下,我将更新答案以获得额外的稳健性。
【解决方案2】:

我看书,还有我的几个数学方面的 CS 朋友,我们目前的判断是,这不能像你描述的那样完成。

我认为您对字符串解析感到困惑。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-17
    • 2011-05-18
    相关资源
    最近更新 更多