IEEE二进制浮点数算术标准(IEEE 754)。IEEE 754 定义了表示浮点数的格式、反常值、特殊数值、浮点数运算,以及四种数值舍入规则和五种例外状况。

浮点数的二进制表示方式

IEEE 754规定了四种表示浮点数值的方式:单精度(32bit)、双精度(64bit)、延伸单精度(>43bit)和延伸双精度(>79bit)。以下以单精度为例说明。

存储格式

IEEE 754 规定的二进制浮点数分为三部分,由高位bit到位bit依次为:符号部分、指数部分、有效数(小数)部分。单精度表示如下,其中符号部分1bit、指数部分8bit、小数部分23bit。

1
2
3
 X   XXXXXXXX   XXXXXXXXXXXXXXXXXXXXXXX
 S   EEEEEEEE   FFFFFFFFFFFFFFFFFFFFFFF
31  30-----23  22---------------------0

符号

最高位为符号位,符号位的值为1表示负数,值为0表示正数。

指数部分

真实的指数值加上一个固定的偏移值,得到指数部分的编码值。将用原码表示的编码值保存在浮点数的指数部分。

对于单精度浮点数,其指数部分位宽为8bit,所以其编码值能够表示的数值范围是0到255;而其偏移值是\(2^{8-1}-1 = 127\);因此真实的指数值能够表示的数值范围是-127到128。事实上-127和128指数会做特殊处理,单精度指数部分的实际取值范围是-126到127,见下面关于数值解析的说明。

有效部分

浮点数的有效部分,也叫小数部分,表示的是一段小数,小数点紧邻在最高位之前,根据数值解析情况的不同,小数点前面的整数为0或者1,这个整数不会被储存。

以单精度为例,其小数部分有23bit,那么根据情况不同,读出的数值可能为\((0+编码值*2^{-23})\)或者\((0+编码值*2^{-23})\)。

数值解析

规约形式的解析

当指数部分的编码值在\([1, 2^8-2]\)范围,即真实指数值在\([-126, 127]\)的范围内时,分数部分小数点前的整数字为1,分数部分表示的值在\([1,2)\)的范围内。例如:

1
0   00000001   11000000000000000000000

表示的数值为\(+1.75*2^{-126}\)。

非规约形式的解析

当指数部分的编码值为0时,偏移值为126,真实的指数值为-126,分数部分小数点前的整数字为0,分数部分表示的值在\((0,1)\)的范围内。例如:

1
0   00000001   11000000000000000000000

表示的数值为\(+0.75*2^{-126}\)。

这里出现了一个问题,为什么不像规约形式那样,定义上面那个数字为\(+1.75*2^{-127}\)呢?为什么要修改指数和小数部分的解析方式呢?

如果使用统一的规约形式解析,这种表示称为突然式下溢出。按照这种解析方式,绝对值最小的浮点数为\(+(1+1*2^{-23})*2^{-127}\),而小浮点数之间的距离为\(+1*2^{-23}*2^{-127}\),当计算两个小浮点数的差时将会得到结果0。如下:

1
2
3
4
// 突然式下溢出
0   00000000   00000000000000000000000  // zero
0   00000000   00000000000000000000001  // smallest
0   00000000   00000000000000000000010  // second smallest

而使用当前规定的,在指数部分编码值为0时,进行非规约解析的这种表示,称为渐进式下溢出。绝对值最小的浮点数为\(+(0+1*2^{-23})*2^{-126}\),小浮点数之间的距离为\(+1*2^{-23}*2^{-127}\)。如下:

1
2
3
4
// 渐进式下溢出
0   00000000   00000000000000000000000  // zero
0   00000000   00000000000000000000001  // smallest
0   00000000   00000000000000000000010  // second smallest

特殊值

  • 如果指数编码值为0,而且小数部分为0,表示正负0。
  • 如果指数编码值为255,而且小数部分为0,表示\(\pm Inf\)。
  • 如果指数编码值为255,而且小数部分不为0,表示非数(NaN)。

各种精度的位宽定义

精度 符号位 指数位数 小数位数
单精度 1 8 23
双精度 1 11 52

浮点数的数值比较

指数部分如此设计,优势在于这使得对浮点数的比较更容易,可以按照字典次序比较两个浮点表示的大小,先比较符号,之后是指数的高位、指数的低位、小数的高位、小数的低位。

浮点数的舍入

IEEE标准列出4种不同的舍入方法:

  • 舍入到最接近:会将结果舍入为最接近且可以表示的值,但是当存在两个数一样接近的时候,则取其中的偶数(在二进制中是以0结尾的)。
  • 朝正无穷方向舍入:会将结果朝正无限大的方向舍入。
  • 朝负无穷方向舍入:会将结果朝负无限大的方向舍入。
  • 朝0方向舍入:会将结果朝0的方向舍入。

浮点数的运算

标准运算

  • 加减乘除。加减运算中\(+0.0 = -0.0\)
  • 平方根。\(\sqrt{x} \ge 0 (x \ge 0)\),\(\sqrt{-0.0} = -0.0\)
  • 近似到最近的整数\(round(x)\)。恰好在中间则近似到偶数。
  • 浮点余数。返回\(x-(round(x/y)*y)\)
  • 比较运算。\(-Inf < 负规约 < 负非规约 < -0.0 < +0.0 < 正非规约 < 正规约 < +Inf\)
  • 特殊比较。\(-Inf=-Inf\),\(+Inf=+Inf\),NaN与任何浮点数(包括NaN)比较都为假。

精度

单精和双精浮点数的有效数字分别是有存储的23和52个位,加上最左手边没有存储的第1个位,即是24和53个位。 $$ \begin{aligned} &\log 2^{24} = 7.22 \\ &\log 2^{53} = 15.95 \end{aligned} $$ 由以上的计算,单精和双精浮点数可以保证7位和15位十进制有效数字。