Immer der hohe Teil des 64-bit-integer-Multiplikation
In C++, sagen, dass:
uint64_t i;
uint64_t j;
dann i * j
wird der Ertrag einer uint64_t
hat als Wert den unteren Teil der Multiplikation zwischen i
und j
, d.h., (i * j) mod 2^64
.
Nun, was ist, wenn ich wollte, dass der höhere Teil der Multiplikation? Ich weiß, dass es existiert eine Montageanleitung tun, um so etwas wie, dass bei der Verwendung von 32-bit-Ganzzahlen, aber ich bin überhaupt nicht vertraut mit der Montage, also war ich auf Hilfe hoffend.
Was ist der effizienteste Weg, um so etwas wie:
uint64_t k = mulhi(i, j);
- Verweis: blogs.msdn.com/b/oldnewthing/archive/2014/12/08/10578956.aspx
- GCC hat
uint128_t
für diesen Zweck. Visual Studio hat keine solche option, obwohl. - Sieht aus wie uint128_t nicht vorhanden sind, unter meiner Umgebung (ich bin mit Xcode unter osx). Darüber hinaus wird explizit berechnen Sie den oberen und unteren Teil der Multiplikation, die ich gerne vermeiden möchte.
- vielen Dank für den Verweis! Wie gesagt, ich habe wenig bis keine Erfahrung mit der Montage. Könnten Sie ein einfaches Beispiel-code, das tun, was ich brauche? Sorry, ich sollte auf jeden Fall studieren Versammlung ein für alle mal!
- Es ist nicht möglich zu berechnen höheren Teil ohne untere Teil, da das tragen von unteren Teil vermehrt in den höher gelegenen Teil.
- Es geht nicht um die Montage. Ich versuche nur, Ihnen zu zeigen, die Mathematik.
- das stimmt in der Tat. Danke.
- Wenn die Leistung nicht ein großes Anliegen sein, versuchen Sie, eine beliebige Länge integer-Klasse, um das Ergebnis zu erhalten.
- Leistung ist meine größte Sorge, eigentlich...
- Also, wenn ich die Migration auf eine Plattform, wo ich
uint128_t
das ist wahrscheinlich der effizienteste Weg, das zu tun, was ich brauche? - Wenn die Leistung ist die eigentliche Sorge. Sie müssen lernen, genug assembly code diese inline. Auf einem 64-bit-Prozessor, es werden (sollten? ) werden Anweisungen zum multiplizieren der oberen und unteren 32 bit-zahlen.
- stackoverflow.com/questions/25095741/... stackoverflow.com/questions/28766755/... stackoverflow.com/questions/87771/... stackoverflow.com/questions/28807341/...
- Es ist
__int128
im gcc sowie llvm-einschließlich Apple-Tang Clan. stackoverflow.com/questions/13187629/... - Einige mehr high-bits von long-Multiplikation in Java? Computing hohe 64 bits einer 64x64 int Produkt in C Einigermaßen tragbaren Weg, um top-64-bit aus 64x64 bit multiplizieren? Pure-high-bit-Multiplikation in Assembler?
- danke, ich denke, ich werde einfach verwenden Sie 128-bit-Multiplikation an dieser Stelle. Das klingt mehr Leistung als jede andere Lösung, die ich umsetzen konnte, die auf meinen eigenen, da ich vermute, dass jede mögliche Optimierung muss bereits umgesetzt worden, die von jenen entwickelt, die den compiler.
- Diese Frage ist nicht ein Duplikat des einen verbunden. Dass die andere Frage ist die Fokussierung auf 32-bit-Multiplikationen, während dieser ist die Fokussierung auf 64-bit-Multiplikationen. Wenn die Leute kommen zu dieser Frage, die Sie Folgen Sie dem link (wie ich) und gehen Sie zurück zu dieser Frage. Ich denke, es sollte wieder aufgenommen werden (und vielleicht wieder geschlossen, mit einem besseren dup).
- es sollte kein Unterschied sein. Doppelklicken Sie einfach jede variable Typ, und das problem ist gelöst
- aber ja, ist wohl die andere Frage nicht gut genug generische Antwort
- Sie können nicht verdoppeln, die variable Typen als leicht. Sie müssten eine 128-bit-integer-Typ.
- Nein, Sie brauchen es nicht nur um den höheren Teil einer 64x64-Multiplikation, z.B. verbreitern der Montageanleitung, die andere Frage und du bist gut zu gehen. Und hast du meine verlinkte andere Fragen?
- Dies ist eine C++ Frage, keine Montage-Frage. Natürlich kann ich eine Lösung finden, die in der Montage die Multiplikation von zwei 64-bit-Register. Der springende Punkt ist, zu wissen, ob dies möglich ist portabel in C++. Und wenn Sie stecken in portable C++, 32-bit-Frage eine triviale Antwort (multiplizieren von zwei std::uint64_t) und die 64-bit-Frage ist schwierig (denn wir haben nicht ein std::uint128_t)
- Lassen Sie uns weiter, diese Diskussion im chat.
- Ein besseres dupe ist Computing-hoch 64 bits einer 64x64 int Produkt in C und hat Antwort, die deutlich zeigt, wie derive gute Ergebnisse für ähnliche Probleme.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Wenn Sie mit gcc und die version, die Sie haben, unterstützt die 128-bit-zahlen (versuchen Sie, __uint128_t) als die Ausführung mit 128 multiplizieren, und extrahieren Sie die oberen 64 bit ist wahrscheinlich der effizienteste Weg, um das Ergebnis.
Wenn Ihr compiler nicht unterstützt 128-bit-zahlen, dann Yakk die Antwort richtig ist. Es kann jedoch zu kurz für den Allgemeinen Verbrauch. Insbesondere eine tatsächliche Umsetzung hat, vorsichtig zu sein überlaufen und 64-bit-integars.
Die einfache und portable Lösung, die er vorschlägt, ist zu brechen, jedes von a und b in 2 32-bit-zahlen und dann multiplizieren Sie diese 32-bit-zahlen mit 64 bit-Multiplikation. Wenn wir schreiben:
dann ist es offensichtlich, dass:
und:
sofern die Berechnung erfolgt mit einer 128-bit (oder mehr) rechnen.
Aber dieses problem erfordert, dass wir alle Berechungen mit 64-bit-Arithmetik, also müssen wir uns sorgen machen überlauf.
Seit a_hi, a_lo, b_hi, und b_lo sind alle vorzeichenlosen 32-bit-zahlen, deren Produkt passt in eine vorzeichenlose 64 bit Zahl ohne überlauf. Doch die Zwischenergebnisse der oben genannten Berechnung nicht.
Den folgenden code implementieren mulhi(a, b), wenn die mathemetics muss durchgeführt werden modulo 2^64:
Als Yakk Punkte aus, wenn Sie sich nicht kümmern, aus durch +1 in den oberen 64 bits, können Sie weglassen der Berechnung der carry-bit.
Dies ist ein unit-getestete version, die ich kam mit heute Abend, stellt die vollständige 128-bit-Produkt. Bei der Inspektion es scheint einfacher, als die meisten anderen online-Lösungen (in z.B. Botan-Bibliothek und anderen Antworten hier), weil es nutzt, wie der MITTLERE TEIL nicht überlaufen, wie in den Kommentaren im code.
Kontext ich es geschrieben habe für dieses github-Projekt: https://github.com/catid/fp61
_umul128
ist nicht verfügbar.Lange Multiplikation sollte ok sein, Leistung.
Split
a*b
in(hia+loa)*(hib+lob)
. Dies gibt 4 32 bit multipliziert, plus einige Verschiebungen. Tun Sie in 64 bit, und tun trägt, manuell, an, und Sie erhalten die hohe Anteil.Beachten Sie, dass eine Angleichung der hohe Anteil getan werden kann, mit weniger multipliziert -- präzise innerhalb von 2^33 oder so mit 1 multiplizieren, und innerhalb von 1 mit 3 multipliziert.
Ich glaube nicht, dass es eine tragbare alternative.
TL:DR mit GCC für 64-bit-ISA:
(a * (unsigned __int128)b) >> 64
kompiliert schön, zu einem ganzen zu multiplizieren oder zu hoch-die Hälfte multiply-Anweisung. Keine Notwendigkeit, mess around mit inline-asm.Leider aktuellen Compilern nicht optimieren @craigster0 ist schön portable version, also, wenn Sie möchten, um die Vorteile von 64-bit-CPUs, Sie können es nicht verwenden, außer als fallback für Ziele, die Sie nicht haben eine
#ifdef
für. (Ich sehe nicht, eine generische Art und Weise zu optimieren; Sie müssen einer 128-bit-Typ oder einer systeminternen.)GNU C (gcc, clang, ICC) hat
unsigned __int128
auf den meisten 64-bit-Plattformen. (Oder in älteren Versionen__uint128_t
). GCC nicht implementieren dieses Typs auf 32-bit-Plattformen, obwohl.Dies ist ein einfacher und effizienter Weg, um den compiler zu emittieren, die eine 64-bit-voll-multiply-Anweisung und halten Sie die Obere Hälfte. (GCC weiß, dass ein uint64_t Besetzung einer 128-bit-integer hat immer noch die Obere Hälfte, die alle null sind, so dass Sie nicht bekommen, eine 128-bit-multiplizieren mit drei 64-bit multipliziert.)
MSVC hat auch eine
__umulh
intrinsische für 64-bit-high-Hälfte Multiplikation, aber wieder, es ist nur verfügbar auf 64-bit-Plattformen (insbesondere x86-64 und AArch64. Die docs auch erwähnen, IPF (IA-64) mit_umul128
erhältlich, aber ich habe nicht MSVC für Itanium verfügbar. (Wahrscheinlich sowieso nicht relevant.)Für x86-64, AArch64 und PowerPC64 (und andere), dies stellt an eine
mul
Unterricht, und ein paarmov
s Umgang mit der Aufrufkonvention (die optimieren der Weg nach dieser inlines).Von die Godbolt compiler explorer (mit Quelle + asm für x86-64, PowerPC64, und AArch64):
(oder mit
clang -march=haswell
zu ermöglichen, BMI2:mov rdx, rsi
/mulx rax, rcx, rdi
um die high-Hälfte in RAX direkt. gcc ist dumm und nutzt noch eine zusätzlichemov
.)Für AArch64 (mit gcc
unsigned __int128
oder MSVC mit__umulh
):Mit einer compile-Zeit-Konstante Leistung von 2 Multiplikator, wir in der Regel die erwarteten rechts-shift zu greifen, ein paar high-bits. Aber gcc lustig verwendet
shld
(siehe Godbolt link).Leider aktuellen Compilern nicht optimieren @craigster0 ist schön portable version. Sie bekommen 8x
shr r64,32
, 4ximul r64,r64
, und eine Reihe vonadd
/mov
Anweisungen für x86-64. d.h. es kompiliert eine Menge von 32x32 => 64-bit multipliziert und entpackt die Ergebnisse. Also, wenn Sie etwas wollen, die die Vorteile von 64-bit-CPUs, müssen Sie einige#ifdef
s.Full-multiplizieren
mul 64
Anleitung ist 2 uops auf Intel-Prozessoren, aber immer noch nur 3-Zyklus-Latenz, wieimul r64,r64
das erzeugt nur eine 64-bit-Ergebnis. Also die__int128
/innere-version ist 5-bis 10-mal günstiger in die Latenz und der Durchsatz (Auswirkungen auf die umliegenden code) auf modernen x86-64 als die portable version, von einem schnellen Augapfel Vermutung basiert auf http://agner.org/optimize/.Check it out auf der Godbolt compiler-explorer auf den obigen link.
gcc nicht vollständig optimieren Sie diese Funktion bei der Multiplikation mit 16, aber: Sie bekommen einen einzigen shift rechts, effizienter als mit
unsigned __int128
multiplizieren.in kind
Stimmen, nach wahrgenommenen Nützlichkeit der Antwort (siehe voting schwebt) oder die Abstimmung nach unten, denn du hast mich?Hier ist der asm für ARMv8 oder Aarch64-version:
Und hier ist der asm für alte DEC-Compiler:
Wenn Sie x86 ist BMI2 und verwenden möchten
mulxq
:Und generic x86 multiplizieren mit
mulq
:unsigned __int128
statt, wie meine Antwort zeigt. Was ist der use-case dafür? Einige GCC oder clang-Versionen scheitern zu emittieren nur eineumulh
für(a * (unsigned __int128)b) >> 64
? Oh, ich hatte gerade einen Blick auf meine Antwort, und es zeigt AArch64 GCC-emittingumulh
.umulh
mit GNU-C inline-asm scheint völlig nutzlos für mich. Vor allem, wenn meine Antwort schon zeigt, dass die Anweisung existiert.[inline-assembly]
.__int128
? (Außer alten gcc). Wenn dem so ist, so sagen in Ihrer Antwort, und ich werde upvote."g"
umfasst unmittelbare, der x86 mul nicht unterstützt. godbolt.org/z/r0NeIi. Wahrscheinlich am besten zu verwenden"r"
zu stoppen, clang von shooting selbst in den Fuß und speichern erste, wenn man ihm die option Speicher.