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

2022-09-03 13:27:39
int a = 1 << 32;
int b = 1 << 31 << 1;

为什么这样做? 正如我预期的那样为0。a == 1b


答案 1

所有移位都针对 ints 完成 mod 32,对于长距离执行 mod 64。

规范的 15.19 节中

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

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

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

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

对于班次操作,也指定了统一行为。它可以使用单个额外的指令(&0x1F或&0x3F)来实现,这在现代处理器上只产生很小的成本,特别是因为它不引用内存。与浮点运算不同,如果任由处理器的心血来潮,移位行为的差异将是巨大的。而不是精度上的微小差异,将产生完全不同的积分结果。

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

(然后列出了一些示例。

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


答案 2

处理器实现移位指令的方式存在一些差异。

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

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


推荐