计算机中的浮点数表示
本文介绍小数在计算机中的表现形式,基于IEEE标准。
浮点数表示
浮点数(floating point):二进制小数点不固定的表达数的记数法。
在许多编程语言中,都提供了一种或者多种浮点数类型。例如 C
语言中提供了双精度浮点类型double
,单精度浮点类型float
,这两种数据类型都采用浮点数来表示。浮点数
\(\pm m \times n^e\) 是指由符号 (sign)
、指数 (exponent) 、尾数 (fraction) 、基数 (cardinal number)
四部分来表示的小数,数被表示为二进制小数点左边只有一位非零数的形式。在计算机中使用二进制表示数据,因此基数为2。因此计算机中的浮点数可以分成三个部分,符号,尾数和指数。
- 双精度浮点数(64位)
1位 | 8位 | 52位 |
---|---|---|
符号部分 | 指数部分 | 尾数部分 |
- 单精度浮点数(32位)
1位 | 8位 | 23位 |
---|---|---|
符号部分 | 指数部分 | 尾数部分 |
下面以单精度浮点数为例,双精度浮点数表示类似。
符号部分
符号部分的表示使用1-bit ,和二进制整数的符号位表示法类似,符号位在最高位,0为正数,1为负数。
将符号放在最高为,可以快速测试出大于、小于、等于0的情况。
尾数部分
尾数:位于小数点的位数字段,值在0和1之间。
单精度浮点数中尾数长度是23个二进制数字。尾数部分是被正则化的,即小数点左边有且仅有一位非0数字,在二进制表示方式下,非0即1,因此这个数字只能为1,IEEE 754隐藏了规格化二进制数的前导位1,这样,23-bit 的尾数部分实际上可以表示 24-bit 的精度。
指数部分
指数:位于浮点数的指数字段,表示小数点的位置。
但制数部分如果为负数,使用补码,可能会使得一个负指数显得像是一个大数,因此指数部分使用的是 EXCESS 系统,将中间数字设置为0,负数不需要负号来表示,将最小的负指数表示为\(00\cdots00_2\) ,最大的正指数表示为\(11\cdots11_2\),这种记数法称为带偏阶的记数法(biased notation),要从带偏阶的指数中减去偏阶,才能获得真实的值。8-bit 的指数字段可以表示 256 个不同的指数值,但 0000 0000 和 1111 1111 有特殊含义,稍后再提。剩下的 254 个数值,即 1~254,实际的指数值等于该无符号整数减单精度偏阶127所得的值。
这样单精度浮点数表示为: \[ N =(-1)^s \times (1+Fraction) \times 2 ^{Exponent-Bias},\quad 1 \le Exponent \le 254, Bias = 127 \]
0000 0000 的情况
如果指数字段是0000 0000,则代表指数值-126,且尾数中小数点左边默认数字是0(而不是1),这种情况下,浮点数值为: \[ N = (-1)^s \times 0.尾数 \times 2 ^{-126} \] 若指数与尾数均为0,则根据符号位表示 \(\pm 0\)。
但为什么不是用$1.尾数 ^{-127} $呢?考虑以下情况,\(1.11\cdots11\times 2^{-127}\) 与 \(1.11\cdots 10 \times 2 ^{-127}\) 两个数相减的情况,差为\(0.00\cdots 01 \times 2 ^{-127}\) ,这个差值将无法使用这种表示法来表示。为了解决这种情况,在表示比\(1.00\cdots 00\times 2 ^{-126}\)小的数时,我们用0取代被我们忽略的默认小数点左边的1。
1111 1111 的情况
- 尾数为0 :根据符号位表示 \(\pm \infty\)
- 尾数非0:表示
NaN
非数字, (Not a Number)
IEEE 754 浮点数的编码
单精度 | 双精度 | 表示对象 | ||
---|---|---|---|---|
指数 | 尾数 | 指数 | 尾数 | |
0 | 0 | 0 | 0 | 0 |
0 | 非0 | 0 | 非0 | \(\pm\) 非规格化数 |
1-254 | 任何值 | 1-2046 | 任何值 | \(\pm\)浮点数 |
255 | 0 | 2047 | 0 | \(\pm \infty\) |
255 | 非0 | 2047 | 非0 | NaN |
十进制小数转IEEE浮点数
方法
- 将十进制小数表示为二进制小数
- 正则化处理
- 根据符号确定符号位
- 计算指数部分
- 计算尾数部分
举例
下面我们将\(-6 \frac{5}{8}\) 表示为IEEE浮点数:
首先将\(-6 \frac{5}{8}\) 表示为二进制数: \[ -6 \frac{5}{8} = -(1\times 2^2 + 1 \times 2^1 + 0 \times 2^0 + 1\times 2^{-1} + 0 \times 2^{-2} + 1\times 2^{-3} \]
正则化处理得到 \(-1.10101 \times 2 ^2\)
负数,符号位为1
计算指数部分:实际指数为2,加上127得到129,二进制表示指数部分为1000 0001
计算尾数部分,忽略小数点左边的1,得到尾数部分为 1010 1000 0000 0000 0000 000 。
C语言实现 1
1 | /* 用于确认单精度浮点数表示方法的C语言程序(摘自《程序是怎样跑起来的》),做了一点小改动 */ |
IEEE 浮点数转十进制数
方法
- 由第一位判断符号
- 由第二位至第九位计算十进制值减去127得到指数值
- 后23位为尾数值
举例
将浮点数 0 1000 0011 0010 1000 0000 0000 0000 000 转为十进制数:
- 符号位为0,该数为正数
- 指数字段 1000 0011 转为十进制数为 131,得到实际指数值为4
- 尾数部分为 0010 1000 0000 0000 0000 000,在小数点左边补1,得到1.0010 1
- 因为实际指数值为4,故将小数点右移4位,得到10010.1
- 转为十进制得到18.5
参考资料
- 《计算机系统概论》
- 《程序是怎样跑起来的》
- 《计算机组成与设计——硬件/软件接口》
- IEEE 754 - 维基百科,自由的百科全书
- floating point - Understanding exponent 00000000 and 11111111 in IEEE - Stack Overflow