C++ 运算符中的隐式类型转换规则

Implicit type conversion rules in C++ operators

提问人:Matt Montag 提问时间:4/6/2011 更新时间:7/14/2021 访问量:133012

问:

我想更好地了解何时应该投掷。C++ 中加法、乘法等时的隐式类型转换规则是什么?例如

int + float = ?
int * float = ?
float * int = ?
int / float = ?
float / int = ?
int / int = ?
int ^ float = ?

等等。。。

表达式是否始终被评估为更精确的类型?Java 的规则是否不同? 如果我对这个问题的措辞不准确,请纠正我。

C++ 强制转换 隐式

评论

20赞 GManNickG 4/6/2011
请记住是 XOR。^
19赞 Serge Dundich 4/6/2011
@int ^ float = 编译错误 :)

答:

39赞 Sarfaraz Nawaz 4/6/2011 #1

涉及结果的算术运算。floatfloat

int + float = float
int * float = float
float * int = float
int / float = float
float / int = float
int / int = int

有关更多详细信息,请回答。看看 C++ 标准中的 §5/9 部分是怎么说的

许多二进制运算符,期望 算术或枚举的操作数 类型原因转换和产量 结果类型与此类似。这 目的是产生一个通用类型,这也是结果的类型

这种模式称为通常的 算术转换,即 定义如下:

— 如果任一操作数的类型为 long 双倍,另一个应转换 到长双。

— 否则,如果任一 操作数是双倍的,另一个应该是 转换为双精度。

— 否则,如果 任一操作数为浮点数,另一方为浮数 应转换为浮点数。

— 否则,积分促销 (4.5) 应在两者上执行 操作数.54)

— 然后,如果任一操作数 未签名的,另一个应 转换为无符号长整型。

— 否则,如果一个操作数是长 int 和其他无符号 int,则 如果一个长整型 int 可以表示所有 无符号 int 的值, unsigned int 应转换为 长整数;否则两个操作数 应转换为无符号长整型 整数。

— 否则,如果任一操作数是 long,另一个应转换为 长。

— 否则,如果任一操作数 未签名的,另一个应 转换为无符号。

[注意:否则,唯一剩下的情况是 两个操作数都是 int ]

评论

4赞 CB Bailey 4/6/2011
...只要另一种类型既不是也不是。doublelong double
1赞 Sarfaraz Nawaz 4/6/2011
@Charles:正确。我引用了《标准》中的相关部分以进一步澄清。
0赞 Marco A. 2/25/2014
那么,整数可以总是转换为浮点数而不会丢失任何数据吗?(例如,通过将指数归零并使用所有内容作为尾数)?
1赞 chux - Reinstate Monica 1/8/2016
这个答案已经过时了。建议更新。特别是,这里没有解决。long longunsigned long
0赞 Mark Ransom 6/2/2020
@MarcoA。32 位的尾数中没有足够的位(IEEE-754 为 24 位)用于 32 位,因此可能会丢失一些数据。64 位应该没问题。floatintdouble
1赞 Baltasarq 4/6/2011 #2

当两个部分不属于同一类型时,表达式的类型将转换为两个部分中最大的类型。这里的问题是要了解哪个比另一个大(它与字节大小无关)。

在涉及实数和整数的表达式中,整数将被提升为实数。例如,在 int + float 中,表达式的类型为 float。

另一个区别与类型的功能有关。例如,涉及 int 和 long int 的表达式将产生 long int 类型的结果。

评论

2赞 CB Bailey 4/6/2011
事实并非如此。在可能平台上,a 比 a “大”,但 + 的类型是什么?longfloatlongfloat
1赞 Paul R 4/6/2011
-1: 你说的最大是什么意思?浮点数比 int 吗?反之亦然
2赞 Baltasarq 4/6/2011
感谢您的评论。是的,这里的字节大小完全没有兴趣。显然,将最大字放在斜体中并不足以解释答案。无论如何,更深入地解释它是没有意义的,因为现在还有其他非常彻底的答案。
1赞 BЈовић 4/6/2011 #3

