不需要的Java位移行为

3

我正在生成一些位掩码进行计算,其中我需要屏蔽一个整数,使得除了最右边的x位之外,所有其他位都变为零。我使用以下方法进行此操作:

int mask = ~(-1 << x);

这段代码对于除了x等于32的所有值都可以正常工作,但是当x等于32时应该返回-1,但实际上它返回了0。这里发生了什么?

另外,我尝试过这个:

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

当x = 0时,它应该返回0,但实际上它返回的是-1。不知何故,将某些东西向左移32位会导致操作返回运算符的左侧。当我尝试将-1向左移动33或34位时,它返回一个值,就好像它向左移动了1或2位。我是否正确地认为Java实际上会执行这样的操作:

int mask = ~(-1 << x % 32);

并且

int mask = -1 >>> (32 - x) % 32;

如果超过32位int长度,为什么你还想要这种循环行为呢?Oracle的文档清楚地说明:

无符号右移位运算符">>>"会在最左边位置插入0

但是当它需要移动超过32位时,显然并不是这样做的...

1个回答

8

是的,你是正确的;在应用移位之前,移位数会被32(或64,对于long)取模。

JLS 15.19

如果左操作数的提升类型为int,则仅使用右操作数的最低5位作为移位距离。好像右操作数经过按位逻辑AND运算符&(§15.22.1)与掩码值0x1f(0b11111)。因此实际使用的移位距离始终在0到31之间,包括31。

如果左操作数的提升类型为long,则仅使用右操作数的最低6位作为移位距离。好像右操作数经过按位逻辑AND运算符&(§15.22.1)与掩码值0x3f(0b111111)。因此实际使用的移位距离始终在0到63之间,包括63。

至于为什么Java选择了这种行为,我无法为您提供任何建议。


好的,谢谢您的快速回答,但是为什么呢?对于按数据类型大小进行模运算的这个额外工作有什么原因?为什么不只是对于移位等于或高于数据类型大小的情况返回0呢?也许有一个经常使用并且可以证明这种行为的用例吗? - Erik Stens
嗯,也许现在你已经展示了它只是掩盖了运算符右侧,这样做可能比检查移位是否超过32并返回0(我想要的行为)更快,因为任何其他移位,特别是int的最大值,可能需要计算太长时间... - Erik Stens
1
如果让我猜的话?我会说在这种特殊情况下,硬件转移具有不同的行为,而这种实现在几乎所有处理器上都是无需分支的,而另一种选择则需要在大多数人期望非常快速的操作上进行分支。我还怀疑按固定距离进行移位比替代方案更常见。 - Louis Wasserman

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