2011-08-28 31 views
69

結果が負の場合に同じ型の別の整数から符号なし整数を減算する際に問題があると思われる人からのコードを見つけました。このようなコードは、ほとんどのアーキテクチャでうまく動作しても間違っています。符号なし整数減算は動作を定義していますか?

unsigned int To, Tf; 

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

これは、私が見つけることができるC標準の曖昧な唯一の引用です。

符号なしオペランドを含む計算することができる決して超える流動、低減され、得られた符号なし整数 型で表現することができない 結果は、で表すことができる最大 値より1大きい数を法とするので結果の型。

右のオペランドが大きいときは、モジュロ・トランケートされた数値のコンテキストで操作が意味をなさないように調整されていると思います。

は0x0000

すなわち - は0x0001 == 0X 0000 - 0xFFFFの

== 0x0001の実装に依存する署名されたセマンティクスを使用するのではなくとして:

0000 - は0x0001 ==(符号なし) (0 + -1)==(0xFFFFでも0xFFFEまたは0x8001)

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

+3

標準の単語の選択は不幸です。 「オーバーフローできない」というのは、エラー状況ではないことを意味します。オーバーフローするのではなく、標準の用語を使用して値が「ラップする」。 – danorton

答えて

81

unsigned型で負の値を生成する減算結果が明確に定義されている:

  1. [...]オーバーフロー、 符号なしオペランドを決してでき伴わない計算できないため、結果結果の符号なし整数型で表現されるのは、 であり、結果の型によって表される最大値の1より大きい数を減じたものです。 (ISO/IEC 9899:1999(E)§6.2.5/ 9)

あなたが見ることができるように、(unsigned)0 - (unsigned)1は-1に等しいモジュロUINT_MAX + 1、すなわち、UINT_MAX。それは言ってないが、「符号なしのオペランドを含む計算がオーバーフロー決してすることができます」それが唯一の上限を超えるために適用されることを信じるようにあなたを導く可能性がある、これは実際の結合部分のモチベーションとして提示されていないことを

注意"結果の符号なし整数型で表現できない結果は、 の結果の型で表される最大値よりも1大きい数を法としたものになります。"この句は、型の上限のオーバーフローに限定されず、表現するには低すぎる値にも等しく適用されます。

+2

ありがとう!私は今、私が見逃していた解釈を見ます。私は彼らがより明確な言葉遣いを選ぶことができたと思う。 – jbcreix

+1

「uint」はいつも数学的な[リング](https://en.wikipedia.org/)を表すことを意図していたので、無署名の追加が0に転がり、 UINT_MAX + 1 'を加算して乗算した演算で、UINT_MAXからUINT_MAXまでの整数のwiki/Ring_(数学))であり、オーバーフローではないからである。しかし、リングがそのような基本的なデータ型である場合、その言語は他のサイズのリングには一般的なサポートを提供しません。 –

+0

@TheodoreMurdock私はその質問に対する答えは簡単だと思います。私が言うことができる限り、それがリングであるという事実は結果であり、原因ではありません。本当の必要条件は、符号なしの型はすべてのビットが値表現に関与していなければならないということです。リング状の振る舞いは自然に流れます。あなたが他の型からこのような振る舞いを望むなら、必要なモジュラスを適用して算術演算を行います。基本的な演算子を使用します。 –

91

あなたは符号なし種類、modular arithmeticで動作(も行動を「ラップアラウンド」として知られている)で行われています。他の方向にあるので、

enter image description here

9 + 4 = 1(1213 MOD):このモジュラ算術を理解するだけでこれらのクロックを見て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にキャスト。あなたは数の差を取得し、モジュラー算術が適用されないことを確認するときに、あなたはstdlib.hで定義されたabs()機能使用を検討してください:

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) 
    // ... 
+2

時計付きのいい人ですが、_proof_は正しい答えになります。質問の前提には、これがすべて真実かもしれないという主張がすでに含まれています。 –

+4

@ LightnessRacesinOrbit:ありがとうございます。私は誰かがそれが非常に役に立つと思うかもしれないと思うので、私はそれを書きました。私はそれが完全な答えではないことに同意します。 – LihO

+0

私は同意するので、とにかくupvoteを持っています; –

3

まあ、最初の解釈は正しい。しかし、この文脈での "署名された意味論"についてのあなたの推論は間違っています。

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

しかし、同じ結果を得るには、2番目の解釈(「符号付きセマンティクス」に基づく解釈)も必要です。私。署名付きタイプのドメインで0 - 1を評価し、中間結果として-1を取得したとしても、を生成する必要があります(後で符号なしタイプに変換する場合)。プラットフォームによっては、符号付き整数(1の補数、符号付きの大きさ)にエキゾチックな表現を使用していても、符号付き整数値を符号なし整数に変換する際にモジュロ算術のルールを適用する必要があります。例えば

、この評価

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

は依然としてプラットフォームが符号付き整数のエキゾチックな表現を使用した場合でも、cUINT_MAXを生成することが保証されています。タイプunsigned int以上の符号なしの数で

+1

私は32ビットではなく16ビットの符号なしタイプを意味すると思います。 – xioxox

2

は、型変換の非存在下で、a-bbに加え、符号なしの数を生じるように定義され、aをもたらします。負の数から符号なしへの変換は、符号反転した元の数に加えたときにゼロを生じる数を生じるように定義される(したがって-5を符号なしに変換すると、5に加算されたときにゼロとなる) 。 unsigned intよりも小さい符号なしの数値が減算前intを入力して昇進も

注、a-bの挙動は、intの大きさに依存します。

関連する問題