Welche der folgenden Techniken ist die beste Möglichkeit, eine ganze Zahl durch 2 zu teilen, und warum?
Technik 1:
x = x >> 1;
Technik 2:
x = x / 2;
Hier x
ist eine ganze Zahl.
Welche der folgenden Techniken ist die beste Möglichkeit, eine ganze Zahl durch 2 zu teilen, und warum?
Technik 1:
x = x >> 1;
Technik 2:
x = x / 2;
Hier x
ist eine ganze Zahl.
Ich stimme mit anderen Antworten überein, dass Sie Folgendes bevorzugen sollten x / 2
weil seine Absicht klarer ist und der Compiler ihn für Sie optimieren sollte.
Ein weiterer Grund für die Bevorzugung von x / 2
über x >> 1
ist, dass das Verhalten der >>
ist abhängig von der Implementierung, wenn x
ist eine vorzeichenbehaftete int
und ist negativ.
Aus Abschnitt 6.5.7, Aufzählungspunkt 5 der ISO C99-Norm:
Das Ergebnis von
E1 >> E2
estE1
rechts verschobenE2
Bit-Positionen. WennE1
h einen Typ ohne Vorzeichen hat oder wennE1
h der Wert des Ergebnisses ist der ganzzahlige Teil des Quotienten ausE1
/ 2E2
. WennE1
einen vorzeichenbehafteten Typ und einen negativen Wert hat, ist der resultierende Wert ist implementierungsabhängig.
x / 2
ist klarer, und x >> 1
ist nicht viel schneller (laut einem Micro-Benchmark etwa 30 % schneller für eine Java JVM). Wie bereits von anderen erwähnt, ist die Rundung bei negativen Zahlen etwas anders, so dass Sie dies berücksichtigen müssen, wenn Sie negative Zahlen verarbeiten wollen. Einige Compiler können automatisch konvertieren x / 2
à x >> 1
wenn sie wissen, dass die Zahl nicht negativ sein kann (auch wenn ich dies nicht nachprüfen konnte).
Sogar x / 2
den (langsamen) Divisions-CPU-Befehl nicht verwenden darf, weil einige Abkürzungen sind möglich aber es ist immer noch langsamer als x >> 1
.
(Dies ist eine C / C++ Frage, andere Programmiersprachen haben mehr Operatoren. Für Java gibt es auch die vorzeichenlose Rechtsverschiebung, x >>> 1
, was wiederum anders ist. Sie ermöglicht die korrekte Berechnung des Mittelwerts (Durchschnittswerts) von zwei Werten, so dass (a + b) >>> 1
gibt den Mittelwert auch bei sehr großen Werten von a
et b
. Dies ist zum Beispiel für die binäre Suche erforderlich, wenn die Array-Indizes sehr groß werden können. Es gab ein Fehler in vielen Versionen der Binärsuche denn sie verwendeten (a + b) / 2
um den Durchschnitt zu berechnen. Dies funktioniert nicht korrekt. Die richtige Lösung ist die Verwendung von (a + b) >>> 1
stattdessen).
Schauen Sie sich die Compiler-Ausgabe an, um eine Entscheidung zu treffen. Ich habe diesen Test auf x86-64 mit
gcc (GCC) 4.2.1 20070719 [FreeBSD]
Siehe auch Compiler-Ausgaben online bei Godbolt .
Was Sie sehen, ist, dass der Compiler eine sarl
(arithmetische Rechtsverschiebung) in beiden Fällen, so dass die Ähnlichkeit zwischen den beiden Ausdrücken erkannt wird. Wenn Sie die Divisionsanweisung verwenden, muss der Compiler auch negative Zahlen berücksichtigen. Dazu verschiebt er das Vorzeichenbit auf das Bit niedrigster Ordnung und addiert es zum Ergebnis. Auf diese Weise wird das Problem der Verschiebung negativer Zahlen um eins behoben, verglichen mit dem, was ein Divide tun würde.
Da der Divide-Fall zwei Verschiebungen vornimmt, während der explizite Shift-Fall nur eine vornimmt, können wir nun einige der Leistungsunterschiede erklären, die durch andere Antworten gemessen wurden.
C-Code mit Assembler-Ausgabe:
Bei einer Teilung würde Ihre Eingabe lauten
int div2signed(int a) {
return a / 2;
}
und dies kompiliert zu
movl %edi, %eax
shrl $31, %eax # (unsigned)x >> 31
addl %edi, %eax # tmp = x + (x<0)
sarl %eax # (x + 0 or 1) >> 1 arithmetic right shift
ret
ähnlich für Schicht
int shr2signed(int a) {
return a >> 1;
}
mit Ausgang:
sarl %edi
movl %edi, %eax
ret
Andere ISAs können dies ebenso effizient tun, wenn nicht sogar noch effizienter. Zum Beispiel verwendet GCC für AArch64:
add w0, w0, w0, lsr 31 // x += (unsigned)x>>31
asr w0, w0, 1 // x >>= 1
ret
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.