115

結果が負になるときに、同じタイプの別の整数から符号なし整数を減算する際に問題があると思われる人からのコードに出くわしました。そのため、このようなコードは、ほとんどのアーキテクチャで機能する場合でも正しくありません。

unsigned int To, Tf;

To = getcounter();
while (1) {
    Tf = getcounter();
    if ((Tf-To) >= TIME_LIMIT) {
        break;
    } 
}

これは私が見つけたC標準からの漠然と関連する唯一の引用です。

結果の符号なし整数型で表現できない結果は、結果の型で表現できる最大値より1大きい数を法として減少するため、符号なしオペランドを含む計算はオーバーフローすることはありません。

その引用符は、右のオペランドが大きい場合、モジュロ切り捨て数のコンテキストで意味を持つように演算が調整されることを意味すると考えられます。

すなわち

0x0000-0x0001 == 0x 1 0000-0x0001 == 0xFFFF

実装に依存する署名付きセマンティクスを使用するのとは対照的に:

0x0000-0x0001 ==(符号なし)(0 + -1)==(0xFFFFだけでなく、0xFFFEまたは0x8001)

どちらまたはどのような解釈が正しいですか?それはまったく定義されていますか?

4

6 に答える 6

136

符号なし型を使用する場合、モジュラー演算( 「ラップアラウンド」動作とも呼ばれます)が実行されます。このモジュラー演算を理解するには、次のクロックを見てください。

ここに画像の説明を入力してください

9 + 4 = 113 mod 12)なので、反対方向には1-4 = 9-3 mod 12)になります。符号なしタイプを操作する場合も、同じ原則が適用されます。結果のタイプが、の場合unsigned、モジュラー演算が実行されます。


unsigned int次に、結果を:として保存する次の操作を見てください。

unsigned int five = 5, seven = 7;
unsigned int a = five - seven;      // a = (-2 % 2^32) = 4294967294 

int one = 1, six = 6;
unsigned int b = one - six;         // b = (-5 % 2^32) = 4294967291

結果がであることを確認したい場合はsigned、それをsigned変数に格納するか、にキャストしsignedます。数値の差を取得し、モジュラー演算が適用されないことを確認する場合は、次のようにabs()定義された関数の使用を検討する必要がありstdlib.hます。

int c = five - seven;       // c = -2
int d = abs(five - seven);  // d =  2

特に条件を書き込むときは、次の理由から非常に注意してください。

if (abs(five - seven) < seven)  // = if (2 < 7)
    // ...

if (five - seven < -1)          // = if (-2 < -1)
    // ...

if (one - six < 1)              // = if (-5 < 1)
    // ...

if ((int)(five - seven) < 1)    // = if (-2 < 1)
    // ...

しかし

if (five - seven < 1)   // = if ((unsigned int)-2 < 1) = if (4294967294 < 1)
    // ...

if (one - six < five)   // = if ((unsigned int)-5 < 5) = if (4294967291 < 5)
    // ...
于 2013-02-22T17:56:56.963 に答える
118

符号なし型で負の数を生成する減算の結果は、明確に定義されています。

  1. [...]結果の符号なし整数型で表現できない結果は、結果の型で表現できる最大値より1大きい数を法として減少するため、符号なしオペランドを含む計算はオーバーフローすることはありません。(ISO / IEC 9899:1999(E)§6.2.5/ 9)

ご覧のとおり、(unsigned)0 - (unsigned)1UINT_MAX + 1を法として-1、つまりUINT_MAXに等しくなります。

「符号なしオペランドを含む計算はオーバーフローしない」と書かれているため、上限を超えた場合にのみ適用されると思われるかもしれませんが、これは文の実際のバインディング部分の動機として提示されていることに注意してください。結果の符号なし整数型で表現できない結果は、結果の型で表現できる最大値より1大きい数を法として減少します。」この句は、型の上限のオーバーフローに制限されず、表現するには低すぎる値にも同様に適用されます。

于 2011-08-28T14:06:32.693 に答える
5

さて、最初の解釈は正しいです。ただし、このコンテキストでの「署名されたセマンティクス」についてのあなたの推論は間違っています。

