J'ai toujours supposé qu'en faisant (a % 256)
l'optimiseur utiliserait naturellement une opération efficace au niveau du bit, comme si j'écrivais (a & 0xFF)
.
Lors du test sur l'explorateur de compilateur 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
Et en essayant l'autre code:
// Type your code here, or load an example.
int mod(int num) {
return num & 0xFF;
}
mod(int):
movzx eax, dil
ret
Il semble que je manque complètement quelque chose. Des idées?
c++
optimization
Elad Weiss
la source
la source
%
n'est pas non&
plus.num
ouiunsigned
?Réponses:
Ce n'est pas la même chose. Essayez
num = -79
, et vous obtiendrez des résultats différents des deux opérations.(-79) % 256 = -79
, tandis que(-79) & 0xff
c'est un certain nombre positif.En utilisant
unsigned int
, les opérations sont les mêmes et le code sera probablement le même.PS - Quelqu'un a commenté
Ce n'est pas ainsi que cela est défini en C, C ++, Objective-C (c'est-à-dire tous les langages où le code de la question se compilerait).
la source
Réponse courte
-1 % 256
cède-1
et non255
ce qui est-1 & 0xFF
. Par conséquent, l'optimisation serait incorrecte.Longue réponse
C ++ a la convention que
(a/b)*b + a%b == a
, ce qui semble tout à fait naturel.a/b
renvoie toujours le résultat arithmétique sans la partie fractionnaire (tronquée vers 0). Par conséquent,a%b
a le même signe quea
ou vaut 0.La division
-1/256
cède0
et-1%256
doit donc être-1
pour satisfaire la condition ci-dessus ((-1%256)*256 + -1%256 == -1
). C'est évidemment différent de-1&0xFF
ce qui est0xFF
. Par conséquent, le compilateur ne peut pas optimiser comme vous le souhaitez.La section pertinente de la norme C ++ [expr.mul §4] à partir de N4606 déclare:
Activer l'optimisation
Cependant, en utilisant des
unsigned
types, l'optimisation serait complètement correcte , satisfaisant la convention ci-dessus:Voyez aussi ceci .
Autres langues
Ceci est géré de manière très différente selon les langages de programmation, comme vous pouvez le rechercher sur Wikipedia .
la source
Depuis C ++ 11,
num % 256
doit être non positif sinum
est négatif.Ainsi, le modèle de bits dépendrait de l'implémentation des types signés sur votre système: pour un premier argument négatif, le résultat n'est pas l'extraction des 8 bits les moins significatifs.
Ce serait une autre question si
num
dans votre cas étaitunsigned
: ces jours-ci, je m'attendrais presque à ce qu'un compilateur fasse l'optimisation que vous citez.la source
num
est négatif, alorsnum % 256
est zéro ou négatif (c'est-à-dire non positif).(-250+256)%256==6
, mais(-250%256)+(256%256)
doit être, selon la norme, "non positif", et donc pas6
. Briser l'associativité comme celle-ci a des effets secondaires réels: par exemple, lors du calcul du "zoom arrière", le rendu en coordonnées entières, il faut décaler l'image pour que toutes les coordonnées soient non négatives.(128+128)%256==0
mais(128%256)+(128%256)==256
. Il y a peut-être une bonne objection au comportement spécifié, mais je ne suis pas sûr que ce soit celui que vous avez dit.256==0
. La clé est d'avoir exactementN
les valeurs possibles enN
arithmétique modulo , ce qui n'est possible que si tous les résultats sont dans la plage0,...,(N-1)
, non-(N-1),...,(N-1)
.Je n'ai pas d'aperçu télépathique du raisonnement du compilateur, mais dans le cas où
%
il y a la nécessité de traiter des valeurs négatives (et la division arrondit vers zéro), alors que&
le résultat est toujours les 8 bits inférieurs.L'
sar
instruction me semble "décaler l'arithmétique vers la droite", remplissant les bits vides avec la valeur du bit de signe.la source
Mathématiquement parlant, modulo est défini comme suit:
a% b = a - b * plancher (a / b)
Ce droit ici devrait éclaircir pour vous. Nous pouvons éliminer le plancher pour les nombres entiers car la division entière est équivalente au plancher (a / b). Cependant, si le compilateur devait utiliser une astuce générale comme vous le suggérez, il devrait fonctionner pour tout a et tout b. Malheureusement, ce n'est tout simplement pas le cas. Mathématiquement parlant, votre astuce est correcte à 100% pour les entiers non signés (je vois une réponse indique que les entiers signés cassent mais je peux le confirmer ou le nier car -a% b devrait être positif). Cependant, pouvez-vous faire cette astuce pour tous les b? Probablement pas. C'est pourquoi le compilateur ne le fait pas. Après tout, si modulo était facilement écrit comme une opération au niveau du bit, alors nous ajouterions simplement un circuit modulo comme pour l'addition et les autres opérations.
la source
-2.3
est-3
, tandis que si vous tronquez-2.3
à un entier, vous obtenez-2
. Voir en.wikipedia.org/wiki/Truncation . "pour les nombres négatifs, la troncature ne s'arrondit pas dans le même sens que la fonction de plancher". Et le comportement de%
pour les nombres négatifs est précisément la raison pour laquelle l'OP voit le comportement décrit.