web-dev-qa-db-fra.com

Implémentation d'un décalage logique à droite dans C

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é. 

15
Dan
int lsr(int x, int n)
{
  return (int)((unsigned int)x >> n);
}
28

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, laissez
x     = -2147483648 (10000000 00000000 00000000 00000000), puis
x >> 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.

12
mingyc

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)

http://en.wikipedia.org/wiki/Logical_shift

3
Bernd Elkemann

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); 
}
2
Levenson

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.

1
Ghulam Murtaza

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.

0
modulitos

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)
0
int logicalShift(int x, int n) {
  int mask = x>>31<<31>>(n)<<1;
  return mask^(x>>n);
}

Seulement pour 32 bits

0
Toch