为什么Java和C#有位移运算符?

5

整数乘法的差异(暂时忽略除法)是否仍然支持移位,如果是,差异有多大?

它似乎只是一种低级优化,即使你想要它,(C#/Java)到字节码编译器或jit在大多数情况下也应该捕获它,不应该吗?

注意:我测试了C#的编译输出(使用gmcs Mono C#编译器版本2.6.7.0),即使乘以2的倍数,乘法示例也没有使用移位来进行乘法。

C# http://csharp.pastebin.com/hcrRnPrb

cil http://csharp.pastebin.com/0js9F2c1

P.S. 我忘记了它在处理字节时可能会有一些用处,但仍然在处理数字时遇到了一些问题。


10
位移运算符不仅限于乘法运算。 - Cristian
3
有时候位移运算符比乘法运算符更能清晰地表达意思。 - Kirk Woll
2
你正在查看IL,你真的需要在JIT编译后看到机器代码,以便确定框架是否正确优化了它们。 - Ben Voigt
Cristian,你是指除法还是说其他什么? - Roman A. Taycher
8个回答

22

第一个原因:

有时候,甚至是大多数时候,您想把整数视为数字。但有时候,整数是表示一组位的便捷方式。

乘法是数字上的运算。

移位是对一组位的操作。

乘法结果和移位结果之间恰好存在某种关系并不特别相关。这些操作在逻辑上不同。

第二个原因:

C#和Java都被设计为熟悉C开发人员,尽管只是表面水平。因此,C中的常见习惯用语被包括在C#和Java中。


C#和Java(尤其是后者)从C和C++中删除了许多东西。 - Roman A. Taycher
更重要的是,我好奇为什么你想将两个字节表示为整数,如果它不是一个数字。 - Roman A. Taycher
1
@Roman 无论什么东西是不是性能瓶颈,都不能脱离上下文进行辩论。如果您有两个字节上的多个操作,例如1000个操作和几千个并发用户,那将会产生巨大差异。 - Rune FS
1
@Roman:与外部设备的信号传输:声卡、PLC等。 - GvS
2
@Roman:由于CIDR,IP地址作为位序列具有意义,而不是数值。使用32位整数执行(IP地址) & (网络掩码)比循环遍历字节数组快大约10倍,并且比展开的循环快4倍。 - Ben Voigt
显示剩余3条评论

12
如果我想把一个数字乘以4,我会写* 4。如果我的意图是将某些位左移2个位置,则会写<< 2
对于Java和C#为什么具有位移操作符的问题,我在二进制数据方面做了很多工作,那里我不考虑整数等-只考虑二进制-在这个领域中,使用移位操作符是完全合理的。
当然,我可以输入* 2等,但实际上我想要做的是移动位。
这在一系列涉及字节的领域中很常见(例如图形编程、序列化等)。
此外,还有一些微妙之处的移位操作,其中您不想它像整数一样运行,特别是在处理边缘时...当您将一个位左移超出映射时会发生什么,或者将位右移映射中(-ve vs +ve等)的规则是被充分理解但至关重要的。同样,整数乘法的checked/unchecked行为有时非常重要。

8
您说得没错,如果移位运算符只是用作乘法的替代品,那么应该由编译器来处理。
我想您可能忽略了以下应用程序:
- 加密/解密 - CRC计算 - 位图操作(图形、数据库锁定) - 压缩/解压缩 - 配置硬件寄存器的数据 - 更改编码
等等,这些都需要进行位操作以实现高效的实现,而无需使用本地代码。

1
你所问的实际上不是为什么C# / Java中有位移运算符,而是为什么javac编译器不会将2的幂次方的乘法和除法优化为位移。
对此的第一反应是乘法和除法与位移具有不同的语义,因此不能完全替换操作。
此外,您忽略了JIT(HotSpot)中发生的额外编译步骤,其中发生各种附加优化。 实际上没有必要优化这个特定步骤,而在C中,代码就像编译器生成的那样。

0

因为语言设计者认为拥有它们是很好的。

它们是否等效于其他操作并不重要,编译器足够聪明以高效地实现这些操作。如果我们只需要这些,那么你只需要一个宏汇编器和一个真正好的链接时优化器,或许在一个带垃圾回收器的虚拟机上。但这并不是语言设计者通常追求的目标。


0
例如,您的程序可能使用类似位掩码的东西。在这种情况下,位移操作是必需的。或者,如果您只是解决某些需要以指定方式编码状态的奇怪任务。
观看此tutorial - 大多数示例来自数学问题。如果您只是制作网站或GUI应用程序,则可能不需要移位,但有时确实需要...

我同意位移运算符在某些情况下可以使代码更易于阅读/编写,但是你也可以不使用它们,而是使用*2^n或/2^n代替<<n和>>n。因此,它们并非必需品,但是有了它们会更好。 - Rune FS
2
@Rune FS,-1>>1与-1/2不同,无论是否进行符号扩展。仅使用数学运算符重写移位操作实际上相当棘手。 - Jon Hanna
@Jon,你没有理解重点,即它们不是必需的,但可以被重新编写(当谈论位模式时,符号并没有意义,因为它们暗示了模式的特定解释,而这不是模式本身的一部分。例如,在给定上下文中,有多个位模式表示-1,并且在不同的上下文中,它们都代表正整数,而在另一个上下文中,它们是文件加密的一部分)。 - Rune FS
@Rune FS,你没有理解我的观点,即它们不能轻易地被重写。我们可以仅使用减法和负数分支指令来重写每个指令,但是我们不这样做有很好的原因。最后,当你谈到-1时,你证明了我的观点,因为你混淆了数学思维和位模式思维,我认为这两者最好分开使用不同的运算符。 - Jon Hanna
@Jon 我在我的组织评论中表示我认为移位运算符是好的,但答案说它们是必需的,这是不正确的。同时,实现运算符 >> (uint mask, uint places) 非常简单,代码如下:return mask / (uint)Math.Pow(2,places); 这正是我上面所试图解释的内容。而且正如你自己所说的“我们可以重写……”,也就是说,移位运算符_并不是_必需的,但是拥有它们_很好_。 - Rune FS
显示剩余3条评论

0

这样你就可以左右移位了。你想让这些位和它们的移位操作代表什么完全取决于你。


@ Rune FS... 哦!老兄…哈哈哈…那是一个非常搞笑的错误。感谢你的纠正。 - explorer

0
除了其他原因外,还有很多情况下你可能需要进行位移(或其他位操作)以便与第三方库或通过网络连接的远程应用程序进行接口交互。

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