かなり基本的な質問がありますが、コンセプトを理解しているかどうかわかりません。次のように仮定します。
int a = 1000000;
int b = 1000000;
long long c = a * b;
これを実行すると、c
に負の値が表示されるため、a
とb
もlong long
に変更したところ、すべて問題ありませんでした。では、なぜa
とb
の値がint
の範囲にあり、それらの積がc
(long long
)?
C/C++を使用しています
int
sは、乗算前にlong long
に昇格されず、int
sと積のままになります。次に、製品はlong long
にキャストされますが、遅すぎてオーバーフローが発生しました。
a
またはb
long long
のいずれかを使用すると、もう一方が昇格するので、同様に機能します。
算術演算子の場合、結果のタイプは、結果の割り当て先ではなく、オペランドのタイプに依存します。算術演算子の場合、オペランドに対して 通常の算術変換 が実行されます。これは、オペランドを共通の型にするために使用されます。これは、値が可能な場合、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次に、昇格されたオペランドに次のルールが適用されます。
- 両方のオペランドが同じタイプの場合、それ以上の変換は必要ありません。
アセンブラー命令は常に計算するので、それは一種のばかげています
int * int-> 64ビット長
したがって、マシンコードを見ると、次のように表示されます。imul 64ビットをeax edxに格納し、次にcdq eaxのビット符号をedxに挿入します(これにより、完全な64ビットが失われます)。結果)、次にeax edxが64ビット変数に格納されます
乗算の前に32ビットの値を64ビットに変換すると、理由もなく64ビットの乗算関数が呼び出されます。
(私はチェックしました:コードが最適化されている場合はそうではありません)