4

変換と操作がCで定義される方法のため、符号付き変数と符号なし変数のどちらを使用するかはめったに問題にならないようです。

uint8_t u; int8_t i;

u = -3;    i = -3;
u *= 2;    i *= 2;
u += 15;   i += 15;
u >>= 2;   i >>= 2;

printf("%u",u); // -> 2
printf("%u",i); // -> 2

では、どの条件で変数の符号が実際に違いを生むかを判断するための一連のルールはありますか?

4

6 に答える 6

9

これらのコンテキストでは重要です。

  • 除算とモジュロ:-2/2 = 1、、、、-2u/2 = UINT_MAX/2-1-3%4 = -3-3u%4 = 1
  • シフトします。負の符号付きの値の場合、>>およびの結果<<は実装で定義されているか、定義されていないか、それぞれです。符号なしの値の場合、それらは常に定義されます。
  • リレーショナル-2 < 0-2u > 0
  • オーバーフローします。コンパイラは、符号付きタイプの場合はx+1 > x常にtrueであると見なすことができます。 x
于 2011-11-07T17:30:19.103 に答える
8

はい。符号は、Cの大なり記号と小なり記号の結果に影響します。次のコードを検討してください。

unsigned int a = -5;
unsigned int b = 7;

if (a < b)
    printf("Less");
else
    printf("More");

この例では、コンパイラによって-5が非常に高い正の数に変換されるため、「More」が誤って出力されます。

これは、さまざまなサイズの変数を使用した算術にも影響します。繰り返しますが、この例を考えてみましょう。

unsigned char a = -5;
signed short b = 12;

printf("%d", a+b);

返される結果は263であり、予期された7ではありません。これは、コンパイラによって-5が実際に251として扱われるためです。オーバーフローにより、同じサイズの変数に対して操作が正しく機能しますが、展開時に、コンパイラーは符号なし変数の符号ビットを展開しないため、より大きなサイズの空間での元の正の表現として扱います。2つの褒め言葉がどのように機能するかを研究すると、この結果がどこから来るのかがわかります。

于 2011-11-07T17:18:22.690 に答える
6

これは、変数に格納できる値の範囲に影響します。

于 2011-11-07T17:17:18.807 に答える
3

主に比較して関係があります。

printf("%d", (u-3) < 0); // -> 0
printf("%d", (i-3) < 0); // -> 1
于 2011-11-07T17:17:06.023 に答える
3

符号なし整数のオーバーフローは、ラップアラウンドするだけです。符号付きの値では、これは未定義の動作であり、すべてが発生する可能性があります。

于 2011-11-07T17:18:44.970 に答える
2

2の補数の符号は、単に数値をどのように解釈するかによって決まります。3ビットの数字を想像してみてください。

000
001
010
011
100
101
110
111

ゼロと人間にとって自然な数と考えると000、次のように解釈されます。

000: 0
001: 1
010: 2
011: 3
100: 4
101: 5
110: 6
111: 7

これは「符号なし整数」と呼ばれます。あなたはすべてをゼロより大きい/等しい数として見ます。

さて、あなたがいくつかの数を負にしたいとしたらどうしますか?さて、2の補数が助けになります。2の補数は、ほとんどの人に単なる数式として知られていますが、実際には、2 ^ nを法とする合同です。ここで、nは数値のビット数です。

合同の例をいくつか挙げましょう。

2 = 5 = 8 = -1 = -4 module 3
-2 = 6 = 14 module 8

ここで、便宜上、数値の左端のビットをその符号として使用することにしたとします。だからあなたは持っていたい:

000: 0
001: positive
010: positive
011: positive
100: negative
101: negative
110: negative
111: negative

2 ^ 3(= 8)を法として一致する数値を表示すると、次のことがわかります。

4 = -4
5 = -3
6 = -2
7 = -1

したがって、あなたはあなたの番号を次のように見ます:

000: 0
001: 1
010: 2
011: 3
100: -4
101: -3
110: -2
111: -1

ご覧のとおり、-3と5の実際のビット(たとえば)は同じです(数値が3ビットの場合)。x = -3したがって、またはを書くx = 5と同じ結果が得られます。

2 ^ nを法として一致する数値を解釈することには、他の利点があります。マイナスとプラスの2つの数字を合計すると、紙の上でキャリーが捨てられる可能性がありますが、結果は正しいです。なんで?そのキャリーは2^nであり、これは2^nを法として0に一致します。便利じゃないですか?

オーバーフローも合同のもう1つのケースです。この例では、2つの符号なし数値5と6を合計すると、3、つまり実際には11になります。

では、なぜ符号付きと符号なしを使用するのですか?CPUの場合、実際にはほとんど違いはありません。しかしあなたのために:

  • 数値がnビットの場合、unsignedは0から2^n-1までの数値を表します
  • 数値がnビットの場合、符号付きは-2 ^(n-1)から2 ^(n-1)-1までの数値を表します

したがって、たとえば、符号なしの数値に-1を割り当てる場合、2^n-1を割り当てるのと同じです。

あなたの例によると、それはまさにあなたがしていることです。uint8_tに-3を割り当てていますが、これは不正ですが、CPUに関する限り、253を割り当てています。次に、残りのすべての操作は両方のタイプで同じであり、最終的に同じ結果が得られます。

ただし、例で見逃している点があります。符号付き数値の演算子>>は、シフト時に符号を拡張します。シフトする前の両方の操作の結果は9であるため、これに気付くことはありません。+15がなかった場合は、-6インチiと250インチuになり、>> 2結果は-2i%u、254で印刷された場合)と62インチになりuます。(いくつかの技術については、以下のPeter Cordesのコメントを参照してください)

これをよりよく理解するために、次の例を見てください。

  (signed)101011 (-21) >> 3 ----> 111101 (-3)
(unsigned)101011 ( 43) >> 3 ----> 000101 ( 5)

お気づきのように、floor(-21/8)は実際には-3で、floor(43/8)は5です。ただし、-3と5は等しくありません(64を法として合同ではありません(6ビットがあるため64))。

于 2011-11-07T17:38:02.347 に答える