5

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

这看起来相当简单,所以任何人都可以向我解释为什么当给 bar 的值为 -128 时,这段代码会为 foo 生成 -2 的值:

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

这样做的目的是取一个 8 位字节,掩码最左边的 2 位,并将它们移到最右边的 2 位。IE:

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

结果实际上是-2,即

0b11111110

IE。1s 而不是 0s 被移到左边的位置

4

3 回答 3

8

这是因为 & 实际上正在执行提升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);

这样,您实际上只剩下您想要的两位,而不是在最重要的 24 位中加载 1。

于 2010-01-31T20:41:03.363 回答
2

AFAIK,在 Java 中,大多数运算符(+、-、>>、& 等)不能处理小于ints 的任何内容。因此,您的按位移动并将&值隐式转换到int后台,然后byte通过您在外部的显式转换返回。最后一次转换消除了高位中的零。

要获得您期望的结果,请尝试在ints 上执行此操作。

于 2010-01-31T20:50:00.990 回答
2

其他人已经告诉过你原因,但我会进一步分解并提供真正问题的答案。

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

由于 & 运算符会将所有内容提升为 int,因此本质上是:

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

如果 bar 为 -128,则 (int)bar 为 0xFFFFFF80,然后用 0xFFFFFFC0 进行 &'ed... 即:0xFFFFFF80,然后向右移动 6 个位置以获得:0x3FFFFFFE

正确答案非常简单:

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

bar 被提升为 int 用于 & 操作,因此该 int 中唯一剩下的将是原始字节的前两位。然后将其右移 6 位并将其转换回字节。

于 2010-01-31T20:51:43.770 回答