问题 为什么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 << (Integer.SIZE - shift));
if (carry > 0)
    ; // code...

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

更新:

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

#include<stdio.h>
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

6816
2018-03-29 17:58


起源

我永远使用Java但从未见过!答案就在这个SO问题中, stackoverflow.com/questions/14344546/...  Java Spec就像你推测的那样,最低5位。 - david van brink
好吧,我可以给 一个 原因:在x86上实现这样的转换会很烦人,因为原生移位指令也会执行掩码操作。 (顺便说一下 不 做一个模数,它会有红利的标志。它掩盖了所有的高位) - harold
所以这不是一个错误。这是一个功能。请注意,必须定义它的作用。如果> = 32,另一种选择是将其定义为0结果。 - Lee Meador
注意,在C中,移位具有超出范围[0 31]的数字的32位整数的结果是未定义的。 - notso


答案:


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

简而言之,做你想做的事情会给高性能代码预期会快速发展的操作带来巨大的开销。

作为参考,逻辑并不完全相同 %,这是一个面具。看到 JLS 15.19 详情:

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


8
2018-03-29 18:06



我只是查找了一些移位器的基本逻辑图,现在这很有意义。 - azz
在最初的8088和8086,以及我相信80286,使用可变操作数(CL寄存器)的移位指令将加载CL的内部寄存器;当寄存器非零时,它们会递减寄存器并执行移位。因此,计算 myInt16 >> 240 会花费更长的时间 myInt16 >> 15 但会计算相同的结果[那些都是16位处理器,因此通常通过子程序处理32位值的移位]。注意 myInt16 >> 259 可能会表现得像 myInt16 >> 3因为CL只有8位。 - supercat


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

简而言之 0xFFFFFFFF << 32 是等价的 to 0xFFFFFFFF << (32 & 0x1f) 相当于 0xFFFFFFFF << 0


4
2018-03-29 18:09