Wednesday 14 May 2008

Java里的移位

在Java里,移位都是基于int的。如果不是int,就转为int再移位。

0xff >> 2 -> 0x3f
0xff >> 4 -> 0x0f

之上都是对int的移位。

那对byte的移位呢,比如:((byte)0xff) >> 2 -> ?

会做以下3步:
1. int 0xff -> byte,byte 0xff就是byte的-1
2. 移位前byte需要转为int, byte 0xff -> int, 变成了int的-1,就是0xffffffff
3. 0xffffffff >> 2 --> 0xffffffff, >>是用符号位1补位

因此下列断言是正确的:
assertEquals(0xffffffff >> 2, ((byte)0xff) >> 2)
或者:
assertEquals(-1 >> 2, ((byte)0xff) >> 2)
或者:
assertEquals(-1, ((byte)0xff) >> 2); // 由于是1补位,-1怎么移位也是-1。

>>补位用的是符号位,也就是说,如果符号位为1,就用1,否则用0

>>>补位只用0。这是两者的区别。

那0xff >> 2 和((byte)0xff) >>> 2是否一样呢?答案是否定的。

这里还牵涉到byte和int相互转化的问题。((byte)0xff) >>> 2相当于:
1. int 0xff -> byte 0xff, byte 0xff = -1
2. 移位前byte需要转为int, byte 0xff -> int, 变成了int的-1,就是0xffffffff
3. 0xffffffff >> 2 --> 0x3fffffff, 0补位,和0xff >> 2 -> 0x3f不同。

关于byte转int,以下断言也是对的:
assertEquals(0xfffffff0, (int)((byte)0xf0))
相当于:
assertEquals(0xfffffff0, (byte)0xf0)
这是有符号位的情况。没符号位的情况:
assertEquals(0x0000000f, (int)((byte)0x0f))

byte的移位并没有太多含糊的地方,只是byte移位之前,会转为int;再加上byte的
符号位,会把人搞糊涂。

一个办法是在byte移位之前显示地转int,别让byte自己转。操作看起来会清晰些:
assertEquals(0x3f, ((byte)0xff)&0xff >> 2)
或者:
assertEquals(0x3f, ((byte)-1)&0xff >> 2)
用0xff并一下,这样byte的-1转为int就是255了。刚好和C里的无符号byte一致。

大部分教科书的移位是按C的无符号byte讲的。

这样这些教科书里的东西也可以用起来了。

No comments: