Поведение побитового xor с использованием двоичных литералов
Мне любопытно узнать, что на самом деле происходит при побитовом сравнении с использованием двоичных литералов. Я просто наткнулся на следующую вещь:
byte b1 = (new Byte("1")).byteValue();
// check the bit representation
System.out.println(String.format("%8s", Integer.toBinaryString(b1 & 0xFF)).replace(' ', '0'));
// output: 00000001
System.out.println(b1 ^ 0b00000001);
// output: 0
Так что все ведет себя как положено, xor
сравнение равно 0
, Однако при попытке сделать то же самое с отрицательным числом это не сработает:
byte b2 = (new Byte("-1")).byteValue();
// check the bit representation
System.out.println(String.format("%8s", Integer.toBinaryString(b2 & 0xFF)).replace(' ', '0'));
// output: 11111111
System.out.println(b2 ^ 0b11111111);
// output: -256
Я бы ожидал, что последний xor
сравнение также равно 0
, Однако это только в том случае, если я делаю явное приведение двоичного литерала к byte
:
byte b2 = (new Byte("-1")).byteValue();
// check the bit representation
System.out.println(String.format("%8s", Integer.toBinaryString(b2 & 0xFF)).replace(' ', '0'));
// output: 11111111
System.out.println(b2 ^ (byte)0b11111111);
// output: 0
Для меня это выглядит так до xor
сравнение обоих b1
а также 0b11111111
имеют одинаковое представление битов, так что даже если они приводятся к int
(или что-то еще) xor
все равно должен быть равен 0
, Как вы пришли к результату -256
который 11111111 11111111 11111111 00000000
в двоичном представлении? Почему я должен сделать явное приведение к byte
чтобы получить 0
?
2 ответа
Двоичные литералы без определенного преобразования представляют 32-разрядные целочисленные значения, независимо от количества цифр. Например 0b00000001
это сокращение для 0b00000000 00000000 00000000 00000001
,
Побитовые сравнения в Java используют двоичное числовое продвижение (см. Javadocs). В данном конкретном случае это означает, что оба операнда преобразуются в int
перед выполнением сравнения.
0b11111111
уже представляет int
(без ведущих 0
с) и просто представляет 0b00000000 00000000 00000000 11111111
, в то время как b2
это байт, представляющий значение -1
, Во время преобразования в int
значение сохраняется и, таким образом, b2
приводится к 32-разрядному целому числу, представляющему то же число (-1
): 0b11111111 11111111 11111111 11111111
,
xor
затем оценивает 0b11111111 11111111 11111111 00000000
который является 32-битным двоичным представлением -256
,
В случае, если xor
сравнение выполняется с использованием (byte)0b11111111
двоичный литерал также будет обрабатываться как байт и, таким образом, эквивалентно приведен к 32-разрядному целому числу, представляющему -1
,
Важно отметить, что бинарные сравнения выполняются либо с double, float, long
или же int
(как указано в Javadocs). Если в сравнении участвуют только другие типы (например, byte
) они будут преобразованы в int
, Вот почему следующий фрагмент кода выдаст ошибку компиляции:
byte b1 = (byte)0b00000001;
byte b2 = (byte)0b00000001;
byte b3 = b1 & b2;
>>> error: incompatible types: possible lossy conversion from int to byte
... потому что результат побитового сравнения двух byte
является int
,
Дальнейшее чтение о том, почему можно сделать здесь:
Когда вы используете b1 ^ 0b11111111
вы на самом деле делаете xor
между байтом до целогоbyte
переменная 8 бит в то время как int
это 32-битное число. Итак, что вы сделали, это:b1 ^ 0b(00000000 00000000 00000000 11111111)
поэтому, когда вы используете xor
между byte
(с дополнительными 1 с перед тем, как использовать его с целыми числами. 1 с, потому что это отрицательное число. Если бы оно было положительным, это было бы 0 с) и int
результат будет целым числом, а в вашем случае -256.
Когда вы разыгрываете целое число в byte
ты используешь xor
между двумя байтами, и результат будет байтом.