2.const限定符
const关键字用来声明一个常量,常量的名称通常以大写字母开头。
const int Months = 12;
这行代码声明了一个常量Months,并且赋值为12,此后不允许再对常量的值进行修改。因此在声明常量的同时就需要进行赋值。
#define也可以完成定义常量的操作,但是const与#define相比的优势在于:
- const可以明确常量的类型。
- const定义的常量可以限制其作用域,而#define只能定义全局的常量。
- const可以用于更复杂的类型的定义。
定义常量时请使用const。
3.浮点数
浮点数能够表示带小数部分的数字,浮点就是因为小数点可以浮动而得名。浮点数可以表示小数、非常大和非常小的数字。浮点数在计算机内部存储的形式与表示的方法与整数有很大的不同。
3.1 书写浮点数
C++中的浮点数有两种书写方式。第一种方式是常用的标准小数点表示法:
- 12.34
- 939001.32
- 0.00023
- 8.0
即使小数部分为0,小数点也将确保该数字以浮点格式(而不是整数格式)表示。
第二种表示浮点值的方式叫做E表示法:
- 2.52e+8,在2.52的基础上小数点向右移动8位
- 8.33E-4,在8.33的基础上小数点向左移动4位
e或E后面的部分是指数,数值代表小数点移动的位数,负数向左移,正数向右移。
3.2 浮点类型
C++有3种浮点类型:float、double和long double。这三种类型的有效位可以一样多,通常float为32位,double为64位,long double为80、96或128位。而三种类型的指数范围至少是-37到37。
#include <iostream>
int main()
{
using namespace std;
cout.setf(ios_base::fixed, ios_base::floatfield);
float tub = 10.0 / 3.0;
double mint = 10.0 / 3.0;
const float million = 1.0e6;
cout << "tub = " << tub;
cout << ", a million tubs = " << million * tub;
cout << "\nand ten million tubs = ";
cout << 10 * million * tub << endl;
cout << "mint = " << mint << " and a million mints = ";
cout << million * mint << endl;
return 0;
}
下面是该程序的输出:
tub = 3.333333, a million tubs = 3333333.250000
and ten million tubs = 33333332.000000
mint = 3.333333 and a million mints = 3333333.333333
可以看到,double类型的精度是远远高于float类型的,在我的系统中float能够精确地表示前7位数字,而7位之后的数字就出现了误差。因此对计算结果的精度要求较高时,尽量选择double类型。上面的程序使用了ostream类种的setf()。迫使输出使用定点数表示法而不是E表示法,并使程序的输出显示到小数点后6位。
3.4 浮点数的优缺点
优点:
- 浮点数可以表示整数之间的值
- 由于有缩放因子,因此浮点数可表示的范围比整数大很多
缺点:浮点数的运算速度通常比整数运算慢,并且浮点数运算后会丢失精度
#include <iostream>
int main()
{
using namespace std;
float a = 2.34E+22f; // 末尾的f表示这是一个float类型的常量,赋值给变量a
float b = a + 1.0f;
cout << "a = " << a << endl;
cout << "b - a = " << b - a << endl;
return 0;
}
下面是该程序的输出:
a = 2.34e+022
b - a = 0
本来b-a的正确结果应该是1,但是由于加1是在第23位上加,而float类型只能够精确地表示前6位或者7位,因此这个修改不会对变量b的数值产生任何影响。
前面说float类型至少有32位,但这里却说float类型只能精确地表示前6位或7位,这不是前后矛盾吗?32位指的是二进制位,后面的6位指的是十进制位。由于单精度浮点数(float)在计算机中存储时有23位尾数(存储的是小数部分的二进制位)和1位始终为1的整数部分(该部分不进行存储),加起来一共24位,而4位二进制数可以完整的表示一个十进制数,因此24位二进制位可以表示6位十进制位,超过这个范围数字就不准确了。由于单精度浮点数尾数占23位,而某些十进制数的小数部分不能用二进制位准确的表示出来(例如0.33),可能会超过23位或者无限循环的情况,存储时自然就会丢失精度。
4.C++算数运算符
C++提供几种算数运算符来完成5种基本的算数运算:加、减、乘、除、取模。
#include <iostream>
int main()
{
using namespace std;
float hats, heads;
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << "Enter a number: ";
cin >> hats;
cout << "Enter another number: ";
cin >> heads;
cout << "hats = " << hats << ", heads = " << heads << endl;
cout << "hats + heads = " << hats + heads << endl;
cout << "hats - heads = " << hats - heads << endl;
cout << "hats * heads = " << hats * heads << endl;
cout << "hats / heads = " << hats / heads << endl;
return 0;
}
下面是该程序的输出:
Enter a number: 50.25
Enter another number: 11.17
hats = 50.250000, heads = 11.170000
hats + heads = 61.419998
hats - heads = 39.080002
hats * heads = 561.292480
hats / heads = 4.498657
可以看到计算的结果出现了误差,这正是由于一些小数是无法使用二进制精确的表示的,导致尾数很长(尾数只能存23位二进制位),而C++只保证float有6位十进制有效位,当有效位超过6位时数值就不精确了。我想这也正是浮点数常量默认是double类型的原因之一,因此若需要更高的精度,请使用double或者long double。
4.1 除法分支
如果参与除法运算的是两个整数,那么C++会进行整除,丢弃小数部分,只保留整数部分。只要其中一个数是浮点数就会保留小数部分。不同类型进行运算时,C++将把它们全部转换为同一类型。
#include <iostream>
int main()
{
using namespace std;
cout.setf(ios_base::fixed, ios_base::floatfield);
cout << "Integer division: 9/5 = " << 9 / 5 << endl;
cout << "Floating-point division: 9.0/5.0 = " << 9.0 / 5.0 << endl;
cout << "Mixed division: 9.0/5 = " << 9.0 / 5 << endl;
cout << "double constants: 1e7/9.0 = ";
cout << 1e7 / 9.0 << endl;
cout << "float constants: 1e7f/9.0f = ";
cout << 1e7f / 9.0f << endl;
return 0;
}
下面是该程序的输出:
Integer division: 9/5 = 1
Floating-point division: 9.0/5.0 = 1.800000
Mixed division: 9.0/5 = 1.800000
double constants: 1e7/9.0 = 1111111.111111
float constants: 1e7f/9.0f = 1111111.125000
C++重载了除法运算符,是其可以完成3种不同的运算:int除法、float除法、double除法。使用相同符号完成多种不同的操作叫做运算符重载,C++内置了一些重载示例,并且还允许扩展运算符,例如可以允许两个类相加等。
4.2 类型转换
C++发生自动类型转换的情况:
- 将一种算术类型的值赋给另一种算数类型的变量时,C++将对值进行转换
- 表达式中包含不同的类型时,C++将对值进行转换
- 将参数传递给函数时,C++将对值进行转换
低精度会自动转换为高精度,高精度类型转换为低精度类型需要进行强制类型转换,结果可能因为精度丢失而出错;0赋给bool变量时,将被转换为false;非零值将被转换为true。