¿Por qué la máscara Java cambia los operandos con 0x1F?

En Java:

(0xFFFFFFFF <<  1) = 0xFFFFFFFE = 0b1111111111111110
                :         :               :
(0xFFFFFFFF << 30) = 0xE0000000 = 0b1110000000000000
(0xFFFFFFFF << 30) = 0xC0000000 = 0b1100000000000000
(0xFFFFFFFF << 31) = 0x80000000 = 0b1000000000000000

Sin embargo:

(0xFFFFFFFF << 32) = 0xFFFFFFFF = 0b1111111111111111

Lógicamente, esto no tiene sentido, pero lo que creo que está sucediendo es que Java realice una operación similar a:

a << (b % Integer.SIZE) [editar, al parecer:]a << (b & 0x1F)

Esto aplica a>> y>>>, también.

Obviamente, desplazando en> = 32 (en el caso de un Integer) elimina todos los datos del tipo de datos, pero hay ocasiones en que esto es útil. Por ejemplo:

int value = 0x3F43F466; // any value
int shift = 17; // any value >= 0
int carry = value & (-1 << (Integer.SIZE - shift));
if (carry > 0)
    ; // code...

Por supuesto, esto puede solucionarse, pero encontrar estos errores puede llevar bastante tiempo (solo pasé horas rastreando uno similar). Entonces, mi pregunta:¿Hay alguna razón para no devolver el valor lógico? cuando se desplaza todos los bits hacia fuera?

ACTUALIZAR:

Intenté esto en C99, usando lo siguiente:

#include<stdio.h>
main()
{
   int i, val;
   for (i = 0; i <=36; i++) {
       val = (-1 << i);
       printf("%d :\t%d\n", i, val);
   }
}

Encontré que se comporta igual que Java, enmascarando.i & 0x1F, mientras que proporciona una advertencia en la compilación cuando se le da un valor constante:

warning: left shift count >= width of type

Respuestas a la pregunta(2)

Su respuesta a la pregunta