IEEE 754标准出现之前,业界对机器浮点数表示各有一套说法,这就造成了浮点代码在机器间的移植性表现极差。IEEE规定了通用机器浮点数标准,今天几乎所有计算机都支持IEEE 754,且默认使用的浮点数标准就是IEEE 754。关于浮点数应具备的基本常识,课本上讲的也稍微枯燥,自己理解起来不容易。希望此篇通俗易懂的简要介绍,能使你对IEEE 754浮点数知识体系有深刻的认识。
一、为什么使用浮点数?
浮点数的概念是相对于定点数而出现的。定点数是指小数点固定的数,包括无符号整数,有符号整数,定点小数。举几个例子:
无符号整数:
unsigned int a = 23
有符号整数:
long long a = 3294294899455
定点小数:
在数据位宽为5位时,01101表示+0.1101,若符号位为1则为负
浮点数就是小数点是逻辑上浮动的数。既然定点数用起来非常简单,且运算速度极快,那为什么还要使用浮点数呢?考虑以下的情况:
现欲表示5 * 2
100
,写成二进制计数就是:1.01 * 2
102
。
如果是定点表示,那么要表示成:
这105个bits,都要老老实实地在内存中存储,未免太浪费空间,不切实际。因此,借鉴科学计数法的思想,我们不是只要把 X * 2
Y
中的X和Y表示一下,就可以了吗?在我们这个例子中,X就是101
(B)
,Y就是102
(D)
上述的改进思想其实就是IEEE 754的核心方法。
二、IEEE 754 浮点数的4种表示格式
均以float为例,double自己类推就好。
以下记符号位为 s,指数位为 e,尾数位为 f
规格化
它表示的数是:(-1)
s
* 1.f * 2
e
注意在IEEE 754浮点标准中,指数位都是移码表示,对float而言,也就是:
实际指数 = 位表示的指数 - 127
也就是说,如果现在指数位为
0111 1111
,则实际指数应该为
0111 1111
的十进制表示127减去127,也就是0;反过来,如果现在要表示的是0次幂,则应该表示为
0111 1111
以5为例,其表示为:
其中,第1位为0,说明它是一个非负数;[2,9]为
1000 0001
(129),减去127得到实际指数为2,尾数位为1.01,则表示的就是
5
= 1.01 * 2
2
-
最小的规格化数为:
0 00000001 00000000000000000000000
-
最大的规格化数为:
0 11111110 11111111111111111111111
非规格化
非规格化表示中,指数位全为0
它表示的数是:(-1)
s
* 0.f * 2
-126
-
最小的非规格化数为:
0 00000000 00000000000000000000001
-
最大的非规格化数为:
0 00000000 11111111111111111111111
无穷大
例如:double(7) / double(0) 的结果为
非数
例如:sqrt(-1)的结果为
三、关于舍入
舍入,实际上就是在原有四舍五入的基础上加以修改。例如,0.49还是舍入到0,0.51还是舍入到1,关键在于0.5的舍入。一般采用
向偶数舍入(也叫向最接近的值舍入)
,这样一来,对于位居中央的数,就向偶数舍入。例如,1.5舍入到2,2.5舍入到2,3.5舍入到4…向偶数舍入可以理解为:最后一位表示为 0
23位实在太长有点难以距离,我们假设现在有一个玩具模型,有1位符号位,4位指数位和4位尾数位。那么:
33 = 1.00001 * 2
5
,假设现在尾数位数足够的话,那么33应该被表示成
0 1100 00001
,但是现在由于尾数只有4位可以存储,所以会发生截断舍入,舍入的结果(要让尾数的第 4 位为偶数)为
0 1100 0000
35 = 1.00011 * 2
5
,假设现在尾数位数足够的话,那么35应该被表示成
0 1100 00011
,但是现在由于尾数只有4位可以存储,所以会发生截断舍入,舍入的结果(要让尾数的第 4 位为偶数)为
0 1100 0010
四、如何查看浮点数的位表示?
写一段C++代码就可以解决了:
考虑因素:
- Windows是小端法机器
# include <iostream>
# include <bitset>
# include <math.h>
using namespace std;
typedef unsigned char* ptr;
int main() {
float a;
cin >> a;
ptr st = ptr(&a);
for (int i = 3; i >= 0; --i) {
bitset<8> bs(st[i]);
for (int j = 7; j >= 0; --j) {
cout << bs[j];
}
cout << " ";
}
cout << endl;
return 0;
}