浮点数的计算机表示(IEEE 754),由 UCB 数学教授 William Kahan 主要起草。后者也因其卓越贡献于1989年获得图灵奖。计算机组成原理与汇编语言这两门课均对该内容有所讲解。与课程中直接抛出公式与概念不同,我想首先与各位探讨"科学计数法"这个概念,进而讨论设计二进制的科学计数法需要涉及到哪些元素。接着,我们讨论如何在内存上表达这个方案。最后讨论计算机的具体实现。
我们都了解科学计数法。科学计数法的精妙之处在于,其将"量级"与"数值"两个信息拆分,让使用者对这两个信息更加明确。
如上,我们可以将任何有理数拆分成
的形式。值得注意的是:
的取值范围是
一定是一个整数
对于任何有理数,我们都可以用两个范围狭小(规则明确)的数字 B 与 C 来表示。
此外,我们知道,十进制只不过是记录数字大小的一种方式而已。历史上出现过的二进制、三进制、二十进制,都可以毫无障碍地表示数字,并且还有其独具的数学特性。
那么,二进制可以用科学计数法表示吗?答案当然是肯定的。
注意,这里下标2,代表这个数是二进制。 同理,
对应十进制中的数字
。
通过观察十进制的科学计数法形式,对于二进制,我们自然可以做出如下约定:
的取值范围是
一定是一个整数
这里我们补充说明一下,二进制的小数是什么样的。对于
这个十进制数,如果要将其转换为二进制:
5
,我们使用"不断除以2取余数"的方法,得到 101
;.25
,我们使用"不断乘以2取整数"的方法,得到 .01
。关于进制转换的具体方法与背后的数学原理,我写过一篇文章进行讨论,见这里:十进制转二进制 / 八进制 / 十六进制的手算方法,及其数学原理的通俗解释。
这里,我们只需要明确,二进制是存在小数形式的,且可以表示一切十进制可表示的数(的近似)。
接着,我们步入正题:只会表示0/1的计算机,如何记录并表达浮点数呢?
给一个32位的空间,如果不做任何约束,我们只能将其理解为一个整数,并且其取值范围为
。
这是因为,计算机只能记录 0 和 1 这两个信息,并不能直接记录小数点点在哪里。因此,我们需要设置一定规则,取出一定位,用于表示小数点点在哪里。这必将牺牲一定的精度与取值范围。
因此,我们将这 32 位空间分为三部分:
精度
,即这个数字值是多少,对应上面的B;小数点
,即量级,对应上面的C;正负
,只需要使用1位。在 IEEE 754 中,我们分别将上述一、二、三部分叫做尾数M
,阶码E
,符号s
。
于是我们有了二进制的表达式:
为了表示尽可能多的、常用的小数,我们有如下需求:
;
可以注意到,对于 M 、 E ,我们并不能直接用二进制表示,还需要设定一定规则。
假设尾数 M 一共有 f 位,则 f 可表示的整数取值范围为
,我们称 f 直接对应的非负整数为 C 。为了将其投影到
,我们做出如下变换:
假设解码 E 一共有 e 位,则 e 可表示的整数取值范围为
,我们称 e 直接对应的非负整数为 Exp 。我们希望 E 可以取到负数,因此做出如下变换:
这样,我们的 E 取值范围就来到了
。
有了如上设计的规则,我们便知晓了计算机记录浮点数的方式,以及转换流程。以下图为例。
二进制转换到其对应的十进制数0.15625过程
上面我们讨论了 IEEE 754 的思想,但是并不严谨,比如:
因此,我们从数学上严谨地讨论一道例题,考虑一下规格化浮点数。例题源自我的汇编语言笔记。
给定一个浮点格式(IEEE 754),有k位指数和n位小数,对于下列数,写出阶码E、尾数M、小数f和值V的公式。另外,请描述其位表示。
IEEE 754约定,计算机中浮点数二进制表示为:
数字形式:
编码形式:
s | exp | frac |
---|
exp域:E(注意,E要进行变换,再存储在exp中); frac域:M。
这里讨论到规格化浮点数(Normalized):
,e = exp的域的位数
)
)
则E为
E最大值为
。(为什么不是
呢?因为有规定:exp全部取1为“非规格化浮点数”,因此规格化浮点数中exp不能全部取1,顶多为(1)*(0)
)
E的最小值为
。(为什么不是
呢?因为有规定:exp全部取0为“非规格化浮点数”,因此规格化浮点数中exp不能全部取0,顶多为(0)*(1)
)
抛开例题,来看一个例子:
取值范围如下表。
s | exp | frac | E | value |
---|---|---|---|---|
0 | 0000 | 000 | -6 | 0 |
0 | 0000 | 001 | -6 | 1/8 * 1/64 = 1/512 |
0 | 0000 | 010 | -6 | 2/8 * 1/64 = 2/512 |
... | ||||
0 | 0000 | 110 | -6 | 6/8 * 1/64 = 6/512 |
0 | 0000 | 111 | -6 | 7/8 * 1/64 = 7/512 |
0 | 0001 | 000 | -6 | 8/8 * 1/64 = 8/512 |
0 | 0001 | 001 | -6 | 9/8 * 1/64 = 9/512 |
... | ||||
0 | 0110 | 110 | -1 | 14/8 * 1/2 = 14/16 |
0 | 0110 | 111 | -1 | 15/8 * 1/2 = 15/16 |
0 | 0111 | 000 | 0 | 8/8 * 1 = 1 |
0 | 0111 | 001 | 0 | 9/8 * 1 = 9/8 |
0 | 0111 | 010 | 0 | 10/8 * 1 = 10/8 |
... | ||||
0 | 1110 | 110 | 7 | 14/8 * 128 = 224 |
0 | 1110 | 111 | 7 | 15/8 * 128 = 240 |
0 | 1111 | 000 | n/a | inf |
可以看出,假设frac有f位,则M可视为:
其中,C是整数,由frac决定,即
;
并且C满足
。
则浮点数V的十进制即为:
也可写作
:
其中,
、
分别为exp、frac位数,为常数。
较为简单,直接解决如下。
0.5 // 转换为二进制 ==>
0.1 // 移动小数点,使其在最左边的1之后
M = 1.0 // 小数点后的数字存储在frac中
E = -1 // 因为是左移
frac= 0* // 共n位
exp = E + Bias
= -1 + (2^(e-1) - 1)
则,位的描述为:
s | exp | frac |
---|---|---|
0 | bin(-1 + (2^(e-1) - 1)) | 00....(共n位) |
根据前置工作二,可以看出,对于规格化浮点数可化简为:
现在的任务有两个:
应大于等于
);
不是奇数,因此使
为奇数,则
取1,则取
)。
下面分类讨论:
情况一:E可以取到f时,
即
时,
取
,
取其能取的最大奇数,对应的二进制为: exp:dec2bin(
),frac:1*
(frac全为1)
情况二:E取不到f时,
这种情况不可能,因为E取不到f,则很多整数都不能表示。
观察:
则
取
,
、
分别取最小。
由前置工作一,
取
,
取
,对应的二进制为: exp:0*1
,frac:0*
后记:我第一学习浮点数是在2019年年末,当时对于浮点数的笔记和理解是有问题的。在此感谢一位湖南大学的朋友(公众号 / CSDN:梓酥),给我发邮件,指出我的问题~
Piper蛋窝
,回复「微信」来加我微信联系~