Why is unsigned short (multiply) unsigned short converted to signed int? [duplicate]

*爱你&永不变心* 提交于 2021-01-21 12:07:18

问题


Why is unsigned short * unsigned short converted to int in C++11?

The int is too small to handle max values as demonstrated by this line of code.

cout << USHRT_MAX * USHRT_MAX << endl;

overflows on MinGW 4.9.2

-131071

because (source)

USHRT_MAX = 65535 (2^16-1) or greater*

INT_MAX = 32767 (2^15-1) or greater*

and (2^16-1)*(2^16-1) = ~2^32.


Should I expect any problems with this solution?

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

This program

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

gives output

t
i

on

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

with both

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

which proves that t*t is converted to int on these compilers.


Usefull resources:

Signed to unsigned conversion in C - is it always safe?

Signed & unsigned integer multiplication

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


Edit: I have demonstrated the problem on the following image.


回答1:


You may want to read about implicit conversions, especially the section about numeric promotions where it says

Prvalues of small integral types (such as char) may be converted to prvalues of larger integral types (such as int). In particular, arithmetic operators do not accept types smaller than int as arguments

What the above says is that if you use something smaller than int (like unsigned short) in an expression that involves arithmetic operators (which of course includes multiplication) then the values will be promoted to int.




回答2:


It's the usual arithmetic conversions in action.

Commonly called argument promotion, although the standard uses that term in a more restricted way (the eternal conflict between reasonable descriptive terms and standardese).

C++11 §5/9:

Many binary operators that expect operands of arithmetic or enumeration type cause conversions and yield result types in a similar way. The purpose is to yield a common type, which is also the type of the result. This pattern is called the usual arithmetic conversions […]

The paragraph goes on to describe the details, which amount to conversions up a ladder of more general types, until all arguments can be represented. The lowest rung on this ladder is integral promotion of both operands of a binary operation, so at least that is performed (but the conversion can start at a higher rung). And integral promotion starts with this:

C++11 §4.5/1:

A prvalue of an integer type other than bool, char16_t, char32_t, or wchar_t whose integer conversion rank (4.13) is less than the rank of int can be converted to a prvalue of type int if int can represent all the values of the source type; otherwise, the source prvalue can be converted to a prvalue of type unsigned int

Crucially, this is about types, not arithmetic expressions. In your case the arguments of the multiplication operator * are converted to int. Then the multiplication is performed as an int multiplication, yielding an int result.




回答3:


As pointed out by Paolo M in comments, USHRT_MAX has type int (this is specified by 5.2.4.2.1/1: all such macros have a type at least as big as int).

So USHRT_MAX * USHRT_MAX is already an int x int, no promotions occur.

This invokes signed integer overflow on your system, causing undefined behaviour.


Regarding the proposed solution:

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

This does not help because t*t itself causes undefined behaviour due to signed integer overflow. As explained by the other answers, t is promoted to int before the multiplication occurs, for historical reasons.

Instead you could use:

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

which, after integer promotion, is an unsigned int multiplied by an int; and then according to the rest of the usual arithmetic conversions, the int is promoted to unsigned int, and a well-defined modular multiplication occurs.




回答4:


With integer promotion rules

USHRT_MAX value is promoted to int. then we do the multiplication of 2 int (with possible overflow).




回答5:


It seems that nobody has answered this part of the question yet:

Should I expect any problems with this solution?

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

Yes, there is a problem here: it first computes t*t and allows it to overflow, then it converts the result to unsigned. Integer overflow causes undefined behavior according to the C++ standard (even though it may always work fine in practice). The correct solution is:

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

Note that the second t is promoted to unsigned before the multiplication because the first operand is unsigned.




回答6:


As it has been pointed out by other answers, this happens due to integer promotion rules.

The simplest way to avoid the conversion from an unsigned type with a smaller rank than a signed type with a larger rank, is to make sure the conversion is done into an unsigned int and not int.

This is done by multiplying by the value 1 that is of type unsigned int. Due to 1 being a multiplicative identity, the result will remain unchanged:

unsigned short c = t * 1U * t;

First the operands t and 1U are evaluated. Left operand is signed and has a smaller rank than the unsigned right operand, so it gets converted to the type of the right operand. Then the operands are multiplied and the same happens with the result and the remaining right operand. The last paragraph in the Standard cited below is used for this promotion.

Otherwise, the integer promotions are performed on both operands. Then the following rules are applied to the promoted operands:

-If both operands have the same type, then no further conversion is needed.

-Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.

-Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.



来源:https://stackoverflow.com/questions/33731158/why-is-unsigned-short-multiply-unsigned-short-converted-to-signed-int

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!