我发现了3种将Uint8Array转换为BigInt的方法,它们都给出了不同的结果。你能告诉我哪个是正确的,哪个是我应该使用的?
使用bigint-conversion库的
bigintConversion.bufToBigint()函数得到一个BigInt。实现如下: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
}使用
let view = new DataView(arr.buffer, 0);
let result = view.getBigUint64(0, true);使用FOR循环的
let result = BigInt(0);
for (let i = arr.length - 1; i >= 0; i++) {
result = result * BigInt(256) + BigInt(arr[i]);
}我真的搞不懂哪一个是对的,因为他们都给出了不同的结果,但确实给出了结果。
发布于 2022-07-31 14:18:07
,我对BE或LE都没意见,但我只想知道为什么这3种方法会给出不同的结果。
不同结果的一个原因是它们使用不同的endianness。
让我们将您的代码片段转换为可以执行和比较它们的表单:
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)不必要地冗长,只需编写0n和256n即可。额外的好处:123456789123456789n做了你期望的事情,而BigInt(123456789123456789)却没有。)
那么你应该使用哪种方法呢?这取决于:
(1)您的传入数组是否假定是编码还是LE编码?
(2)您的BigInts是否限制在64位或任意大?
(3)这是性能关键代码,还是所有方法都“足够快”?
退一步:如果您控制整个进程的两个部分(将BigInts转换为Uint8Array,然后传输/存储它们,然后将它们转换回BigInt),那么可以考虑使用十六进制字符串:这将更容易编写代码、更容易调试,而且速度更快。类似于:
function serialize(bigint) {
return "0x" + bigint.toString(16);
}
function deserialize(serialized_bigint) {
return BigInt(serialized_bigint);
}发布于 2022-10-29 14:22:11
如果您需要存储不绑定到任何base64或128的大整数,并保留负数,那么这是一个解决方案.
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()<input type="number" value="-39287498324798237498237498273323423" style="width: 100%">
https://stackoverflow.com/questions/73181182
复制相似问题