オーバーフロー/アンダーフローの正確な動作は、型に対してのみ指定されていunsigned
ます。
符号なし整数は、2^n を法とする算術演算の法則に従います。ここで、n は、整数の特定のサイズの値表現のビット数です。
出典:ドラフト N3690 §3.9.1 センテンス 4
これは、結果の符号なし整数型で表現できない結果が、結果の符号なし整数型で表現できる最大値よりも 1 大きい数値を法として減らされるため、符号なし算術演算がオーバーフローしないことを意味します。
出典: §3.9.1 のドラフト N3690 Note 47
代わりに、通常の符号付き整数型の場合、C++ 標準では、何も起こり得ないと単純に述べています。
式の評価中に、結果が数学的に定義されていないか、その型の表現可能な値の範囲内にない場合、動作は未定義です。
出典:ドラフト N3690 §5 センテンス 4
x86プロセッサ(または他のほとんどの最新のプロセッサ)について話している場合、実際の動作はまさにあなたが説明したものであり、CPUの場合、符号付きの値と符号なしの値の間に違いはありません(符号付きと符号なしの操作がありますが、値それ自体は単なるビットです)。
コンパイラは、正しいプログラムでは、たとえば次のようなコードでは、符号付き整数のオーバーフローが発生しないと想定できることに注意してください (そして、最新の最適化コンパイラのほとんどは実際に想定しています)。
int do_something();
int do_something_else();
void foo() {
int x = do_something();
int y = x + 1;
if (x < y) {
do_something();
} else {
do_something_else();
}
}
else
有効なプログラムでは、符号付き intx
は常により小さいためx+1
(符号付きオーバーフローは有効な動作とは見なされないため) 、コンパイラは生成されたコードのテストと分岐を自由にスキップできます。ただし、に置き換えるint
とunsigned int
、コンパイラはテスト用と else 分岐用のコードを生成する必要があります。これは、符号なしの型の場合、x > x+1
.
たとえば、clang はfoo
toのコードをコンパイルします。
foo(): # @foo()
push rax
call do_something()
pop rax
jmp do_something() # TAILCALL
ここでは、頌歌がdo_something
2 回呼び出しているだけで ( の奇妙な処理を除いて)、実際にはrax
言及されていないことがわかります。do_something_else
によってほぼ同じコードが生成されgcc
ます。