Странность Java бит-смещения - PullRequest
5 голосов
/ 31 января 2010

Java имеет 2 оператора сдвига битов для правых сдвигов:

>> shifts right, and is dependant on the sign bit for the sign of the result

>>> shifts right and shifts a zero into leftmost bits

http://java.sun.com/docs/books/tutorial/java/nutsandbolts/op3.html

Это кажется довольно простым, поэтому кто-нибудь может объяснить мне, почему этот код, когда ему дано значение -128 для бара, выдает значение -2 для foo:

byte foo = (byte)((bar & ((byte)-64)) >>> 6);

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

initial = 0b10000000 (-128)
-64 = 0b11000000
initial & -64 = 0b10000000
0b10000000 >>> 6 = 0b00000010

Результат на самом деле равен -2, то есть

0b11111110

Т.е.. 1, а не нули сдвинуты влево

Ответы [ 3 ]

8 голосов
/ 31 января 2010

Это потому, что & на самом деле выполняет повышение до int - что оставляет очень много «1» битов. Затем вы сдвигаетесь вправо, оставляя 2 самых левых бита равными 0, но затем игнорируете эти самые левые биты, возвращая их обратно в байт.

Это становится понятнее, когда вы отделяете операции:

public class Test
{
    public static void main(String[] args)
    {
        byte bar = -128;
        int tmp = (bar & ((byte)-64)) >>> 6;
        byte foo = (byte)tmp;
        System.out.println(tmp);
        System.out.println(foo);
    }
}

печать

67108862
-2

Итак, снова сделайте свою битовую арифметику:

initial = 0b10000000 (-128)
-64 = 0b11000000
initial & -64 = 0b11111111111111111111111110000000 // it's an int now
0b10000000 >>> 6 = 0b00111111111111111111111111100000 // note zero-padding
(byte) (0b10000000 >>> 6) = 11100000 // -2

Даже если вы получите правильный результат операции & (приведя к этому моменту), >>> в любом случае переведет первый операнд на int первый.

РЕДАКТИРОВАТЬ: решение состоит в том, чтобы изменить, как вы маскируете вещи. Вместо маскировки на -64 замаскируйте ее просто 128 + 64 = 192 = 0xc0:

byte foo = (byte)((bar & 0xc0) >>> 6);

Таким образом, вы действительно останетесь только с двумя битами, которые вам нужны, вместо того, чтобы иметь нагрузку 1 с в старших 24 битах.

2 голосов
/ 31 января 2010

Другие говорили, почему, но я разберу его дальше и дам ответ на реальную проблему.

byte foo = (byte)((bar & ((byte)-64)) >>> 6);

Так как оператор & будет продвигать все в int, то по сути это то, что он делает:

byte foo = (byte)(((int)bar & (int)((byte)-64)) >>> 6);

Если бар равен -128, тогда (int) бар равен 0xFFFFFF80, который затем & 0 редактируется с 0xFFFFFFC0 ... который равен: 0xFFFFFF80, затем вы сдвигаете это право на 6 мест, чтобы получить: 0x3FFFFFFE

Правильный ответ действительно прост:

byte foo = (byte)((bar & 0xC0) >> 6);

bar переводится в int для операции &, поэтому единственное, что остается в этом int, это верхние два бита исходного байта. Затем сдвиньте его вправо на 6 бит и преобразуйте обратно в байты.

2 голосов
/ 31 января 2010

AFAIK, в Java большинство операторов (+, -, >> и т. Д.) Не могут работать с чем-либо меньшим, чем int s. Итак, ваши побитовые сдвиги и & неявно преобразуют значения в int в фоновом режиме, а затем обратно в byte с помощью явного приведения вне. Последнее приведение избавляет от нулей в старших битах.

Чтобы получить ожидаемые результаты, попробуйте сделать это на int с.

...