Byte Array zu Uint64 als String

7

Denken wir über die folgende Situation nach.

Die Go-Routine erstellt ein Byte-Array, in das eine Uint64 -Nummer 5577006791947779410 in 8 Byte Big Endian [77, 101, 130, 33, 7, 252, 253, 82] .

packt

In JavaScript-Code erhalte ich diese Bytes als Uint8Array . Wir wissen, dass JavaScript Uint64 derzeit nicht als sicherer numerischer Typ unterstützt und keine bitweisen Operationen für ganze Zahlen größer als 32 Bit ausführen kann, so dass Dinge wie buf[0] << 56 niemals funktionieren.

Was ist der Prozess der Dekodierung dieser Bytes direkt in die numerische Zeichenkette "5577006791947779410" ?

PS Ich weiß, dass es viele Bibliotheken für die Arbeit mit großen Ganzzahlen in JavaScript, aber im Allgemeinen Sie sind riesig und bieten viele mathematische Operationen, die ich hier nicht brauche. Ich suche nach einer einfachen modernen einfachen Lösung für nur Entschlüsselung BE-packed Uint64 und Int64 Bytes in numerische Zeichenfolge. Hast du etwas im Sinn?

    
VisioN 02.08.2017, 08:51
quelle

4 Antworten

8

BEARBEITEN: Zum Konvertieren von (U) int64 würde ich jetzt definitiv die Lösung von @ LS_DEV empfehlen. Ich würde meine Lösung nur verwenden, wenn ich eine unbekannte oder größere Anzahl von Bytes habe.

Ich begann mit Ссылка und änderte es:

%Vor%

Zunächst wird das Vorzeichen berechnet, indem geprüft wird, ob das oberste Bit gesetzt ist ( bytes[0] > 128 ). Bei negativen Zahlen müssen die Bits negiert werden ( 255 - byte ) und 1 muss zu der Zahl addiert werden (daher 256 anstelle von 255 für das letzte Byte).

Die Grundidee der forEach-Schleife besteht darin, jedes Byte in seine Dezimalziffern aufzuteilen ( byte % 10 und den Overhead (byte - digits[i]) / 10 bzw. Math.floor(byte / 10) für die nächste Ziffer zu berechnen). Für das nächste Byte muss das verschobene Ergebnis der Ziffern der letzten Bytes addiert werden ( byte += digits[i] * 256 bzw. digits[i] << 8 ).

Dieser Code ist auf Kürze, Einfachheit und Flexibilität optimiert. Wenn Sie mit Strings anstelle von Bytes oder Zahlen arbeiten und keine Bibliotheken verwenden möchten, scheint die Konvertierungsleistung nicht wirklich von Bedeutung zu sein. Andernfalls könnte die Funktion für die Performance optimiert werden: Bis zu vier Bytes könnten gleichzeitig behandelt werden, nur die 0x100 und 0x80 müssen zusätzlich ersetzt werden (bei einem (U) Int64 verbleiben nur noch zwei Bytegruppen) Die forEach -Schleife kann entrollt werden. Das Gruppieren der Dezimalziffern erhöht wahrscheinlich nicht die Leistung, da die resultierenden Zeichenfolgen mit Nullen aufgefüllt werden müssten, was die Notwendigkeit zum Entfernen von führenden Nullen im Endergebnis mit sich bringt.

    
Stephan 04.08.2017, 11:33
quelle
2

Ein anderer Ansatz: Teile das Problem in zwei uint32, um die Berechnungen überschaubar zu halten.

Betrachten Sie niedrigere und höhere uint32 ( l und h ). Die vollständige Nummer könnte als h*0x100000000+l geschrieben werden. Betrachtet man die Dezimalzahl, könnte man auch niedrigere 9 Ziffern und verbleibende höhere Ziffern berücksichtigen ( ld und hd ): ld=(h*0x100000000+l)%1000000000 und hd=(h*0x100000000+l)/1000000000 . Mit einigen Eigenschaften von Arithmetik- und Algebra-Operatoren kann man diese Operation in sichere "halbe" 64-Bit-Operationen zerlegen und am Ende eine Zeichenfolge erstellen.

%Vor%

Wie in den Kommentaren beschrieben, funktioniert der Algorithmus, obwohl (h % D) * (H % D) größer als Number.MAX_SAFE_INTEGER werden kann, weil die verlorenen Bits trotzdem null waren.

    
LS_ᴅᴇᴠ 11.08.2017 09:14
quelle
1

Hier ist meine Lösung. Die allgemeine Strategie ist dies:

  • Wenn die Zahl negativ ist, negiere sie mit dem Zweierkomplement und füge am Ende ein negatives Vorzeichen hinzu
  • Repräsentiert beliebige Größennummern als LE-Arrays von Ziffern von 0 bis 9
  • Multiplizieren Sie für jedes Byte in Uint8Array (vom höchsten zum niedrigsten) die laufende Summe mit 256 und addieren Sie dazu den Wert des neuen Bytes
  • Um eine Zahl mit 256 zu multiplizieren, verdoppeln Sie sie 8-mal (seit 2 ** 8 == 256 )
  • Um zwei Zahlen hinzuzufügen, verwenden Sie den Grundschulalgorithmus:
    • Beginnen Sie mit der niedrigstwertigen Ziffer
    • Fügen Sie die entsprechenden Ziffern der zwei Zahlen hinzu
    • Ergebnisziffer ist die Summe mod 10; Übertrag ist 1, wenn die Summe 10 oder mehr ist, sonst 0
    • Fügen Sie weitere Ziffern mit dem Übertrag hinzu, bis wir die höchstwertigen Ziffern hinzugefügt haben, und tragen Sie 0

Einige Anmerkungen zur Kurzschrift:

  • n1[i] || 0 erhält die i -te Ziffer von n1 . Wenn dies nach dem Ende von i ist, behandeln wir es als 0 (stellen Sie sich Zahlen vor, die mit unendlichen 0 vor ihnen dargestellt sind). Gleiches mit n2 .
  • added > 9 erzeugt einen booleschen Wert, der automatisch in eine Zahl umgewandelt wird (1 wenn added >= 10 , 0 andernfalls)
  • i < n1.length || i < n2.length || carry überprüft, ob es in einem der Summanden mehr Ziffern gibt oder ob der Übertrag immer noch ungleich Null ist
  • String(b).split('').map(Number).reverse() konvertiert z. 100 bis '100' , dann ['1', '0', '0'] , dann [1, 0, 0] , dann [0, 0, 1] , also wird es in LE base-10
  • dargestellt
  • result.reverse().join('') konvertiert z. [0, 0, 1] bis [1, 0, 0] , dann '100'

Code:

%Vor%     
csander 04.08.2017 20:03
quelle
1

Das macht die UInt64 version - ich kann mir nicht vorstellen, dass ein Austausch das schwierig ist:

%Vor%

Die paddedBinary() -Funktion gibt eine 'volle' 8-Bit-Binärzahl zurück, so dass wir 'fnl' als 64-Bit-String des BigEndian erzeugen können.

Da JavaScript keine vollständige 64-Bit-Arithmetik ausführt, erstelle ich das dec[] -Array, um jede Potenz von 2 als einzelne Ziffern zu speichern, indem ich jede vorhergehende Ziffer verdopple und die Zehnerstelle glätte.

Dann ist alles übrig, um die Bits hinzuzufügen, die wir wollen, die eine ähnliche Methode verwenden, um die Zehner zu glätten.

(und die Antwort wird umgekehrt gegeben!)

    
JonMark Perry 08.08.2017 13:13
quelle