Поведение побитового 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 между двумя байтами, и результат будет байтом.

Другие вопросы по тегам