Wor*_*han 3 javascript biginteger data-conversion uint typescript
我发现了 3 种将 Uint8Array 转换为 BigInt 的方法,由于某种原因,它们都给出了不同的结果。您能告诉我哪一种是正确的以及我应该使用哪一种吗?
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
}
Run Code Online (Sandbox Code Playgroud)
let view = new DataView(arr.buffer, 0);
let result = view.getBigUint64(0, true);
Run Code Online (Sandbox Code Playgroud)
let result = BigInt(0);
for (let i = arr.length - 1; i >= 0; i++) {
result = result * BigInt(256) + BigInt(arr[i]);
}
Run Code Online (Sandbox Code Playgroud)
老实说,我很困惑哪一个是正确的,因为它们都给出了不同的结果,但确实给出了结果。
我对 BE 或 LE 都很好,但我只是想知道为什么这 3 种方法会给出不同的结果。
不同结果的原因之一是它们使用不同的字节序。
让我们将您的代码片段转换为可以执行和比较它们的形式:
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));
Run Code Online (Sandbox Code Playgroud)
请注意,这包括对 method3 的错误修复:您在何处编写for (let i = arr.length - 1; i >= 0; i++),您i--在末尾明确表示了这一点。
对于“method1”,将打印:ffeeddccbbaa998877665544332211
因为 method1 是大端转换(数组的第一个字节是结果的最高有效部分),没有大小限制。
对于“method2”,将打印:8899aabbccddeeff
因为 method2 是小端转换(数组的第一个字节是结果的最低有效部分),限制为 64 位。
如果将第二个getBigUint64参数从切换true为false,则会得到大端行为:ffeeddccbbaa9988。
为了消除大小限制,您必须添加一个循环:使用getBigUint64您可以获得 64 位块,您可以使用类似于方法 1 和方法 3 的移位来组装这些块。
对于“method3”,将打印:112233445566778899aabbccddeeff
因为 method3 是没有大小限制的小端转换。如果反转for-loop 的方向,您将获得与 method1 相同的大端行为:给出与;result * 256n相同的值。result << 8n后者更快一些。
(旁注:BigInt(0)和BigInt(256)是不必要的冗长,只需写0n和256n即可。额外的好处:123456789123456789n做你所期望的,BigInt(123456789123456789)而不是。)
那么你应该使用哪种方法呢?这取决于:
(1) 您的传入数组是否采用 BE 或 LE 编码?
(2) 您的 BigInt 是否仅限于 64 位或任意大?
(3) 这是性能关键的代码,还是所有方法都“足够快”?
退一步:如果您控制整个过程的两个部分(将 BigInt 转换为 Uint8Array,然后传输/存储它们,然后转换回 BigInt),请考虑简单地使用十六进制字符串:这将更容易编码,更容易调试,并且速度明显加快。就像是:
function serialize(bigint) {
return "0x" + bigint.toString(16);
}
function deserialize(serialized_bigint) {
return BigInt(serialized_bigint);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2818 次 |
| 最近记录: |