5

質問のタイトルが示すように、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: このプログラムは、Linux Mint を実行している 32 ビット システムで実行されました (それが役立つ場合)。

4

3 に答える 3

10

この行で:

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;
于 2012-04-02T08:51:09.740 に答える
5

1 << 32失敗する理由1は、正しいタイプがないためです (それは ですint)。コンパイラは、割り当て自体が実際に行われる前に変換マジックを実行しないため、算術1 << 32を使用して評価されint、オーバーフローに関する警告が表示されます。

それぞれand型を持つ1LLorを代わりに使用してみてください。1ULLlong longunsigned long long

于 2012-04-02T08:51:27.063 に答える
3

この線

unsigned long long n = 1<<32;

intリテラル 1 は型で1 << 32あり、ほとんどの場合 32 ビットである int でもあるため、オーバーフローが発生します。

この線

unsigned long long n = 1<<31;

同じ理由でオーバーフローします。1 は typesigned intであるため、実際には値に 31 ビット、符号に 1 ビットしかないことに注意してください。したがって、 をシフト1 << 31すると、値のビットがオーバーフローし、 になり-2147483648、これが unsigned long long に変換され18446744071562067968ます。変数を検査して変換すると、デバッガーでこれを確認できます。

だから使う

unsigned long long n = 1ULL << 31;
于 2012-04-02T09:04:12.637 に答える