Il y a quelques jours, Anto, membre de StackExchange, s'est renseigné sur les utilisations valides des opérateurs bit à bit. J'ai déclaré que le décalage était plus rapide que la multiplication et la division d'entiers par des puissances de deux. Daemin, membre de StackExchange, a répliqué en déclarant que le décalage vers la droite présentait des problèmes avec les nombres négatifs.
À ce stade, je n'avais jamais vraiment pensé à utiliser les opérateurs de décalage avec des entiers signés. J'ai principalement utilisé cette technique dans le développement de logiciels de bas niveau; par conséquent, j'ai toujours utilisé des entiers non signés. C effectue des décalages logiques sur des entiers non signés. Aucune attention n'est accordée au bit de signe lors de l'exécution d'un décalage logique vers la droite. Les bits libérés sont remplis de zéros. Cependant, C effectue une opération de décalage arithmétique lors du décalage vers la droite d'un entier signé. Les bits libérés sont remplis du bit de signe. Cette différence fait arrondir une valeur négative vers l'infini au lieu d'être tronquée vers zéro, ce qui est un comportement différent de la division entière signée.
Quelques minutes de réflexion ont abouti à une solution de premier ordre. La solution convertit conditionnellement les valeurs négatives en valeurs positives avant le décalage. Une valeur est conditionnellement reconvertie dans sa forme négative après que l'opération de décalage a été effectuée.
int a = -5;
int n = 1;
int negative = q < 0;
a = negative ? -a : a;
a >>= n;
a = negative ? -a : a;
Le problème avec cette solution est que les instructions d'affectation conditionnelles sont généralement traduites en au moins une instruction de saut, et les instructions de saut peuvent être coûteuses sur les processeurs qui ne décodent pas les deux chemins d'instruction. Le fait de devoir réamorcer deux fois un pipeline d'instructions constitue une bonne réduction de tout gain de performance obtenu en déplaçant la division.
Cela dit, je me suis réveillé samedi avec la réponse au problème d'affectation conditionnelle. Le problème d'arrondi que nous rencontrons lors de l'exécution d'une opération de décalage arithmétique se produit uniquement lorsque nous travaillons avec la représentation du complément à deux. Cela ne se produit pas avec la représentation du complément. La solution au problème consiste à convertir une valeur de complément à deux en valeur de complément à un avant d'effectuer l'opération de décalage. Nous devons ensuite reconvertir la valeur du complément à un en valeur du complément à deux. Étonnamment, nous pouvons effectuer cet ensemble d'opérations sans convertir conditionnellement les valeurs négatives avant d'effectuer l'opération de décalage.
int a = -5;
int n = 1;
register int sign = (a >> INT_SIZE_MINUS_1) & 1
a = (a - sign) >> n + sign;
La valeur négative du complément à deux est convertie en valeur négative du complément à un en soustrayant un. D'un autre côté, la valeur négative du complément à un est convertie en valeur négative du complément à deux en ajoutant un. Le code répertorié ci-dessus fonctionne car le bit de signe est utilisé pour convertir le complément à deux en complément à un et vice versa . Seules les valeurs négatives auront leurs bits de signe définis; par conséquent, le signe variable sera égal à zéro lorsque a est positif.
Cela dit, pouvez-vous penser à d'autres hacks au niveau du bit comme celui ci-dessus qui ont fait partie de votre sac d'astuces? Quel est votre hack au niveau du bit préféré? Je suis toujours à la recherche de nouveaux hacks bit à bit axés sur les performances.