繰り返しますが、最初の解釈は正しいです。符号なし演算は、モジュロ演算の規則に従います。つまり、32ビットの符号なし型に対して0x0000 - 0x0001評価されます。0xFFFF

ただし、同じ結果を生成するには、2番目の解釈(「署名されたセマンティクス」に基づく解釈)も必要です。0 - 1つまり、符号付き型の定義域で評価し-1、中間結果として取得した場合でも、後で符号なし型に変換されるときに-1生成する必要があります。0xFFFF一部のプラットフォームが符号付き整数(1の補数、符号付きの大きさ)にエキゾチックな表現を使用している場合でも、このプラットフォームでは、符号付き整数値を符号なし整数に変換するときに、モジュロ算術の規則を適用する必要があります。

たとえば、この評価

signed int a = 0, b = 1;
unsigned int c = a - b;

プラットフォームが符号付き整数にエキゾチックな表現を使用している場合でも、はで生成UINT_MAXされることが保証されています。c

于 2013-02-22T18:22:17.620 に答える
4

タイプ以上の符号なし数値のunsigned int場合、型変換がない場合はa-b、符号なし数値を生成することとして定義されます。これをに追加するとb、が生成されaます。負の数を符号なしに変換すると、符号が反転した元の数に加算されるとゼロになる数値が生成されると定義されます(したがって、-5を符号なしに変換すると値が生成され、5に加算するとゼロになります)。 。

減算の前unsigned intに型に昇格される可能性があるよりも小さい符号なし数値に注意してください。の動作はのサイズによって異なります。inta-bint

于 2014-01-09T17:22:18.723 に答える
0

さて、符号なし整数の減算は動作を定義しました、またそれはトリッキーなことです。2つの符号なし整数を減算する場合、result(lvalue)型が明示的に指定されていない場合、resultは上位型intにプロモートされます。後者の場合、たとえば、int8_t result = a --b; (aとbがint8_tタイプの場合)非常に奇妙な動作をする可能性があります。つまり、推移性のプロパティが失われる可能性があります(つまり、a>bおよびb>cの場合、a> cであることは事実です)。推移性が失われると、ツリータイプのデータ構造の作業が破壊される可能性があります。ソート、検索、符号なし整数減算を使用してどちらのキーが高いか低いかを推測するツリー構築のための比較機能を提供しないように注意する必要があります。

以下の例を参照してください。

#include <stdint.h>
#include <stdio.h>

void main()
{
    uint8_t a = 255;
    uint8_t b = 100;
    uint8_t c = 150;

    printf("uint8_t a = %+d, b = %+d, c = %+d\n\n", a, b, c);

    printf("          b - a  = %+d\tpromotion to int type\n"
           " (int8_t)(b - a) = %+d\n\n"
           "          b + a  = %+d\tpromotion to int type\n"
           "(uint8_t)(b + a) = %+d\tmodular arithmetic\n"
           "     b + a %% %d = %+d\n\n", 
           b - a,  (int8_t)(b - a), 
           b + a, (uint8_t)(b + a),
           UINT8_MAX + 1,
           (b + a) % (UINT8_MAX + 1));

    printf("c %s b (b - c = %d), b %s a (b - a = %d), AND c %s a (c - a = %d)\n",
           (int8_t)(c - b) < 0 ? "<" : ">", (int8_t)(c - b),
           (int8_t)(b - a) < 0 ? "<" : ">", (int8_t)(b - a),
           (int8_t)(c - a) < 0 ? "<" : ">", (int8_t)(c - a));
}
$ ./a.out 
uint8_t a = +255, b = +100, c = +150

          b - a  = -155 promotion to int type
 (int8_t)(b - a) = +101

          b + a  = +355 promotion to int type
(uint8_t)(b + a) = +99  modular arithmetic
     b + a % 256 = +99

c > b (b - c = 50), b > a (b - a = 101), AND c < a (c - a = -105)
于 2021-11-16T09:59:51.637 に答える
0
int d = abs(five - seven);  // d =  2

std :: absは、符号なし整数には「適していません」。ただし、キャストが必要です。

于 2021-11-26T09:58:56.347 に答える