写点什么

什么是浮点数?

用户头像
Kaito
关注
发布于: 2020 年 12 月 29 日
什么是浮点数?

阅读本文大约需要 7 分钟。


在上一篇文章 什么是定点数?我们主要介绍了在计算机中使用定点数表示数字的方式。


简单回顾一下,简单来说,用定点数表示数字时,会约定小数点的位置固定不变,整数部分和小数部分分别转换为二进制,就是定点数的结果。


但用定点数表示小数时,存在数值范围、精度范围有限的缺点,所以在计算机中,我们一般使用「浮点数」来表示小数。


这篇文章,我们就来详细看一下浮点数到底是如何表示小数的,以及浮点数的的范围和精度有多大。


什么是浮点数?


首先,我们需要理解什么是浮点数?


之前我们学习了定点数,其中「定点」指的是约定小数点位置固定不变。那浮点数的「浮点」就是指,其小数点的位置是可以是漂浮不定的。


这怎么理解呢?


其实,浮点数是采用科学计数法的方式来表示的,例如十进制小数 8.345,用科学计数法表示,可以有多种方式:


8.345 = 8.345 * 10^08.345 = 83.45 * 10^-18.345 = 834.5 * 10^-2...
复制代码


看到了吗?用这种科学计数法的方式表示小数时,小数点的位置就变得「漂浮不定」了,这就是相对于定点数,浮点数名字的由来。


使用同样的规则,对于二进制数,我们也可以用科学计数法表示,也就是说把基数 10 换成 2 即可。


浮点数如何表示数字?


我们已经知道,浮点数是采用科学计数法来表示一个数字的,它的格式可以写成这样:


V = (-1)^S * M * R^E
复制代码


其中各个变量的含义如下:


  • S:符号位,取值 0 或 1,决定一个数字的符号,0 表示正,1 表示负

  • M:尾数,用小数表示,例如前面所看到的 8.345 * 10^0,8.345 就是尾数

  • R:基数,表示十进制数 R 就是 10,表示二进制数 R 就是 2

  • E:指数,用整数表示,例如前面看到的 10^-1,-1 即是指数


如果我们要在计算机中,用浮点数表示一个数字,只需要确认这几个变量即可。


假设现在我们用 32 bit 表示一个浮点数,把以上变量按照一定规则,填充到这些 bit 上就可以了:


假设我们定义如下规则来填充这些 bit:


  • 符号位 S 占 1 bit

  • 指数 E 占 10 bit

  • 尾数 M 占 21 bit


按照这个规则,将十进制数 25.125 转换为浮点数,转换过程就是这样的(D 代表十进制,B 代表二进制):


  1. 整数部分:25(D) = 11001(B)

  2. 小数部分:0.125(D) = 0.001(B)

  3. 用二进制科学计数法表示:25.125(D) = 11001.001(B) = 1.1001001 * 2^4(B)


所以符号位 S = 0,尾数 M = 1.001001(B),指数 E = 4(D) = 100(B)。


按照上面定义的规则,填充到 32 bit 上,就是这样:

浮点数的结果就出来了,是不是很简单?


但这里有个问题,我们刚才定义的规则,符号位 S 占 1 bit,指数位 E 占 10 bit,尾数 M 占 21 bit,这个规则是我们拍脑袋随便定义出来的。


如果你也想定一个新规则,例如符号位 S 占 1 bit,指数位 E 这次占 5 bit,尾数 M 占 25 bit,是否也可以?当然可以。


按这个规则来,那浮点数表示出来就是这样:


我们可以看到,指数和尾数分配的位数不同,会产生以下情况:


  1. 指数位越多,尾数位则越少,其表示的范围越大,但精度就会变差,反之,指数位越少,尾数位则越多,表示的范围越小,但精度就会变好

  2. 一个数字的浮点数格式,会因为定义的规则不同,得到的结果也不同,表示的范围和精度也有差异


早期人们提出浮点数定义时,就是这样的情况,当时有很多计算机厂商,例如 IBM、微软等,每个计算机厂商会定义自己的浮点数规则,不同厂商对同一个数表示出的浮点数是不一样的。


这就会导致,一个程序在不同厂商下的计算机中做浮点数运算时,需要先转换成这个厂商规定的浮点数格式,才能再计算,这也必然加重了计算的成本。


那怎么解决这个问题呢?业界迫切需要一个统一的浮点数标准。


浮点数标准


直到 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


为了使其表示的数字范围、精度最大化,浮点数标准还对指数和尾数进行了规定:


  1. 尾数 M 的第一位总是 1(因为 1 <= M < 2),因此这个 1 可以省略不写,它是个隐藏位,这样单精度 23 位尾数可以表示了 24 位有效数字,双精度 52 位尾数可以表示 53 位有效数字

  2. 指数 E 是个无符号整数,表示 float 时,一共占 8 bit,所以它的取值范围为 0 ~ 255。但因为指数可以是负的,所以规定在存入 E 时在它原本的值加上一个中间数 127,这样 E 的取值范围为 -127 ~ 128。表示 double 时,一共占 11 bit,存入 E 时加上中间数 1023,这样取值范围为 -1023 ~ 1024。


