C 语言数的表示(二)
纯理论记录 C 语言是如何表示数值的,原理向,仅记录备忘。
十进制与二进制的转换
先记公式,任意一个二进制浮点数 V 可以表示成下面的形式:
V = (-1)s × M × 2E
(-1)^s表示符号位,当 s = 0 时,V 为正数,当 s = 1 时,V 为负数。M 表示有效数字,大于等于 1,小于 2。
2^E表示指数位。
举例说明,十进制的 5.0,写成二进制是 101.0,相当于 1.01 × 2^2,那么,s = 0,M = 1.01,E = 2。
- 32 位浮点数,最高 1 位是符号位 S,接着 8 位是指数 E,剩下 23 位为有效数字 M。
- 64 位浮点数,最高 1 位是符号位 S,接着 11 位是指数 E,剩下 52 位为有效数字 M。
IEEE 754 对有效数字 M 和指数 E 还有一些特别的规定:
对于有效数字 M
1 ≤ M < 2,也就是说 M 可以写成 1.XXXX 的形式,其中 XXXX 表示小数部分。
在计算机内部保存 M 时,默认这个数的第一位总是 1,因此可以被舍去,只保存后面的 XXXX 部分。比如保存 1.01 的时候,只保存 01,等到读取的时候,再把第一位的 1 加上去。这样的目的,是节省 1 位有效数字。
以 32 位浮点数为例,留给 M 只有 23 位,将第 1 位舍去以后,等于可以保存 24 位有效数字。
对于指数 E
首先,E 为一个无符号数 unsigned int,如果 E 为 8 位,它的取值范围为 [0, 255];如果 E 为 11 位,它的取值范围为 [0, 2047]。
但是,我们知道,科学计数法中 E 是可以出负数的,所以 IEEE 754 规定,E 的真实值必须再减去一个中间数,对于 8 位的 E,这个中间数是 127,对于 11 位的 E,这个中间数是 1023。
E 减去中间数得到的值即为指数阶码值(exponent bias),是指浮点数表示法中的指数域的编码值为实际值加上某个固定值,IEEE 754 规定该固定值为 2^(e-1) - 1,其中 e 为存储指数的比特的长度。
比如 2^10 的 E 是 10,保存的成 32 位的浮点数时,必须保存成 10 + 127 = 137,即 10001001。
采用指数的实际值加上固定阶码值的办法表示浮点数的指数,好处是可以用长度为 e 个比特的无符号数表示所有的指数取值,这使得两个浮点数的指数大小的比较更为容易,实际上可以按照字典序比较两个浮点数的大小,这种移码表示指数部分,中文称为阶码。
以单精度型阶码表示示例如下:
例 1
如果我们要表示 0,则有 0 + 127 = 127,二进制表示为:0000 0000 + 0111 1111 = 0111 1111
例 2
如果我们要表示 1,则有 1 + 127 = 128,二进制表示为:0000 0001 + 0111 1111 = 1000 0000
例 3
如果我们要表示 2,则有 2 + 127 = 129,二进制表示为:0000 0010 + 0111 1111 = 1000 0001
例 4
如果我们要表示 128,则有 128 + 127 = 255,二进制表示为:1000 0000 + 0111 1111 = 1111 1111
移码 128 即为 8 位二进制移位存储表示的最大正数。
我们再来看一下负数:
例 5
如果我们要表示 -1,则有 -1 + 127 = 126,二进制表示为:1111 1111 + 0111 1111 = 0111 1110
然后,指数 E 还分三种情况:
E 不全为 0 或不全为 1 时,浮点数就采用上面的规则表示,即指数 E 的计算值减去 127(或 1023),得到真实值,再将有效数字 M 前加上第一位的 1。
E 全为 0 时,浮点数的指数 E 等于
1 - 127(1-1023),有效数字 M 不再加上第一位的 1,而是还原为0.XXXX的小数,这样作是为了表示 ±0,以及接近 0 的很小的数值。E 全为 1 时,如果有效数字 M 全为 0,表示 ± 无穷大(正负取决于符号 s);如果有效数字 M 不全为 0,表示这个数不是一个数 (NaN)。