浮点数存储规则

二进制小数

首先让我们来看看二进制表示法。二进制表示法使用如下的形式表示:

b m b m − 1 ⋯ b 1 b 0 . b − 1 b − 2 ⋯ b − n b_mb_{m-1} \cdots b_1b_0.b_{-1}b_{-2} \cdots b_{-n} bmbm1b1b0.b1b2bn

其中每个二进制数 b i b_i bi 的取值是 0 或 1。

二进制小数

这种表示方法表示的数 b b b ∑ i = − n m 2 i × b i \sum_{i=-n}^{m} 2^i \times b_i i=nm2i×bi

数字的权的定义与二进制小数点符号相关,这意味着小数点左边的数字的权是 2 的正幂,得到整数值;而小数点右边的数字的权是 2 的负幂,得到小数值。

例如, 101.1 1 2 101.11_2 101.112 表示数字 1 × 2 2 + 0 × 2 1 + 1 × 2 0 + 1 × 2 − 1 + 2 × 2 − 2 = 4 + 0 + 1 + 1 2 + 1 4 = 5 3 4 1 \times 2^2 + 0 \times 2^1 + 1 \times 2^0 + 1 \times 2^{-1} + 2 \times 2^{-2} = 4 + 0 + 1 + \frac{1}{2} + \frac{1}{4} = 5\frac{3}{4} 1×22+0×21+1×20+1×21+2×22=4+0+1+21+41=543

假定我们仅考虑有限长度的编码,那么二进制表示法不能准确地表达像 1 3 \frac{1}{3} 31 4 7 \frac{4}{7} 74 这样的数。小数的二进制表示法只能表示那些能被写成 x × 2 y x \times 2^y x×2y 的数,其他的值只能被近似的表示。例如,数字 1 5 \frac{1}{5} 51 可以用十进制小数 0.20 0.20 0.20 精确表示。不过,我们并不能把它准确地表示为一个二进制小数,我们只能近似地表示它,增加二进制表示的长度可以提高表示的精度:

0.2小数表示

IEEE 浮点表示

IEEE 754 浮点标准用 V = ( − 1 ) S × M × 2 E V = (-1)^S \times M \times 2^E V=(1)S×M×2E 的形式表示一个数:

  • 符号 s(sign)决定数的正负
    • 当 s = 0 时是正数
    • 当 s = 1 时是负数
    • 对于数值 0 的符号位解释作为特殊情况处理
  • 尾数(significand)M 是一个二进制小数,它的表示范围是 1 ∼ 2 − ϵ (无穷小) 1 \sim 2 - \epsilon \text{(无穷小)} 12ϵ(无穷小)
  • 阶码(exponent)E 的作用是对浮点数加权,这个权重是 2 的 E 次幂

在单精度浮点格式(C 语言中的 float),s、exp、frac 字段分别为 1 位、k = 8 位和 n = 23 位,得到一个 32 位的表示。

在双精度浮点格式(C 语言中的 double),s、exp、frac 字段分别为 1 位、k = 11 位和 n = 52 位,得到一个 64 位的表示。

浮点数存储

有效数字 M

前面说过,1 ≤ M < 2,也就是说,M 可以写成 1.xxx 的形式,其中 xxx 表示小数部分。IEEE 754 规定,在计算机内部保存 M 时,默认这个数的第一位总是 1,因此 1 可以被舍去,只保存后面的 xxx 部分。比如保存 1.01 的时候,只保存 01 ,等到读取的时候,再把第一位的 1 加上去,这样做的目的,是为了节省 1 位有效数字。以 32 位浮点数为例,留给 M 只有 23 位,将第一位的 1 舍去以后,相当于可以保存 24 位有效数字。

C 语言中 float 和 double 表示的精度

float 占用 32 位,其中 24 位用于存放尾数,所以 float 有 24 个二进制有效位位数。

  • 1 0 7 < 2 24 = 16777216 < 1 0 8 10^7 < 2^{24} = 16777216 < 10^8 107<224=16777216<108
  • 所以 C 语言 float 的有效数字位是 7 7 7
    • 注意是有效位数,不是小数点后的位数

有时会不足 7 位,这是因为发生舍入导致的。有时又会超过 7 位,具体原因可以看:为什么说 32 位浮点数的精度是 7 位有效数

double 占用 64 位,其中 53 位用于存放尾数,所以 double 有 53 个二进制有效位位数。

  • 1 0 15 < 2 53 = 9007199254740992 < 1 0 16 10^{15} < 2^{53} = 9007199254740992 < 10^{16} 1015<253=9007199254740992<1016
  • 所以 C 语言 double 的有效数字位是 15 15 15

指数 E

E 为一个无符号整数

  • 这意味着,如果 E 为 8 位,它的取值范围为 0 ∼ 255 0 \sim 255 0255;如果 E 为 11 位,它的取值范围为 0 ∼ 2047 0 \sim 2047 02047
  • 科学计数法中的 E 是可以出现负数的,所以 IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数
    • 对于 8 位的 E,这个中间数是 127
    • 对于 11 位的 E,这个中间数是 1023
  • 比如, 2 10 2^{10} 210 的 E 是 10,所以保存成 32 位浮点数时,必须保存成 10 + 127 = 137 10+127=137 10+127=137,即 10001001 10001001 10001001

指数 E 从内存中取出还可以再分成三种情况:

E 不全为 0 或不全为 1

这时,浮点数就采用下面的规则表示,即指数 E 的计算值减去 127(或 1023),得到真实值,再将有效数字 M 前加上第一位的 1。

比如:0.5 的二进制形式为 0.1,由于规定整数部分必须为 1,即将小数点右移 1 位,则为 1.0 × 2 − 1 1.0 \times 2^{-1} 1.0×21。其阶码为 − 1 + 127 = 126 -1+127=126 1+127=126,表示为 01111110 01111110 01111110,而尾数 1.0 去掉整数部分为 0,则其二进制表示形式为: 0   0111111   000000000000000000000000 0\ 0111111\ 000000000000000000000000 0 0111111 000000000000000000000000

E 全为 0

这时,浮点数的指数 E 等于 1 − 127 1 - 127 1127(或者 1 − 1023 1 - 1023 11023),有效数字 M 不再加上第一位的 1,而是还原为 0.xxxxxx 的小数。这样做是为了表示 ± 0 \pm0 ±0,以及接近于 0 的很小的数字。

E 全为 1

这时,如果有效数字 M 全为 0,表示 ± ∞ \pm\infty ±(正负取决于符号位 s)。如果有效数字 M 不全为 0,结果值被称为 NaN,即不是一个数(Not a Number)的缩写。

  • 32
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值