为什么Java掩码移位操作数为0x1F?
在Java中:
(0xFFFFFFFF << 1) = 0xFFFFFFFE = 0b1111111111111110 : : : (0xFFFFFFFF << 30) = 0xE0000000 = 0b1110000000000000 (0xFFFFFFFF << 30) = 0xC0000000 = 0b1100000000000000 (0xFFFFFFFF << 31) = 0x80000000 = 0b1000000000000000
然而:
(0xFFFFFFFF << 32) = 0xFFFFFFFF = 0b1111111111111111
逻辑上这没有任何意义,但我认为正在发生的是Java执行类似于以下操作的操作:
a << (b % Integer.SIZE)
[编辑,显然:] a << (b & 0x1F)
这也适用于>>
和>>>
。
显然,移位> = 32(在整数的情况下)会从数据类型中删除所有数据,但有时这很有用。 例如:
int value = 0x3F43F466; // any value int shift = 17; // any value >= 0 int carry = value & (-1 < 0) ; // code...
当然这可以修复,但找到这些错误可能非常耗时(我只花了几个小时跟踪类似的一个)。 所以,我的问题: 是否有理由在将所有位移出时不返回逻辑值 ?
更新:
我在C99中尝试了这个,使用以下内容:
#include main() { int i, val; for (i = 0; i <=36; i++) { val = (-1 << i); printf("%d :\t%d\n", i, val); } }
我发现它的行为与Java相同,掩盖了i & 0x1F
,而在给定一个常量值时它会在编译时提供警告:
warning: left shift count >= width of type
当然,有:大多数处理器(特别是包括x86)实现位移,并做你想要的 – 检查转移是否大于32,如果是,返回零 – 需要一个分支,这可以在现代CPU上很昂贵。 这不仅仅是“讨厌”,它可以减少数量级的事情。
简而言之,做你想做的事情会给高性能代码预期会快速发展的操作带来巨大的开销。
作为参考,逻辑与%
不完全相同,它是一个掩码。 有关详细信息,请参阅JLS 15.19 :
如果左侧操作数的提升类型是int,则只使用右侧操作数的五个最低位作为移位距离。 就好像右手操作数受到按位逻辑AND运算符&(§15.22.1)和掩码值0x1f(0b11111)的影响。 因此,实际使用的移位距离始终在0到31的范围内,包括0和31。
JLS 15.19 如果左侧操作数的提升类型为int,则只使用右侧操作数的五个最低位作为移位距离。 就好像右手操作数受到按位逻辑AND运算符&(§15.22.1)和掩码值0x1f(0b11111)的影响。 因此,实际使用的移位距离始终在0到31的范围内,包括0和31
把它简单地称为0xFFFFFFFF << 32
等于to 0xFFFFFFFF << (32 & 0x1f)
相当于0xFFFFFFFF << 0