为什么Java掩码移位操作数为0x1F?

azz*_*azz 11 java bit-manipulation

在Java中:

(0xFFFFFFFF <<  1) = 0xFFFFFFFE = 0b1111111111111110
                :         :               :
(0xFFFFFFFF << 30) = 0xE0000000 = 0b1110000000000000
(0xFFFFFFFF << 30) = 0xC0000000 = 0b1100000000000000
(0xFFFFFFFF << 31) = 0x80000000 = 0b1000000000000000
Run Code Online (Sandbox Code Playgroud)

然而:

(0xFFFFFFFF << 32) = 0xFFFFFFFF = 0b1111111111111111
Run Code Online (Sandbox Code Playgroud)

逻辑上这没有任何意义,但我认为发生的事情是Java执行类似于以下操作的操作:

a << (b % Integer.SIZE) [编辑,显然:] a << (b & 0x1F)

这也适用于>>>>>,太.

显然,移位> = 32(在整数的情况下)会从数据类型中删除所有数据,但有时这很有用.例如:

int value = 0x3F43F466; // any value
int shift = 17; // any value >= 0
int carry = value & (-1 << (Integer.SIZE - shift));
if (carry > 0)
    ; // code...
Run Code Online (Sandbox Code Playgroud)

当然这可以修复,但找到这些错误可能非常耗时(我只花了几个小时跟踪类似的一个).所以,我的问题:是否有理由在将所有位移出时不返回逻辑值

更新:

我在C99中尝试了这个,使用以下内容:

#include<stdio.h>
main()
{
   int i, val;
   for (i = 0; i <=36; i++) {
       val = (-1 << i);
       printf("%d :\t%d\n", i, val);
   }
}
Run Code Online (Sandbox Code Playgroud)

我发现它的行为与Java相同,掩盖i & 0x1F,而在给定一个常量值时,它在编译时提供警告:

warning: left shift count >= width of type
Run Code Online (Sandbox Code Playgroud)

Lou*_*man 8

当然,有:大多数处理器(特别是包括x86)实现位移,并做你想要的 - 检查移位是否大于32,如果是,返回零 - 需要一个分支,这可以在现代CPU上很昂贵.这不仅仅是"令人讨厌",它可以减慢数量级的速度.

简而言之,执行您想要的操作会增加高性能代码预期会快速运行的操作.

作为参考,逻辑并不完全相同%,它是一个掩码.有关详细信息,请参阅JLS 15.19:

如果左侧操作数的提升类型是int,则只使用右侧操作数的五个最低位作为移位距离.就好像右手操作数受到按位逻辑AND运算符&(§15.22.1)和掩码值0x1f(0b11111)的影响.因此,实际使用的移位距离始终在0到31的范围内,包括0和31.