Как следует из заголовка вопроса, присвоение 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("%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;
}
Вот результат:
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("%llu\n",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 ‹< 1 не всегда ведут себя одинаково, поэтому я попробовал следующее:
void s()
{
unsigned long long n = 1;
for(int a=0;a<63;a++) n = n*2;
printf("%llu\n",n);
}
Это дает правильное значение 2 ^ 63 как результат 9223372036854775808
(я проверил это с помощью python). Но что плохого в том, чтобы делать левое дерьмо?
Левый арифметический сдвиг на n эквивалентен умножению на 2 n (при условии, что значение не переполняется)
- Википедия
Значение не переполняется, появится только знак минус, поскольку значение равно 2 ^ 63 (все биты установлены).
Я все еще не могу понять, что происходит с левым переключением, кто-нибудь может объяснить это?
PS: Эта программа была запущена в 32-битной системе под управлением Linux Mint (если это помогает)
unsigned long long n = 1ULL<<31;
- person kirilloid   schedule 02.04.2012