22

为什么在 C++11中unsigned short * unsigned short转换为?int

int这行代码所示,它太小而无法处理最大值。

cout << USHRT_MAX * USHRT_MAX << endl;

在 MinGW 4.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


编辑:我已经在下图中展示了这个问题。

在此处输入图像描述

4

6 回答 6

14

您可能想阅读有关隐式转换的内容,尤其是有关数字促销的部分,其中说

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

上面所说的是,如果您在涉及算术运算符(当然包括乘法)的表达式中使用小于int(like )的值,那么这些值将被提升为。unsigned shortint

于 2015-11-16T08:50:48.023 回答
11

这是通常的算术转换

通常称为参数提升,尽管标准以更受限制的方式使用该术语(合理的描述性术语和标准术语之间的永恒冲突)。

C++11 §5/9:

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

该段继续描述细节,这相当于转换更一般类型的阶梯,直到可以表示所有参数。此阶梯上的最低梯级是二元运算的两个操作数的整数提升,因此至少执行了此操作(但转换可以从更高的梯级开始)。积分促销由此开始:

C++11 §4.5/1:

一个除bool, char16_t,之外的整数类型的纯右值char32_t,或者wchar_t其整数转换等级 (4.13) 小于 的等级,如果可以表示源类型的所有值,则int可以转换为类型的纯右值;否则,源纯右值可以转换为类型的纯右值intintunsigned int

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

于 2015-11-16T08:55:49.980 回答
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);

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

相反,您可以使用:

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

其中,在整数提升之后,是一个unsigned int乘以一个int;然后根据其余通常的算术转换,将int提升为unsigned int,并发生定义明确的模乘。

于 2015-11-16T10:00:14.563 回答
5

带整数提升规则

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

于 2015-11-16T08:51:05.930 回答
4

似乎还没有人回答这部分问题:

我应该期待这个解决方案有什么问题吗?

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

是的,这里有一个问题:它首先计算t*t并允许溢出,然后将结果转换为unsigned. 根据 C++ 标准,整数溢出会导致未定义的行为(即使它在实践中可能总是可以正常工作)。正确的解决方案是:

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

请注意,第二个在乘法之前t被提升到unsigned,因为第一个操作数是unsigned

于 2015-11-16T16:58:19.517 回答
3

正如其他答案所指出的那样,这是由于整数提升规则而发生的。

避免从具有较小等级的无符号类型转换为具有较大等级的有符号类型的最简单方法是确保将转换完成为 anunsigned int和 not int

这是通过乘以 unsigned int 类型的值 1 来完成的。由于 1 是乘法恒等式,结果将保持不变:

unsigned short c = t * 1U * t;

首先计算操作数 t 和 1U。左操作数是有符号的,并且比无符号的右操作数具有更小的等级,因此它被转换为右操作数的类型。然后将操作数相乘,结果和剩余的右操作数也会发生同样的情况。下面引用的标准中的最后一段用于此促销活动。

否则,对两个操作数都执行整数提升。然后将以下规则应用于提升的操作数:

- 如果两个操作数具有相同的类型,则不需要进一步转换。

- 否则,如果两个操作数都具有有符号整数类型或都具有无符号整数类型,则具有较小整数转换等级的类型的操作数将转换为具有较高等级的操作数的类型。

- 否则,如果无符号整数类型的操作数的等级大于或等于另一个操作数类型的等级,则将有符号整数类型的操作数转换为无符号整数类型的操作数的类型。

于 2015-11-16T15:42:07.377 回答