web-dev-qa-db-ja.com

unsigned short(乗算)unsignedshortがsignedintに変換されるのはなぜですか?

なぜunsigned short * unsigned short C++ 11でintに変換されましたか?

このコード行で示されているように、intは小さすぎて最大値を処理できません。

cout << USHRT_MAX * USHRT_MAX << endl;

minGW4.9.2でオーバーフロー

-131071

なぜなら( ソース

USHRT_MAX = 65535(2 ^ 16-1)以上*

INT_MAX = 32767(2 ^ 15-1)以上*

および(2^16-1)*(2^16-1) = ~2^32


このソリューションに問題があると予想する必要がありますか?

unsigned u = static_cast<unsigned>(t*t);

このプログラム

unsigned short t;
cout<<typeid(t).name()<<endl;
cout<<typeid(t*t).name()<<endl;

出力を与える

t
i

オン

gcc version 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
gcc version 4.8.2 (GCC)
MinGW 4.9.2

双方と

g++ p.cpp
g++ -std=c++11 p.cpp

これはt*tは、これらのコンパイラでintに変換されます。


有用なリソース:

Cでの符号付きから符号なしへの変換-常に安全ですか?

符号付きおよび符号なし整数の乗算

https://bytes.com/topic/c-sharp/answers/223883-multiplication-types-smaller-than-int-yields-int

http://www.cplusplus.com/reference/climits

http://en.cppreference.com/w/cpp/language/types


編集:次の画像で問題を示しました。

enter image description here

22
Slazer

暗黙の変換 について、特に 数値プロモーション についてのセクションを読むことをお勧めします。

小さい整数型(char)など)のprvalueは、大きい整数型(intなど)のprvalueに変換できます。特に 算術演算子 小さい型は受け入れません引数としてintより

上記のことは、 算術演算子 (もちろん乗算を含む)を含む式でintよりも小さいもの(unsigned shortなど)を使用する場合、値はintに昇格します。

12

これは、通常の算術変換の動作です。

一般に引数promotionと呼ばれますが、標準ではその用語をより制限された方法で使用します(合理的な説明用語と標準との間の永遠の対立)。

算術型または列挙型のオペランドを期待する多くの二項演算子は、同様の方法で変換を引き起こし、結果型を生成します。目的は、結果のタイプでもある共通のタイプを生成することです。このパターンは、通常の算術変換と呼ばれます[…]

この段落では、すべての引数を表現できるようになるまで、より一般的なタイプのはしごを上る変換に相当する詳細について説明します。このラダーの最低のラングは、二項演算の両方のオペランドの汎整数拡張であるため、少なくともそれが実行されます(ただし、変換はより高いラングで開始できます)。そして、汎整数拡張はこれから始まります:

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

重要なのは、これは算術式ではなく、型に関するものです。あなたの場合、乗算演算子*の引数はintに変換されます。次に、乗算はint乗算として実行され、intの結果が得られます。

10

Paolo Mがコメントで指摘しているように、USHRT_MAXのタイプはintです(これは5.2.4.2.1/1で指定されています。このようなマクロはすべて、少なくともint)。

したがって、USHRT_MAX * USHRT_MAXはすでにint x intであり、プロモーションは発生しません。

これにより、システムで符号付き整数オーバーフローが発生し、未定義の動作が発生します。


提案された解決策について:

unsigned u = static_cast<unsigned>(t*t);

t*t自体が符号付き整数オーバーフローのために未定義の動作を引き起こすため、これは役に立ちません。他の回答で説明されているように、歴史的な理由から、乗算が発生する前にtintに昇格されます。

代わりに、次のものを使用できます。

auto u = static_cast<unsigned int>(t) * t;

これは、integer Promotionの後、unsigned intintを掛けたものです。次に、残りの通常の算術変換に従って、intunsigned intにプロモートされ、明確に定義されたモジュラー乗算が発生します。

6
M.M

整数拡張ルールあり

USHRT_MAX値はintにプロモートされます。次に、2 intの乗算を実行します(オーバーフローの可能性があります)。

5
Jarod42

質問のこの部分にはまだ誰も答えていないようです。

このソリューションで問題が発生する可能性がありますか?

u = static_cast<unsigned>(t*t);

はい、ここに問題があります。最初にt*tを計算してオーバーフローさせ、次に結果をunsignedに変換します。整数オーバーフローは、C++標準に従って未定義の動作を引き起こします(実際には常に正常に機能する場合があります)。正しい解決策は次のとおりです。

u = static_cast<unsigned>(t)*t;

最初のオペランドはtであるため、2番目のunsignedは乗算の前にunsignedにプロモートされることに注意してください。

4
Eugene

他の回答で指摘されているように、これは整数拡張ルールが原因で発生します。

ランクが大きい符号付き型よりもランクが小さい符号なし型からの変換を回避する最も簡単な方法は、変換がintではなくunsigned intに行われるようにすることです。

これは、unsignedint型の値1を掛けることによって行われます。 1は乗法的単位元であるため、結果は変更されません。

unsigned short c = t * 1U * t;

最初に、オペランドtと1Uが評価されます。左のオペランドは符号付きで、符号なしの右のオペランドよりもランクが小さいため、右のオペランドの型に変換されます。次に、オペランドが乗算され、結果と残りの右側のオペランドについても同じことが起こります。以下に引用されている基準の最後の段落は、このプロモーションに使用されます。

それ以外の場合、整数昇格は両方のオペランドで実行されます。次に、プロモートされたオペランドに次のルールが適用されます。

-両方のオペランドのタイプが同じである場合、それ以上の変換は必要ありません。

-それ以外の場合、両方のオペランドが符号付き整数型であるか、両方が符号なし整数型である場合、整数変換ランクが小さい方のオペランドは、ランクが大きい方のオペランドの型に変換されます。

-それ以外の場合、符号なし整数型のオペランドのランクが他のオペランドの型のランク以上の場合、符号付き整数型のオペランドは符号なし整数型のオペランドの型に変換されます。

3
this