首页 文章

为什么unsigned short(multiply)unsigned short转换为signed int? [重复]

提问于
浏览
19

这个问题在这里已有答案:

为什么 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: 我在下图中展示了问题 .

enter image description here

6 回答

  • 6

    您可能想要阅读有关implicit conversions的内容,尤其是有关numeric promotions的部分

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

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

  • 3

    这是通常的算术转换 .

    通常称为论证推广,尽管该标准以更受限制的方式使用该术语(合理描述性术语与标准术之间的永恒冲突) .
    C11§5/ 9:

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

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

    “除bool之外的整数类型的prvalue,char16_t,char32_t或wchar_t的整数转换等级(4.13)小于int的等级,如果int可以表示所有的值,则可以转换为int类型的prvalue来源类型;否则,源prvalue可以转换为unsigned int类型的prvalue

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

  • 5

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

    所以 USHRT_MAX * USHRT_MAX 已经是 int x 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 ,并且发生了明确定义的模乘 .

  • 12

    使用整数提升规则

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

  • 4

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

    我是否应该期待此解决方案出现任何问题?

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

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

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

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

  • 10

    正如其他答案所指出的那样,这是由整数推广规则引起的 .

    避免从具有较小等级的有符号类型的无符号类型转换的最简单方法是确保转换完成 unsigned int 而不是 int .

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

    unsigned short c = t * 1U * t;
    

    首先是操作数t和评估1U . 左操作数是有符号的并且具有比无符号右操作数小的等级,因此它被转换为右操作数的类型 . 然后操作数相乘,结果与剩余的右操作数相同 . 下面引用的标准中的最后一段用于此促销 .

    否则,将对两个操作数执行整数提升 . 然后将以下规则应用于提升的操作数: - 如果两个操作数具有相同的类型,则不需要进一步转换 . - 否则,如果两个操作数都有有符号整数类型或两者都有无符号整数类型,则具有较小整数转换等级类型的操作数将转换为具有更高等级的操作数的类型 . - 否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的等级,则具有有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型 .

相关问题