Поведение без знакового сдвига вправо для байтовой переменной
Рассмотрим следующий снимок java-кода
byte b=(byte) 0xf1;
byte c=(byte)(b>>4);
byte d=(byte) (b>>>4);
выход:
c=0xff
d=0xff
ожидаемый вывод:
c=0x0f
как?
как b в двоичном 1111 0001
после беззнакового сдвига вправо 0000 1111
следовательно 0x0f
, но почему это 0xff
как?
Ответы
Ответ 1
Проблема заключается в том, что перед началом операции сдвига все аргументы сначала продвигаются до int
:
byte b = (byte) 0xf1;
b
, поэтому его значение равно -15.
byte c = (byte) (b >> 4);
b
сначала расшифровывается знаком до целого числа -15 = 0xfffffff1
, а затем сдвигается вправо до 0xffffffff
и усекается до 0xff
приложением byte
.
byte d = (byte) (b >>> 4);
b
сначала расшифровывается знаком до целого числа -15 = 0xfffffff1
, а затем сдвигается вправо до 0x0fffffff
и усекается до 0xff
приложением byte
.
Вы можете сделать (b & 0xff) >>> 4
, чтобы получить желаемый эффект.
Ответ 2
Догадываюсь, что перед изменением знак b
будет расширен до int
.
Итак, это может сработать так, как ожидалось:
(byte)((0x000000FF & b)>>4)
Ответ 3
В соответствии с Побитовые и операторы сдвига бит:
Беззнаковый оператор сдвига вправо " → > " сдвигает нуль в крайнее левое положение, а крайняя левая позиция после " → " зависит от расширения знака.
Итак, с b >> 4
вы преобразуете 1111 0001
в 1111 1111
(b отрицательно, поэтому он добавляет 1
), который равен 0xff
.
Ответ 4
Java пытается сэкономить на явной поддержке базовых типов без знака, вместо этого определяя два разных оператора сдвига.
В этом вопросе речь идет о беззнаковом сдвиге справа, но в примерах используются как (подписанные, так и неподписанные) и отображается значение подписанного сдвига ( → ).
Ваши вычисления были бы правильными для беззнакового сдвига ( → > ).