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

Weird result after assigning 2^31 to a signed and unsigned 32-bit integer variable

本文关键字:整数 32位 变量 结果 无符号 符号      更新时间:2023-10-16

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

这是一个短程序(在C++中),我制作它是为了看看发生了什么:

#include <cstdio>
using namespace std;
int main()
{
    unsigned long long n = 1<<31;
    long long n2 = 1<<31;  // this works as expected
    printf("%llun",n);
    printf("%lldn",n2);
    printf("size of ULL: %d, size of LL: %dn", sizeof(unsigned long long), sizeof(long long) );
    return 0;
}

这是输出:

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

然后我添加了另一个函数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("%llun",n);
}

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

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 /

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

我突然想到,也许n*2和n<lt;1,不要总是以同样的方式行事,所以我尝试了这个:

void s()
{
   unsigned long long n = 1;
   for(int a=0;a<63;a++) n = n*2;
   printf("%llun",n);
}

这给出了2^63的正确值作为9223372036854775808的输出(我使用python验证了它)。但是做左大便有什么错?

左算术移位n等于乘以2n(如果值不溢出)

-维基百科

该值没有溢出,由于该值为2^63(所有位都已设置),因此只会出现减号。

我仍然无法弄清楚左班是怎么回事,有人能解释一下吗?

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

在这一行:

unsigned long long n = 1<<32;

问题是文字1的类型是int,可能只有32位。因此,移位会将其推到界外。

存储到较大的数据类型并不意味着表达式中的所有内容都是以较大的大小完成的。

因此,要更正它,您需要将其转换为unsigned long long文字:

unsigned long long n = (unsigned long long)1 << 32;
unsigned long long n = 1ULL << 32;

1 << 32失败的原因是1没有正确的类型(它是int)。编译器在赋值本身实际发生之前不会进行任何转换,因此1 << 32使用int算法进行计算,并发出溢出警告。

请尝试使用1LL1ULL,它们分别具有long longunsigned long long类型。

unsigned long long n = 1<<32;

导致溢出,因为文字1的类型是int,所以1 << 32也是int,在大多数情况下是32位。

线路

unsigned long long n = 1<<31;

由于同样的原因,也会溢出。请注意,1的类型是signed int,因此它实际上只有31位的值和1位的符号。因此,当您对1 << 31进行移位时,它会溢出值位,从而产生-2147483648,然后将其转换为无符号长整型,即18446744071562067968。如果检查变量并进行转换,则可以在调试器中对此进行验证。

所以使用

unsigned long long n = 1ULL << 31;