25

C++ でプログラミングをしていると、奇妙なことに遭遇しました。単純な掛け算についてです。

コード:

unsigned __int64 a1 = 255*256*256*256;
unsigned __int64 a2= 255 << 24; // same as the above

cerr()<<"a1 is:"<<a1;
cerr()<<"a2 is:"<<a2;

興味深いことに、結果は次のとおりです。

a1 is: 18446744073692774400 
a2 is: 18446744073692774400 

一方、次のようにする必要があります:(電卓を使用して確認します)

4278190080

どうすればそれが可能になるのか、誰か教えてもらえますか?

4

5 に答える 5

39
 255*256*256*256

すべてのオペランドがintオーバーフローしていintます。符号付き整数のオーバーフローは、CおよびC++では未定義の動作です。

編集:

タイプが。255 << 24の場合、2番目の宣言の式も未定義動作を呼び出すことに注意してください。これはaで表すことはできません(最大値は通常、2の補数表現のaにあります)。int32-bit255 x (2^24)427819008032-bit int214748364732-bit int

CとC++はどちらも、が符号付きタイプで正であり、タイプで表現できない場合、プログラムは未定義動作を呼び出すと言ってE1 << E2います。これが数学のべき級数演算子です。E1E1 x (2^E2)E1^

于 2012-09-20T13:49:39.567 に答える
17

あなたのリテラルはint. これは、すべての操作が で実際に実行されint、すぐにオーバーフローすることを意味します。このオーバーフローした値は、unsigned 64bit int に変換されたときに観察される値です。

于 2012-09-20T13:50:21.353 に答える
16

おそらく、番号 18446744073692774400 を生成するために何が起こったのかを説明する価値があります。技術的に言えば、記述した式は「未定義の動作」をトリガーするため、コンパイラーは結果として何かを生成する可能性があります。ただし、 が 32 ビット型であると仮定intすると、最近ではほぼ常にそうですが、

uint64_t x = (int) (255u*256u*256u*256u);

その式は未定義の動作をトリガーしません。unsigned int( からへの変換にint実装定義の動作が含まれますが、1 の補数または符号と大きさの CPU は何年にもわたって作成されていないため、遭遇する可能性が高いすべての実装でまったく同じ方法で定義されています。)ここで述べていることはすべて、C と C++ に等しく適用されるためです。

まず、乗算を見てみましょう。右辺を 16 進数で書いているのは、その方が何が起こっているかを簡単に確認できるからです。

255u * 256u               = 0x0000FF00u
255u * 256u * 256u        = 0x00FF0000u
255u * 256u * 256u * 256u = 0xFF000000u (= 4278190080)

その最後の結果 に0xFF000000uは、32 ビットの数値セットの最上位ビットがあります。したがって、その値を符号付き32 ビット型にキャストすると、あたかも 2 32が減算されたかのように負の値になります (これは、前述の実装定義の操作です)。

(int) (255u*256u*256u*256u) = 0xFF000000 = -16777216

値を符号付き型に変換しても値のビット パターンが変わらないことuを強調するために、サフィックスなしで16 進数をそこに書きます。再解釈されるだけです。

これで、変数に -16777216 を代入すると、 2 64uint64_tを追加することで符号なし as-if に逆変換されます。(符号なしから符号付きへの変換とは異なり、このセマンティクスは標準で規定されています。)これビット パターンを変更し、数値の上位 32 ビットをすべて 0 ではなく 1 に設定します。

(uint64_t) (int) (255u*256u*256u*256u) = 0xFFFFFFFFFF000000u

10 進数で書く0xFFFFFFFFFF000000と、18446744073692774400 になります。

最後のアドバイスとして、C または C++ から「不可能な」整数を取得した場合は、16 進数で出力してみてください。2 の補数の固定幅演算の奇妙さをそのように見る方がはるかに簡単です。

于 2012-09-20T15:51:49.350 に答える
0

ここでオーバーフローがintで発生し、unsigned int64に割り当てると、4278190080ではなく18446744073692774400に変換されます。

于 2012-09-21T05:19:32.903 に答える
0

答えは簡単です。オーバーフローです。

于 2012-09-21T03:29:59.293 に答える