这个问题在这里已有答案:
为什么 unsigned short * unsigned short
在C 11中转换为 int
?
int
太小而无法处理最大值,如此行代码所示 .
cout << USHRT_MAX * USHRT_MAX << endl;
在MinGW 4.9.2上溢出
-131071
因为(source)
USHRT_MAX = 65535(2 ^ 16-1)或更高* INT_MAX = 32767(2 ^ 15-1)或更高*
和 (2^16-1)*(2^16-1) = ~2^32
.
我应该期待这个 solution 有什么问题吗?
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
.
有用的资源:
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: 我在下图中展示了问题 .
6 回答
您可能想要阅读有关implicit conversions的内容,尤其是有关numeric promotions的部分
上面所说的是如果你在一个涉及arithmetic operators(当然包括乘法)的表达式中使用小于
int
(如unsigned short
)的东西,那么这些值将被提升为int
.这是通常的算术转换 .
通常称为论证推广,尽管该标准以更受限制的方式使用该术语(合理描述性术语与标准术之间的永恒冲突) .
C11§5/ 9:
该段继续描述细节,这些细节相当于转换更一般类型的阶梯,直到所有参数都可以表示 . 此梯形图的最低梯级是二进制操作的两个操作数的 integral promotion ,因此至少执行该梯级(但转换可以从更高的梯级开始) . 积分促销从这开始:
C11§4.5/ 1:
至关重要的是,这是关于类型,而不是算术表达式 . 在您的情况下,乘法运算符
*
的参数将转换为int
. 然后乘法作为int
乘法执行,产生int
结果 .正如Paolo M在评论中指出的那样,
USHRT_MAX
具有类型int
(这由5.2.4.2.1 / 1指定:所有这些宏的类型至少与int
一样大) .所以
USHRT_MAX * USHRT_MAX
已经是int
xint
,没有促销活动 .这将在您的系统上调用有符号整数溢出,从而导致未定义的行为 .
关于提议的解决方案:
这没有用,因为
t*t
本身会因有符号整数溢出而导致未定义的行为 . 正如其他答案所解释的那样,由于历史原因,t
在乘法发生之前被提升为int
.相反,你可以使用:
在整数提升后,
unsigned int
乘以int
;然后根据通常的算术转换的其余部分,int
被提升为unsigned int
,并且发生了明确定义的模乘 .使用整数提升规则
USHRT_MAX
值被提升为int
. 然后我们进行2 int的乘法(可能有溢出) .似乎没有人回答这部分问题:
是的,这里有一个问题:它首先计算
t*t
并允许它溢出,然后将结果转换为unsigned
. 根据C标准,整数溢出会导致未定义的行为(即使它在实践中总是可以正常工作) . 正确的解决方案是:请注意,第二个
t
在乘法之前被提升为unsigned
,因为第一个操作数是unsigned
.正如其他答案所指出的那样,这是由整数推广规则引起的 .
避免从具有较小等级的有符号类型的无符号类型转换的最简单方法是确保转换完成
unsigned int
而不是int
.这是通过乘以unsigned int类型的值1来完成的 . 由于1是乘法标识,结果将保持不变:
首先是操作数t和评估1U . 左操作数是有符号的并且具有比无符号右操作数小的等级,因此它被转换为右操作数的类型 . 然后操作数相乘,结果与剩余的右操作数相同 . 下面引用的标准中的最后一段用于此促销 .