JavaScript 类型数组:64 位整数

JavaScript typed arrays: 64-bit integers?

本文关键字:整数 类型 数组 JavaScript      更新时间:2023-09-26

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

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

BigInt 和数组类型仍然相对较新,因此如果您需要支持较旧的浏览器或 Node 版本,请参阅下文。您可以使用 CanIUse.com 等资源来查看哪些浏览器可帮助您确定它是否是一个选项。Polyfills也可以作为一种解决方法,直到您逐步停止对不受支持的浏览器的支持。

<小时 />

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

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

如果你真的需要一个 64 位整数数组,不管性能如何,Google Closure 库有一个 64 位Long类,我想它比更通用的大整数库更快。我从来没有用过它,我不知道你是否可以轻松地将它与图书馆的其余部分分开。

您可以安全地读取低于 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);
  }
}

您可以使用

Float64ArrayFloat32Array。但我不知道这是否是你要找的,因为它是一个漂浮物。