IEEE754浮点数标准和舍入规则

整理一下之前的笔记

浮点数与定点数

定点数

小数点在计算机中通常有两种表示方法,一种是约定所有数值数据的小数点隐含在某一个固定位置上,称为定点表示法,简称定点数。

其实整数也算是一种特殊的定点数,只是小数点默认处于末尾。

如何表示一个有符号定点数?例如,我们定义一个八位定点数表示方法,第一位为符号位,小数点位于第三位后面,则对于小数1.375,定点数原码表示为00101100,即小数点前后分别由十进制转二进制:+ 01.011,补码表示则进行对应的处理。

定点数的缺陷有:
+ 由于固定的小数点位置决定了整数部分位数和小数部分的位数,导致其能表示的数的范围偏小。
+ 小数点位置难统一,现代计算机中出现的定点数已经全部规定为纯小数了,即默认整数部分是0,小数点位置位于符号位之后。

浮点数

浮点数与定点数对应,它不固定小数点位置,这是怎么做到的呢?

对应的原理其实就是,一个小数,通过小数点移位,总能将小数点移到一个统一的位置上。例如对于十进制小数0.035 = 3.5 * 10^-2,对于0.000059 = 5.9 * 10^-5

那么二进制小数也是这样,例如,0.0101 = 1.01 * 2^-2。浮点数通过统一将小数表示成这种方式,做到不限制小数点的位置。

具体来说,浮点数把一个二进制数表示成如下图:

IEEE754浮点数标准和舍入规则

那么根据S、M、E就能唯一确定一个小数。要将S、M、E表示到一起,就出现了IEEE754标准。

IEEE二进制浮点数算术标准(IEEE 754)是20世纪80年代以来最广泛使用的浮点数运算标准,为许多CPU与浮点运算器所采用。这个标准定义了表示浮点数的格式(包括负零-0)与反常值(denormal number)),一些特殊数值(无穷(Inf)与非数值(NaN)),以及这些数值的“浮点数运算符”;它也指明了四种数值舍入规则和五种例外状况(包括例外发生的时机与处理方式)。

IEEE754将一个二进制串表示为上图末尾的那一条,其中S就是符号位,exp对应的是阶码E,而frac对应的是尾数M。

exp和frac在单精度浮点数和双精度浮点数中的长度如下所示:

IEEE754浮点数标准和舍入规则

规格化浮点数

那么如何确定exp和frac?

首先要了解的是,虽然阶码E是有符号的,例如 1.01 * 2^-2中,尾数为1.01,阶码为-2,但IEEE754中会把exp的范围转移到非负数区域,即它是一个无符号表示。(不能全为0或者全为1,因为被非规格化浮点数和特殊值占用了)

而尾数frac,则是M的小数部分,例如1.01 * 2^-2中,M为1.01,frac为01

IEEE754浮点数标准和舍入规则

举例如何将十进制数转浮点数:

IEEE754浮点数标准和舍入规则

非规格化浮点数

虽然规格化浮点数能表示的范围已经很牛X了,但对于很小的数还是难以表示,比如,单精度数的阶码上限不是127吗,假设我有一个小数0.00...(省略127个0)...111,在小数点后面连续0的个数达到了阶码上限,则通过小数点移位后,阶码会超过127,此时规格化浮点数就无法表示了。

有没有方法解决这个问题呢?

IEEE定义了非规格化浮点数,此时就不要求尾数部分以1.*开头了,而是用0.*表示。然后阶码域必须最小,即exp全为0,因为尾数域的规则改了,要平滑过渡到规格化浮点数,此时E=-Bias+1,和规格化浮点数的计算有不同。

IEEE754浮点数标准和舍入规则

这种思想其实就是用精度换范围,因为frac的长度本身有限,非规格化浮点数的尾数部分前面用0填充,使得尾数部分有效位个数减少,精度也就对应减少。

IEEE754浮点数标准和舍入规则

非规格化浮点数可以表示0,并且区分+0和-0(即符号位不同)。

特殊值

IEEE754还定义了无穷、NaN,它们的exp全为1。

IEEE754浮点数标准和舍入规则

有意思的是,浮点数运算中,任何数除以0都会等于无穷。

舍入(Rounding)规则

因为部分小数在计算机中不能精确表示成浮点数,IEEE754定义了四种舍入规则。它们是:

  • 向偶数舍入,也称为向最接近的值舍入。它和四舍五入类似,但是四舍五入会遇到问题,比如2.5和2、3之间的差值都为0.5,那么它应该往2舍入还是往3舍入呢?四舍五入是往3舍入,而向偶数舍入确保舍入后的最低有效数字是偶数。 则应该往2舍入。 对于二进制而言,就代表舍入后最低有效数字必须为0。
    • 向偶数舍入是计算机的默认舍入方式。
  • 向0舍入,对应C/C++的类型转换。(int) 1.324 = 1(int) -1.324 = -1
  • 向下舍入,C/C++函数floor()。例如:floor(1.324) = 1floor(-1.324) = -2
  • 向上舍入:C/C++函数ceil()。例如:ceil(1.324) = 2Ceil(-1.324) = -1

原创文章,作者:彭晨涛,如若转载,请注明出处:https://www.codetool.top/article/ieee754%e6%b5%ae%e7%82%b9%e6%95%b0%e6%a0%87%e5%87%86%e5%92%8c%e8%88%8d%e5%85%a5%e8%a7%84%e5%88%99/