Je travaille sur la création d'une fonction de décalage à droite logique en C en utilisant uniquement des opérateurs au niveau du bit. Voici ce que j'ai
int logical_right_shift(int x, int n)
{
int size = sizeof(int); // size of int
// arithmetic shifts to create logical shift, return 1 for true
return (x >> n) & ~(((x >> (size << 3) - 1) << (size << 3) -1)) >> (n-1);
}
Cela fonctionne dans tous les cas, sauf si n = 0. J'ai essayé de trouver un moyen de résoudre le problème afin que cela fonctionne également pour n = 0, mais je suis coincé.
int lsr(int x, int n)
{
return (int)((unsigned int)x >> n);
}
Voici ce dont vous avez besoin:
int logical_right_shift(int x, int n)
{
int size = sizeof(int) * 8; // usually sizeof(int) is 4 bytes (32 bits)
return (x >> n) & ~(((0x1 << size) >> n) << 1);
}
Explique
x >> n
décale n bits
à droite. Toutefois, si x
est négatif, le bit de signe (bit le plus à gauche) sera copié à sa droite, par exemple:
Supposons que chaque int est 32 bits ici, laissezx = -2147483648 (10000000 00000000 00000000 00000000)
, puisx >> 1 = -1073741824 (11000000 00000000 00000000 00000000)
x >> 2 = -536870912 (11100000 00000000 00000000 00000000)
etc.
Nous devons donc effacer ces bits de signe supplémentaires lorsque n est négatif.
Supposez n = 5
ici:
0x1 << size
déplace 1
à la position la plus à gauche:
(10000000 00000000 00000000 00000000)
((0x1 << size) >> n) << 1
copie 1 dans ses voisins n-1
:
(11111000 00000000 00000000 00000000)
~((0x1 << size) >> n) << 1!
inverse tous les bits:
(00000111 11111111 11111111 11111111)
nous avons donc finalement obtenu un masque pour extraire ce dont nous avons vraiment besoin de x >> n
:
(x >> n) & ~(((0x1 << size) >> n) << 1)
l'opération &
fait l'affaire.
Et le coût total de cette fonction est de 6
opérations.
Stockez simplement votre int
dans un unsigned int
et exécutez >>
dessus.
(Le signe n'est pas étendu ni préservé si vous utilisez unsigned int)
Je pense que le problème est dans votre ">> (n-1)" partie. Si n est 0 alors la partie gauche sera décalée de -1. Donc, voici ma solution
int logical_right_shift(int x, int n)
{
int mask = ~(-1 << n) << (32 - n);
return ~mask & ( (x >> n) | mask);
}
Dérivé de implémentation par php du décalage logique vers la droite
function logical_right_shift( i , shift ) {
if( i & 2147483648 ) {
return ( i >> shift ) ^ ( 2147483648 >> ( shift - 1 ) );
}
return i >> shift;
}
Pour les plates-formes 32 bits uniquement.
La réponse de Milnex est excellente et a une explication géniale, mais la mise en œuvre a malheureusement échoué en raison du décalage de la taille totale. Voici une version de travail:
int logicalShift(int x, int n) {
int totalBitsMinusOne = (sizeof(int) * 8) - 1; // usually sizeof(int) is 4 bytes (32 bits)
return (x >> n) & ~(((0x1 << totalBitsMinusOne) >> n) << 1);
}
Pour avoir 1 comme bit le plus significatif et tous les zéros ailleurs, nous devons décaler 0x1
de number of bits - 1
. Je soumets ma propre réponse parce que ma modification de la réponse acceptée a été rejetée.
Comme avec le commentaire de @ Ignacio, je ne sais pas pourquoi vous voudriez le faire (sans simplement transtyper unsigned
comme dans les autres réponses), mais qu'en est-il (en supposant que le complément à deux et le binaire, et les décalages signés sont arithmétiques) :
(x >> n) + ((1 << (sizeof(int) * CHAR_BIT - n - 1)) << 1)
ou:
(x >> n) ^ ((INT_MIN >> n) << 1)
int logicalShift(int x, int n) {
int mask = x>>31<<31>>(n)<<1;
return mask^(x>>n);
}
Seulement pour 32 bits