首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Javascript中将Uint8Array转换为BigInt

在Javascript中将Uint8Array转换为BigInt
EN

Stack Overflow用户
提问于 2022-07-31 06:42:50
回答 2查看 472关注 0票数 2

我发现了3种将Uint8Array转换为BigInt的方法,它们都给出了不同的结果。你能告诉我哪个是正确的,哪个是我应该使用的?

使用bigint-conversion库的

  1. 。我们可以使用bigintConversion.bufToBigint()函数得到一个BigInt。实现如下:

代码语言:javascript
复制
export function bufToBigint (buf: ArrayBuffer|TypedArray|Buffer): bigint {
  let bits = 8n
  if (ArrayBuffer.isView(buf)) bits = BigInt(buf.BYTES_PER_ELEMENT * 8)
  else buf = new Uint8Array(buf)

  let ret = 0n
  for (const i of (buf as TypedArray|Buffer).values()) {
    const bi = BigInt(i)
    ret = (ret << bits) + bi
  }
  return ret
}

使用

  1. DataView:

代码语言:javascript
复制
let view = new DataView(arr.buffer, 0);
let result = view.getBigUint64(0, true);

使用FOR循环的

代码语言:javascript
复制
let result = BigInt(0);
for (let i = arr.length - 1; i >= 0; i++) {
  result = result * BigInt(256) + BigInt(arr[i]);
}

我真的搞不懂哪一个是对的,因为他们都给出了不同的结果,但确实给出了结果。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2022-07-31 14:18:07

,我对BE或LE都没意见,但我只想知道为什么这3种方法会给出不同的结果。

不同结果的一个原因是它们使用不同的endianness。

让我们将您的代码片段转换为可以执行和比较它们的表单:

代码语言:javascript
复制
let source_array = new Uint8Array([
    0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 
    0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11]);
let buffer = source_array.buffer;

function method1(buf) {
  let bits = 8n
  if (ArrayBuffer.isView(buf)) {
    bits = BigInt(buf.BYTES_PER_ELEMENT * 8)
  } else {
    buf = new Uint8Array(buf)
  }

  let ret = 0n
  for (const i of buf.values()) {
    const bi = BigInt(i)
    ret = (ret << bits) + bi
  }
  return ret
}

function method2(buf) {
  let view = new DataView(buf, 0);
  return view.getBigUint64(0, true);
}

function method3(buf) {
  let arr = new Uint8Array(buf);
  let result = BigInt(0);
  for (let i = arr.length - 1; i >= 0; i--) {
    result = result * BigInt(256) + BigInt(arr[i]);
  }
  return result;
}

console.log(method1(buffer).toString(16));
console.log(method2(buffer).toString(16));
console.log(method3(buffer).toString(16));

请注意,这包括了method3的bug修复:在编写for (let i = arr.length - 1; i >= 0; i++)的地方,很明显,最后的意思是i--

对于"method1“这个打印:ffeeddccbbaa998877665544332211

因为method1是一个大端转换(数组的第一个字节是结果中最重要的部分),所以没有大小限制。

对于"method2“这个打印:8899aabbccddeeff

因为method2是一个小的端点转换(数组的第一个字节是结果中最不重要的部分),所以限制在64位。

如果将第二个getBigUint64参数从true切换到false,则会得到大端行为:ffeeddccbbaa9988

为了消除大小限制,您必须添加一个循环:使用getBigUint64可以获得64位块,您可以使用类似于method1和method3的移位来组装这些块。

对于"method3“这个打印:112233445566778899aabbccddeeff

因为method3是一个没有大小限制的小端点转换。如果您反转for-循环的方向,您将得到与method1相同的大端行为:result * 256n给出的值与result << 8n相同;后者的速度要快一些。

(附带注意:BigInt(0)BigInt(256)不必要地冗长,只需编写0n256n即可。额外的好处:123456789123456789n做了你期望的事情,而BigInt(123456789123456789)却没有。)

那么你应该使用哪种方法呢?这取决于:

(1)您的传入数组是否假定是编码还是LE编码?

(2)您的BigInts是否限制在64位或任意大?

(3)这是性能关键代码,还是所有方法都“足够快”?

退一步:如果您控制整个进程的两个部分(将BigInts转换为Uint8Array,然后传输/存储它们,然后将它们转换回BigInt),那么可以考虑使用十六进制字符串:这将更容易编写代码、更容易调试,而且速度更快。类似于:

代码语言:javascript
复制
function serialize(bigint) {
  return "0x" + bigint.toString(16);
}
function deserialize(serialized_bigint) {
  return BigInt(serialized_bigint);
}
票数 4
EN

Stack Overflow用户

发布于 2022-10-29 14:22:11

如果您需要存储不绑定到任何base64或128的大整数,并保留负数,那么这是一个解决方案.

代码语言:javascript
复制
function encode(n) {
  let hex, bytes

  // shift all numbers 1 step to the left and xor if less then 0
  n = (n << 1n) ^ (n < 0n ? -1n : 0n) 

  // convert to hex
  hex = n.toString(16)
  // pad if neccesseery
  if (hex.length % 2) hex = '0' + hex

  // convert hex to bytes
  bytes = hex.match(/.{1,2}/g).map(byte => parseInt(byte, 16))

  return bytes
}

function decode(bytes) {
  let hex, n

  // convert bytes back into hex
  hex = bytes.map(e => e.toString(16).padStart(2, 0)).join('')

  // Convert hex to BigInt
  n = BigInt(`0x`+hex)

  // Shift all numbers to right and xor if the first bit was signed
  n = (n >> 1n) ^ (n & 1n ? -1n : 0n) 

  return n
}

const input = document.querySelector('input')
input.oninput = () => {
  console.clear()
  const bytes = encode(BigInt(input.value))
  // TODO: Save or transmit this bytes
  // new Uint8Array(bytes)
  console.log(bytes.join(','))

  const n = decode(bytes)
  console.log(n.toString(10)+'n') // cuz SO can't render bigints...
}
input.oninput()
代码语言:javascript
复制
<input type="number" value="-39287498324798237498237498273323423" style="width: 100%">

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73181182

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档