Wie kann ich mit nur Bitverschiebung und Addition multiplizieren und dividieren?
c
assembly
bit-manipulation
division
multiplication
Spidfire
quelle
quelle
Antworten:
Um in Bezug auf das Addieren und Verschieben zu multiplizieren, möchten Sie eine der Zahlen durch Zweierpotenzen wie folgt zerlegen:
(
_2
bedeutet Basis 2)Wie Sie sehen können, kann die Multiplikation in Addieren und Verschieben und wieder zurück zerlegt werden. Dies ist auch der Grund, warum die Multiplikation länger dauert als das Verschieben oder Addieren von Bits - es ist O (n ^ 2) anstelle von O (n) in der Anzahl der Bits. Reale Computersysteme (im Gegensatz zu theoretischen Computersystemen) haben eine endliche Anzahl von Bits, so dass die Multiplikation im Vergleich zum Addieren und Verschieben ein konstantes Vielfaches der Zeit in Anspruch nimmt. Wenn ich mich richtig erinnere, können moderne Prozessoren, wenn sie richtig geleitet werden, die Multiplikation genauso schnell wie die Addition durchführen, indem sie mit der Verwendung der ALUs (arithmetischen Einheiten) im Prozessor herumspielen.
quelle
Die Antwort von Andrew Toulouse kann auf die Teilung ausgedehnt werden.
Die Division durch ganzzahlige Konstanten wird im Buch "Hacker's Delight" von Henry S. Warren (ISBN 9780201914658) ausführlich behandelt.
Die erste Idee zur Implementierung der Division besteht darin, den inversen Wert des Nenners in Basis zwei zu schreiben.
Z.B,
1/3 = (base-2) 0.0101 0101 0101 0101 0101 0101 0101 0101 .....
Also
a/3 = (a >> 2) + (a >> 4) + (a >> 6) + ... + (a >> 30)
für 32-Bit-Arithmetik.Indem wir die Begriffe auf offensichtliche Weise kombinieren, können wir die Anzahl der Operationen reduzieren:
b = (a >> 2) + (a >> 4)
b += (b >> 4)
b += (b >> 8)
b += (b >> 16)
Es gibt aufregendere Möglichkeiten, Division und Rest zu berechnen.
EDIT1:
Wenn das OP die Multiplikation und Division beliebiger Zahlen bedeutet, nicht die Division durch eine konstante Zahl, kann dieser Thread von Nutzen sein: https://stackoverflow.com/a/12699549/1182653
EDIT2:
Eine der schnellsten Möglichkeiten zum Teilen durch ganzzahlige Konstanten besteht darin, die modulare Arithmetik und die Montgomery-Reduktion auszunutzen: Was ist der schnellste Weg, eine ganze Zahl durch 3 zu teilen?
quelle
b += r * 11 >> 5
mitr = a - q * 3
. Link: hackersdelight.org/divcMore.pdf Seite 2+.X * 2 = 1 Bit Verschiebung nach links
X / 2 = 1 Bit Verschiebung nach rechts
X * 3 = Verschiebung um 1 Bit nach links und addiere dann X.
quelle
add X
für den letzten?x << k == x multiplied by 2 to the power of k
x >> k == x divided by 2 to the power of k
Mit diesen Verschiebungen können Sie jede Multiplikationsoperation ausführen. Beispielsweise:
x * 14 == x * 16 - x * 2 == (x << 4) - (x << 1)
x * 12 == x * 8 + x * 4 == (x << 3) + (x << 2)
Um eine Zahl durch eine Zweierpotenz zu teilen, ist mir kein einfacher Weg bekannt, es sei denn, Sie möchten eine Logik auf niedriger Ebene implementieren, andere binäre Operationen verwenden und eine Form der Iteration verwenden.
quelle
quelle
Ich habe den Python-Code in C übersetzt. Das angegebene Beispiel hatte einen kleinen Fehler. Wenn der Dividendenwert alle 32 Bits beanspruchen würde, würde die Verschiebung fehlschlagen. Ich habe nur intern 64-Bit-Variablen verwendet, um das Problem zu umgehen:
quelle
Ein Verfahren zum Teilen von ganzen Zahlen, das Verschiebungen und Additionen verwendet, kann auf einfache Weise aus der dezimalen Langhandteilung abgeleitet werden, wie sie in der Grundschule gelehrt wird. Die Auswahl jeder Quotientenziffer wird vereinfacht, da die Ziffer entweder 0 oder 1 ist: Wenn der aktuelle Rest größer oder gleich dem Divisor ist, ist das niedrigstwertige Bit des Teilquotienten 1.
Genau wie bei der dezimalen Langhanddivision werden die Ziffern der Dividende eine Ziffer nach der anderen von der signifikantesten zur niedrigstwertigen betrachtet. Dies wird leicht durch eine Verschiebung der binären Teilung nach links erreicht. Außerdem werden Quotientenbits gesammelt, indem die aktuellen Quotientenbits um eine Position nach links verschoben und dann das neue Quotientenbit angehängt werden.
In einer klassischen Anordnung werden diese beiden Linksverschiebungen zu einer Linksverschiebung eines Registerpaars kombiniert. Die obere Hälfte enthält den aktuellen Rest, die untere Hälfte die Dividende. Wenn die Dividendenbits durch Linksverschiebung in das Restregister übertragen werden, werden die nicht verwendeten niedrigstwertigen Bits der unteren Hälfte verwendet, um die Quotientenbits zu akkumulieren.
Unten finden Sie die x86-Assemblersprache und C-Implementierungen dieses Algorithmus. Diese spezielle Variante einer Shift & Add-Division wird manchmal als "No-Performing" -Variante bezeichnet, da die Subtraktion des Divisors vom aktuellen Rest nur durchgeführt wird, wenn der Rest größer oder gleich dem Divisor ist. In C gibt es keine Vorstellung von dem Übertragsflag, das von der Baugruppenversion in der Linksverschiebung des Registerpaars verwendet wird. Stattdessen wird es emuliert, basierend auf der Beobachtung, dass das Ergebnis eines Additionsmoduls 2 n kleiner sein kann, das entweder nur addiert, wenn es eine Ausführung gab.
quelle
Nehmen Sie zwei Zahlen, sagen wir 9 und 10, und schreiben Sie sie als Binärzahlen - 1001 und 1010.
Beginnen Sie mit einem Ergebnis R von 0.
Nehmen Sie eine der Zahlen, in diesem Fall 1010, wir nennen sie A und verschieben sie um ein Bit nach rechts. Wenn Sie eine Eins herausschieben, fügen Sie die erste Zahl hinzu, wir nennen sie B zu R.
Verschieben Sie nun B um ein Bit nach links und wiederholen Sie den Vorgang, bis alle Bits aus A heraus verschoben wurden.
Es ist einfacher zu sehen, was los ist, wenn Sie es ausgeschrieben sehen. Dies ist das Beispiel:
quelle
Von hier genommen .
Dies ist nur zur Teilung:
quelle
Dies sollte für die Multiplikation funktionieren:
quelle
Die folgende Methode ist die Implementierung der binären Division, wenn beide Zahlen positiv sind. Wenn die Subtraktion ein Problem darstellt, können wir dies auch mithilfe von Binäroperatoren implementieren.
Code
Zur Multiplikation:
quelle
-(int)multiplyNumber:(int)num1 withNumber:(int)num2
?Für alle , in einer Lösung , 16-Bit - x86 interessiert, es ist ein Stück Code , der von JasonKnight hier 1 (er enthält auch eine Multiplikation mit Vorzeichen Stück, das habe ich nicht getestet). Dieser Code weist jedoch Probleme mit großen Eingaben auf, bei denen der Teil "add bx, bx" überlaufen würde.
Die feste Version:
Oder dasselbe in der GCC-Inline-Montage:
quelle
es multipliziert und dividiert im Grunde genommen mit der Grundleistung 2
nach links verschieben = x * 2 ^ y
nach rechts verschieben = x / 2 ^ y
shl eax, 2 = 2 * 2 ^ 2 = 8
shr eax, 3 = 2/2 ^ 3 = 1/4
quelle
eax
kann einen Bruchwert wie nicht halten1/4
. (Es sei denn, Sie verwenden einen Festpunkt anstelle einer Ganzzahl, aber das haben Sie nicht angegeben.)Versuche dies. https://gist.github.com/swguru/5219592
quelle