Почему Java беззнаковое смещение бит для отрицательного байта настолько странно?

95
13

У меня есть байтовая переменная:


byte varB = (byte) -1; // binary view: 1111 1111

Я хочу увидеть два самых левых бита и сделать unsigned right shift из 6 цифр:


varB = (byte) (varB >>> 6);

Но я получаю -1, как если бы это был тип int, и получив 3, только если я перейду на 30!


Как я могу обойти это и получить результат только с 6-значным сдвигом?

спросил(а) 2020-04-04T03:49:52+03:00 3 месяца назад
1
Решение
117

Причиной является расширение знака, связанное с числовой рекламой, до int, которая возникает при смещении бит. Перед сдвигом значение varB увеличивается до int. Беззнаковый сдвиг бит вправо происходит, но его эффекты отбрасываются при возврате к byte, который сохраняет только последние 8 бит:


varB (byte)     : 11111111
promoted to int : 11111111 11111111 11111111 11111111
shift right 6 : 00000011 11111111 11111111 11111111
cast to byte : 11111111

Вы можете использовать побитовый и оператор & для маскировки нежелательных бит перед переносом. Биттинг с 0xFF содержит только 8 наименее значимых бит.

varB = (byte) ((varB & 0xFF) >>> 6);

Вот что происходит сейчас:


varB (byte)     : 11111111
promoted to int : 11111111 11111111 11111111 11111111
bit-and mask : 00000000 00000000 00000000 11111111
shift right 6 : 00000000 00000000 00000000 00000011
cast to byte : 00000011

ответил(а) 2020-04-04T04:04:28.160311+03:00 3 месяца назад
87

Потому что это то, как сдвиг для байтов в java определяется на языке: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.19.


Суть в том, что типы, меньшие, чем int, постепенно расширяются до int, сдвигаются, а затем сужаются назад.


Что делает вашу единственную строку эффективным эквивалентом:

byte b = -1;      // 1111_1111
int temp = b; // 1111_1111_1111_1111_1111_1111_1111_1111
temp >>>= 6; // 0000_0011_1111_1111_1111_1111_1111_1111
b = (byte) temp; // 1111_1111

Чтобы сместить только байт, вам нужно явно сделать расширенное преобразование с помощью неподписанной семантики (и сужение конверсии должно быть также и вручную):


byte b = -1;          // 1111_1111
int temp = b & 0xFF; // 0000_0000_0000_0000_0000_0000_1111_1111
temp >>>= 6; // 0000_0000_0000_0000_0000_0000_0000_0011
b = (byte) temp; // 0000_0011

ответил(а) 2020-04-04T03:49:52+03:00 3 месяца назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема