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


167

我想更好地知道应该何时施展。加,乘等时,C ++中的隐式类型转换规则是什么。例如,

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

等等...

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


15
记住^是XOR。
GManNickG 2011年

15
@int ^ float =编译错误:)
Serge Dundich 2011年

Answers:


223

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

If either is      long          double the other is promoted to      long          double
If either is                    double the other is promoted to                    double
If either is                    float  the other is promoted to                    float
If either is long long unsigned int    the other is promoted to long long unsigned int
If either is long long          int    the other is promoted to long long          int
If either is long      unsigned int    the other is promoted to long      unsigned int
If either is long               int    the other is promoted to long               int
If either is           unsigned int    the other is promoted to           unsigned int
If either is                    int    the other is promoted to                    int
Both operands are promoted to int

注意。最小操作数为int。所以short/ char被提升到int操作完成之前。

在您执行的所有表达式中,都会将int其提升为a float。运算的结果是float

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
“最小操作数为int。” -这很奇怪(有效支持char / short操作的体系结构是什么?)这真的在C ++规范中吗?
2011年

3
@Rafal:是的。int被认为是在特定平台上操作的最有效的整数类型。char必须始终为1,但short可以与int相同。
马丁·约克

1
@Rafał:是的,这很奇怪,符合标准。在许多情况下,您描述的架构可以使用其超高效char类型。如果将的值char + char分配给char,则它可以进行算术运算,char例如环绕。但是,如果将结果分配给,int则它必须以足够大的类型进行算术运算,以得到大于时的正确结果CHAR_MAX
史蒂夫·杰索普

2
我只想强调一个事实,即int被提升为unsigned int!我在bug方面苦苦挣扎了几天,因为我的印象是两者都会被提升为 int或long,因此可能的负面结果不会导致下溢/环绕。
nitsas

10
问题示例“将int提升为unsigned int ”:((int) 4) - ((unsigned int) 5)将导致429496729532位int和32位unsigned int。
nitsas

33

涉及float结果的算术运算float

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

有关更多详细信息,请回答。查看C ++标准的§5/ 9部分所说的内容

许多期望算术或枚举类型的操作数的二进制运算符都以类似的方式引起转换并产生结果类型。目的是产生一个通用类型, 它也是结果的类型

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

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

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

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

—否则,应在两个操作数上执行积分提升(4.5)(54)。

—然后,如果其中一个操作数为无符号长,则另一个应转换为无符号长。

—否则,如果一个操作数是long int,另一个操作数是unsigned int,则如果long int可以表示unsigned int的所有值,则将unsigned int转换为long int。否则,两个操作数均应转换为unsigned long int。

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

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

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


3
...只要另一种类型既不是也不doublelong double
CB Bailey

1
@查尔斯:对。我引用了标准中的相关章节以进一步阐明。
Nawaz

那么,是否可以将整数始终转换为浮点数而不丢失任何数据?(例如,将指数归零并使用所有的尾数)?
Marco A.

1
这个答案是过时的。建议更新。特别是,long longunsigned long没有解决在这里。
chux-恢复莫妮卡

@MarcoA。32位float的尾数(对于IEEE-754为24位)没有足够的位int,因此可能会有一些数据丢失。64位double就可以了。
Mark Ransom

17

由于其他答案不涉及C ++ 11中的规则,因此这里是一个。根据C ++ 11标准(n3337草案)§5/ 9(强调了区别):

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

—如果任一操作数属于范围枚举类型,则不执行任何转换;否则,将不执行任何转换。如果另一个操作数的类型不同,则该表达式的格式不正确。

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

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

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

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

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

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

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

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

—否则,两个操作数均应转换为与带符号整数类型的操作数类型相对应的无符号整数类型。

请参阅此处以获取经常更新的列表。


1
这些规则在所有C ++版本中都是相同的,除了在C ++ 11中当然添加了作用域枚举
MM

6

这个答案主要针对@RafałDowgird的评论:

“最小操作数为int。” -这很奇怪(有效支持char / short操作的体系结构是什么?)这真的在C ++规范中吗?

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

3)这项规定有时称为“按原样”规则,因为只要可以从可观察的范围内确定结果,就可以无视标准的任何要求,而该实现可以自由地执行程序的行为。

int即使编译器最快,也不能将其设置为8位,因为该标准要求最小为16位int

因此,在理论计算机具有超快8位运算的情况下,int对算术的隐式提升可能很重要。但是,对于许多操作,您无法确定编译器是否实际上以an的精度执行了这些操作int,然后将其转换为a char以存储在您的变量中,或者这些操作是否始终以char进行。

例如,考虑unsigned char = unsigned char + unsigned char + unsigned char,加法会溢出(让我们假设每个值都为200)。如果提升为int,您将获得600,然后将其隐式转换为unsigned char,将以256为模进行换行,从而得出最终结果88。如果未进行此类提升,则必须在第一个换行之间进行换行。两个加法,将问题从减少200 + 200 + 200144 + 200,即344,减少到88。换句话说,程序不知道它们之间的区别,因此,int如果操作数具有以下含义,则编译器可以随意忽略执行中间操作的要求排名低于int

通常,加法,减法和乘法都是如此。通常,除数或模数不是正确的。


4

如果排除无符号类型,则有一个有序的层次结构:带符号的char,short,int,long,long long,float,double,long double。首先,上述int之前的所有内容都将转换为int。然后,在二进制操作中,排名较低的类型将转换为较高的类型,结果将是较高的类型。(您会注意到,从层次结构中,每当涉及浮点和整数类型时,整数类型都会转换为浮点类型。)

Unsigned使事情变得有些复杂:它扰乱了排名,并且排名的一部分变成了实现定义。因此,最好不要在同一个表达式中混合使用带符号和无符号。(除非涉及到按位运算,否则大多数C ++专家似乎都避免使用unsigned。至少,这是Stroustrup建议的。)


