ラップアラウンド動作が発生しています。
符号なし型は循環的です(一方、符号付き型は循環的である場合とそうでない場合がありますが、信頼すべきではない未定義の動作です)。つまり、可能な最小値より1つ小さい値が、可能な最大値です。次のスニペットを使用して、これを自分で示すことができます。
int main()
{
unsigned int x = 5;
for (int i = 0; i < 10; ++i) cout << x-- << endl;
return 0;
}
ゼロに達した後、xの値が表現可能な最大値である2^32-1にジャンプすることに気付くでしょう。さらに減算すると、期待どおりに機能します。
符号なし0から1を引くと、ビットパターンは次のように変化します。
0000 0000 0000 0000 0000 0000 0000 0000 // before (0)
1111 1111 1111 1111 1111 1111 1111 1111 // after (2^32 - 1)
符号なしの数値では、負の数値はゼロから減算された正の数値のように扱われます。したがって、(unsigned int) -10
に等しくなり((unsigned int) 0) - ((unsigned int) 10)
ます。
私はそれをunsignedintがより高精度の任意の値の下位32ビットであると考えるのが好きです。このような:
v imaginary high order bit
1 0000 0000 0000 0000 0000 0000 0000 0000 // before (2^32)
0 1111 1111 1111 1111 1111 1111 1111 1111 // after (2^32 - 1)
これらのオーバーフローの場合のunsignedintの動作は、256から1を引いたときのunsigned intの下位8ビットの動作とまったく同じです。このようにunsignedchar(1バイト)を見る方が理にかなっています。unsigned char
制限された精度が余分なビットを破棄するため、にキャストされた場合、値0と256は等しいためです。
0 0000 0000 0000 0000 0000 0001 0000 0000 // before (256)
0 0000 0000 0000 0000 0000 0000 1111 1111 // before (255)
他の人が指摘しているように、これはモジュロ算術と呼ばれます。高精度の値を使用して、高次のビットをマスクするため、ラップアラウンド時に行われる遷移を視覚化するのに役立ちます。それが何であったかは関係ないので、それは何でもかまいません、それはただ捨てられます。整数はモジュラス2^32を超える値であるため、2^32の倍数は整数のスペースでゼロに等しくなります。だから私は最後に余分なビットがあるふりをして逃げることができます。
次のステートメントで使用されているように、プログラムで2 ^ 32以外の数値を計算する必要がある場合に備えて、モジュラス演算には専用の演算子があります。
int forty_mod_twelve = 40 % 12;
// value is 4: 4 + n * 12 == 40 for some whole number n
2の累乗(2 ^ 32など)でのモジュラス演算は、上位ビットをマスクするように直接単純化されます。64ビット整数を2 ^ 32を法として計算すると、値は変換した場合とまったく同じになります。符号なし整数に。
01011010 01011100 10000001 00001101 11111111 11111111 11111111 11111111 // before
00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111 // after
プログラマーは、このプロパティを使用してプログラムを高速化することを好みます。これは、いくつかのビットを簡単に切り落とすことができるためですが、モジュラス演算の実行ははるかに困難です(除算を実行するのと同じくらい困難です)。
それは理にかなっていますか?