c / c ++ left shift unsigned vs signed

时间:2014-04-07 06:03:39

标签: c++ bit-shift

我有这段代码。

#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 bridge架构上。

第一个结果是有道理的,但我不明白第二个数字的来源。 1表示为4byte int signed或unsigned是

00000000000000000000000000000001

当你向左移动31次时,你最终会

10000000000000000000000000000000

没有?我知道向左移动正数基本上是2 ^ k,其中k是你移动它的次数,假设它仍然适合于界限。为什么我得到这么奇怪的数字?

5 个答案:

答案 0 :(得分:13)

据推测,您对此为何感兴趣:unsigned long int uwantsum = 1 << 31;会产生“奇怪”的价值。

问题非常简单:1是普通int,因此转换是在普通int上完成的,只有在完成后才会将结果转换为unsigned long。< / p>

但是,在这种情况下,1<<31溢出了32位signed int的范围,因此结果是未定义的 1 。转换为无符号后,结果仍未定义。


  1. §5.8/ 2:

      

    E1的值<&lt; E2是E1左移E2位位置;空位是零填充的。如果E1具有无符号类型,则结果的值为E1×2E2,比结果类型中可表示的最大值减少一个模数。否则,如果E1具有有符号类型和非负值,并且E1×2E2可在结果类型的相应无符号类型中表示,则转换为结果类型的该值是结果值; 否则,行为未定义。   

  2.   

答案 1 :(得分:6)

没有1的文字U是签名的int,因此当你转移<< 31时,你会得到整数溢出,产生一个负数(在未定义的行为)。

将此负数分配给unsigned long会导致符号扩展,因为long的位数多于int,并且通过取其模数,将负数转换为大的正数2 64 ,这是签名到无符号转换的规则。

答案 2 :(得分:2)

这不是“离奇”。

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

std::cout << std::hex << i << 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;

答案 3 :(得分:1)

我认为这是依赖于编译器的 它给出相同的值
2147483648 2147483648
在我的机器上(g ++) 证明http://ideone.com/cvYzxN

如果存在溢出,那么因为uwantsumunsigned long int且无符号值始终为正,所以使用(uwantsum)%2^64从已签名转换为无符号转换

希望这有帮助!

答案 4 :(得分:-2)

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