relacionales prioridad operadores operador condicionales asignacion java programming-languages bit-manipulation

prioridad - ¿Cuál es el propósito del operador de cambio a la derecha sin signo “>>>” en Java?



operadores relacionales en java (6)

Entiendo lo que hace el operador de turno derecho sin signo ">>>" en Java, pero ¿por qué lo necesitamos y por qué no necesitamos un operador de turno a la izquierda sin signo correspondiente?


Básicamente, esto tiene que ver con el signo (cambios numéricos) o los cambios sin firmar (normalmente cosas relacionadas con píxeles).

Dado que el desplazamiento a la izquierda, no se ocupa del bit de signo de todos modos, es lo mismo (<<< y <<) ...

De cualquier manera, todavía no conozco a nadie que necesite usar el >>>, pero estoy seguro de que están haciendo cosas increíbles.

Como acaba de ver, el operador >> rellena automáticamente el bit de orden superior con su contenido anterior cada vez que se produce un cambio. Esto conserva el signo del valor. Sin embargo, a veces esto es indeseable. Por ejemplo, si está cambiando algo que no representa un valor numérico, es posible que no desee que tenga lugar la extensión de signo. Esta situación es común cuando se trabaja con valores y gráficos basados ​​en píxeles. En estos casos, generalmente querrá cambiar un cero al bit de orden superior sin importar cuál fue su valor inicial. Esto se conoce como un turno sin firmar. Para lograr esto, utilizará el operador sin signo, shift-right de java, >>>, que siempre cambia los ceros al bit de orden superior.

Otras lecturas:

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

http://www.java-samples.com/showtutorial.php?tutorialid=60


El operador de cambio a la derecha firmado es útil si uno tiene un int que representa un número y uno desea dividirlo por una potencia de dos, redondeando hacia el infinito negativo. Esto puede ser bueno cuando se hacen cosas como escalar las coordenadas para mostrarlas; no solo es más rápido que la división, sino que las coordenadas que difieren por el factor de escala antes de escalar diferirán en un píxel después. Si en lugar de usar Shifting One usa división, eso no funcionará. Cuando se escala por un factor de dos, por ejemplo, -1 y +1 difieren en dos y, por lo tanto, deben diferir en uno después, pero -1 / 2 = 0 y 1/2 = 0. Si, por el contrario, uno utiliza un cambio a la derecha con signo, las cosas funcionan bien: -1 >> 1 = -1 y 1 >> 1 = 0, con valores de un píxel separados.

El operador sin signo es útil ya sea en los casos en que se espera que la entrada tenga exactamente un bit establecido y uno querrá que el resultado también lo haga, o en los casos en que uno usará un bucle para generar todos los bits de una palabra y quiere que termine limpiamente. Por ejemplo:

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

Si el código utilizara una operación de cambio a la derecha firmada y se pasara un valor negativo, generaría 1 de forma indefinida. Sin embargo, con el operador sin signo de cambio a la derecha, el bit más significativo termina siendo interpretado como cualquier otro.

El operador de turno a la derecha sin signo también puede ser útil cuando un cálculo, aritméticamente, arrojaría un número positivo entre 0 y 4,294,967,295 y uno desea dividir ese número por una potencia de dos. Por ejemplo, cuando se calcula la suma de dos valores int que se sabe que son positivos, uno puede usar (n1+n2)>>>1 sin tener que promover los operandos a long . Además, si uno desea dividir un valor int positivo por algo como pi sin usar matemática de punto flotante, se puede calcular ((value*5468522205L) >>> 34) [(1L << 34) / pi es 5468522204.61, que se redondea hasta rendimientos 5468522205]. Para los dividendos superiores a 1686629712, el cálculo del value*5468522205L arrojaría un valor "negativo", pero como se sabe que el valor aritméticamente correcto es positivo, el uso del desplazamiento a la derecha sin signo permitirá el uso del número positivo correcto.


En el dominio de Java, la mayoría de las aplicaciones típicas, la forma de evitar los desbordamientos es usar casting o Big Integer, como int a long en los ejemplos anteriores.

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))));


Un cambio a la derecha normal de un número negativo lo mantendrá negativo. Es decir, se conservará el bit de signo.

Un cambio a la derecha sin signo >>> también desplazará el bit de signo, reemplazándolo con un bit cero.

No es necesario tener el desplazamiento a la izquierda equivalente porque solo hay un bit de signo y es el bit de la izquierda, por lo que solo interfiere cuando se desplaza a la derecha.

Esencialmente, la diferencia es que uno conserva el bit de signo, el otro se desplaza en ceros para reemplazar el bit de signo.

Para los números positivos actúan de manera idéntica.

Para ver un ejemplo de uso de >> y >>> vea BigInteger shiftRight .


>>> es también la forma segura y eficiente de encontrar la media redondeada de dos enteros (grandes):

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

Si los enteros high y low están cerca del entero de la máquina más grande, lo anterior será correcto pero

int mid = (low + high) / 2;

puede obtener un resultado incorrecto debido al desbordamiento.

He aquí un ejemplo de uso , corregir un error en una búsqueda binaria ingenua.


El operador >>> permite tratar int y long como tipos integrales sin firma de 32 y 64 bits, que faltan en el lenguaje Java.

Esto es útil cuando cambia algo que no representa un valor numérico. Por ejemplo, podría representar una imagen de mapa de bits en blanco y negro utilizando int s de 32 bits, donde cada int codifica 32 píxeles en la pantalla. Si necesita desplazar la imagen hacia la derecha, preferiría que los bits a la izquierda de un int conviertan en ceros, de modo que pueda colocar fácilmente los bits de los int s adyacentes:

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; }

El código anterior no presta atención al contenido de los tres bits superiores, porque el operador >>> hace

No hay un operador << correspondiente porque las operaciones de desplazamiento a la izquierda en los tipos de datos firmados y sin firmar son idénticos.