除了规定尾数和指数位,还做了以下规定:


  • 指数 E 非全 0 且非全 1:规格化数字,按上面的规则正常计算

  • 指数 E 全 0,尾数非 0:非规格化数,尾数隐藏位不再是 1,而是 0(M = 0.xxxxx),这样可以表示 0 和很小的数

  • 指数 E 全 1,尾数全 0:正无穷大/负无穷大(正负取决于 S 符号位)

  • 指数 E 全 1,尾数非 0:NaN(Not a Number)


标准浮点数的表示


有了这个统一的浮点数标准,我们再把 25.125 转换为标准的 float 浮点数:


  1. 整数部分:25(D) = 11001(B)

  2. 小数部分:0.125(D) = 0.001(B)

  3. 用二进制科学计数法表示:25.125(D) = 11001.001(B) = 1.1001001 * 2^4(B)


所以 S = 0,尾数 M = 1.001001 = 001001(去掉 1,隐藏位),指数 E = 4 + 127(中间数) = 135(D) = 10000111(B)。填充到 32 bit 中,如下:


这就是标准 32 位浮点数的结果。


如果用 double 表示,和这个规则类似,指数位 E 用 11 bit 填充,尾数位 M 用 52 bit 填充即可。


浮点数为什么有精度损失?


我们再来看一下,平时经常听到的浮点数会有精度损失的情况是怎么回事?


如果我们现在想用浮点数表示 0.2,它的结果会是多少呢?


0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止,在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。


0.2 * 2 = 0.4 -> 00.4 * 2 = 0.8 -> 00.8 * 2 = 1.6 -> 10.6 * 2 = 1.2 -> 10.2 * 2 = 0.4 -> 0(发生循环)...
复制代码


所以 0.2(D) = 0.00110...(B)。


因为十进制的 0.2 无法精确转换成二进制小数,而计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。


浮点数的范围和精度有多大?


最后,我们再来看一下,用浮点数表示一个数字,其范围和精度能有多大?


以单精度浮点数 float 为例,它能表示的最大二进制数为 +1.1.11111...1 * 2^127(小数点后 23 个 1),而二进制 1.11111...1 ≈ 2,所以 float 能表示的最大数为 2^128 = 3.4 * 10^38,即 float 的表示范围为:-3.4 * 10^38 ~ 3.4 * 10 ^38。


它能表示的精度有多小呢?


float 能表示的最小二进制数为 0.0000....1(小数点后 22 个 0,1 个 1),用十进制数表示就是 1/2^23。


用同样的方法可以算出,double 能表示的最大二进制数为 +1.111...111(小数点后 52 个 1)* 2^1023 ≈ 2^1024 = 1.79 * 10^308,所以 double 能表示范围为:-1.79 * 10^308 ~ +1.79 * 10^308。


double 的最小精度为:0.0000...1(51 个 0,1 个 1),用十进制表示就是 1/2^52。


从这里可以看出,虽然浮点数的范围和精度也有限,但其范围和精度都已非常之大,所以在计算机中,对于小数的表示我们通常会使用浮点数来存储。


总结


这篇文章我们主要讲了数字的浮点数表示方式,总结如下:


  1. 浮点数一般用科学计数法表示

  2. 把科学计数法中的变量,填充到固定 bit 中,即是浮点数的结果

  3. 在浮点数提出的早期,各个计算机厂商各自制定自己的浮点数规则,导致不同厂商对于同一个数字的浮点数表示各不相同,在计算时还需要先进行转换才能进行计算

  4. 后来 IEEE 组织提出了浮点数的标准,统一了浮点数的格式,并规定了单精度浮点数 float 和双精度浮点数 double,从此以后各个计算机厂商统一了浮点数的格式,一直延续至今

  5. 浮点数在表示小数时,由于十进制小数在转换为二进制时,存在无法精确转换的情况,而在固定 bit 的计算机中存储时会被截断,所以浮点数表示小数可能存在精度损失

  6. 浮点数在表示一个数字时,其范围和精度非常大,所以我们平时使用的小数,在计算机中通常用浮点数来存储


关注「水滴与银弹」公众号,第一时间获取优质技术干货。7 年资深后端研发,用简单的方式把技术讲清楚。

发布于: 2020 年 12 月 29 日阅读数: 54
用户头像

Kaito

关注

公众号「水滴与银弹」,不一样的技术视角。 2017.11.17 加入

坐标北京,7年资深后端研发,从事基础架构和数据库中间件研发。擅长把晦涩难懂的技术,用简单的方式讲清楚。

评论

发布
暂无评论
什么是浮点数?