为什么像C# / Java这样的高级语言掩盖位移计数操作数?

16

这更多是关于语言设计而非编程问题。

以下是来自JLS 15.19移位运算符的摘录:

如果左操作数的提升类型为int,则仅使用右操作数的五个最低位作为移位距离。

如果左操作数的提升类型为long,则仅使用右操作数的六个最低位作为移位距离。

这种行为在C#中也有规定,虽然我不确定它是否在Javascript的官方规范中(如果有的话),但至少根据我的测试结果,它也是正确的。

其结果是以下内容为真:

(1 << 32) == 1

我理解这个规范可能受到底层硬件只为32位值(64位为6位)的计数操作数取5位的启发,我可以理解这种行为在JVM级别上进行了规定,但是为什么高级语言如C#和Java会保留这种相对低级别的行为呢?它们不应该提供一个更抽象的视图超越硬件实现并表现得更加直观吗?(如果它们能将负数计数视为向其他方向移位,那就更好了!)

3个回答

8
Java和C#并非完全的“高级语言”。它们努力使自己能够编译成高效的代码,以在微基准测试中表现出色。这就是为什么它们有“值类型”,如int,而不是默认的整数类型,真正的整数类型将是对象本身,而不仅仅是固定范围内的数字。
因此,它们模仿硬件的工作方式。它们稍微削减了一些,因为它们强制执行掩码操作,而C只允许它。尽管如此,Java和C#仍然是“中级语言”。

判断一种编程语言是否为高级语言非常主观,我理解。然而,我认为大多数人会将Java和C#归类为“高级语言”,至少对于非脚本语言来说是这样的。 - polygenelubricants
当然可以。但是Java和C#仍然保留了低级别的特征,出于效率考虑(或者至少是“被认为的效率”)。32位的int类型和移位计数掩码就是这些特征之一。其他语言,如Scheme,在这方面是“更高级别”的。 - Thomas Pornin

5
因为在大多数编程环境中,整数只有32位。所以5位(足以表示32个值)就足够移动整个整数了。对于64位的long类型也存在类似的推理:只需6位就可以完全移动整个值。
我能理解部分混淆的原因:如果您的右操作数是计算结果,该结果最终具有大于32的值,您可能会期望它只移动所有位而不是应用掩码。

我理解为什么有效地移动32位值只需要最多5位,而超过这个范围基本上会清除整个寄存器 - 作为语言的用户,有时这正是我想要的!正如我所说,问题不在于参数为什么被选择为这样,而是为什么这么低级别的东西被保留在高级语言中。 - polygenelubricants
如果您的右操作数是一个计算结果,其值大于32,则无法使用5位存储0~31之间的值,因此无法将其移位32位,这样做也没有用处,因为简单的xor reg,reg可以达到相同的效果。 - underscore_d

5

C#和Java将移位定义为仅使用移位计数的低位,因为这是sparc和x86移位指令的行为。Java最初由Sun在sparc处理器上实现,而C#由Microsoft在x86上实现。

相比之下,如果移位计数不在0..31范围内(对于32位int),则C/C++将移位指令的行为留为空。这是因为在最初实现C时,不同的硬件处理这些问题的方式不同。例如,在VAX上,通过负数移位会使移动方向相反。因此,对于C,编译器可以只使用硬件移位指令并执行任何操作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接