【问题标题】:JavaScript typed arrays: 64-bit integers?JavaScript 类型数组:64 位整数?
【发布时间】:2011-05-18 07:12:39
【问题描述】:

在 Firefox 4 和 Chrome 7 中实现的 JavaScript 类型数组是在 JavaScript 中存储和处理二进制数据的一种非常有效的方式。但是,当前的实现仅提供每个成员最多 32 位的整数视图,Int32ArrayUint32Array。是否计划实施 64 位整数视图?如何实现 64 位整数视图?它们会慢多少?

【问题讨论】:

  • 你可以使用 JS BigInt 库,并将它们存储在数组中?
  • 这对于轻度使用来说还不错,但对于创建类型化数组来解决的目的(即高效的整数/浮点数存储和大规模操作)来说速度非常慢。
  • 使用 Int32Array 作为基础存储的特定 BigInt 库?
  • 注意BigInt proposal 现在是第 3 阶段(尽管 7 年后...

标签: javascript typed-arrays


【解决方案1】:

您可以安全地读取 2^53-1 以下的数字(又名 0x1fffffffffffff 或 9007199254740991),但不能高于该数字。下面是执行此操作的代码。

如前所述,您不能安全地使用 Javascript 数字超出 2^53-1 整数,因为在 Javascript 中,数字实际上总是表示为双精度 64 位浮点数。这些将数字表示为这 64 位中的基数和指数,其中 53 位为基数,其余为指数,因此当您超过 53 位并需要使用指数时,您将失去精确的整数精度。

但至少可以通过以下方法检查 Uint8Array 中的无符号 64 位长整数是否小于 2^53-1,然后根据需要安全地读取它:

function getUint64(inputArray, index, littleEndian) {
  const dataView = new DataView(inputArray.buffer);
  let hi = dataView.getUint32(index, littleEndian);
  let lo = dataView.getUint32(index + 4, littleEndian);
  if (littleEndian) {
    const tmp = hi;
    hi = lo;
    lo = tmp;
  }
  if (hi > 0x1fffff) {
    throw new Error(
      'Cannot safely parse uints over 2^53 - 1 (0x1fffffffffffff) in to a 64 bit float.'
    );
  }
  const numberValue = (hi * 0x100000000) + lo;
  return numberValue;
}

// Tests gotten from this other excellent answer here: https://stackoverflow.com/a/53107482/628418
// [byteArray, littleEndian, expectedValue, expectError]
const testValues = [
  // big-endian
  [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0xff]),  false, 255], 
  [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0xff, 0xff]),  false, 65535],
  [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0xff, 0xff, 0xff, 0xff]),  false, 4294967295],
  [new Uint8Array([0x00, 0x00, 0x00, 0x01,  0x00, 0x00, 0x00, 0x00]),  false, 4294967296],
  [new Uint8Array([0x00, 0x1f, 0xff, 0xff,  0xff, 0xff, 0xff, 0xff]),  false, 9007199254740991], // maximum precision
  [new Uint8Array([0x00, 0x20, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]),  false, 9007199254740992, true], // precision lost
  [new Uint8Array([0x00, 0x20, 0x00, 0x00,  0x00, 0x00, 0x00, 0x01]),  false, 9007199254740992, true], // precision lost

  // little-endian
  [new Uint8Array([0xff, 0x00, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]),  true, 255], 
  [new Uint8Array([0xff, 0xff, 0x00, 0x00,  0x00, 0x00, 0x00, 0x00]),  true, 65535],
  [new Uint8Array([0xff, 0xff, 0xff, 0xff,  0x00, 0x00, 0x00, 0x00]),  true, 4294967295],
  [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x01, 0x00, 0x00, 0x00]),  true, 4294967296],
  [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x01, 0x00, 0x00]),  true, 1099511627776],
  [new Uint8Array([0x00, 0x00, 0x00, 0x00,  0x00, 0x00, 0x01, 0x00]),  true, 281474976710656],
  [new Uint8Array([0xff, 0xff, 0xff, 0xff,  0xff, 0xff, 0x1f, 0x00]),  true, 9007199254740991], // maximum precision
];

testValues.forEach(testGetUint64);

function testGetUint64([bytes, littleEndian, expectedValue, expectError]) {
  if (expectError) {
    try {
      const val = getUint64(bytes, 0, littleEndian);
      console.error('did not get the expected error');
    } catch(error) {
      console.log('got expected error: ' + error.message);
    }
  } else {
    const val = getUint64(bytes, 0, littleEndian);
    console.log(val === expectedValue? 'pass' : 'FAIL. expected '+expectedValue+', received '+val);
  }
}

【讨论】:

    【解决方案2】:

    ECMAScript 2020 现在有一个内置的BigInt 类型,带有BigInt64ArrayBigUint64Array 类型数组。内部 64 位表示与 BigInt 值相互转换,这是保持完整精度所必需的。

    BigInt 和数组类型还是比较新的,所以如果您需要支持旧浏览器或 Node 版本,请参阅下文。您可以使用CanIUse.com 之类的资源来查看哪些浏览器可以帮助您确定是否可以选择。在您逐步停止对不受支持的浏览器的支持之前,Polyfills 也可以作为一种解决方法。


    旧浏览器/节点环境的答案:

    没有实用的方法来实现Int64Array,因为 JavaScript 中的所有数字都是 64 位 浮点 数字,其精度只有 53 位。就像 Simeon 在他的评论中所说,你可以使用一个大整数库,但它会慢得多。

    如果您真的需要一个 64 位整数数组,无论性能如何,Google Closure 库都有一个 64-bit Long class,我认为它比更通用的大整数库更快。不过我从未使用过它,我不知道您是否可以轻松地将它与库的其他部分分开。

    【讨论】:

    • 只是把我的 0.02 美元扔在一个老话题上:我需要一个 64 位 int 类在我之前写的一个 Flash 应用程序中。我的实现基于没有问题的闭包“Long”类 - 没有外部类依赖关系。
    • 那个链接失效了,好像他们移到了 GitHub:google.github.io/closure-library/api/goog.math.Long.html
    【解决方案3】:

    您可以使用Float64ArrayFloat32Array。 但我不知道那是不是你要找的东西,因为它是一个浮点数。

    【讨论】:

      猜你喜欢
      • 2016-08-09
      • 2011-02-28
      • 1970-01-01
      • 2012-03-27
      • 2010-11-02
      • 1970-01-01
      • 2019-07-20
      • 2014-06-22
      • 1970-01-01
      相关资源
      最近更新 更多