Java位运算符:>>>和<<<<<移位

8
为什么如果
int x = -1 // binary: 11111111111111111111111111111111
x = x >>> 31; 

我们有00000000000000000000000000000001

但是如果

int x = -1
x = x >>> 32;

我们有11111111111111111111111111111111(再次减去1)

但不是00000000000000000000000000000000吗?


1
因为Java中的移位操作总是对移位后的值取模长度。 - Hot Licks
1
这真的很好知道,考虑到从数学角度来看它是完全错误的! - Markus A.
3个回答

13

来自JLS第15.19节

如果左操作数的提升类型为int,则仅使用右操作数的最低的五位二进制数作为移位距离。就像右操作数被位逻辑AND运算符&(§15.22.1)与掩码值0x1f(0b11111)进行操作一样。因此,实际使用的移位距离始终在0到31的范围内。

重点是我的。所以:

x >>> n

等同于:

x >>> n & 0x1f  // or x >>> n % 32

所以,x >>> 32 等同于 x >>> 32 & 0x1f <==> x >>> 0 == x。因此,经验法则是,每当您将数字移位多个 32int32 位)时,您将得到相同的值。

4
这是由JLS 15.19所规定的。但是,这样做的原因可能是人们希望位移操作是非常快速的操作,而这种实现方式几乎可以在每个处理器上无需分支。 - Louis Wasserman
1
@MarkusA... 由于Java中的int32位,因此您只能向右移动最多31位。所以,这并不意味着我们可以避免向右移动32。那么,当一个int被移位32次或更多次时,你会期望发生什么?如果你按照数学方式计算,结果将始终为0?这就是为什么出现了这种移位方式的原因。 - Rohit Jain
@MarkusA.. 嗯,我不是评论这个的合适人选。现在你正在针对最初的Java创作者。我离他们很远,我不能说为什么会设计成这样。只有他们能够解释。 - Rohit Jain
@MarkusA.. 是的,这在JLS中有列出。如果左操作数的提升类型为int,则仅使用右操作数的五个最低位作为移位距离。就好像右操作数被位逻辑AND运算符&(§15.22.1)与掩码值0x1f(0b11111)进行了处理。因此实际使用的移位距离始终在0到31之间,包括0和31。 - Rohit Jain
@MarkusA.. 所以,-1 >>> -3 等于:-1 >>> 29,因为-3 & 0x1f = 29。所以,-1 >>> 29 = 7 - Rohit Jain
显示剩余9条评论

2

当应用位移操作时,只考虑右操作数的最低5位。由于32 === 0 // mod 32,因此结果不会发生位移。


0

花了一整天的时间琢磨为什么 long l = i << 32 行为奇怪,然后编写了一些基本测试,有了 WTF 的时刻,然后改成了 long l = (long) i << 32 才让它正常工作。

我对 Rohit 的回答唯一的补充是原因。来自 IA-32 Intel Architecture Software Developer’s Manual 3:

8086 不会屏蔽移位计数。但是,所有其他 IA-32 处理器(从 Intel 286 处理器开始)都会将移位计数屏蔽为 5 位,从而得到最大计数为 31。这种屏蔽在所有操作模式(包括虚拟 8086 模式)中都会进行,以减少指令的最大执行时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接