8.3 - 数值转换
Key Takeaway
- 符合数值提升规则的类型转换是首先是数值提升,不是数值转换
- 数值转换涵盖了数值提升规则未涉及的其他类型转换
- 5 种基本数值转换:
- 整型转整型
- 浮点转浮点
- 浮点转整型
- 整型转浮点
- 整型或浮点转布尔
- 数值提升总是安全的,但是数值转换不一定,可能造成数据或精度丢失
- 缩窄转换会造成精度丢失。编译器会在发生隐式缩窄转换时报告错误
- 尽可能避免缩窄转换。如果一定要进行转换,使用
static_cast
显式地进行缩窄转换。 - 括号初始化不允许隐式地缩窄转换
- 一些需要注意的重要规则:
- 将值转换为其范围不支持该值的类型将可能导致意想不到的结果
- 只要值适合较小类型的范围,从较大的整型或浮点类型转换为同一族的较小类型通常是有效的
- 整数转浮点数,只要范围ok就可以
- 浮点数转整数,只要范围ok就可以,但是小数部分会丢失
在上一节课 (8.2 - 浮点数和整型提升) 中,我们介绍了数值提升,它属于一种类型转换,可以将更窄的数据类型转换为更宽的数据类型(通常是 int
或 double
) 使其可以被更高效地处理。
C++ 支持另一种类数值类型转换,称为数值转换(numeric conversions),它涵盖了数值提升规则没有涵盖的其他类型转换。
关键信息
任何符合数值提升规则(8.2 - 浮点数和整型提升)的类型转换,都属于数值提升而不是数值转换。
五种基本的数值转换:
- 把一种整型类型转换为另外一种整型类型(整型提升除外)
1 2 3
short s = 3; // convert int to short long l = 3; // convert int to long char ch = s; // convert short to char
- 把一种浮点类型转换为另外一种浮点类型(浮点提升除外):
1 2
float f = 3.0; // convert double to float long double ld = 3.0; // convert double to long double
- 将浮点数类型转换为任何整型类型:
1
int i = 3.5; // convert double to int
- 将整型类型转换为任何浮点类型:
1
double d = 3; // convert int to double
- 将整型或浮点型转换为bool类型:
1 2
bool b1 = 3; // convert int to bool bool b2 = 3.0; // convert double to bool
题外话
由于大括号初始化不允许进行一些数值转换(稍后会详细介绍),为了使示例简单,我们在本课中使用了复制初始化(这种初始化方法没有这样的限制)。
缩窄转换
与数值提升(始终是安全的)不同,数值转换可能(也可能不会)导致数据或精度的丢失。
有些数值转换总是安全的(例如 int
到 long
,或者 int
到 double
)。其他数值转换,例如 double
到 int
,可能会导致数据丢失(取决于转换的特定值和/或基础类型的范围):
1 2 |
|
在 C++ 中缩窄转换(narrowing conversion) 是一种可能会造成数据丢失的数值转换。这种缩窄转换包括为:
- 浮点数类型转换为整型类型;
- 较宽的浮点类型转换为较窄的浮点类型,除非被转换的值是
constexpr
类型且它的范围在目标类型范围内(即使较窄的类型不具备存放完整数据的精度)。 - 从整数类型转换为浮点类型,除非转换的值是
constexpr
且在目标类型范围内,并且可以转换回原始类型而不丢失数据。 - 从较宽的整型转换为较窄的整型,除非转换的值是
constexpr
且整型提升后适合目标类型。
好消息是你不需要记住这些。当编译器确定需要隐式缩窄转换时,它通常会发出警告(或错误)。
注意
当将signed int
转换为unsigned int
时,编译器通常会不会发出警告,反之亦然,即使这些是缩窄转换。要特别小心这些类型之间无意的转换(特别是当向带相反符号形参的函数传递实参时)。
例如,当编译下面的程序时:
1 2 3 4 |
|
Visual Studio 会产生如下告警信息:
1 |
|
一般来说,应该避免缩窄转换,但在某些情况下可能需要这样做。在这种情况下,您应该使用 static_cast
显式地进行缩窄转换。例如:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
最佳实践
尽可能避免缩窄转换。如果一定要进行转换,使用 static_cast
显式地进行缩窄转换。
括号初始化不允许隐式缩窄转换
在使用大括号初始化时,严格禁止缩窄转换(这也是首选此初始化形式的主要原因之一):
1 2 3 4 |
|
Visual Studio 会产生如下错误信息:
1 |
|
关于数值转换的更多信息
数值转换的具体规则又多又复杂,所以这里我们只讲最重要、最需要记忆的。
在所有情况下,将值转换为其范围不支持该值的类型将可能导致意想不到的结果。例如:
1 2 3 4 5 6 7 8 9 |
|
在本例中,我们将一个大整数赋给一个 char
型变量(范围为-128到127)。这会导致 char
溢出,并产生意想不到的结果:
1 |
|
只要值适合较小类型的范围,从较大的整型或浮点类型转换为同一族的较小类型通常是有效的。例如:
1 2 3 4 5 6 7 |
|
能够输出预期的结果:
1 2 |
|
对于浮点数的例子,在更小的类型中可能出现舍入误差,例如:
1 2 |
|
在这个例子中,我们可以看到由于float
不能支持double
类型的精度,出现了精度丢失的问题:
1 |
|
从整数到浮点数的转换通常只要值在浮点类型的范围内就可以工作。例如:
1 2 3 |
|
可以输出预期的结果:
1 |
|
从浮点数到整数的转换只要值在整数的范围内就可以工作,但是任何小数值都会丢失。例如:
1 2 |
|
在这个例子中,丢失了小数值(.5),留下以下结果:
1 |
|
虽然数值转换规则看起来很可怕,但实际上,如果你试图做一些危险的事情(不包括一些有符号/无符号的转换),编译器通常会发出警告。