Zum Beispiel :
f(8)=8
f(9)=8
Kann ich tun x = x/2*2;? Besteht das Risiko, dass der Compiler einen solchen Ausdruck optimiert?
Zum Beispiel :
f(8)=8
f(9)=8
Kann ich tun x = x/2*2;? Besteht das Risiko, dass der Compiler einen solchen Ausdruck optimiert?
x >>= 1; x <<= 1;
x &= ~(1)' Where the 1` könnte eine Zeichenfolge von 1s der gewünschten Länge sein. Zum Beispiel: x &= ~(0x07);für drei Bits usw.
Antworten:
Der Compiler darf beliebige Optimierungen vornehmen, solange er keine Nebenwirkungen in das Programm einbringt. In Ihrem Fall kann es die 2 nicht aufheben, da der Ausdruck dann einen anderen Wert für ungerade Zahlen hat.
x / 2 * 2wird streng als ausgewertet (x / 2) * 2und x / 2in ganzzahliger Arithmetik ausgeführt, wenn xes sich um einen ganzzahligen Typ handelt.
Dies ist in der Tat eine idiomatische Rundungstechnik.
(x>>1)<<1. IIRC gibt es Compiler, die dies auch dann tun, wenn Optimierungen technisch deaktiviert sind.
(x & 0xfffffffe):)
Da Sie angegeben haben, dass die Ganzzahlen ohne Vorzeichen sind, können Sie dies mit einer einfachen Maske tun:
x & (~1u)
Dadurch wird das LSB auf Null gesetzt, wodurch die unmittelbare gerade Zahl erzeugt wird, die nicht größer als ist x. Das heißt, wenn xes einen Typ gibt, der nicht breiter als ein ist unsigned int.
Sie können natürlich erzwingen 1, dass derselbe Typ wie ein breiterer ist x, wie folgt:
x & ~((x & 1u) | 1u)
Aber an diesem Punkt sollten Sie diesen Ansatz wirklich als eine Übung zur Verschleierung betrachten und Bathshebas Antwort akzeptieren.
Ich habe natürlich die Standardbibliothek vergessen. Wenn Sie einschließen stdint.h(oder cstdint, wie Sie es in C ++ - Code sollten). Sie können die Implementierung sich um die Details kümmern lassen:
uintmax_t const lsb = 1;
x & ~lsb;
oder
x & ~UINTMAX_C(1)
x & (~static_cast<decltype(x)>(1))keinen Kaffee oder brauche ich ihn?
UINT_MAX, nein? Wer'e noch im gleichen Boot , wenn xist unsigned long, ich denke .
x & (~1u)funktioniert nicht, wenn der Typ xgrößer ist alsunsigned int . Umgekehrt x & ~1würde sich für alle Typen wie erwartet verhalten. Dies ist eine kontraintuitive Falle. Wenn Sie darauf bestehen, eine vorzeichenlose Konstante zu verwenden, müssen Sie schreiben, x & ~(uintmax_t)1da x & ~1ULLdies bei xeinem größeren Typ als sogar fehlschlagen würde unsigned long long. Erschwerend kommt hinzu, dass viele Plattformen jetzt größere Ganzzahltypen haben als uintmax_tz __uint128_t.
C und C ++ verwenden bei der Optimierung im Allgemeinen die "als ob" -Regel. Das Berechnungsergebnis muss so sein, als ob der Compiler Ihren Code nicht optimiert.
In diesem Fall 9/2*2=8. Der Compiler kann eine beliebige Methode verwenden, um das Ergebnis 8 zu erzielen. Dazu gehören Bitmasken, Bitverschiebungen oder CPU-spezifische Hacks, die dieselben Ergebnisse liefern (x86 verfügt über einige Tricks, die darauf beruhen, dass nicht zwischen Zeigern unterschieden wird und ganze Zahlen, im Gegensatz zu C und C ++).
-O1. Alle Ansätze werden auf den gleichen Maschinencode destilliert.
and RAX, -2Methode für alle 3 Varianten sogar anwenden -O0.
x/2*2Kompiliert zu einer einzelnen Anweisung. Im Allgemeinen wird das Kompilieren ohne Optimierungen zu Debugging-Zwecken durchgeführt, und dann möchten Sie eine 1: N-Entsprechung zwischen Quellcode und Assembly.
Sie können schreiben x / 2 * 2und der Compiler erzeugt sehr effizienten Code, um das niedrigstwertige Bit zu löschen, wennx einen vorzeichenlosen Typ hat.
Umgekehrt könnte man schreiben:
x = x & ~1;
Oder wahrscheinlich weniger lesbar:
x = x & -2;
Oder auch
x = (x >> 1) << 1;
Oder auch das:
x = x - (x & 1);
Oder dieser letzte, von Supercat vorgeschlagene, der für positive Werte aller ganzzahligen Typen und Darstellungen funktioniert:
x = (x | 1) ^ 1;
Alle oben genannten Vorschläge funktionieren korrekt für alle vorzeichenlosen Ganzzahltypen auf den Komplementarchitekturen von 2. Ob der Compiler optimalen Code erzeugt, ist eine Frage der Konfiguration und der Qualität der Implementierung.
Beachten Sie jedoch, dass x & (~1u)dies nicht funktioniert, wenn der Typ xgrößer als ist unsigned int. Dies ist eine kontraintuitive Falle. Wenn Sie darauf bestehen, eine vorzeichenlose Konstante zu verwenden, müssen Sie schreiben, x & ~(uintmax_t)1da x & ~1ULLdies bei xeinem größeren Typ als sogar fehlschlagen würde unsigned long long. Erschwerend kommt hinzu, dass viele Plattformen jetzt ganzzahlige Typen haben, die größer als sinduintmax_t z __uint128_t.
Hier ist ein kleiner Maßstab:
typedef unsigned int T;
T test1(T x) {
return x / 2 * 2;
}
T test2(T x) {
return x & ~1;
}
T test3(T x) {
return x & -2;
}
T test4(T x) {
return (x >> 1) << 1;
}
T test5(T x) {
return x - (x & 1);
}
T test6(T x) { // suggested by supercat
return (x | 1) ^ 1;
}
T test7(T x) { // suggested by Mehrdad
return ~(~x | 1);
}
T test1u(T x) {
return x & ~1u;
}
Wie von Ruslan vorgeschlagen, zeigt das Testen im Compiler-Explorer von Godbolt , dass für alle oben genannten Alternativen gcc -O1derselbe exakte Code für erzeugt unsigned intwird, der Typ Tjedoch unsigned long longgeändert wird, um unterschiedlichen Code für anzuzeigen test1u.
x. Können Sie zeigen, für welchen Wert es nicht funktionieren würde?
1ist positiv und wird immer als dargestellt 00...01. |1stellt das LSB ein. ^1schaltet das gleiche Bit um. Das Komplement beeinflusst den Wert des MSB cq den Wert von, INT_MINaber wir berühren das MSB nicht. IOW, Zahlendarstellungen spielen eine Rolle, wenn Sie arithmetische und numerische Operationen mischen, und das tun wir nicht. (Aber die Frage lässt offen, wie man negative Zahlen rundet,
x = x - (x%2)? Dies hat den Vorteil der Skalierung auf jeden nworoundToMultiple(int x, int n) { return x - (x % n); }
Wenn Ihre Werte von einem vorzeichenlosen Typ sind, wie Sie sagen, ist dies am einfachsten
x & -2;
Die Wunder der vorzeichenlosen Arithmetik machen es so, dass -2es in den Typ von konvertiert xwird und ein Bitmuster hat, das alle hat, aber für das niedrigstwertige Bit, das ist 0.
Im Gegensatz zu einigen anderen vorgeschlagenen Lösungen sollte dies mit jedem vorzeichenlosen Ganzzahltyp funktionieren, der mindestens so breit ist wie unsigned. (Und Sie sollten sowieso nicht mit engeren Typen rechnen.)
Zusätzlicher Bonus, wie von Supercat bemerkt, verwendet nur die Konvertierung eines signierten Typs in einen nicht signierten Typ. Dies wird durch den Standard als Modulo-Arithmetik definiert. Das Ergebnis ist also immer UTYPE_MAX-1für UTYPEden vorzeichenlosen Typ von x. Insbesondere ist es unabhängig von der Vorzeichendarstellung der Plattform für vorzeichenbehaftete Typen.
Eine Option, von der ich überrascht bin, dass sie bisher nicht erwähnt wurde, ist die Verwendung des Modulo-Operators. Ich würde argumentieren, dass dies Ihre Absicht mindestens genauso gut darstellt wie Ihr ursprüngliches Snippet und vielleicht sogar noch besser.
x = x - x % 2
Wie andere bereits gesagt haben, wird der Optimierer des Compilers jeden vernünftigen Ausdruck gleichwertig behandeln. Machen Sie sich also Gedanken darüber, was klarer ist als was Sie für am schnellsten halten. Alle Bit-Tweaking-Antworten sind interessant, aber Sie sollten definitiv keine von ihnen anstelle von arithmetischen Operatoren verwenden (vorausgesetzt, die Motivation ist eher arithmetisch als Bit-Tweaking).
x % d = x - x / d * d, dass mein Snippet garantiert identische Ergebnisse wie die x = x/2*2in der Frage genannten liefert . Angenommen, xSie befürchten, negativ zu sein (und nicht als negativer Ersatz für 2), dann gibt dies das, was OP wahrscheinlich will: wenn x=-7dann x/2*2 == x - x%2 == -6.
x/2*2mit negativen ganzen Zahlen überraschend sind. Ich bin mir also nicht sicher, wie ich vermeiden soll, Code zu haben, der sich für jemanden mit negativen ganzen Zahlen überraschend verhält!