web-dev-qa-db-fra.com

À quoi sert l'opérateur de décalage à droite non signé ">>>" en Java?

Je comprends ce que l'opérateur de décalage à droite non signé ">>>" dans Java le fait, mais pourquoi en avons-nous besoin, et pourquoi n'avons-nous pas besoin d'un opérateur de décalage à gauche non signé correspondant?

29
Tyler Durden

L'opérateur >>> Vous permet de traiter int et long comme des types intégraux 32 et 64 bits non signés, qui manquent dans Java langue.

Ceci est utile lorsque vous déplacez quelque chose qui ne représente pas une valeur numérique. Par exemple, vous pouvez représenter une image bitmap en noir et blanc à l'aide de ints 32 bits, où chaque int code 32 pixels à l'écran. Si vous devez faire défiler l'image vers la droite, vous préféreriez que les bits à gauche d'un int deviennent des zéros, afin que vous puissiez facilement mettre les bits des ints adjacents:

 int shiftBy = 3;
 int[] imageRow = ...
 int shiftCarry = 0;
 // The last shiftBy bits are set to 1, the remaining ones are zero
 int mask = (1 << shiftBy)-1;
 for (int i = 0 ; i != imageRow.length ; i++) {
     // Cut out the shiftBits bits on the right
     int nextCarry = imageRow & mask;
     // Do the shift, and move in the carry into the freed upper bits
     imageRow[i] = (imageRow[i] >>> shiftBy) | (carry << (32-shiftBy));
     // Prepare the carry for the next iteration of the loop
     carry = nextCarry;
 }

Le code ci-dessus ne fait pas attention au contenu des trois bits supérieurs, car l'opérateur >>> Les fait

Il n'y a pas d'opérateur << Correspondant car les opérations de décalage vers la gauche sur les types de données signés et non signés sont identiques.

26
dasblinkenlight

>>> est également le moyen sûr et efficace de trouver la moyenne arrondie de deux (grands) entiers:

int mid = (low + high) >>> 1;

Si les entiers high et low sont proches du plus grand entier de la machine, ce qui précède sera correct mais

int mid = (low + high) / 2;

peut obtenir un mauvais résultat en raison d'un débordement.

Voici un exemple d'utilisation , corrigeant un bug dans une recherche binaire naïve.

13
Meng Lu

Fondamentalement, cela a à voir avec le signe (décalages numériques) ou les décalages non signés (normalement des éléments liés aux pixels).

Depuis le décalage à gauche, ne s'occupe pas du bit de signe de toute façon, c'est la même chose (<<< et <<) ...

Quoi qu'il en soit, je n'ai encore rencontré personne qui avait besoin d'utiliser le >>>, mais je suis sûr qu'ils font des choses incroyables.

Comme vous venez de le voir, l'opérateur >> remplit automatiquement le bit de poids fort avec son contenu précédent chaque fois qu'un décalage se produit. Cela préserve le signe de la valeur. Cependant, cela n'est parfois pas souhaitable. Par exemple, si vous déplacez quelque chose qui ne représente pas une valeur numérique, vous ne souhaiterez peut-être pas que l'extension de signe ait lieu. Cette situation est courante lorsque vous travaillez avec des valeurs et des graphiques basés sur des pixels. Dans ces cas, vous souhaiterez généralement déplacer un zéro dans le bit de poids fort, quelle que soit sa valeur initiale. Ceci est connu comme un changement non signé. Pour ce faire, vous utiliserez l'opérateur non signé et décalé vers la droite de >>>, qui déplace toujours les zéros dans le bit de poids fort.

Lectures complémentaires:

http://henkelmann.eu/2011/02/01/Java_the_unsigned_right_shift_operator

http://www.Java-samples.com/showtutorial.php?tutorialid=6

3
Dory Zidon

