让我们考虑以下情况。
Go例程创建一个字节数组,其中包含8字节Big Endian 5577006791947779410
的 Uint64 编号[77, 101, 130, 33, 7, 252, 253, 82]
。
在JavaScript代码中,我将这些字节作为Uint8Array
接收。我们知道JavaScript当前不支持 Uint64 作为安全数字类型,并且不能对大于32位的整数执行按位运算,因此buf[0] << 56
之类的东西将无法工作。
那么将这些字节直接解码为数字字符串"5577006791947779410"
的过程是什么?
PS 我知道有plenty of libraries用于处理JavaScript中的大整数,但通常它们很庞大并且提供了大量的数学运算,这里我不需要。我正在寻找一个简单的现代直接解决方案,只需解码BE-packed Uint64 和 Int64 字节到数字字符串。你有什么想法吗?
答案 0 :(得分:8)
编辑:为了转换(U)int64,我现在肯定会推荐@ LS_DEV的解决方案。只有在拥有未知或更大的字节数时,我才会使用我的解决方案。
我从https://stackoverflow.com/a/21668344/3872370开始修改它:
function Int64ToString(bytes, isSigned) {
const isNegative = isSigned && bytes.length > 0 && bytes[0] >= 0x80;
const digits = [];
bytes.forEach((byte, j) => {
if(isNegative)
byte = 0x100 - (j == bytes.length - 1 ? 0 : 1) - byte;
for(let i = 0; byte > 0 || i < digits.length; i++) {
byte += (digits[i] || 0) * 0x100;
digits[i] = byte % 10;
byte = (byte - digits[i]) / 10;
}
});
return (isNegative ? '-' : '') + digits.reverse().join('');
}
const tests = [
{
inp: [77, 101, 130, 33, 7, 252, 253, 82],
signed: false,
expectation: '5577006791947779410'
},
{
inp: [255, 255, 255, 255, 255, 255, 255, 255],
signed: true,
expectation: '-1'
},
];
tests.forEach(test => {
const result = Int64ToString(test.inp, test.signed);
console.log(`${result} ${result !== test.expectation ? '!' : ''}=== ${test.expectation}`);
});
&#13;
首先通过检查最顶部位是否设置(bytes[0] > 128
)来计算符号。对于负数,这些位必须被否定(255 - byte
),并且必须将1添加到数字中(因此对于最后一个字节,256
而不是255
。
forEach循环的基本思想是将每个字节分成十进制数字byte % 10
并计算下一个数字的开销(byte - digits[i]) / 10
和Math.floor(byte / 10)
。对于下一个字节,必须添加最后一个字节的移位结果。数字(byte += digits[i] * 256
和digits[i] << 8
)。
该代码针对简洁性,简单性和灵活性进行了优化。如果您使用的是字符串而不是字节或数字,并且不想使用任何库,那么转换性能似乎并不重要。否则,该函数可以针对性能进行优化:可以同时处理多达四个字节,另外一个只需要替换0x100
和0x80
(在一个情况下只剩下两个字节组) U)Int64)可以展开forEach
循环。对小数位进行分组可能不会提高性能,因为生成的字符串必须用零填充,因此需要在最终结果中删除前导零。
答案 1 :(得分:3)
另一种方法:在两个uint32中划分问题以保持计算的可管理性。
考虑更低和更高的uint32(l
和h
)。完整号码可以写为h*0x100000000+l
。考虑到十进制,也可以考虑较低的9位数和更高的数字(ld
和hd
):ld=(h*0x100000000+l)%1000000000
和hd=(h*0x100000000+l)/1000000000
。通过一些算术和代数运算符属性,人们可以将这些操作分解为安全的一半&#34; 64位操作并在结束时撰写字符串。
function int64_to_str(a, signed) {
const negative = signed && a[0] >= 128;
const H = 0x100000000, D = 1000000000;
let h = a[3] + a[2] * 0x100 + a[1] * 0x10000 + a[0]*0x1000000;
let l = a[7] + a[6] * 0x100 + a[5] * 0x10000 + a[4]*0x1000000;
if(negative) {
h = H - 1 - h;
l = H - l;
}
const hd = Math.floor(h * H / D + l / D);
const ld = (((h % D) * (H % D)) % D + l) % D;
const ldStr = ld + '';
return (negative ? '-' : '') +
(hd != 0 ? hd + '0'.repeat(9 - ldStr.length) : '') + ldStr;
}
let result = int64_to_str([77, 101, 130, 33, 7, 252, 253, 82], false);
let expectation = '5577006791947779410';
console.log(result + ' ' + (result === expectation ? '===' : '!==') + ' ' + expectation);
result = int64_to_str([255, 255, 255, 255, 255, 255, 255, 255], true);
expectation = '-1';
console.log(result + ' ' + (result === expectation ? '===' : '!==') + ' ' + expectation);
&#13;
尽管(h % D) * (H % D)
可能会大于Number.MAX_SAFE_INTEGER
,但在评论中详细说明算法仍然有效,因为丢失的位数仍为零。
答案 2 :(得分:1)
这是我的解决方案。总体战略是:
Uint8Array
中的每个字节(从最高到最低),将运行总数乘以256并将新字节的值加上2 ** 8 == 256
起)关于速记的一些注释:
n1[i] || 0
获取i
的{{1}}位数。如果这已超过n1
的结尾,我们将其视为0(想象在它们前面以无限0表示的数字)。与i
相同。n2
生成一个布尔值,它会自动转换为数字(如果added > 9
则为1,否则为0)added >= 10
检查其中任何一个加数中是否有更多数字,或者进位是否仍为非零i < n1.length || i < n2.length || carry
转换,例如String(b).split('').map(Number).reverse()
至100
,然后'100'
,然后['1', '0', '0']
,然后[1, 0, 0]
,以便在LE base-10中表示[0, 0, 1]
转换,例如result.reverse().join('')
至[0, 0, 1]
,然后[1, 0, 0]
代码:
'100'
答案 3 :(得分:1)
这是binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = LinearLayoutManager(this ,LinearLayoutManager.VERTICAL ,false)
binding.recyclerView.adapter = customAdapter(this ,getList())
版本 - 我无法想象交换 很难:
UInt64
<!DOCTYPE html>
<html>
<body>
<span id='out1'></span>
<br>
<span id='out2'></span>
<br>
<span id='out3'></span>
</body>
<script>
fnl='';
be=[77, 101, 130, 33, 7, 252, 253, 82];
function paddedBinary(n) {
pad='';
sv=128;
while (sv>n) {pad+='0';sv/=2;}
return pad+n.toString(2);
}
for (let i=0;i<8;i++)
fnl+=paddedBinary(be[i]);
out1.textContent=fnl;
dec=new Array(64);
for (let i=0;i<64;i++) dec[i]=new Array(21).fill(0);
function make2s() {
dec[0][0]=1;
for (let i=1;i<64;i++) {
for (let j=0;j<21;j++)
dec[i][j]=2*dec[i-1][j];
for (let j=0;j<21;j++)
if (dec[i][j]>9) {
dec[i][j]-=10;
dec[i][j+1]++;
}
}
}
function int64add(v1,v2) {
var res=new Array(21).fill(0);
for (let i=0;i<21;i++)
res[i]=v1[i]+v2[i];
for (let i=0;i<21;i++)
if (res[i]>9) {
res[i]-=10;
res[i+1]++;
}
return res;
}
make2s();
for (let i=0;i<64;i++)
out2.textContent+=dec[i]+' :: ';
cv=new Array(21).fill(0);
for (let i=0;i<fnl.length;i++)
if (fnl[i]=='1') cv=int64add(cv,dec[63-i]);
out3.textContent=cv;
</script>
</html>
函数返回一个'完整的'8位二进制数,因此我们可以将'fnl'创建为BigEndian的64位字符串。
由于JavaScript没有完整的64位算术,我创建了paddedBinary()
数组,将每个2的幂作为单独的数字存储,通过将每个前一个数字加倍并平滑数字。
然后剩下的就是添加我们想要的位,它使用类似的方法来平滑数十位。
(答案是反过来的!)