web-dev-qa-db-ja.com

C ++での2つの整数の乗算

かなり基本的な質問がありますが、コンセプトを理解しているかどうかわかりません。次のように仮定します。

int a = 1000000;
int b = 1000000;
long long c = a * b;

これを実行すると、cに負の値が表示されるため、ablong longに変更したところ、すべて問題ありませんでした。では、なぜabの値がintの範囲にあり、それらの積がclong long)?

C/C++を使用しています

18
essa

intsは、乗算前にlong longに昇格されず、intsと積のままになります。次に、製品はlong longにキャストされますが、遅すぎてオーバーフローが発生しました。

aまたはblong longのいずれかを使用すると、もう一方が昇格するので、同様に機能します。

20
Yves Daoust

算術演算子の場合、結果のタイプは、結果の割り当て先ではなく、オペランドのタイプに依存します。算術演算子の場合、オペランドに対して 通常の算術変換 が実行されます。これは、オペランドを共通の型にするために使用されます。これは、値が可能な場合、unsigned/signedintより小さい型を意味しますunsigned/signedintに昇格するように適合します。この場合、これらはすでに両方がintであるため、変換は必要ありません。理由の詳細については、 CおよびC++で算術演算の前にshortをintに変換する必要があるのはなぜですか? を参照してください。

符号付き整数オーバーフローは未定義の動作であるため、今のところ未定義の動作です。これはC++標準ドラフトセクション5[Expr]で説明されています。

式の評価中に結果が数学的に定義されていないか、その型の表現可能な値の範囲にない場合、動作は未定義です。 [注:C++のほとんどの既存の実装は整数オーバーフローを無視します。ゼロ除算の処理、ゼロ除数を使用した剰余の形成、およびすべての浮動小数点例外はマシン間で異なり、通常はライブラリー関数によって調整可能です。 —エンドノート]

今日、これらのタイプの未定義の動作をキャッチするサニタイザーがあり、-fsanitize=undefinedをclangとgccの両方で使用すると、実行時に次のエラー(それを参照)でこれをキャッチしますライブ):

実行時エラー:符号付き整数オーバーフロー:1000000 * 1000000はタイプ 'int'では表現できません

参照セクションについては、5.6[expr.mul]は次のように述べています。

[...]通常の算術変換がオペランドに対して実行され、結果のタイプを決定します。

とセクション5は言う:

それ以外の場合、整数の昇格(4.5)は両方のオペランドで実行されます。61次に、昇格されたオペランドに次のルールが適用されます。

  • 両方のオペランドが同じタイプの場合、それ以上の変換は必要ありません。
4
Shafik Yaghmour

アセンブラー命令は常に計算するので、それは一種のばかげています

int * int-> 64ビット長

したがって、マシンコードを見ると、次のように表示されます。imul 64ビットをeax edxに格納し、次にcdq eaxのビット符号をedxに挿入します(これにより、完全な64ビットが失われます)。結果)、次にeax edxが64ビット変数に格納されます

乗算の前に32ビットの値を64ビットに変換すると、理由もなく64ビットの乗算関数が呼び出されます。

(私はチェックしました:コードが最適化されている場合はそうではありません)

2
Edgard Neuman