【问题标题】:c/c++ left shift unsigned vs signedc/c++ 左移无符号与有符号
【发布时间】:2014-04-07 06:03:38
【问题描述】:

我有这个代码。

#include <iostream>

int main()
{
    unsigned long int i = 1U << 31;
    std::cout << i << std::endl;
    unsigned long int uwantsum = 1 << 31;
    std::cout << uwantsum << std::endl;
    return 0;
}

打印出来了。

2147483648
18446744071562067968

在 Arch Linux 64 位、gcc、ivy 桥架构上。

第一个结果是有道理的,但我不明白第二个数字是从哪里来的。 1 表示为 4byte int 有符号或无符号是

00000000000000000000000000000001

当你将它向左移动 31 次时,你会得到

10000000000000000000000000000000

不是吗?我知道正数左移本质上是 2^k,其中 k 是你移动它的次数,假设它仍然在范围内。为什么我会得到这么奇怪的数字?

【问题讨论】:

  • bizarre number?检查二进制形式
  • 我说的是第二行。更新了问题。

标签: c++ bit-shift


【解决方案1】:

大概你对为什么会感兴趣:unsigned long int uwantsum = 1 &lt;&lt; 31; 会产生一个“奇怪”的值。

问题很简单:1 是一个普通的int,所以移位是在一个普通的int 上完成的,只有在完成之后才会将结果转换为unsigned long

但是,在这种情况下,1&lt;&lt;31 会溢出 32 位有符号整数的范围,因此结果是未定义的1。转换为无符号后,结果仍然未定义。

也就是说,在大多数典型情况下,1&lt;&lt;31 可能会给出10000000000000000000000000000000 的位模式。当被视为有符号 2 的补码2 数时,这是 -2147483648。由于这是负数,当它转换为 64 位类型时,它将被符号扩展,因此前 32 位将被第 31 位中的内容的副本填充。这给出:1111111111111111111111111111111110000000000000000000000000000000(33 个 1 位后跟31 个 0 位)。

如果我们将其视为无符号 64 位数字,我们将得到 18446744071562067968。


  1. §5.8/2:

    E1的值否则,行为未定义。

  2. 理论上,计算机可以对有符号数使用 1 的补码或有符号的幅度 - 但 2 的补码目前比这两种中的任何一种都更常见。如果它确实使用了其中一个,我们预计最终结果会有所不同。

【讨论】:

  • 这是有道理的。我不确定我是否遇到了未定义的行为。谢谢!我想在像 msvc 这样的不同编译器下,我会得到完全不同的东西?
  • @No_name:不能保证,但很有可能你会得到硬件的任何一种方式,所以在同一个 CPU 上的两个编译器通常会产生相同的结果。
  • 你说“都是普通的ints”——只有1的类型才重要; 1 &lt;&lt; 31ull1 &lt;&lt; 31 相同。
  • @MattMcNabb:说得好——虽然在技术上是正确的,但这是一种误导。
  • “比结果类型中可表示的最大值减少模一”是什么意思?
【解决方案2】:

没有U 的文字1 是带符号的int,因此当您移动&lt;&lt; 31 时,会出现整数溢出,生成一个负数(在未定义行为的保护伞下)。

将此负数分配给unsigned long 会导致符号扩展,因为long 的位数比int 多,并且它会将负数转换为一个较大的正数,取模为264,这是有符号到无符号转换的规则。

【讨论】:

    【解决方案3】:

    这并不“奇怪”。

    尝试以十六进制打印数字,看看是否更容易识别:

    std::cout &lt;&lt; std::hex &lt;&lt; i &lt;&lt; std::endl;

    并始终记住适当地使用“U”、“L”和/或“LL”来限定您的文字:

    http://en.cppreference.com/w/cpp/language/integer_literal

    unsigned long long l1 = 18446744073709550592ull;
    unsigned long long l2 = 18'446'744'073'709'550'592llu;
    unsigned long long l3 = 1844'6744'0737'0955'0592uLL;
    unsigned long long l4 = 184467'440737'0'95505'92LLU;
    

    【讨论】:

      【解决方案4】:

      我认为它取决于编译器。
      它给出了相同的值
      2147483648 2147483648
      在我的机器上(g++)。
      证明http://ideone.com/cvYzxN

      如果存在溢出,那么因为uwantsumunsigned long int 并且无符号值总是为正,所以使用(uwantsum)%2^64 完成从有符号到无符号的转换。

      希望这会有所帮助!

      【讨论】:

        【解决方案5】:

        它以您打印出来的方式。 使用格式说明符 %lu 应该代表一个适当的 long int

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-11-25
          • 2012-10-20
          • 1970-01-01
          • 2022-05-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-01-06
          相关资源
          最近更新 更多