单精度浮点数简介

IEEE 754: Single-precision floating-point format - 关于单精度浮点数编码的简介

参考文献
维基百科编者. IEEE 754[G/OL]. 维基百科, 2020(20200403)[2020-04-03]. -{R|https://zh.wikipedia.org/w/index.php?title=IEEE_754&oldid=58965505}-.
维基百科编者. 單精度浮點數[G/OL]. 维基百科, 2019(20190714)[2019-07-14]. -{R|https://zh.wikipedia.org/w/index.php?title=%E5%96%AE%E7%B2%BE%E5%BA%A6%E6%B5%AE%E9%BB%9E%E6%95%B8&oldid=55208237}-.

什么是IEEE 754?

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

IEEE 754规定了四种表示浮点数值的方式:单精确度(32位)、双精确度(64位)、延伸单精确度(43比特以上,很少使用)与延伸双精确度(79比特以上,通常以80位实现)。只有32位模式有强制要求,其他都是选择性的。大部分编程语言都提供了IEEE浮点数格式与算术,但有些将其列为非必需的。例如,IEEE 754问世之前就有的C语言,现在包括了IEEE算术,但不算作强制要求(C语言的float通常是指IEEE单精确度,而double是指双精确度)。

浮点数是怎样构成的?

一个浮点数(Value)其实可以这样表示:$$Value=sign\times exponent\times fraction$$
也就是说,浮点数的实际值,等于符号位(sign bit)乘以指数偏移值(exponent bias)再乘以分数值(fraction)。

本文表示比特的约定:
W个比特(bit)的数据,从内存地址低端到高端,以0W−1编码。通常将内存地址低端的比特写在最右边,称作最低有效位(Least Significant Bit, LSB),代表最小的比特,改变时对整体数值影响最小的比特。声明这一点的必要性在于X86体系架构是小端序的数据存储。
对于十进制整数$N$,必要时表示为$N_{10}$以与二进制的数的表示$N_2$相区分。
对于一个数,其二进制科学计数法表示下的指数的值,下文称之为指数的实际值;而根据IEEE 754标准对指数部分的编码的值,称之为浮点数表示法指数域的编码值。

490px-General_floating_point_frac.svg

二进制浮点数以符号数值表示法的格式存储——最高有效位被指定为符号位(sign bit);“指数部分”,即次高有效的e个比特,存储指数部分;最后剩下的f个低有效位的比特,存储“有效数”(significand)的小数部分(在非规约形式下整数部分默认为0,其他情况下一律默认为1)。

指数偏移值

指数偏移值(exponent bias),即浮点数表示法中指数域的编码值,等于指数的实际值加上某个固定的值,IEEE 754标准规定该固定值为$2^{e-1}-1$,其中的$e$为存储指数的比特的长度。

以单精度浮点数为例,它的指数域是8个比特,固定偏移值是$2^{8-1}-1=128-1=127$。此为有号数的表示方式,单精度浮点数的指数部分实际取值是从-126到127(-127和128被用作特殊值处理,见下方“非规约形式的浮点数”和“特殊值”)。例如指数实际值为$17_{10}$,在单精度浮点数中的指数域编码值为$144_{10}$,即$144_{10}=17_{10}+127_{10}$。

采用指数的实际值加上固定的偏移值的办法表示浮点数的指数,好处是可以用长度为$e$个比特的无符号整数来表示所有的指数取值,这使得两个浮点数的指数大小的比较更为容易,实际上可以按照字典序比较两个浮点表示的大小。

这种移码表示的指数部分,中文称作阶码。

规约形式的浮点数

如果浮点数中指数部分的编码值在$0< exponent\leq 2^{e}-2$之间,且在科学表示法的表示方式下,分数 (fraction) 部分最高有效位(即整数字)是$1$,那么这个浮点数将被称为规约形式的浮点数。“规约”是指用唯一确定的浮点形式去表示一个值。

由于这种表示下的尾数有一位隐含的二进制有效数字,为了与二进制科学计数法的尾数(mantissa)相区别,IEEE754称之为有效数(significant)

举例来说,双精度 (64-bit) 的规约形式浮点数在指数偏移值的值域为$00000000001$(11-bit)到$11111111110$,在分数部分则是$000…..000$到$111…..111$(52-bit)。

非规约形式的浮点数

如果浮点数的指数部分的编码值是0,分数部分非零,那么这个浮点数将被称为非规约形式的浮点数。一般是某个数字相当接近零时才会使用非规约型式来表示。 IEEE 754标准规定:非规约形式的浮点数的指数偏移值比规约形式的浮点数的指数偏移值小1。例如,最小的规约形式的单精度浮点数的指数部分编码值为1,指数的实际值为-126;而非规约的单精度浮点数的指数域编码值为0,对应的指数实际值也是-126而不是-127。实际上非规约形式的浮点数仍然是有效可以使用的,只是它们的绝对值已经小于所有的规约浮点数的绝对值;即所有的非规约浮点数比规约浮点数更接近0。规约浮点数的尾数大于等于1且小于2,而非规约浮点数的尾数小于1且大于0。

除了规约浮点数,IEEE754-1985标准采用非规约浮点数,用来解决填补绝对值意义下最小规格数与零的距离。(举例说,正数下,最大的非规格数等于最小的规格数。而一个浮点数编码中,如果exponent=0,且尾数部分不为零,那么就按照非规约浮点数来解析)非规约浮点数源于70年代末IEEE浮点数标准化专业技术委员会酝酿浮点数二进制标准时,Intel公司对渐进式下溢出(gradual underflow)的力荐。当时十分流行的DEC VAX机的浮点数表示采用了突然式下溢出(abrupt underflow)。如果没有渐进式下溢出,那么0与绝对值最小的浮点数之间的距离(gap)将大于相邻的小浮点数之间的距离。例如单精度浮点数的绝对值最小的规约浮点数是$1.0\times 2^{-126}$,它与绝对值次小的规约浮点数之间的距离为$2^{-126}\times 2^{-23}=2^{-149}$。如果不采用渐进式下溢出,那么绝对值最小的规约浮点数与0的距离是相邻的小浮点数之间距离的$2^{23}$倍,可以说是非常突然的下溢出到0。这种情况的一种糟糕后果是:两个不等的小浮点数X与Y相减,结果将是。训练有素的数值分析人员可能会适应这种限制情况,但对于普通的程序员就很容易陷入错误了。采用了渐进式下溢出后将不会出现这种情况。例如对于单精度浮点数,指数部分实际最小值是(-126),对应的尾数部分从$1.1111\ldots 11$, $1.1111\ldots 10$一直到$0.0000\ldots 10$, $0.0000\ldots 01$,$0.0000\ldots 00$相邻两小浮点数之间的距离(gap)都是$2^{-126}\times 2^{-23}=2^{-149}$;而与0最近的浮点数(即最小的非规约数)也是$2^{-126}\times 2^{-23}=2^{-149}$。

特殊值

这里有三个特殊值需要指出:

  1. 如果指数是0并且尾数的小数部分是0,这个数±0(和符号位相关)
  2. 如果指数 = $2^{e}-1$并且尾数的小数部分是0,这个数是±∞(同样和符号位相关)
  3. 如果指数 = $2^{e}-1$并且尾数的小数部分非0,这个数表示为非数(NaN)

以上规则,总结如下:

形式 指数 小数部分
0 0
非规约形式 0 大于0小于1
规约形式 $1$到$2^{e}-2$ 大于等于1小于2
无穷 $2^{e}-1$ 0
NaN $2^{e}-1$ 非0

单精度浮点数是怎样构成的?

单精度浮点数格式是一种数据类型,在计算机存储器中占用4个位元(32 bits),利用“浮点”(浮动小数点)的方法,可以表示一个范围很大的数值。

在IEEE 754-2008的定义中,32-bit base 2格式被正式称为binary32格式。这种格式在IEEE 754-1985被定义为single,即单精度。需要注意的是,在更早的一些计算机系统中,也存在着其他4字节的浮点数格式。

第1位表示正负,中间8位表示指数,后23位储存有效数位(有效数位是24位)。

第一位的正负号0代表正,1代表负。

中间八位共可表示$2^8=256$个数,指数可以是二补码;或0到255,0到126代表-127到-1,127代表零,128-255代表1-128。

有效数位最左手边的1并不会储存,因为它一定存在(二进制的第一个有效数字必定是1)。换言之,有效数位是24位,实际储存23位。

1180px-Float_example.svg

$sign=+1$

$exponent=(-127)+124=-3$

$fraction=1+2^{-2}=1.25$

$value=(+1)\times 2^{-3}\times 1.25=+0.15625$