常用算术转换有哪些?

常用算术转换的策略是什么?

什么是整值提升?

 

为什么避免混合使用无符号整数和有符号整数?

 

常用算术转换可用于大多数二元运算符(包括算术运算符、关系运算符和判等运算符)的操作数。例如,假设变量f为float类型,而变量i为int类型。常用算术转换将会应用在表达式f+i的操作数上,因为两者的类型不同。显然把变量 i 转换成float类型(匹配变量f的类型)比把变量 f 转换成int类型(匹配变量 i 的类型)更安全。整数始终可以转换成float类型;可能会发生的最糟糕的事是精度会有少量损失。相反,把浮点数转换成int类型,将有小数部分的损失;更糟糕的是,如果原始数量大于最大可能的整数或者小于最小的整数,那么将会得到一个完全没有意义的结果。常用算术转换的策略是把操作数转换成可以安全地适用于两个数值的“最狭小的”数据类型。(粗略地说,如果某种类型要求的存储字节比另一种类型少,那么这种类型就比另一种类型更狭小。)为了统一操作数的类型,通常可以将相对狭小类型的操作数转换成另一个操作数的类型来实现(这就是所谓的提升)。最常用的提升是整值提升,它把字符或短整数转换成int类型(或者某些情况下是unsigned int类型)。

执行常用算术转换的规则可以划分成两种情况。

任一操作数的类型是浮点型的情况。按照下图将类型较狭小的操作数进行提升:

long double  ←  double  ←  float

也就是说,如果一个操作数的类型为long double,那么把另一个操作数的类型转换成long double类型。否则,如果一个操作数的类型为double类型,那么把另一个操作数转化成double类型。否则,如果一个操作数的类型是float类型,那么把另一个操作数转换成float类型。注意,这些规则涵盖了混合整数和浮点类型的情况。例如如果一个操作数的类型是long int类型,并且另一个操作数的类型是double类型,那么把long int 类型的操作数转换成double类型。

两个操作数的类型都不是浮点类型的情况。首先对两个操作数进行整值提升(保证没有一个操作数是字符类型或短整型)。然后按照下图对类型较狭小的操作数进行提升:

unsigned long int   ←   long int   ←  unsigned int   ←  int

有一种特殊情况,只有在long int 类型和unsigned int 类型长度(比如32位)相同时才会发生。在这类情况下,如果一个操作数的类型是long int ,而另一个的类型是unsigned int, 那么两个操作数都会转换成unsigned long int 类型。

当把有符号操作数和无符号操作数组合时,把有符号操作数“转换”成无符号的值。转换过程中需要加上或者减去n+1的倍数,其中n是无符号类型能表示的最大值。这条规则可能会导致某些隐蔽的编程错误。

假设int类型的变量 i 的值为-10,而unsigned int类型的变量 u 的值为10。如果用< 运算符比较变量 i 和变量 u,那么期望的结果应该是1(真)。但是,在比较前,变量 i 转换成为unsigned int 类型。因为负数不能被表示成无符号整数,所以转换后的值将不再为-10,而是加上4294967296的结果(假定4294967295是最大的无符号整数,)即4294967286.因而i< u比较的结果将为0.有些编译器会在程序试图比较有符号数与无符号数时给出一跳类似“comparison between signed and unsigned”的警告消息。

由于此类陷阱的存在,所以最好尽量避免使用无符号整数,特别是不要把它和有符号整数混合使用。

下面的例子显示了常用算术转换的实际执行情况:

char c; short int s; int i; unsigned int u; long int l; unsigned long int ul; float f; double d; long double ld;
i = i + c; /*c is converted修改 to int*/
i = i + s; u = u + i ; l = l + u; ul = ul + l; f = f + ul; d = d + f; ld = ld + d;

有符号整数(-10) 转换 无符号整数(不能有负数),所以转换的值不再是-10而是加上4294967286