在Java中,当使用位移时,为什么1 << 32!= 1 << 31 << 1?

int a = 1 << 32; int b = 1 << 31 << 1;

为什么a == 1? b是我预期的0。

对于整数,所有的class次都是mod 32,对于long是mod 64。

从规范的第15.19节 :

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

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

至于为什么语言是这样设计的 – 我不知道,但C#有相同的设计决定。 以下是带注释的ECMA C#规范所说的内容:

C#故意将实现定义的行为保持为最小化。 只有在强制统一行为的性能影响过大时(例如某些浮点精度问题),才接受它们。 因此,精确指定每个整数类型的大小,并将字符集固定为Unicode。

对于换档操作,也指定了统一的行为。 它可以使用单个额外指令(&0x1F或&0x3F)来实现,这些指令在现代处理器上只会产生很小的成本,特别是因为它不引用内存。 与浮点运算不同,如果留给处理器突发奇想,转换行为的差异将是巨大的; 而不是精确度的微小差异,将产生完全不同的积分结果。

在做出这个决定时,委员会研究了许多不同处理器架构的参考资料。 对于32位操作数,在-32 .. + 32范围之外的移位计数的行为几乎没有一致性,对于64位操作数,-64 .. + 64的行为几乎没有一致性。

(然后列出一些例子。)

这对我来说似乎是一个完全合理的解释。 一致性肯定是重要的,如果在某些系统上以高效的方式实现不同的一致行为,我认为这是一个合理的解决方案。

处理器实现移位指令的方式有所不同。

例如,IIRC,ARM处理器(32位ISA)占用移位寄存器的最低有效字节。 (移位实际上不是ARM上的独立指令)。

只要底层处理器有一种模糊的移位方式,就可以更容易地清除除最低有效位之外的所有位(通常是一条指令),而不是检查移位是否很大和分支(实际上在ARM上这通常只添加一条指令)因为所有指令都是有条件的)。