なぜ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
に変換されます。
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
編集:次の画像で問題を示しました。
これは、通常の算術変換の動作です。
一般に引数promotionと呼ばれますが、標準ではその用語をより制限された方法で使用します(合理的な説明用語と標準との間の永遠の対立)。
”算術型または列挙型のオペランドを期待する多くの二項演算子は、同様の方法で変換を引き起こし、結果型を生成します。目的は、結果のタイプでもある共通のタイプを生成することです。このパターンは、通常の算術変換と呼ばれます[…]
この段落では、すべての引数を表現できるようになるまで、より一般的なタイプのはしごを上る変換に相当する詳細について説明します。このラダーの最低のラングは、二項演算の両方のオペランドの汎整数拡張であるため、少なくともそれが実行されます(ただし、変換はより高いラングで開始できます)。そして、汎整数拡張はこれから始まります:
”
bool
、char16_t
、char32_t
、またはwchar_t
以外の整数型のprvalueで、整数変換ランク(4.13)が小さいint
がソースタイプのすべての値を表すことができる場合、int
のランクをタイプint
のprvalueに変換できます。それ以外の場合は、ソースprvalueをタイプunsigned int
のprvalueに変換できます。
重要なのは、これは算術式ではなく、型に関するものです。あなたの場合、乗算演算子*
の引数はint
に変換されます。次に、乗算はint
乗算として実行され、int
の結果が得られます。
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
自体が符号付き整数オーバーフローのために未定義の動作を引き起こすため、これは役に立ちません。他の回答で説明されているように、歴史的な理由から、乗算が発生する前にt
はint
に昇格されます。
代わりに、次のものを使用できます。
auto u = static_cast<unsigned int>(t) * t;
これは、integer Promotionの後、unsigned int
にint
を掛けたものです。次に、残りの通常の算術変換に従って、int
がunsigned int
にプロモートされ、明確に定義されたモジュラー乗算が発生します。
整数拡張ルールあり
USHRT_MAX
値はint
にプロモートされます。次に、2 intの乗算を実行します(オーバーフローの可能性があります)。
質問のこの部分にはまだ誰も答えていないようです。
このソリューションで問題が発生する可能性がありますか?
u = static_cast<unsigned>(t*t);
はい、ここに問題があります。最初にt*t
を計算してオーバーフローさせ、次に結果をunsigned
に変換します。整数オーバーフローは、C++標準に従って未定義の動作を引き起こします(実際には常に正常に機能する場合があります)。正しい解決策は次のとおりです。
u = static_cast<unsigned>(t)*t;
最初のオペランドはt
であるため、2番目のunsigned
は乗算の前にunsigned
にプロモートされることに注意してください。
他の回答で指摘されているように、これは整数拡張ルールが原因で発生します。
ランクが大きい符号付き型よりもランクが小さい符号なし型からの変換を回避する最も簡単な方法は、変換がint
ではなくunsigned int
に行われるようにすることです。
これは、unsignedint型の値1を掛けることによって行われます。 1は乗法的単位元であるため、結果は変更されません。
unsigned short c = t * 1U * t;
最初に、オペランドtと1Uが評価されます。左のオペランドは符号付きで、符号なしの右のオペランドよりもランクが小さいため、右のオペランドの型に変換されます。次に、オペランドが乗算され、結果と残りの右側のオペランドについても同じことが起こります。以下に引用されている基準の最後の段落は、このプロモーションに使用されます。
それ以外の場合、整数昇格は両方のオペランドで実行されます。次に、プロモートされたオペランドに次のルールが適用されます。
-両方のオペランドのタイプが同じである場合、それ以上の変換は必要ありません。
-それ以外の場合、両方のオペランドが符号付き整数型であるか、両方が符号なし整数型である場合、整数変換ランクが小さい方のオペランドは、ランクが大きい方のオペランドの型に変換されます。
-それ以外の場合、符号なし整数型のオペランドのランクが他のオペランドの型のランク以上の場合、符号付き整数型のオペランドは符号なし整数型のオペランドの型に変換されます。