3
Stroustrup可以推荐他喜欢的东西,但是int对于永不为负的数字使用可签名的符号,则完全浪费了可用范围的50%。我当然不是Stroustrup,但我unsigned默认使用,并且signed仅在有理由的情况下使用。
underscore_d

1
没问题,underscore_d,直到必须减去的那一天为止。C ++中无符号数字的主要问题在于,当您执行减法运算时,它们将保持无符号状态。因此,假设您编写了一个函数来查看std :: vector是否正确。您可能会写bool in_order(vector<T> vec) { for ( int i = 0; i < size() - 1; ++i) { if (vec[i + 1] < vec[i]) return false; } return true;,然后会发现它因空向量而崩溃,因为size()-1返回18446744073709551615。–
jorgbrown,

3

我对问题的解决方法是WA(错误答案),然后我将其中一个更改intlong long int,它给了AC(接受)。以前,我尝试这样做long long int += int * int,然后将其更正为long long int += long long int * int。我想出了谷歌搜索,

1. 算术转换

类型转换的条件:

满足条件--->转换

  • 两种操作数都属于long double类型。--->其他操作数被转换为long double类型。

  • 不满足前述条件,并且任何一个操作数均为double类型。--->其他操作数将转换为double类型。

  • 不满足前述条件,并且两个操作数均为float类型。--->其他操作数将转换为float类型。

  • 之前的条件不满足(所有操作数都不是浮点类型)。--->积分提升对操作数执行如下:

    • 如果一个操作数的类型为unsigned long类型,则另一个操作数将转换为unsigned long类型
    • 如果前述条件不满足,而且如果操作数的类型是而另一种类型的无符号整型,两个操作数都转换为类型长无符号
    • 如果不满足前面两个条件,并且如果一个操作数的类型为long,则另一个操作数的类型将转换为long类型。
    • 如果不满足前面的三个条件,并且任何一个操作数的类型均为unsigned int,则另一个操作数将转换为unsigned int类型。
    • 如果上述条件都不满足,则两个操作数都将转换为int类型。

2。整数转换规则

  • 整数促销:

当对它们执行运算时,会提升小于int的整数类型。如果原始类型的所有值都可以表示为int,则较小类型的值将转换为int;否则,将其转换为unsigned int。整数提升作为常规算术转换的一部分应用于某些参数表达式;一元+,-和〜运算符的操作数;和移位运算符的操作数。

  • 整数转换排名:

    • 即使两个有符号整数类型具有相同的表示形式,也不应具有相同的等级。
    • 有符号整数类型的等级应大于精度较低的任何有符号整数类型的等级。
    • 的等级long long int应大于的等级long int,后者应大于的等级int,后者的等级应大于的等级short int,等级应大于的等级signed char
    • 任何无符号整数类型的等级应等于相应的有符号整数类型的等级(如果有)。
    • 任何标准整数类型的秩应大于相同宽度的任何扩展整数类型的秩。
    • 军衔char应等于的秩signed charunsigned char
    • 相对于具有相同精度的另一个扩展有符号整数类型的任何扩展有符号整数类型的等级都是实现定义的,但是仍然要遵循其他规则来确定整数转换等级。
    • 对于所有整数类型T1,T2和T3,如果T1的等级高于T2,并且T2的等级高于T3,则T1的等级高于T3。
  • 通常的算术转换:

    • 如果两个操作数具有相同的类型,则无需进一步转换。
    • 如果两个操作数都属于相同的整数类型(有符号或无符号),则将整数转换等级较小的操作数转换为等级较高的操作数的类型。
    • 如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则将带符号整数类型的操作数转换为无符号整数类型的操作数的类型。
    • 如果带符号整数类型的操作数的类型可以表示无符号整数类型的操作数的所有值,则将无符号整数类型的操作数转换为带符号整数类型的操作数的类型。
    • 否则,两个操作数都将转换为与带符号整数类型的操作数类型相对应的无符号整数类型。特定操作可以添加或修改常规算术运算的语义。

1

第4章讨论了转换,但是我认为您应该对转换最感兴趣:

4.5积分促销 [conv.prom]
如果int可以表示源类型的所有值,则可以将char,signed char,unsigned char,short int或unsigned short int类型的右值转换为int类型的右值。否则
,可以将源右值转换为unsigned int类型的右值。
可以将类型wchar_t(3.9.1)或枚举类型(7.2)的右值转换为
以下代表其基础类型的所有值的以下第一个类型的右值:int,unsigned int,
long或unsigned长。
如果int可以表示
位字段的所有值,则可以将整数位字段(9.6)的rvalue转换为int类型的rvalue 。否则,如果unsigned int可以表示-,则可以将其转换为unsigned int。
重新发送该位字段的所有值。如果位字段更大,则不应用整数提升。如果
位字段具有枚举类型,则出于提升目的将其视为该类型的任何其他值。
bool类型的右值可以转换为int类型的右值,false变为零,true
变为1。
这些转换称为整体促销。

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

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

只有一个包含两个int的整数-结果是int:int / int = int


1

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

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

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


2
这不是真的。在可能的平台上,a long比a“大”,float但是long+ 的类型是float什么?
CB Bailey

1
-1:什么是最大?是浮动更大比int?或者反之亦然
Paul R

2
谢谢您的意见。是的,这里的字节大小根本没有意义。结果出来了,显然用大写斜体还不足以解释答案。无论如何,对它进行更深入的解释是没有意义的,因为现在有了其他非常彻底的答案。
Baltasarq,

-2

警告!

转换从左到右发生。

试试这个:

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

8
这不是因为转换,而是因为运算符优先级。j + i * k会导致101
gartenriese
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.