将2 ^ 31分配给有符号和无符号的32位整数变量后的奇怪结果

Rus*_*aul 5 c++ bit-shift unsigned-integer

正如问题标题所示,将2 ^ 31分配给有符号和无符号的32位整数变量会产生意外结果.

这是C++我做的简短程序(in ),看看发生了什么:

#include <cstdio>
using namespace std;

int main()
{
    unsigned long long n = 1<<31;
    long long n2 = 1<<31;  // this works as expected
    printf("%llu\n",n);
    printf("%lld\n",n2);
    printf("size of ULL: %d, size of LL: %d\n", sizeof(unsigned long long), sizeof(long long) );
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

MyPC / # c++ test.cpp -o test
MyPC / # ./test
18446744071562067968      <- Should be 2^31 right?
-2147483648               <- This is correct ( -2^31 because of the sign bit)
size of ULL: 8, size of LL: 8
Run Code Online (Sandbox Code Playgroud)

然后我添加了另一个函数p():

void p()
{
  unsigned long long n = 1<<32;  // since n is 8 bytes, this should be legal for any integer from 32 to 63
  printf("%llu\n",n);
}
Run Code Online (Sandbox Code Playgroud)

在编译和运行时,这让我更加困惑:

MyPC / # c++ test.cpp -o test
test.cpp: In function ‘void p()’:
test.cpp:6:28: warning: left shift count >= width of type [enabled by default]
MyPC / # ./test 
0
MyPC /
Run Code Online (Sandbox Code Playgroud)

为什么编译器会抱怨左移位数太大?sizeof(unsigned long long)返回8,那么这是不是意味着2 ^ 63-1是该数据类型的最大值?

让我感到震惊的是,也许n*2和n << 1,并不总是以相同的方式表现,所以我试过这个:

void s()
{
   unsigned long long n = 1;
   for(int a=0;a<63;a++) n = n*2;
   printf("%llu\n",n);
}
Run Code Online (Sandbox Code Playgroud)

这给出了正确的值2 ^ 63作为输出9223372036854775808(我使用python验证它).但做左撇子有什么问题?

左移算术乘以n相当于乘以2 n (假设该值不溢出)

- 维基百科

该值不会溢出,只会出现一个减号,因为该值为2 ^ 63(所有位都已设置).

我仍然无法弄清楚左移发生了什么,有人可以解释一下吗?

PS:这个程序是在运行linux mint的32位系统上运行的(如果有帮助)

Mys*_*ial 10

在这一行:

unsigned long long n = 1<<32;
Run Code Online (Sandbox Code Playgroud)

问题是文字1是类型int- 可能只有32位.因此,这种转变将使其超越界限.

仅仅因为您存储到更大的数据类型并不意味着表达式中的所有内容都以更大的大小完成.

因此,要纠正它,您需要将其强制转换或使其成为unsigned long long文字:

unsigned long long n = (unsigned long long)1 << 32;
unsigned long long n = 1ULL << 32;
Run Code Online (Sandbox Code Playgroud)


orl*_*rlp 5

1 << 32失败的原因是因为1没有正确的类型(它是int).编译器在赋值实际发生之前不会进行任何转换魔术,因此1 << 32使用intarithmic 进行评估,并发出有关溢出的警告.

尝试使用1LL1ULL代替分别具有long longunsigned long long类型.