6.3.1 算术操作数
TIP
这一节规定的是“在运算开始前,操作数会先被怎样转换”。很多看似诡异的结果,例如 char 先提升成 int、有符号和无符号混算后结果类型变化,根源都在这里。
6.3.1.1 布尔、字符和整数
每个整数类型都具有一个整数转换等级(integer conversion rank)。这个等级决定了整数提升与通常算术转换时,哪个类型“更高”。标准对等级关系作出如下要求:
- 任何两个有符号标准整数类型,只要精度不同,等级就不同。
- 精度更高的有符号整数类型,其等级必须更高。
long long int的等级高于long int,后者高于int,后者高于short int,后者高于signed char。- 位精确有符号整数类型的等级,高于任何位宽更小的标准整数类型,也高于任何位宽更小的位精确整数类型。
- 任一无符号整数类型的等级,若存在对应的有符号类型,则与其对应有符号类型相同。
- 任一标准整数类型的等级,高于任何同宽度的扩展整数类型或位精确整数类型。
- 同宽度的位精确整数类型与扩展整数类型之间,等级关系由实现定义。
char的等级与signed char、unsigned char相同。bool的等级低于所有其他标准整数类型。- 枚举类型的等级,与其兼容整数类型的等级相同。
- 同精度的两个扩展有符号整数类型之间,等级关系由实现定义,但仍必须满足本小节其他规则。
- 等级关系必须满足传递性。
下列实体在表达式中可按
int或unsigned int的方式使用:- 整数类型(但不是
int或unsigned int)的对象或表达式,且其整数转换等级小于或等于int与unsigned int的等级; - 类型为
bool、int、signed int或unsigned int的位字段。
- 整数类型(但不是
对位精确整数类型的位字段,取值后转换为对应的位精确整数类型。对其他适用整数类型,则执行整数提升:
- 如果
int能表示原类型(对位字段还要考虑其位宽限制)的全部值,就转换成int; - 否则转换成
unsigned int。
- 如果
除位精确整数类型的前述情况外,其他整数提升不会改变数值,包括符号。
char是否能表示负值,由实现定义。
IMPORTANT
整数提升不是“凡是小整数都先变成 int”。_BitInt 明确不完全遵循这套老规则,后续做通常算术转换时要单独看等级和位宽。
- 整数提升只在以下位置发生:
- 作为通常算术转换的一部分;
- 用于某些实参表达式;
- 用于一元
+、-、~的操作数; - 用于移位运算符的两个操作数。
6.3.1.2 布尔类型
- 任意标量值转换为
bool时:- 若该值是
0(算术类型)、空指针(指针类型),或者该标量本身类型为nullptr_t,结果为false; - 否则结果为
true。
- 若该值是
6.3.1.3 有符号整数和无符号整数
一个整数类型的值转换为另一种非
bool整数类型时,若新类型能够表示该值,则值保持不变。若新类型是无符号整数类型,而原值不在其表示范围内,则通过反复加上或减去“新类型最大值再加一”的方式,把该值调整到新类型的范围内。
若新类型是有符号整数类型,而原值无法表示,则结果由实现定义,或者引发一个实现定义的信号。
NOTE
标准这里描述的是数学意义上的数值变换,不是“按原对象位模式强行重解释”。
6.3.1.4 实浮点类型与整数
标准浮点类型的有限值转换为非
bool整数类型时,小数部分会被舍弃,也就是朝零截断。若整数部分无法由目标整数类型表示,行为未定义。十进制浮点类型的有限值转换为非
bool整数类型时,同样朝零截断。若整数部分无法由目标整数类型表示,则必须引发"invalid"浮点异常,而转换结果未指定。整数类型的值转换为标准浮点类型时:
- 若能精确表示,则值不变;
- 若落在可表示范围内但不能精确表示,则结果是相邻两个可表示值中较高或较低的那个,具体选择方式由实现定义;
- 若超出可表示范围,则行为未定义。
某些隐式转换的结果,可能会以比目标类型要求更大的范围和精度来表示,见
6.3.1.8与6.8.7.5。整数类型的值转换为十进制浮点类型时,若不能精确表示,则必须按 ISO/IEC 60559 的规定进行正确舍入,并按要求引发异常。
6.3.1.5 实浮点类型之间
一个实浮点类型的值转换为另一种实浮点类型时,若目标类型能够精确表示,则值不变。
转换到标准浮点类型时:
- 若值在目标类型可表示范围内但不能精确表示,则结果是最近的上方或下方可表示值,具体由实现定义;
- 若值超出目标类型可表示范围,则行为未定义。
转换到十进制浮点类型时,若不能精确表示,则结果必须按 ISO/IEC 60559 的规定正确舍入,并按规定引发异常。
某些隐式转换的结果,也可能以大于目标类型要求的范围和精度来表示。
6.3.1.6 复数类型
- 一个复数类型的值转换为另一种复数类型时,其实部和虚部分别按照对应实类型的转换规则处理。
6.3.1.7 实数与复数
实数类型的值转换为复数类型时:
- 复数结果的实部,按转换到对应实类型的规则确定;
- 虚部为正零或无符号零。
复数类型的值转换为非
bool实数类型时,虚部会被丢弃,然后把实部按对应实类型的转换规则继续转换。
6.3.1.8 通常算术转换
许多要求算术类型操作数的运算符,都会以类似方式先做转换,再确定结果类型。其目标是为两个操作数确定一个共同实类型。
对于这类运算,每个操作数都会在不改变类型域的前提下,被转换为一种其对应实类型就是共同实类型的类型。除非另有明确说明:
- 结果的对应实类型也是这个共同实类型;
- 如果两个操作数类型域相同,结果保持该类型域;
- 若类型域不同,则结果属于复数域。
这套规则称为通常算术转换,按以下顺序应用:
- 如果一个操作数是十进制浮点类型,则另一个操作数不得是标准浮点类型、复数类型或虚数类型。
- 若任一操作数类型为
_Decimal128,另一个转换为_Decimal128。 - 否则,若任一操作数类型为
_Decimal64,另一个转换为_Decimal64。 - 否则,若任一操作数类型为
_Decimal32,另一个转换为_Decimal32。 - 否则,若任一操作数的对应实类型为
long double,另一个在不改变类型域的前提下转换到对应实类型为long double的类型。 - 否则,若任一操作数的对应实类型为
double,另一个同样转换到对应实类型为double的类型。 - 否则,若任一操作数的对应实类型为
float,另一个同样转换到对应实类型为float的类型。 - 否则,若两个类型中有任何一个是枚举类型,则先转换为其底层类型。
- 然后,对两个操作数执行整数提升。
- 接着,对提升后的两个操作数按下面规则处理。
整数提升之后的规则如下:
- 若两者类型相同,不再继续转换。
- 若两者同为有符号整数类型,或同为无符号整数类型,则等级较低者转换为等级较高者的类型。
- 若无符号整数类型操作数的等级大于或等于另一操作数类型的等级,则有符号整数类型操作数转换为该无符号类型。
- 否则,若有符号整数类型能够表示无符号整数类型的全部值,则无符号整数类型操作数转换为该有符号类型。
- 否则,两者都转换为“该有符号整数类型所对应的无符号整数类型”。
浮点操作数与浮点表达式结果,允许在比其类型要求更大的范围与精度中表示,但这不会改变它们的类型。
标准示例:_BitInt 与通常算术转换
_BitInt(2) a2 = 1;
_BitInt(3) a3 = 2;
_BitInt(33) a33 = 1;
signed char c = 3;
a2 * a3; /* a2 转换为 _BitInt(3),结果类型为 _BitInt(3) */
a2 * c; /* c 先提升为 int,a2 再转换为 int,结果类型为 int */
a33 * c; /* c 先提升为 int;若 int 的宽度不超过 32,
则再转换为 _BitInt(33),结果类型为 _BitInt(33) */
void func(_BitInt(8) a8, _BitInt(24) a24) {
_BitInt(32) a32 = a8 * (_BitInt(32))a24;
}2
3
4
5
6
7
8
9
10
11
12
13