我有这段代码。
#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是你移动它的次数,假设它仍然适合于界限。为什么我得到这么奇怪的数字?
答案 0 :(得分:13)
据推测,您对此为何感兴趣:unsigned long int uwantsum = 1 << 31;
会产生“奇怪”的价值。
问题非常简单:1是普通int
,因此转换是在普通int
上完成的,只有在完成后才会将结果转换为unsigned long
。< / p>
但是,在这种情况下,1<<31
溢出了32位signed int的范围,因此结果是未定义的 1 。转换为无符号后,结果仍未定义。
E1的值<&lt; E2是E1左移E2位位置;空位是零填充的。如果E1具有无符号类型,则结果的值为E1×2E2,比结果类型中可表示的最大值减少一个模数。否则,如果E1具有有符号类型和非负值,并且E1×2E2可在结果类型的相应无符号类型中表示,则转换为结果类型的该值是结果值; 否则,行为未定义。
答案 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
如果存在溢出,那么因为uwantsum
为unsigned long int
且无符号值始终为正,所以使用(uwantsum)%2^64
从已签名转换为无符号转换
希望这有帮助!
答案 4 :(得分:-2)
它打印出来的方式。 使用formar说明符%lu应该代表一个合适的long int