2015-01-13 8 views
7

2つの符号なし8ビット値を取り、それらを減算して、この値を32ビットアキュムレータに追加する必要があります。 8ビットの減算はアンダーフローする可能性があります(unsigned intのアンダーフローは定義された動作なので問題ありません)。式のstatic_castが分散して動作するのはなぜですか?

static_cast<uint32_t>(foo - bar)は、私が欲しいと思うことを期待しています(foobarはともにuint8_tです)。しかし、これが最初にキャストし、と表示され、は32ビットの減算を行いますが、8ビットの変数としてアンダーフローする必要があります。私はちょうど256をmodすることができることを知っていますが、私はを理解しようとしていますなぜこの方法で動作します。ここ

例:https://ideone.com/TwOmTO

uint8_t foo = 5; 
uint8_t bar = 250; 

uint8_t diff8bit = foo - bar; 
uint32_t diff1 = static_cast<uint32_t>(diff8bit); 

uint32_t diff2 = static_cast<uint32_t>(foo) - static_cast<uint32_t>(bar); 

uint32_t diff3 = static_cast<uint32_t>(foo - bar); 

printf("diff1 = %u\n", diff1); 
printf("diff2 = %u\n", diff2); 
printf("diff3 = %u\n", diff3); 

出力:

diff1 = 11 
diff2 = 4294967051 
diff3 = 4294967051 

私はdiff3diff1と同じ動作を持っているでしょう疑うだろうが、それは実際にdiff2と同じです。

これはどうしてですか?コンパイラが2つの8ビット値を減算してから32ビットにキャストしなければならないと言うことができる限り、明らかにそうではありません。これは、static_castが式でどのように動作するかの仕様と関係していますか?

+3

よりも狭い精度で直接計算を実行することは不可能です。これは 'static_cast'ではなく、減算式のために適用されます。 – dyp

+0

なぜdiff2はdiff1と同じだと思いますか? diff2は明らかに二つの 'uint32_t'を使って減算を行っています。 diff3の結果 - コンパイラは正しいことを決定しました(これは正しい答えを与えます)。それが何か他のことをしたいなら、あなたはそれにそれを伝える必要があります。 – mbgda

+0

@mbgda diff3とdiff1が同じであることを期待しています。 –

答えて

8

ほとんどの算術演算子(-を含む)の場合、オペランドは通常の算術変換に従います。これらの変換の1つは、intより狭いタイプの値がintに昇格されることです。 (標準参照番号:[expr]/10)。

したがって、式foo - bar(int)foo - (int)barとなり、(int)-245となります。それからあなたはそれを大きい正の数を与えるuint32_tに投げつけます。

あなたが意図している結果を得るには、uint32_tの代わりにuint8_tにキャストしてください。あるいは、キャスト結果のモジュラス演算子%uint32_tに使用します。

*通常の算術変換を介し*積分プロモーション*の一例であるint

+1

面白い、ありがとう!ですから、これが8ビットCPU上で行われていれば、当初期待していた結果が得られましたか? –

+1

@KeytarHero C++標準では、intが少なくとも16ビットであることを指定しています(8ビットCPUの場合、コンパイラは標準の要件を満たすためにいくつかのレジスタペアを使用する必要があります) –

4

問題ではないstatic_castなく減算、加法演算子のオペランドが減算の両方のオペランドをもたらす積分プロモーションINTに昇格され、それらにし、この場合に適用される通常の算術変換を持っている:

一方
static_cast<uint32_t>(foo - bar); 
         ^^^ ^^^ 

static_cast<uint8_t>(foo - bar); 

は、所望の結果をもたらすであろう。ドラフトC++標準のセクション5.7[expr.add]から

は言う:

添加演算子+と - グループ左から右へ。通常の算術変換は、算術または列挙型のオペランドの に対して実行されます。

これは、[式]言う積分プロモーション、セクション5もたらす:

そうでない場合、積分プロモーション(4.5)は、両方のオペランド結果

上で実行されなければなりません両方のオペランドがintに変換され、セクション4.5[conv。PROM]は言う:

その整数変換 ランク(4.13)INTのランクよりも小さいタイプのprvalueに変換することができるBOOL、char16_t、char32_t、またはwchar_t型以外の整数型のprvalue int intがすべてソースタイプの値を で表すことができます。それ以外の場合、ソースprvalueは、unsigned int型のprvalueに変換できます。

、次いでのuint32_tにはstatic_castは [conv.integral]次のようにセクション4.7で定義されている変換をもたらす適用される:

宛先タイプが符号なしの場合、結果の値は、ソース の整数(モジュロ2n、nは符号なしタイプを表すために使用されるビットの数)に一致する最小の符号なし整数です。 [int型よりも小さいタイプは算術演算のために推進されている理由を説明しWhy must a short be converted to an int before arithmetic operations in C and C++?

質問。

関連する問題