Ich bin immer davon ausgegangen, dass (a % 256)
der Optimierer natürlich eine effiziente bitweise Operation verwenden würde, als ob ich geschrieben hätte (a & 0xFF)
.
Beim Testen auf dem Compiler Explorer gcc-6.2 (-O3):
// Type your code here, or load an example.
int mod(int num) {
return num % 256;
}
mod(int):
mov edx, edi
sar edx, 31
shr edx, 24
lea eax, [rdi+rdx]
movzx eax, al
sub eax, edx
ret
Und wenn Sie den anderen Code ausprobieren:
// Type your code here, or load an example.
int mod(int num) {
return num & 0xFF;
}
mod(int):
movzx eax, dil
ret
Scheint, als würde mir etwas völlig fehlen. Irgendwelche Ideen?
c++
optimization
Elad Weiss
quelle
quelle
%
ist auch nicht&
.num
istunsigned
?Antworten:
Es ist nicht das gleiche. Versuchen Sie es
num = -79
, und Sie erhalten bei beiden Vorgängen unterschiedliche Ergebnisse.(-79) % 256 = -79
, während(-79) & 0xff
ist eine positive Zahl.Bei Verwendung
unsigned int
sind die Vorgänge identisch, und der Code ist wahrscheinlich identisch.PS: Jemand hat kommentiert
So ist es nicht in C, C ++, Objective-C definiert (dh in allen Sprachen, in denen der Code in der Frage kompiliert werden würde).
quelle
Kurze Antwort
-1 % 256
ergibt-1
und nicht255
was ist-1 & 0xFF
. Daher wäre die Optimierung falsch.Lange Antwort
C ++ hat die Konvention
(a/b)*b + a%b == a
, die ganz natürlich erscheint.a/b
Gibt immer das arithmetische Ergebnis ohne den Bruchteil zurück (Abschneiden in Richtung 0). Infolgedessena%b
hat das gleiche Vorzeichen wiea
oder ist 0.Die Division
-1/256
ergibt0
und-1%256
muss daher sein-1
, um die obige Bedingung zu erfüllen ((-1%256)*256 + -1%256 == -1
). Dies ist offensichtlich anders als-1&0xFF
das ist0xFF
. Daher kann der Compiler nicht wie gewünscht optimieren.Der relevante Abschnitt im C ++ - Standard [Ausdruck § 4] ab N4606 lautet:
Optimierung aktivieren
Bei Verwendung von
unsigned
Typen wäre die Optimierung jedoch vollständig korrekt und würde die obige Konvention erfüllen:Siehe auch dies .
Andere Sprachen
Dies wird in verschiedenen Programmiersprachen sehr unterschiedlich gehandhabt, da Sie auf Wikipedia nachschlagen können .
quelle
Da C ++ 11,
num % 256
muss nicht positiv sein, wennnum
negativ ist.Das Bitmuster hängt also von der Implementierung vorzeichenbehafteter Typen auf Ihrem System ab: Bei einem negativen ersten Argument ist das Ergebnis nicht die Extraktion der niedrigstwertigen 8 Bits.
num
In Ihrem Fall wäre es eine andere Sacheunsigned
: Heutzutage würde ich fast erwarten, dass ein Compiler die von Ihnen zitierte Optimierung vornimmt.quelle
num
es negativ ist,num % 256
ist es null oder negativ (auch bekannt als nicht positiv).(-250+256)%256==6
,(-250%256)+(256%256)
muss aber nach dem Standard "nicht positiv" sein und daher nicht6
. Eine solche Assoziativität zu brechen hat reale Nebenwirkungen: Wenn man beispielsweise das "Verkleinern" -Rendering in ganzzahligen Koordinaten berechnet, muss das Bild verschoben werden, damit alle Koordinaten nicht negativ sind.(128+128)%256==0
aber(128%256)+(128%256)==256
. Vielleicht gibt es einen guten Einwand gegen das angegebene Verhalten, aber mir ist nicht klar, dass es das ist, was Sie gesagt haben.256==0
. Der Schlüssel ist, genauN
mögliche Werte in der Modulo-N
Arithmetik zu haben, was nur möglich ist, wenn alle Ergebnisse im Bereich liegen0,...,(N-1)
, nicht-(N-1),...,(N-1)
.Ich habe keinen telepathischen Einblick in die Argumentation des Compilers, aber im Fall von
%
besteht die Notwendigkeit, mit negativen Werten umzugehen (und Divisionsrunden gegen Null), während&
das Ergebnis immer die unteren 8 Bits sind.Der
sar
Befehl klingt für mich wie "Arithmetik nach rechts verschieben" und füllt die frei gewordenen Bits mit dem Vorzeichenbitwert.quelle
Mathematisch gesehen ist Modulo wie folgt definiert:
a% b = a - b * Etage (a / b)
Dies hier sollte es für Sie klären. Wir können Floor für Ganzzahlen eliminieren, da die Ganzzahldivision dem Floor (a / b) entspricht. Wenn der Compiler jedoch einen allgemeinen Trick verwenden würde, wie Sie ihn vorschlagen, müsste er für alle a und alle b funktionieren. Dies ist leider nicht der Fall. Mathematisch gesehen ist Ihr Trick für vorzeichenlose Ganzzahlen zu 100% korrekt (ich sehe eine Antwort, dass vorzeichenbehaftete Ganzzahlen brechen, aber ich kann dies bestätigen oder leugnen, da -a% b positiv sein sollte). Können Sie diesen Trick jedoch für alle b machen? Wahrscheinlich nicht. Deshalb macht der Compiler das nicht. Wenn Modulo leicht als eine bitweise Operation geschrieben werden könnte, würden wir einfach eine Modulo-Schaltung wie für die Addition und die anderen Operationen hinzufügen.
quelle
-2.3
ist-3
, während, wenn Sie-2.3
auf eine ganze Zahl abschneiden , erhalten Sie-2
. Siehe en.wikipedia.org/wiki/Truncation . "Bei negativen Zahlen rundet das Abschneiden nicht in die gleiche Richtung wie die Bodenfunktion". Und das Verhalten von%
für negative Zahlen ist genau der Grund, warum das OP das beschriebene Verhalten sieht.