簡體   English   中英

字節數組到Uint64作為字符串

[英]Byte Array to Uint64 as a String

讓我們考慮以下情況。

Go例程創建一個字節數組,其中包含8字節Big Endian的Uint645577006791947779410 [77, 101, 130, 33, 7, 252, 253, 82] 5577006791947779410 [77, 101, 130, 33, 7, 252, 253, 82]

在JavaScript代碼中,我將這些字節作為Uint8Array接收。 我們知道JavaScript當前不支持Uint64作為安全數字類型,並且不能對大於32位的整數執行按位運算,因此像buf[0] << 56這樣的東西永遠不會起作用。

那么將這些字節直接解碼為數字字符串"5577006791947779410"什么?

PS我知道在JavaScript中有很多用於處理大整數 ,但通常它們很大並且提供了大量的數學運算,這里我不需要。 我正在尋找一個簡單的現代直接解決方案, 只需將BE打包的Uint64Int64字節解碼為數字字符串。 你有什么想法嗎?

編輯:對於轉換(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}`); }); 

首先,通過檢查最高位是否設置( bytes[0] > 128 )來計算符號。 對於負數,這些位必須被否定( 255 - byte ),並且必須將1添加到該數字(因此最后一個字節為256而不是255 )。

forEach循環的基本思想是將每個字節分成十進制數字( byte % 10並計算下一個(byte - digits[i]) / 10的開銷(byte - digits[i]) / 10Math.floor(byte / 10) )。 對於下一個字節,必須添加最后字節數字的移位結果( byte += digits[i] * 256digits[i] << 8 )。

該代碼針對簡潔性,簡單性和靈活性進行了優化。 如果您正在處理字符串而不是字節或數字,並且不想使用任何庫,那么轉換性能似乎並不重要。 否則,該函數可以針對性能進行優化:最多可以同時處理四個字節,另外一個只需要替換0x1000x80 (另外(在(U)Int64的情況下只剩下兩個字節組) forEach循環可以被展開。 對十進制數字進行分組可能不會提高性能,因為生成的字符串必須用零填充,因此需要在最終結果中刪除前導零。

另一種方法:在兩個uint32中划分問題以保持計算的可管理性。

考慮更低和更高的uint32( lh )。 h*0x100000000+l可寫為h*0x100000000+l 考慮到十進制,還可以考慮低9位和更高位( ldhd ): ld=(h*0x100000000+l)%1000000000hd=(h*0x100000000+l)/1000000000 通過一些算術和代數運算符屬性,可以將這些運算分解為安全的“半”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); 

盡管(h % D) * (H % D)可以大於Number.MAX_SAFE_INTEGER ,但是在評論中詳細說明算法仍然Number.MAX_SAFE_INTEGER ,因為丟失的比特仍為零。

這是我的解決方案。 總體戰略是這樣的:

  • 如果數字為負數,則使用2的補碼將其否定,並在最后添加負號
  • 將任意大小的數字表示為從0到9的LE數組
  • 對於Uint8Array中的每個字節(從最高到最低),將運行總數乘以256並將其添加到新字節的值
  • 要將數字乘以256,將其加倍8倍(因為2 ** 8 == 256
  • 要添加兩個數字,請使用小學算法:
    • 從最低有效數字開始
    • 添加兩個數字的對應數字
    • 得到的數字是總和mod 10; 如果總和為10或更多,則進位為1,否則為0
    • 繼續使用進位添加相應的數字,直到我們添加最高有效數字並且進位為0

關於速記的一些注意事項:

  • n1[i] || 0 n1[i] || 0得到n1i位。 如果這超過了i的結尾,我們將其視為0(想象在它們前面以無限0表示的數字)。 n2相同。
  • added > 9會產生一個布爾值,它會自動轉換為一個數字(如果added >= 10話,則為1 added >= 10 ,否則為0)
  • i < n1.length || i < n2.length || carry i < n1.length || i < n2.length || carry檢查任一加數中是否有更多數字或者進位仍然非零
  • String(b).split('').map(Number).reverse()轉換,例如, 100'100'['1', '0', '0']然后[1, 0, 0] ,然后[0, 0, 1]所以它在LE base-10中表示
  • result.reverse().join('')將例如[0, 0, 1] result.reverse().join('')轉換為[0, 0, 1] result.reverse().join('') [1, 0, 0] ,然后轉換為'100'

碼:

function add(n1, n2) {
    const sum = []
    let carry = 0
    for (let i = 0; i < n1.length || i < n2.length || carry; i++) {
        const added = (n1[i] || 0) + (n2[i] || 0) + carry
        sum[i] = added % 10
        carry = added > 9 //floor(added / 10)
    }
    return sum
}
function times256(n1) {
    for (let i = 8; i; i--) n1 = add(n1, n1)
    return n1
}
function toString(buffer) {
    const isNegative = buffer[0] & 128 //check if high bit is set
    if (isNegative) { //convert to positive, using 2's complement
        buffer = buffer.map(b => ~b) //invert all bits
        let i = buffer.length - 1
        while (buffer[i] === 255) { //add 1 to the number, carrying if necessary
            buffer[i] = 0
            i--
        }
        buffer[i]++
    }
    const result = buffer.reduce((sum, b) =>
        add(
            times256(sum), //multiply sum by 256
            String(b).split('').map(Number).reverse() //then add b
        ),
        []
    )
    const stringResult = result.reverse().join('')
    if (isNegative) return '-' + stringResult
    else return stringResult
}

這是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> 

paddedBinary()函數返回一個“完整的”8位二進制數,因此我們可以將'fnl'創建為BigEndian的64位字符串。

由於JavaScript沒有進行完整的64位運算,因此我創建了dec[]數組,將每個2的冪存儲為單個數字,方法是將每個前一個數字加倍並平滑數字。

然后剩下的就是添加我們想要的位,它使用類似的方法來平滑數十。

(答案是反過來的!)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM