在上一篇文章中,我们主要介绍了在计算机中使用定点数表示数字的方式。
但用定点数表示小数时,存在数值范围、精度范围有限的缺点,所以在计算机中,我们一般使用「浮点数」来表示小数。
首先,我们需要理解什么是浮点数?
这怎么理解呢?
看到了吗?用这种科学计数法的方式表示小数时,小数点的位置就变得「漂浮不定」了,这就是相对于定点数,浮点数名字的由来。
我们已经知道,浮点数是采用科学计数法来表示一个数字的,它的格式可以写成这样:
V = (-1)^S * M * R^E如果我们要在计算机中,用浮点数表示一个数字,只需要确认这几个变量即可。
假设我们定义如下规则来填充这些 bit:
符号位 S 占 1 bit 指数 E 占 10 bit 尾数 M 占 21 bit 所以符号位 S = 0,尾数 M = 1.001001(B),指数 E = 4(D) = 100(B)。
浮点数的结果就出来了,是不是很简单?
如果你也想定一个新规则,例如符号位 S 占 1 bit,指数位 E 这次占 5 bit,尾数 M 占 25 bit,是否也可以?当然可以。
我们可以看到,指数和尾数分配的位数不同,会产生以下情况:
- 指数位越多,尾数位则越少,其表示的范围越大,但精度就会变差,反之,指数位越少,尾数位则越多,表示的范围越小,但精度就会变好
- 一个数字的浮点数格式,会因为定义的规则不同,得到的结果也不同,表示的范围和精度也有差异
这就会导致,一个程序在不同厂商下的计算机中做浮点数运算时,需要先转换成这个厂商规定的浮点数格式,才能再计算,这也必然加重了计算的成本。
直到1985年,IEEE 组织推出了浮点数标准,就是我们经常听到的 IEEE754 浮点数标准,这个标准统一了浮点数的表示形式,并提供了 2 种浮点格式:
单精度浮点数 float:32 位,符号位 S 占 1 bit,指数 E 占 8 bit,尾数 M 占 23 bit 双精度浮点数 float:64 位,符号位 S 占 1 bit,指数 E 占 11 bit,尾数 M 占 52 bit 除了规定尾数和指数位,还做了以下规定:
指数 E 非全 0 且非全 1:规格化数字,按上面的规则正常计算 指数 E 全 0,尾数非 0:非规格化数,尾数隐藏位不再是 1,而是 0(M = 0.xxxxx),这样可以表示 0 和很小的数 指数 E 全 1,尾数全 0:正无穷大/负无穷大(正负取决于 S 符号位) 指数 E 全 1,尾数非 0:NaN(Not a Number) nerror="javascript:errorimg.call(this);">
标准浮点数的表示
所以 S = 0,尾数 M = 1.001001 = 001001(去掉1,隐藏位),指数 E = 4 + 127(中间数) = 135(D) = 10000111(B)。填充到 32 bit 中,如下:
nerror="javascript:errorimg.call(this);">
如果用 double 表示,和这个规则类似,指数位 E 用 11 bit 填充,尾数位 M 用 52 bit 填充即可。
浮点数为什么有精度损失?
如果我们现在想用浮点数表示 0.2,它的结果会是多少呢?
所以 0.2(D) = 0.00110...(B)。
最后,我们再来看一下,用浮点数表示一个数字,其范围和精度能有多大?
它能表示的精度有多小呢?
用同样的方法可以算出,double 能表示的最大二进制数为 +1.111...111(小数点后52个1) * 2^1023 ≈ 2^1024 = 1.79 * 10^308,所以 double 能表示范围为:-1.79 * 10^308 ~ +1.79 * 10^308。
从这里可以看出,虽然浮点数的范围和精度也有限,但其范围和精度都已非常之大,所以在计算机中,对于小数的表示我们通常会使用浮点数来存储。
总结
微信搜索关注「水滴与银弹」公众号,第一时间获取优质技术干货。7年资深后端研发,用简单的方式把技术讲清楚。

