C++中数据,不管是常量还是变量都有对应的数据类型,在计算过程中数据的类型可以发生变化。数据类型的变化有些是自动完成的(自动转换),此外还可以使用强制的方法实现数据类型转换(强制转换数据类型)。
一、数据类型自动转换
来看前面尝试过的程序:
#include<iostream> using namespace std; int main() { // 整数/整数 cout<<5/2<<endl; return 0; }
运行输出结果是2。整数/整数结果是整数。
#include<iostream> using namespace std; int main() { // 浮点数/整数 cout<<5.0/2<<endl; return 0; }
运行输出结果是2.5。除法的被除数是浮点数,除数是整数,结果是浮点数。
通过上面的例子可以发现,运算时如果两个数据的类型不一致,会自动按照较大范围的数据类型出结果,也就是结果的数据范围是两个数的数据类型中较大范围的那一个(上面例子5.0是double
类型、2是int
类型,double
类型存储范围大于int
,所以结果是double
类型)。
如果赋值语句赋值符号左右的数据类型不一致,也会出现自动类型转换:
#include<iostream> using namespace std; int main() { double num = 5; cout<<num/2; return 0; }
输出2.5。第5行 double num = 5;
赋值符号右边是int型常量5,左边是double
类型变量,赋值后num的值是5.0(其实直接输出num仍然是5,这里说是5.0强调是一个浮点数),那么第6行输出num/2
,num是浮点数,所以结果是浮点数2.5
#include<iostream> using namespace std; int main() { int num = 3.14*5; cout<<num; return 0; }
输出15。第5行 int num = 3.14*5;
,赋值符号右边计算结果是浮点数15.7,但是左边是int
类型变量,赋值后num的值是15(只保留了整数部分,出现了精度丢失)
先判断下面程序的输出结果,然后再编程运行检验判断是否正确。如果判断有误,请结合上面的内容再仔细分析程序的执行情况:
#include<iostream> using namespace std; int main() { double d = 5/2; cout<<d<<endl; return 0; }
来看一个问题, 输入两个整数 \(a,b\),计算并输出 \(a \div b\) 的结果。 我们先尝试编写并运行下面三段程序:
#include<iostream> using namespace std; int main() { int a,b; cin>>a>>b; cout<<a/b; return 0; }
变量a和b都是int类型,整数/整数
,结果是除法的商。输入5 2
,会输出2
。
#include<iostream> using namespace std; int main() { int a,b; cin>>a>>b; cout<<1.0*a/b; return 0; }
1.0*a/b
,会先计算1.0*a
,浮点数乘以整数,结果是浮点数,再除以整数,最后结果是浮点数。 输入5 2
,会输出2.5
。
#include<iostream> using namespace std; int main() { int a,b; cin>>a>>b; cout<<a/b*1.0; return 0; }
a/b*1.0
,会先计算a/b
,这里是整数/整数
,结果是除法的商,再乘以1.0,最后结果是浮点数(小数部分为0)。 输入5 2
,会输出2
(cout输出小数部分为0的浮点数时不会输出小数部分)。
再来看一个问题,输入两个整数 \(a,b\),计算并输出 \(a^b\) 的结果。这里可以使用 cmath 库文件中的 pow 函数来计算幂次方:
#include<iostream> #include<cmath> using namespace std; int main() { int a,b; cin>>a>>b; cout<<pow(a,b)<<endl; return 0; }
下面来测试一下程序:
1.输入\(2\;10\),可以正常输出1024
;
2. 输入\(35\;12\),输出的是科学计数法形式的结果:3.37922e+018
。
虽然这里输入的 \(a,b\) 都是整数,数学里计算 \(a^b\) 的结果是整数,但是 pow 函数的结果是小数(可以认为这里 pow 函数的计算结果是小数部分为0的小数),用 cout 直接输出很大的小数会以科学计数法的形式输出,所以出现了上面输出科学计数法形式的情况。考虑到问题里 \(a,b\) 都是整数,那么 \(a^b\) 也是整数,这里我们可以将 pow 的计算结果赋值给一个int
(最好是long long
)类型的变量,这样就把小数结果自动转换成整数了:
#include<iostream> #include<cmath> using namespace std; int main() { int a,b; cin>>a>>b; long long ans = pow(a,b); cout<<ans<<endl; return 0; }
但是要注意:这里 pow 的计算结果是double
类型,如果结果不太大,可以直接赋值给long long
类型的变量;如果计算结果很大(超过了long long
的存储范围),直接赋值给long long
类型的变量会出现数据溢出情况,结果就不可靠了。例如上面的程序,如果输入\(35\;13\),输出的结果是-9223372036854775808
,很明显,这里出现了数据溢出。
二、强制转换数据类型
在必要时,还可以使用强制类型转换来转换数据类型:
#include<iostream> using namespace std; int main() { //注意强制类型转换的语法 cout<<(double)5/2; return 0; }
计算(double)5/2
,首先处理被除数(double)5
,这里5是int常量,前面加上(double)
表示将其强制转换成double类型。结果输出2.5
#include<iostream> using namespace std; int main() { double num = 5; cout<<(int)num/2; return 0; }
计算(int)num/2
,首先处理被除数(int)num
,这里num是double变量,前面加上(int)
表示将其强制转换成int类型。结果输出2
从上面的例子可以看出,强制类型转换的优先级高于算术运算:(int)num/2
,首先是将num强制转换成int类型,再做除法;而不是先做除法,再将结果强制类型转换成int类型。我们其实可以设计一个程序来验证上面的结论:
#include<iostream> using namespace std; int main() { double num = 5; cout<<(int)num/2.0; return 0; }
如果上面结论成立,也就是强制类型转换的优先级高于算术运算,那么先强制类型转换再做除法,结果应该是2.5;
如果上面结论不成立,那么先计算除法,再强制类型转换,结果应该是2;
实际运行结果是2.5。通过这个试验验证了“强制类型转换的优先级高于算术运算”的结论,看吧——实践出真知!一定要活学活用!
还是上面计算 \(a^b\) (\(a,b\) 都是整数)的问题,也可以使用强制类型转换来将 pow 函数的计算结果(double类型小数)转换成整数。不过也要注意的是,强制类型转换也可能会出现数据溢出的情况:
#include<iostream> #include<cmath> using namespace std; int main() { int a,b; cin>>a>>b; cout<<(long long)pow(a,b)<<endl; return 0; }
三、类型转换要注意避免数据溢出
这里总结一下,不管是通过赋值语句的自动类型转换,还是强制类型转换,都要考虑将一个能存储更大范围的数据类型转换成能存储较小范围的数据类型时,可能会出现数据溢出的情况(例如将long long
转换成int
,将double
转换成int
或long long
)。反之,将一个能存储较小范围的数据类型转换成能存储更大范围的数据类型时,一般不用担心数据溢出问题(例如将int
转换成long long
,将int
转换成double
)。解决问题时,只有确保不会出现数据溢出,才能进行数据类型转换。
#include<iostream> using namespace std; int main() { long long m = 123; //long long类型赋值给int类型 //因为long long类型变量的值仍在int存储范围内,不会出现数据溢出 cout<<(int)m<<endl; int n = m; cout<<n<<endl; return 0; }
#include<iostream> using namespace std; int main() { long long m = 2147483648; //long long类型赋值给int类型 //因为long long类型变量的值超出int存储范围内,会出现数据溢出 cout<<(int)m<<endl; int n = m; cout<<n<<endl; return 0; }