整个第 4 章都谈到了转换,但我认为您应该对这些最感兴趣:

4.5 积分促销 [conv.prom]
char、signed char、unsigned char、short int 或 unsigned short 类型的右值 如果 int 可以表示源类型的所有值,则 int 可以转换为 int 类型的右值;在其他
方面,源右值可以转换为无符号 int 类型的右值。 wchar_t 型 (3.9.1) 或枚举类型 (7.2) 的右值可以转换为以下第一种
类型的右值,这些类型可以表示其基础类型的所有值:int、unsigned int、
long 或 unsigned long。

如果 int 可以表示位域的所有
值,则整数位域 (9.6) 的右值可以转换为 int 类型的右值;否则,如果 unsigned int 可以重新
发送位字段的所有值,则可以将其转换为 unsigned int。如果位场还较大,则不对其应用积分提升。如果
位字段具有枚举类型,则出于升级目的,将其视为该类型的任何其他值。
bool 类型的右值可以转换为 int 类型的右值,false 变为零,true
变为 1。
这些转化称为整体促销。

4.6 浮点提升 [conv.fpprom]
float 类型的右值可以转换为 double 类型的右值。该值保持不变。
这种转换称为浮点提升。

因此,所有涉及 float 的转换 - 结果都是 float。

只有涉及两个 int 的那个 - 结果是 int : int / int = 整数

277赞 Martin York 4/6/2011 #4

在 C++ 中,运算符(对于 POD 类型)始终作用于相同类型的对象。
因此,如果它们不相同,则将提升一个以匹配另一个。
操作结果的类型与操作数(转换后)相同。

if:
either is      long double       other is promoted >      long double
either is           double       other is promoted >           double
either is           float        other is promoted >           float
either is long long unsigned int other is promoted > long long unsigned int
either is long long          int other is promoted > long long          int
either is long      unsigned int other is promoted > long      unsigned int
either is long               int other is promoted > long               int
either is           unsigned int other is promoted >           unsigned int
either is                    int other is promoted >                    int

Otherwise:
both operands are promoted to int

注意。最小操作大小为 。所以/被提升到操作完成之前。intshortcharint

在所有表达式中,在执行操作之前,将 提升为 a。操作的结果是 .intfloatfloat

int + float =>  float + float = float
int * float =>  float * float = float
float * int =>  float * float = float
int / float =>  float / float = float
float / int =>  float / float = float
int / int                     = int
int ^ float =>  <compiler error>

评论

