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]
.
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?
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:
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.
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.
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.
Hier ist meine Lösung. Die allgemeine Strategie ist dies:
Uint8Array
(vom höchsten zum niedrigsten) die laufende Summe mit 256 und addieren Sie dazu den Wert des neuen Bytes 2 ** 8 == 256
) 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 result.reverse().join('')
konvertiert z. [0, 0, 1]
bis [1, 0, 0]
, dann '100'
Code:
%Vor% Das macht die UInt64
version - ich kann mir nicht vorstellen, dass ein Austausch das schwierig ist:
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!)
Tags und Links javascript string ecmascript-6 int64 uint64