L'opérateur de décalage à droite signé est utile si l'on a un int qui représente un nombre et que l'on souhaite le diviser par une puissance de deux, en arrondissant vers l'infini négatif. Cela peut être agréable lorsque vous faites des choses comme la mise à l'échelle des coordonnées pour l'affichage; non seulement elle est plus rapide que la division, mais les coordonnées qui diffèrent par le facteur d'échelle avant la mise à l'échelle différeront d'un pixel par la suite. Si au lieu d'utiliser le décalage, on utilise la division, cela ne fonctionnera pas. Lors d'une mise à l'échelle par un facteur de deux, par exemple, -1 et +1 diffèrent de deux, et devraient donc différer de un par la suite, mais -1/2 = 0 et 1/2 = 0. Si à la place on utilise le décalage à droite signé, les choses fonctionnent bien: -1 >> 1 = -1 et 1 >> 1 = 0, produisant correctement des valeurs à un pixel d'intervalle.

L'opérateur non signé est utile soit dans les cas où soit l'entrée est censée avoir exactement un bit défini et l'autre voudra que le résultat le fasse également, soit dans les cas où l'on utilisera une boucle pour sortir tous les bits dans un mot et veut qu'il se termine proprement. Par exemple:

void processBitsLsbFirst(int n, BitProcessor whatever)
{
  while(n != 0)
  {
    whatever.processBit(n & 1);
    n >>>= 1;
  }
}

Si le code devait utiliser une opération de décalage à droite signée et recevait une valeur négative, il produirait 1 indéfiniment. Avec l'opérateur de décalage à droite non signé, cependant, le bit le plus significatif finit par être interprété comme n'importe quel autre.

L'opérateur de décalage à droite non signé peut également être utile lorsqu'un calcul donnerait, arithmétiquement, un nombre positif compris entre 0 et 4 294 967 295 et que l'on souhaite diviser ce nombre par une puissance de deux. Par exemple, lors du calcul de la somme de deux int valeurs connues pour être positives, on peut utiliser (n1+n2)>>>1 sans avoir à promouvoir les opérandes en long. De plus, si l'on souhaite diviser une valeur positive de int par quelque chose comme pi sans utiliser de mathématiques à virgule flottante, on peut calculer ((value*5468522205L) >>> 34) [(1L << 34)/pi est 5468522204.61, ce qui a arrondi les rendements 5468522205]. Pour les dividendes supérieurs à 1686629712, le calcul de value*5468522205L donnerait une valeur "négative", mais comme la valeur arithmétiquement correcte est connue pour être positive, l'utilisation du décalage vers la droite non signé permettrait d'utiliser le nombre positif correct.

3
supercat

Un décalage à droite normal >> d'un nombre négatif le maintiendra négatif. C'est à dire. le bit de signe sera conservé.

Un décalage à droite non signé >>> décale également le bit de signe, le remplaçant par un bit zéro.

Il n'est pas nécessaire d'avoir le décalage gauche équivalent car il n'y a qu'un bit de signe et c'est le bit le plus à gauche, donc il n'interfère que lors du décalage vers la droite.

Essentiellement, la différence est que l'un préserve le bit de signe, l'autre change de zéros pour remplacer le bit de signe.

Pour les nombres positifs, ils agissent de manière identique.

Pour un exemple d'utilisation des deux >> et >>> voir BigInteger shiftRight .

1
OldCurmudgeon

Dans le domaine Java applications les plus courantes, le moyen d'éviter les débordements consiste à utiliser la conversion ou Big Integer, comme int to long dans les exemples précédents.

int hiint = 2147483647;
System.out.println("mean hiint+hiint/2 = " + ( (((long)hiint+(long)hiint)))/2);
System.out.println("mean hiint*2/2 = " + ( (((long)hiint*(long)2)))/2);

BigInteger bhiint = BigInteger.valueOf(2147483647);
System.out.println("mean bhiint+bhiint/2 = " + (bhiint.add(bhiint).divide(BigInteger.valueOf(2))));
0
tslate