¿Quiero una explicación para los operadores de desplazamiento a la derecha sin signo en números hexadecimales en Java?

Estoy reescribiendo mi respuesta porque el problema es más sutil de lo que había pensado originalmente. Dejo mi respuesta original a continuación porque es interesante (al menos para mí) pero no es la respuesta correcta a la pregunta.

La pregunta es realmente sobre las reglas de extensión de tipo de Java. Los operadores >> y >>> toman int s, no byte sy los valores dados al operador se extienden a int s antes de la operación.

Entonces, en la computación c, el byte b se extiende a un int . Ese aumento de tamaño es una operación firmada, entonces (byte) 0xf1 == (int) 0xfffffff1. El valor de c es 0xfffffff1 >> 4 == 0xffffffff == -1.

El cálculo de d es confuso. De nuevo, b es un signo extendido a 0xfffffff1. El turno sin signo produce 0x00ffffff. Pero la conversión de nuevo a un byte produce 0xff == -1 nuevamente. Eso no es lo que esperarías si la operación se hiciera realmente en el byte , pero no lo es.

Para obtener lo que desea, debe tratar a la fuerza el int para eliminar la extensión del signo. Lo obtienes por bitx 0xff, que es un número entero igual a 0x000000ff. Entonces b & 0xff = 0x000000f1, y b & 0xff >> 4 == 0x0000000f = 15.

Esto es desagradable, y otra razón más para que los programadores no jueguen tratando de exprimir cosas en valores de byte individuales. Este es el tipo de cosa que te morderá cada vez.

———————————————————————-
Interesante pero menos relevante digresión original en operadores de turno, en términos aritméticos en lugar de Java:

Los operadores de desplazamiento a la derecha >> y >>> trabajan en bits. Es fácil ir y venir entre binario y hexadecimal, ya que cada dígito hexadecimal se traduce en 4 bits. Entonces:

0xf1 == 1111 0001

desde hexadecimal F = 15 decimal = 1111 binario, y 1 es 0001 binario.

Desplazar a la derecha por 1 mueve todos los bits por 1:

0xf1 >>> 1 == _111 1001 binario = 0x79

El _ es un poco que he dejado fuera; Volveré a eso en un segundo. Cambiar por 4 significa cortar exactamente 1 dígito hexadecimal:

0xf1 >>> 4 == ____ 1111 binario = 0xf hexadecimal

En realidad, hay dos operadores de desplazamiento a la derecha diferentes: con signo (>>) y sin signo (>>>). La diferencia está en lo que va en el _. Para el operador firmado, repite lo que haya allí. Para el operador sin signo, pone a cero:

0xf1 >>> 4 == 0000 1111 binario == 0x0f
0xf1 >> 4 == 1111 1111 binario == 0xff == -1 decimal

El -1 puede parecer un poco sorprendente, pero la forma en que Java interpreta los números a 1 en el bit más a la izquierda significa un número negativo. (Esto se llama “complemento de dos, y dejaré los detalles para otra pregunta.” Desplazarse a la derecha por un lugar puede interpretarse como dividir por 2, siempre que conserve el signo. Entonces:

0xf1 = 1111 0001 = -15 decimal
0xf1 >> 1 == 1111 1000 = -8 decimal
0xf1 >> 2 == 1111 1100 = -4 decimal
0xf1 >> 3 == 1111 1110 = -2 decimal
0xf1 >> 4 = = 1111 1111 = -1 decimal

Tenga en cuenta que esto funciona solo mientras 0xf1 se interprete como un byte. Si dejara la conversión a un byte, se interpretaría como un entero: 0x000000f1, y el bit más a la izquierda sería 0 en lugar de 1.

Si quieres firmar o no firmar es una cuestión de por qué estás haciendo el turno. Si desea la división por potencias de dos, trabajando en números negativos, trabaje con >>. Si solo desea manipular los bits y prefiere que los bits se desplacen a cero, use >>>.

(Nota: todo esto es algo esotérico y ya no es tan importante. No use >> 1 para dividir entre dos: no es más rápido y hace que su código sea confuso. Esa es una resaca de The Good Old Days , cuando los compiladores no se optimizaron tan bien como lo hacen hoy. Hoy, este ejercicio trata más sobre la enseñanza de la aritmética bit a bit de los estudiantes de CS; en realidad no es una cuestión práctica. Las operaciones bit a bit todavía se realizan para las banderas, que deben hacerse sin firmar de hecho, probablemente no debería hacerse con tanta frecuencia como se hace).