1赞 Rafał Dowgird 4/6/2011
“操作的最小大小是 int。” - 这会很奇怪(那些有效支持 char/short 操作的架构呢?这真的在 C++ 规范中吗?
5赞 Martin York 4/6/2011
@Rafal:是的。int 应该是在特定平台上操作的最有效的整数类型。char 必须始终为 1,但 short 的大小可以与 int 相同。
2赞 Steve Jessop 4/6/2011
@Rafa:是的,这很奇怪,而且在标准中。在很多情况下,你描述的架构可以使用其超高效类型。如果将 的值赋值给 ,那么它可以只执行算术运算,例如环绕。但是,如果将结果分配给 ,那么它必须在足够大的类型中进行算术运算,以便在它大于 时得到正确的结果。charchar + charcharcharintCHAR_MAX
8赞 nitsas 4/24/2013
我只想强调一个事实,即 int 被提升为无符号 int!!几天来,我一直在与错误作斗争,因为我的印象是两者都会被提升为 int 或 long,这样可能的负面结果就不会导致下溢/环绕。
15赞 nitsas 4/24/2013
问题示例“int 被提升为无符号 int”:将导致 32 位 int 和 32 位无符号 int((int) 4) - ((unsigned int) 5)4294967295
4赞 James Kanze 4/6/2011 #5

如果排除无符号类型,则有一个有序的 层次结构:有符号 char、short、int、long、long、long、float、 双倍,长双倍。首先,在 int 之前的任何内容 上面将转换为 int。然后,在二进制操作中, 排名较低的类型将转换为排名较高的类型,并且 结果将是较高的类型。(您会注意到,从 层次结构,任何时候浮点和整型类型是 涉及时,整型将转换为浮点型 点类型。

unsigned 使事情变得有点复杂:它扰乱了排名,并且 排名的部分内容将实现定义。以 这样,最好不要将有符号和无符号混用在同一个 表达。(大多数 C++ 专家似乎避免使用无符号,除非 涉及按位运算。至少,是什么 Stroustrup推荐。

评论

3赞 underscore_d 12/18/2015
Stroustrup 可以推荐他喜欢的东西,但对一个永远不需要为负数的数字使用符号是完全浪费了整整 50% 的可用范围。我当然不是 Stroustrup,但我默认使用,并且只有在我有理由时才使用。intunsignedsigned
1赞 jorgbrown 2/4/2019
underscore_d,这一切都很好,直到你必须减法的那一天。C++ 中无符号数字的主要问题是,当您执行减法时,它们保持无符号状态。因此,假设您编写了一个函数来查看 std::vector 是否按顺序排列。你可能会写,然后你会恼火地发现它因空向量而崩溃,因为 size() - 1 返回 18446744073709551615。bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;
7赞 David Stone 1/20/2012 #6

这个答案很大程度上是针对 @Rafa łDowgird 的评论:

“操作的最小大小是 int。” - 这会很奇怪 (有效支持 char/short 的架构呢? 操作?这真的在 C++ 规范中吗?

请记住,C++ 标准具有非常重要的“假设”规则。请参见第 1.8 节:程序执行:

3) 这一规定有时被称为“假设”规则,因为 实施可以自由地无视标准的任何要求 只要结果是好像已经遵守了要求,就 从程序的可观察行为可以确定。

编译器不能将大小设置为 8 位,即使它是最快的,因为标准要求最小值为 16 位。intint

因此,对于具有超快 8 位运算的理论计算机,隐式提升算术可能很重要。但是,对于许多操作,您无法判断编译器是否实际上以 an 的精度执行了操作,然后转换为 a 以存储在变量中,或者这些操作是否一直在 char 中完成。intintchar

例如,考虑 ,其中加法会溢出(假设每个值为 200)。如果你提升到 ,你会得到 600,然后它被隐式下放到 ,这将包装模 256,从而得到 88 的最终结果。如果你没有进行这样的促销,你就必须在前两个添加之间换行,这将使问题从 到 ,即 344,减少到 88。换言之,程序不知道其中的区别,因此,如果操作数的排名低于 ,编译器可以自由地忽略执行中间操作的命令。unsigned char = unsigned char + unsigned char + unsigned charintunsigned char200 + 200 + 200144 + 200intint

一般来说,加法、减法和乘法都是如此。一般来说,除法或模量不是这样。

19赞 legends2k 5/26/2014 #7

由于其他答案没有讨论 C++11 中的规则,所以这里有一个。摘自 C++11 标准(草案 n3337)§5/9(强调差异):

这种模式称为通常的算术转换,其定义如下:

— 如果任一操作数为作用域枚举类型,则不执行任何转换;如果另一个操作数没有相同的类型,则表达式格式不正确。

— 如果其中一个操作数的类型为 long double,则另一个操作数应转换为 long double。

— 否则,如果其中一个操作数是双精度的,则另一个操作数应转换为双精度。

— 否则,如果其中一个操作数是浮点数,则另一个操作数应转换为浮点数。

— 否则,应在两个操作数上执行整体提升。然后,以下规则应应用于提升的操作数:

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

— 否则,如果两个操作数都有带符号整数类型,或者两者都有无符号整数类型, 类型为较小整数转换秩的操作数应转换为 操作数具有更高的等级。

— 否则,如果具有无符号整数类型的操作数的秩大于或等于 另一个操作数类型的 rank,带符号整数类型的操作数应转换为 具有无符号整数类型的操作数的类型。

— 否则,如果具有有符号整数类型的操作数类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数应 转换为具有有符号整数类型的操作数类型。

— 否则,两个操作数都应转换为对应于 具有有符号整数类型的操作数的类型。

有关经常更新的列表,请参阅此处

评论

2赞 M.M 9/5/2019
这些规则在所有版本的 C++ 中都是相同的,当然除了在 C++11 中添加的作用域枚举
3赞 garakchy 6/15/2014 #8

我对问题的解决方案得到了 WA(错误答案),然后我更改了其中一个,它给了 AC(接受)。以前,我试图做,在我纠正它之后。我想出的谷歌搜索,intlong long intlong long int += int * intlong long int += long long int * int

1. 算术转换

类型转换的条件:

转换--->满足条件

  • 任一操作数均为 long double 类型。---> 其他操作数转换为类型 long double

  • 不满足上述条件,并且任一操作数的类型为 double。---> 其他操作数转换为类型 double

  • 不满足上述条件,并且任一操作数的类型为 float。---> 其他操作数转换为浮点型。

  • 不满足上述条件(所有操作数都不是浮点类型)。---> 对操作数执行整数提升,如下所示:

    • 如果其中一个操作数的类型为 unsigned long,则另一个操作数将转换为 unsigned long 类型
    • 如果不满足上述条件,并且如果其中一个操作数的类型为 long,而另一个操作数的类型为 unsigned int,则两个操作数都将转换为 unsigned long 类型。
    • 如果不满足上述两个条件,并且其中一个操作数为 long 类型,则其他操作数将转换为 long 类型。
    • 如果不满足上述三个条件,并且其中一个操作数的类型为 unsigned int,则另一个操作数将转换为 unsigned int 类型。
    • 如果上述条件均不满足,则两个操作数都将转换为 int 类型。

2 .整数转换规则

  • 整数促销:

小于 int 的整数类型在对它们执行操作时被提升。如果原始类型的所有值都可以表示为 int,则较小类型的值将转换为 int;否则,它将转换为无符号 int。整数升级作为对某些参数表达式的通常算术转换的一部分应用;一元 +、- 和 ~ 运算符的操作数;以及轮班操作员的操作数。

  • 整数转换排名:

    • 任何两个有符号整数类型都不应具有相同的等级,即使它们具有相同的表示形式。
    • 有符号整数类型的秩应大于任何精度较低的有符号整数类型的秩。
    • 的等级应大于 的等级 ,而 的等级应大于 的等级 ,该等级应大于 的等级。long long intlong intintshort intsigned char
    • 任何无符号整数类型的排名都应等于相应有符号整数类型的排名(如果有)。
    • 任何标准整数类型的秩都应大于任何具有相同宽度的扩展整数类型的秩。
    • 的等级应等于 和 的等级。charsigned charunsigned char
    • 任何扩展有符号整数类型相对于具有相同精度的扩展有符号整数类型的排名都是由实现定义的,但仍受用于确定整数转换排名的其他规则的约束。
    • 对于所有整数类型 T1、T2 和 T3,如果 T1 的排名高于 T2,而 T2 的排名高于 T3,则 T1 的排名高于 T3。
  • 通常的算术转换:

    • 如果两个操作数具有相同的类型,则无需进一步转换。
    • 如果两个操作数都属于相同的整数类型(有符号或无符号),则具有较小整数转换等级类型的操作数将转换为具有较高等级的操作数类型。
    • 如果具有无符号整数类型的操作数的秩大于或等于其他操作数类型的秩,则具有有符号整数类型的操作数将转换为具有无符号整数类型的操作数的类型。
    • 如果具有有符号整数类型的操作数类型可以表示具有无符号整数类型的操作数类型的所有值,则具有无符号整数类型的操作数将转换为具有有符号整数类型的操作数类型。
    • 否则,两个操作数都将转换为无符号整数类型,该类型对应于具有有符号整数类型的操作数类型。特定运算可以添加或修改通常算术运算的语义。
-3赞 user3905560 8/4/2014 #9

警告!

转换从左到右进行。

试试这个:

int i = 3, j = 2;
double k = 33;
cout << k * j / i << endl; // prints 22
cout << j / i * k << endl; // prints 0

评论

11赞 gartenriese 10/12/2015
这不是因为转换,而是因为运算符的优先级。 将导致 101。j + i * k