为什么unsigned short(multiply)unsigned short转换为signed int?

Sla*_*zer 22 c++ types type-conversion integer-promotion c++11

为什么unsigned short * unsigned short转换为intC++ 11?

int太小的这行代码为证明办理最大值.

cout << USHRT_MAX * USHRT_MAX << endl;
Run Code Online (Sandbox Code Playgroud)

在MinGW 4.9.2上溢出

-131071
Run Code Online (Sandbox Code Playgroud)

因为(来源)

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);
Run Code Online (Sandbox Code Playgroud)

这个计划

unsigned short t;
cout<<typeid(t).name()<<endl;
cout<<typeid(t*t).name()<<endl;
Run Code Online (Sandbox Code Playgroud)

给出输出

t
i
Run Code Online (Sandbox Code Playgroud)

gcc version 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC)
gcc version 4.8.2 (GCC)
MinGW 4.9.2
Run Code Online (Sandbox Code Playgroud)

g++ p.cpp
g++ -std=c++11 p.cpp
Run Code Online (Sandbox Code Playgroud)

这证明了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


编辑:我已经在下面的图像上演示了这个问题.

在此输入图像描述

Som*_*ude 12

你可能想了解隐式转换,尤其是对部分数值提升的地方说:

小整数类型的Prvalues(例如char)可以转换为较大整数类型的prvalues(例如int).特别是,算术运算符不接受小于int参数的类型

上面所说的是,如果你在一个涉及算术运算符的表达式中使用小于int(like unsigned short)的东西(当然包括乘法),那么值将被提升为.int

  • 这不是一个设计缺陷吗?特别是因为无符号类型的溢出是定义的行为,而签名类型的溢出不是?我会理解`char`和`short`会被提升为签名`int`但是会期望`unsigned char`和`unsigned short`会被提升为`unsigned int`以允许定义溢出...或者我是这里错误并且指定`unsigned short`算术运算的签名`int`结果可以安全地转换为`unsigned short`而不会激怒UB? (6认同)

Che*_*Alf 10

这是通常的算术转换.

通常称为论证推广,虽然该标准以更受限制的方式使用该术语(合理描述性术语与标准术之间的永恒冲突).

C++11§5/ 9:

"许多期望算术或枚举类型操作数的二元运算符会以类似的方式引起转换并产生结果类型.目的是产生一个通用类型,它也是结果的类型.这种模式称为通常的算术转换 [...]

该段继续描述细节,这些细节相当于转换为更一般类型的阶梯,直到所有参数都可以表示.此梯形图中的最低梯级是二进制操作的两个操作数的整体提升,因此至少执行该操作(但转换可以从更高的梯级开始).积分促销从这开始:

C++11§4.5/ 1:

"以外的整数类型的prvalue bool,char16_t,char32_t,或wchar_t,其整数转换秩(4.13)小于的秩int可以被转换成类型的prvalue int如果int可以表示源类型的所有值; 否则,源prvalue可以转换为类型的prvalueunsigned int

至关重要的是,这是关于类型,而不是算术表达式.在您的情况下,乘法运算符的参数*将转换为int.然后乘法作为乘法执行int,产生int结果.


M.M*_*M.M 6

正如Paolo M在评论中所指出的那样,USHRT_MAX有类型int(这由5.2.4.2.1/1指定:所有这些宏的类型至少与其一样大int).

所以USHRT_MAX * USHRT_MAX已经是intx int,没有促销活动.

这将在您的系统上调用有符号整数溢出,从而导致未定义的行为.


关于提议的解决方案:

unsigned u = static_cast<unsigned>(t*t);
Run Code Online (Sandbox Code Playgroud)

这没有用,因为t*t它本身会因有符号整数溢出而导致未定义的行为.正如其他答案所解释的那样,由于历史原因,在乘法发生之前t被提升int.

相反,你可以使用:

auto u = static_cast<unsigned int>(t) * t;
Run Code Online (Sandbox Code Playgroud)

其中,后整数推广,被一个unsigned int乘以一个int; 然后根据通常的算术转换的其余部分,将int其提升为unsigned int,并且发生明确定义的模乘.


Jar*_*d42 5

使用整数提升规则

USHRT_MAX价值被提升为int.然后我们做2 int的乘法(可能有溢出).