【问题标题】:64bit shift problem64位移位问题
【发布时间】:2009-06-21 23:06:06
【问题描述】:

为什么这段代码没有写 0 作为最后一个元素,而是 18446744073709551615? (用g++编译)

#include <iostream>

using namespace std;
int main(){
    unsigned long long x = (unsigned long long) (-1);
    for(int i=0; i <= 64; i++)
        cout << i << " " << (x >> i) << endl;
    cout << (x >> 64) << endl;
    return 0;
}

【问题讨论】:

  • 我知道这完全不相关,但是,永远不要使用“使用命名空间 std;。这只是一个等待发生的意外,花费数小时追逐一个发生的错误,因为你或一个库恰好有一个与 std 中某物名称匹配的方法。

标签: c++ 64-bit


【解决方案1】:

当您将一个值移动的位数超过字长时,它通常会移动mod word-size。基本上,将其移位 64 位意味着移位 0 位,这等于根本不移位。您不应该依赖它,因为它不是由标准定义的,并且在不同的架构上可能会有所不同。

【讨论】:

  • > 将其移动 64 位意味着移动 0 位,这等于根本不移动:我以前看过这句话,但我不明白这对于移位操作如何适用. val &lt;&lt; 64 == val 不是真的描述了 rotate 操作吗? 0xffff ffff ffff ffff &lt;&lt; 64 应该为 0,这与完全不移位明显不同。
  • @U007D 你是对的,从数学上讲,这对于移位操作来说是不正确的。我正在描述实际发生的情况,这通常会导致在 x86 架构(由 C 未定义)上实际观察到的行为:CPU 基本上会忽略最左边的位,这相当于对移位位计数进行 mod 64,然后等效移到零。我并不是要暗示数学上的 64 位移位操作相当于移位 0。
  • 啊。我假设 0x8000 0000 0000 0000 (即趋向于全零)。但是由于 可能 或 应该 做什么纯粹是我的猜测。 @Mehrdad Afshari 谢谢你的澄清——我真的很困惑! :)
【解决方案2】:

将数字移动等于或大于其宽度的位数是未定义的行为。您只能安全地将 64 位整数在 0 到 63 个位置之间移动。

【讨论】:

  • 这是错误的,右移的最大量是31,即移位量在内部总是被0x1F屏蔽。请参阅有关 SHR 指令的英特尔手册。
  • @jn:英特尔手册将其定义为“x86 程序集”。它在 C 级别是未定义的。
  • @Mehrdad:正确 - 我刚刚检查了 MSVC 编译代码的汇编输出。如果移位量大于 32,则用 0x1F 屏蔽该量,如果量大于 64,则移位结果为零。
【解决方案3】:

编译器的这个警告应该是一个提示:

“警告:右移计数 >= 类型宽度”

这会导致未定义的行为:

http://sourcefrog.net/weblog/software/languages/C/bitshift.html

【讨论】:

    【解决方案4】:

    好吧,你换了太多次了。您正在从 0 转移到 64(含),总共 65 次。您通常希望:

    for(int i=0; i < 64; i++)
        ....
    

    【讨论】:

      【解决方案5】:

      你溢出了班次。如果你注意到了,GCC 甚至会警告你:

      警告:右移计数 >= 类型宽度

      怎么会?您将 64 作为有效班次包括在内,这是一种未定义的行为。 从 0 到 64 有 65 个数字(包括 0)。 0 是第一位(很像数组)。

      #include <iostream>
      
      using namespace std;
      int main(){
          unsigned long long x = (unsigned long long) (-1);
          for(int i=0; i < 64; i++)
              cout << i << " " << (x >> i) << endl;
          cout << (x >> 63) << endl;
          return 0;
      }
      

      将产生您期望的输出。

      【讨论】:

        【解决方案6】:

        你可以使用:

        static inline pack_t lshift_fix64(pack_t shiftee, short_idx_t shifter){
          return (shiftee << shifter) & (-(shifter < 64));
        }
        

        对于这样的伎俩,

        (-(shifter < 64)) == 0xffff ffff ffff ffff
        

        如果移位器

        (-(shifter < 64)) == 0x0
        

        否则。

        【讨论】:

          【解决方案7】:

          我明白了:

          test.c:8: warning: right shift count >= width of type
          

          所以也许这是未定义的行为?

          【讨论】:

            【解决方案8】:

            对于 64 位类型,-1 的位模式在十六进制中看起来像 0xFFFFFFFFFFFFFFFF。因此,如果您将其打印为无符号变量,您将看到无符号 64 位变量可以容纳的最大值,即 18446744073709551615。

            当移位时,我们不关心值在这种情况下的含义,即变量是有符号还是无符号都无关紧要,它的处理方式相同(在这种情况下将所有位向右移动一步)。

            【讨论】:

            • 当你右移时,值是否有符号很重要。有符号右移将最高位填充到符号位,无符号右移将用 0 填充它们。实际上,右移“k”位将具有与除以 2^k 相同的效果。
            【解决方案9】:

            粗心的另一个陷阱:我知道这是一个旧线程,但我来这里寻求帮助。当我的意思是 1L&LT;&LT;k; 时,我在使用 1&LT;&LT;k 的 64 位机器上被抓住了。在这种情况下,编译器没有帮助:(

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2012-07-13
              • 1970-01-01
              • 2012-06-23
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多