【问题标题】:Why do Java and C# have bitshifts operators?为什么 Java 和 C# 有位移运算符?
【发布时间】:2011-04-19 16:41:17
【问题描述】:

整数乘法(暂时忘记除法)之间的差异是否仍然有利于移位,如果是,差异有多大?

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

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

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

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

附: 我忘记了在字节上使用它可能会有什么用处,但在将它用于数字时仍然有些麻烦。

【问题讨论】:

  • 乘法并不是移位运算符的唯一用途。
  • 有时位移运算符比乘法等效项更清晰。
  • 你在看IL,你真的需要看到JIT编译后的机器码,才能判断框架是否正确优化了这些。
  • 克里斯蒂安你的意思是分裂还是你在谈论别的什么?

标签: c# java low-level premature-optimization bit-shift


【解决方案1】:

第一个原因:

有时 - 大多数时候 - 您希望将整数视为 数字。有时,尽管整数是表示一组位的便捷方式。

乘法是对数字的运算。

移位是对一组位的操作。

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

第二个原因:

C# 和 Java 都是为了让 C 开发人员熟悉而设计的,尽管只是肤浅的。因此 C# 和 Java 中包含了来自 C 的常用习语。

【讨论】:

  • C# 和 Java(尤其是后者)从 c 和 c++ 中删除了很多东西。
  • 更重要的是,我很好奇,如果 2 个字节不是数字,你为什么要将其表示为 int。
  • @Roman 不能断章取义地讨论某事是否是性能瓶颈。如果您对两个字节进行多项操作,例如1000 次操作和几千个并发用户就会产生影响
  • @Roman:与外部设备之间的信号:声卡、PLC 等
  • @Roman:由于 CIDR,IP 地址作为位序列有意义,而不是数值。 (IP address) & (network mask) 处理 32 位整数比遍历字节数组快十倍,处理 32 位整数比展开循环快四倍。
【解决方案2】:

如果我想将一个数字乘以 4,我会写 * 4。如果我的意图是将某些位左移 2 位,我会写 << 2

再问:

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

我在二进制数据上做了很多工作,其中 我不是在考虑整数等 - 只是二进制 - 在那个领域使用它完全合乎逻辑不断换班。

当然,我可以输入* 2等,但我真正想做的是移位。

这在字节数很重要的一系列领域中很常见(例如图形编程、序列化等)。

此外,还有一些微妙的移位操作,您希望它表现得像一个整数,特别是在处理边缘时......当您将地图左移一点或将位右移进入地图(-ve vs +ve 等)时会发生什么的规则很好理解但很重要。同样,整数乘法的checked/unckecked 行为有时也很重要。

【讨论】:

    【解决方案3】:

    你是对的,如果移位运算符仅用作乘法的替代,它应该留给编译器。

    我想您忽略了以下应用程序:

    • 加密/解密
    • CRC 计算
    • 位图操作(图形、数据库锁)
    • 压缩/解压
    • 为硬件寄存器设置数据
    • 更改编码

    还有更多需要在没有本机代码的情况下进行有效实施的位旋转。

    【讨论】:

      【解决方案4】:

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

      它们是否等同于其他一些操作并不重要,编译器是否足够聪明以有效地实现这些操作。如果这就是我们要去的地方,那么您只需要一个宏汇编器和一个非常好的链接时间优化器,也许在带有垃圾收集器的 VM 上。这些不是语言设计者通常追求的目标。

      【讨论】:

        【解决方案5】:

        您要问的本质上不是为什么 C#/Java 中有移位运算符,而是为什么 javac 编译器没有将乘法和除法以 2 的幂次方优化为移位。

        对此的下意识反应是乘法和除法的语义与位移不同,因此它不会 100% 映射来替换操作。

        此外,您忘记了在 JIT (HotSpot) 中发生的额外编译步骤,在该步骤中会发生各种额外的优化。坦率地说,没有必要优化这个特定的步骤,而 C 语言的代码是编译器生成的。

        【讨论】:

          【解决方案6】:

          例如,您的程序可能会使用位掩码之类的东西。在这种情况下,移位操作是必要的。或者,如果您只是在解决一些需要以指定方式对状态进行编码的奇怪任务。

          观看此tutorial - 大部分样本来自数学问题。如果你只是制作一个网站或一个 GUI 应用程序,你可能不需要转换,但有时你确实需要......

          【讨论】:

          • 我同意位移有它的区域,它们使代码更容易读/写,没有它们你可以通过简单地使用 *2^n 或 /2^n 而不是 >n.所以它们不是必需的,但很高兴拥有
          • @Rune FS, -1>>1 与 -1/2 不同,无论是否符号扩展。仅使用数学运算符重写移位运算实际上非常繁琐。
          • @Jon 您错过了一点,即它们不是必需的,但可以重写(在谈论位模式时,符号没有意义,因为它们推断出对模式的特定解释,而不是它自己的模式。例如,在给定的上下文中有多个位模式表示 -1,它们在不同的上下文中都表示一个正整数,并且文件的一部分在另一个上下文中被加密
          • @Rune FS,你错过了我的意思是它们不能被简单地重写。我们可以重写每条指令,只给一个减法和分支如果负的指令,但我们有充分的理由不这样做。最后,当您谈到 -1 时,您说明了我的观点,因为您是混合思考数学和思考位模式的人,我认为最好用单独的运算符将两者分开。
          • @Jon 我在我的 org 评论中声明我认为轮班操作员是一件好事,但答案说他们是必需的,这是不正确的。它们很容易实现 operator >> (uint mask, uint places){return mask / (uint)Math.Pow(2,places); }。这就是我在上面试图解释的。正如你所说,你自己“我们可以重写......”即移位运算符不需要,但它们很好拥有
          【解决方案7】:

          这样您就可以左右移动位。您希望这些位及其移位操作代表什么完全取决于您。

          【讨论】:

          • @符文FS...哦!伙计……哈哈哈……那真是个可笑的错误。谢谢指正。
          【解决方案8】:

          除了这里的其他原因之外,在很多情况下,您可能需要转换(或其他位操作)以通过网络与 3rd 方库或远程应用程序接口。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-10-09
            • 2012-12-16
            • 2020-04-07
            • 2010-09-30
            • 1970-01-01
            • 2014-08-22
            • 2016-11-16
            相关资源
            最近更新 更多