Java бит-манипуляция

byte x = -1;
for(int i = 0; i < 8; i++)
{
    x = (byte) (x >>> 1);
    System.out.println("X: " + x);
}

Как я понимаю, java хранит данные в 2'-дополнении, что означает -1 = 11111111 (согласно wikipedia).

Кроме того, из java-документов: "Битовая диаграмма задается левым операндом, а количество позиций сдвигается правым операндом. Беззнаковый оператор сдвига вправо" → > "сдвигает ноль в крайнее левое положение, а крайняя левая позиция после" → " зависит от расширения знака.

Это означает, что → > будет каждый раз смещать 0 влево. Поэтому я ожидаю, что этот код будет

Итерация: бит-представление x

0: 11111111

1: 01111111

2: 00111111

3: 00011111

... так далее

Однако мой вывод всегда X: -1, что означает (я думаю), что → > помещает бит знака в крайнее левое положение. Поэтому я затем попробую → , и тот же результат.

Что происходит? Я ожидал бы, что мой вывод будет: X: -1, x: 127, x: 63 и т.д.

Ответ 1

Тот, кто думал, что эти байты должны быть подписаны, когда Java был изобретен, должен быть вынут и избит мокрой палочкой сельдерея, пока они не кричат: -)

Вы можете делать то, что хотите, отбрасывая int и гарантируя, что вы никогда не переместите 1 в верхний бит, примерно так:

byte x = -1;
int x2 = ((int)x) & 0xff;
for(int i = 0; i < 8; i++)
{
    x2 = (x2 >>> 1);
    System.out.println("X: " + x2);
}

Ваша конкретная проблема заключается в том, что → > перебрасывает int, чтобы выполнить смену, тогда вы возвращаете его обратно в байт, как показано ниже:

byte x = -1;
int x2 = ((int)x) & 0xff;
int x3;
int x4 = x2;
for(int i = 0; i < 8; i++)
{
    x2 = (x2 >>> 1);
    System.out.println("X2: " + x2);
    x3 = (x >>> 1);
    x = (byte)x3;
    x4 = (x4 >>> 1);
    System.out.println("X: " + x3 + " " + x + " " + x4);
}

Какие выходы:

X2: 127
X: 2147483647 -1 127
X2: 63
X: 2147483647 -1 63
X2: 31
X: 2147483647 -1 31
X2: 15
X: 2147483647 -1 15
X2: 7
X: 2147483647 -1 7
X2: 3
X: 2147483647 -1 3
X2: 1
X: 2147483647 -1 1
X2: 0
X: 2147483647 -1 0

Вы можете ясно видеть, что x и x3 не работают (хотя x3 сдвигается правильно, отбрасывая его обратно в байты в x, он снова устанавливает его в -1). x4 отлично работает.

Ответ 2

Помните, что:

  • операнды побитовых операций всегда продвигаются как минимум к int!
  • листы всегда включают расширение знака.

Итак, когда вы делаете (x → > n), даже если вы определили x как байт, для целей сдвига он будет сначала преобразован в int. Если преобразованный байт отрицателен, то все "дополнительные биты" (так, самые левые 24 бита результирующего int), добавленные, чтобы сделать его до int, будут установлены в 1. Или иначе, если исходный байт был -1, вещь, которую вы на самом деле переводите, равна -1 в качестве int, то есть 32-разрядное число со всеми 32 битами, установленными в 1. Сдвиг этого права на 1-8 мест будет по-прежнему приводить к тому, что нижние 8 бит будут установлены до 1, поэтому, когда вы возвращаете байт, вы получаете байт со всеми 8 битами, установленными в 1, или, другими словами, байтовое значение -1. ​​

Ответ 3

Я не уверен в этом. Но, я думаю, что

x >>> 1 

передается в int из байта, потому что буква "1" является int. Тогда то, что вы наблюдаете, имеет смысл.

Ответ 4

Я не знаю, почему это не работает, но простой способ очистить верхний бит - это и с (двоичным) 0111111:

x = (byte) (x >>> 1) & 0x7F;

Ответ 5

Проблема заключается в том, что, как говорилось ранее (давным-давно), x перебрасывается в int (sign-extended) перед выполнением сдвига.
Выполнение преобразования "бит-бит" должно помочь:

byte x = -1;
for(int i = 0; i < 8; i++)
{
    x = (byte) ((x & 0xFF) >>> 1);
    System.out.println("X: " + x);
}