为什么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
。
我是否应该对此解决方案有任何问题?
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
修改:我已在以下图片中展示了该问题。
答案 0 :(得分:12)
您可能希望阅读implicit conversions,尤其是关于numeric promotions的部分
小整数类型的Prvalues(例如
char)
)可能会转换为较大整数类型的prvalues(例如int
)。特别是arithmetic operators不接受小于{arithmetic operators的类型{1}}作为参数
上面所说的是,如果在涉及的表达式中使用小于int
(如int
)的内容(当然包括乘法),则值将为晋升为unsigned short
。
答案 1 :(得分:10)
这是通常的算术转换。
通常称为参数 promotion ,尽管该标准以更受限制的方式使用该术语(合理描述性术语与标准术之间的永恒冲突)。
C ++11§5/ 9:“许多期望算术或枚举类型操作数的二元运算符会导致转换和产生 结果类型以类似的方式。目的是产生一个通用类型,它也是结果的类型。此模式称为通常的算术转换 [...]
该段继续描述细节,这些细节相当于转换更常规类型的梯形图,直到可以表示所有参数。此梯形图中的最低梯级是二进制操作的两个操作数的整数提升,因此至少执行该操作(但转换可以从更高的梯级开始)。整体推广从此开始:
C ++11§4.5/ 1:“除
类型的prvaluebool
,char16_t
,char32_t
或wchar_t
整数转换以外的整数类型的prvalue 如果int
可以代表所有int
,则排名(4.13)小于int
的排名可以转换为unsigned int
类型的公开值 源类型的值;否则,源prvalue可以转换为*
至关重要的是,这是关于类型,而不是算术表达式。在您的情况下,乘法运算符int
的参数将转换为int
。然后乘法作为int
乘法执行,产生<?php
header('Content-type: application/vnd.ms-excel');
header("Content-Disposition: attachment; filename=data.xls");
header("Pragma: no-cache");
$buffer = $_POST['csvBuffer'];
echo $buffer;
?>
结果。
答案 2 :(得分:6)
Paolo M在评论中指出,USHRT_MAX
的类型为int
(这由5.2.4.2.1 / 1指定:所有此类宏的类型至少与{{{{1}一样大1}})。
因此int
已经是USHRT_MAX * USHRT_MAX
x int
,因此不会进行任何促销活动。
这将在您的系统上调用有符号整数溢出,从而导致未定义的行为。
关于提议的解决方案:
int
这没有用,因为unsigned u = static_cast<unsigned>(t*t);
本身会因签名整数溢出而导致未定义的行为。正如其他答案所解释的那样,由于历史原因,t*t
在乘法发生之前被提升为t
。
相反,您可以使用:
int
,在整数提升之后,auto u = static_cast<unsigned int>(t) * t;
乘以unsigned int
;然后根据通常的算术转换的其余部分,将int
提升为int
,并进行明确定义的模乘。
答案 3 :(得分:5)
使用整数提升规则
.empty()
值会提升为USHRT_MAX
。
然后我们进行2 int的乘法(可能有溢出)。
答案 4 :(得分:4)
似乎没有人回答这部分问题:
我是否应该预期此解决方案有任何问题?
u = static_cast<unsigned>(t*t);
是的,这里有一个问题:首先计算t*t
并允许它溢出,然后将结果转换为unsigned
。根据C ++标准,整数溢出会导致未定义的行为(即使它在实践中总是可以正常工作)。正确的解决方案是:
u = static_cast<unsigned>(t)*t;
请注意,第二个t
会在乘法之前提升为unsigned
,因为第一个操作数为unsigned
。
答案 5 :(得分:3)
正如其他答案所指出的那样,这是由整数推广规则引起的。
避免从排名较小的无符号类型转换为具有较高排名的签名类型的最简单方法是确保转换完成unsigned int
而不是int
。
这是通过乘以unsigned int类型的值1来完成的。由于1是乘法身份,结果将保持不变:
unsigned short c = t * 1U * t;
首先评估操作数t和1U。左操作数是有符号的并且具有比无符号右操作数小的等级,因此它被转换为右操作数的类型。然后操作数相乘,结果与剩余的右操作数相同。下面引用的标准中的最后一段用于此次促销。
否则,将对两个操作数执行整数提升。那么 以下规则适用于提升的操作数:
- 如果两个操作数具有相同的类型,则不需要进一步转换。
- 否则,如果两个操作数都有有符号整数类型或两者都有无符号 整数类型,具有较小整数转换等级类型的操作数是 转换为具有更高等级的操作数的类型。
- 否则,如果具有无符号整数类型的操作数的等级大于或等于 等于另一个操作数的类型的等级,然后是操作数 有符号整数类型转换为带有unsigned的操作数的类型 整数类型。