JavaScript 类型化数组:64 位整数?

23

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


你可以使用一个JS BigInt库,并将它们存储在数组中。 - Simeon
2
这对于轻度使用来说可能还可以,但对于创建类型化数组以解决大规模高效整数/浮点数存储和操作的目的来说,速度极慢。 - Delan Azabani
1
使用Int32Array作为基本存储的特定BigInt库,可以吗? - Simeon
注意,BigInt 提案现在已经进入了第三阶段(虽然晚了7年...)。 - tsh
3个回答

18

ECMAScript 2020现在内置了BigInt类型,还有 BigInt64ArrayBigUint64Array 类型数组。这些数组的内部表示是64位,它们被转换为和从BigInt值,这些值需要保留完整精度。

BigInt和数组类型仍然相对较新,因此如果您需要支持旧的浏览器或Node版本,请参考下面内容。您可以使用诸如CanIUse.com之类的资源来查看要支持哪些浏览器,以帮助您决定是否可以使用这种选项。直到逐步淘汰不受支持的浏览器之前,Polyfills也可能是一种解决方法。


旧浏览器 / Node 环境的解决方案:

由于JavaScript中所有数字都是64位的浮点数字,只有53位精度,因此实现一个Int64Array没有实际意义。正如Simeon在评论中所说,您可以使用一个大整数库,但速度会更慢。

如果您真的需要一个64位整数数组,不考虑性能,那么Google Closure库有一个64位的Long,我想它比更普遍的大整数库要快。但我从未使用过,并且不知道您是否可以轻松地将其与库的其余部分分开。


4
关于一个稍旧的话题,我想发表一下我的看法:曾经我在编写一个 Flash 应用程序时需要一个 64 位整数类。我基于闭包实现了一个“Long”类,并没有出现任何外部类依赖的问题。 - jordancpaul
那个链接已经失效了,看起来他们已经迁移到 GitHub 上了:https://google.github.io/closure-library/api/goog.math.Long.html - Max Barraclough

2
你可以安全地读取小于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://dev59.com/jq_la4cB1Zd3GeqPuIVM#53107482
// [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);
  }
}


0

你可以使用 Float64Array 或者 Float32Array。 但我不确定这是否符合你的需求,因为它是一个浮点数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接