浮点数的表示方法

浮点数常采用符号+阶码+尾码的表示方法,单精度浮点数由1位符号位,8位阶码和23位尾码表示;双精度浮点数由1位符号位,11位阶码和52位尾码表示。

1
2
float f = 3.14;
cout << bitset<32>(*reinterpret_cast<int*>(&f)) << endl; // 01000000 01001000 11110101 11000011

浮点数并不能完整地表述所有的十进制小数,就好像十进制小数不能完整表示三分之一一样,浮点数就不能在没有精度丢失的情况下表示0.1。

1
2
3
float zero_one = 0.1;
cout.precision(9);
cout << fixed << zero_one << endl; // 0.100000001

当浮点数多次累加时,会导致误差也累加:

1
2
3
4
5
float sum = 0;
for(int i = 0; i < 10000; i++) {
sum += 0.1;
}
cout << sum << endl; // 999.903

无穷

用阶码全为1,尾码全为0的浮点数表示无穷大,C++中可以使用limits头文件中的numeric_limits::infinity方法获取,使用cmath头文件中的infinity方法判断是否是无穷大。

1
2
3
4
float inf = numeric_limits<float>::infinity();
cout << inf << endl; // inf
cout << bitset<32>(*reinterpret_cast<int*>(&inf)) << endl; // 01111111 10000000 00000000 00000000
cout << isinf(inf) << endl;

无穷数的运算基本符合数学上定义的规则。

最小精度

numeric_limits::min()方法获取到的是规格化数的最小精度,单精度为2的负127次方,numeric_limits::denorm_min()方法获取的是非规格化数的最小精度,单精度为2的负150次方。

1
2
3
4
5
6
7
float fmin = numeric_limits<float>::min();
cout << fmin << endl; // 1.17549e-38
cout << bitset<32>(*reinterpret_cast<int*>(&fmin)) << endl; // 00000000 10000000 00000000 00000000

float dfmin = numeric_limits<float>::denorm_min();
cout << dfmin << endl; // 1.4013e-45
cout << bitset<32>(*reinterpret_cast<int*>(&dfmin)) << endl; // 00000000 00000000 00000000 00000001

浮点数的最大值通过numeric_limits::max()获取,而最小(最低)值是通过numeric_limits::lowest()获取,名字上感觉有一点别扭,很容易错用成numeric_limits::min()

非数

浮点数非数使用阶码全为1,尾码不全为0表示,可分为两类,一类是不会触发任何异常的非数,一般表示无意义或未定义的操作结果;另一类是信号非数,可用于引发信号或异常。

1
2
3
4
5
6
7
8
float nan = numeric_limits<float>::quiet_NaN();
cout << nan << endl; // nan
cout << bitset<32>(*reinterpret_cast<int*>(&nan)) << endl; // 01111111 11000000 00000000 00000000
cout << isnan(nan) << endl;
float s_nan = numeric_limits<float>::signaling_NaN();
cout << s_nan << endl; // nan
cout << bitset<32>(*reinterpret_cast<int*>(&s_nan)) << endl; // 01111111 10100000 00000000 00000000
cout << isnan(s_nan) << endl;

不触发任何异常的非数使用numeric_limits::quiet_NaN()方法获取,信号非数可以使用numeric_limits<float>::signaling_NaN()获取,然后使用cmath中的isnan函数可以判断浮点数是否为非数。像是浮点数类型的零除以零操作,返回的就是非数。