IEEE ⼆进制浮点数算术标准(IEEE754)
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 是指双精确度)。
该标准的全称为IEEE⼆进制浮点数算术标准(ANSI/IEEE Std 754-1985),⼜称IEC 60559:1989,微处理器系统的⼆进制浮点数算术(本来的编号是IEC 559:1989)。后来还有“与基数⽆关的浮点数”的“IEEE 854-1987标准”,有规定基数为2跟10的状况。现在最新标准是“IEEE 854-2008标准”。
在六、七⼗年代,各家计算机公司的各个型号的计算机,有着千差万别的浮点数表⽰,却没有⼀个业界通⽤的标准。这给数据交换、计算机协同⼯作造成了极⼤不便。IEEE的浮点数专业⼩组于七⼗年代末期开始酝酿浮点数的标准。在1980年,英特尔公司就推出了单⽚的8087浮点数协处理器,其浮点数表⽰法及定义的运算具有⾜够的合理性、先进性,被IEEE采⽤作为浮点数的标准,于1985年发布。⽽在此前,这⼀标准的内容已在⼋⼗年代初期被各计算机公司⼴泛采⽤,成了事实上的业界⼯业标准。
浮点数剖析
以下是该标准对浮点数格式的描述。
本⽂表⽰⽐特的约定
把W个⽐特(bit)的数据,从内存地址低端到⾼端,以0到W −1编码。通常将内存地址低端的⽐特写在最右边,称作最低有效位(least significant bit或lsb),代表最⼩的⽐特,改变时对整体数值影响最⼩的⽐特。声明这⼀点的必要性在于X86体系架构是⼩端序的数据存储。
对于⼗进制整数N,必要时表⽰为N 10以与⼆进制的数的表⽰N 2相区分。
对于⼀个数,其⼆进制科学计数法表⽰下的指数的值,下⽂称之为指数的实际值;⽽根据IEEE 754标准对指数部分的编码的值,称之为浮点数表⽰法指数域的编码值。
整体呈现
⼆进制浮点数是以符号数值表⽰法的格式存储——最⾼有效位被指定为符号位(sign bit);“指数部份”,即次⾼有效的e个⽐特,存储指数部分;最后剩下的f个低有效位的⽐特,存储“尾数”(significand)的⼩数部份(在⾮规约形式下整数部份默认为0,其他情况下⼀律默认为1)。
指数偏移值
指数偏移值(exponent bias),是指浮点数表⽰法中的指数域的编码值为指数的实际值加上某个固定的值,IEEE 754标准规定该固定值为2e -1 - 1,其中的e 为存储指数的⽐特的长度。
以单精度浮点数为例,它的指数域是8个⽐特,固定偏移值是28-1 - 1 = 128−1 = 127.单精度浮点数的指数部分实际取值是从128到-127(⽹上很多博客中写的是-128~127,这是8位有符号数的取值范围,是严重误导!)。例如指数实际值为1710,在单精度浮点数中的指数域编码值为14410,即14410 = 1710 + 12710.
采⽤指数的实际值加上固定的偏移值的办法表⽰浮点数的指数,好处是可以⽤长度为e个⽐特的⽆符号整数来表⽰所有的指数取值,这使得两个浮点数的指数⼤⼩的⽐较更为容易。
规约形式的浮点数
如果浮点数中指数部分的编码值在0 < exponent < 2e -1之间,且尾数部分最⾼有效位(即整数字)是1,那么这个浮点数将被称为规约形式的浮点数。
⾮规约形式的浮点数
如果浮点数的指数部分的编码值是0,尾数为⾮零,那么这个浮点数将被称为⾮规约形式的浮点数。IEEE 754标准规定:⾮规约形式的浮点数的指数偏移值⽐规约形式的浮点数的指数偏移值⼤1.例如,最⼩的规约形式的单精度浮点数的指数部分编码值为1,指数的实际值为-126;⽽⾮规约的单精度浮点数的指数域编码值为0,对应的指数实际值也是-126⽽不是-127。实际上⾮规约形式的浮点数仍然是有效可以使⽤的,只是它们的绝对值已经⼩于所有的规约浮点数的绝对值;即所有的⾮规约浮点数⽐规约浮点数更接近0。规约浮点数的尾数⼤于等于1且⼩于2,⽽⾮规约浮点数的尾数⼩于1且⼤于0.
IEEE 754-1985标准采⽤⾮规约浮点数,源于70年代末IEEE浮点数标准化专业技术委员会酝酿浮点数⼆进制标准时,Intel公司对渐进式下溢出(gradual underflow)的⼒荐。当时⼗分流⾏的DEC VAX机的浮点数表⽰采⽤了突然式下溢出(abrupt underflow)。如果没有渐进式下溢出,那么0与绝对值最⼩的浮点数之间的距离(gap)将⼤于相邻的⼩浮点数之间的距离。例如单精度浮点数的绝对值最⼩的规约浮点数是,它与绝对值次⼩的规约浮点数之间的距离为。如果不采⽤渐进式下溢出,那么绝对值最⼩的规约浮点数与0的距离是相邻的⼩浮点数之间距离的倍!可以说是⾮常突然的下溢出到0。这种
情况的⼀种糟糕后果是:两个不等的⼩浮点数X与Y相减,结果将是0.训练有素的数值分析⼈员可能会适应这种限制情况,但对于普通的程序员就很容易陷⼊错误了。采⽤了渐进式下溢出后将不会出现这种情况。例如对于单精度浮点数,指数部分实际最⼩值是(-126),对应的尾数部分从, ⼀直到, ,相邻两⼩浮点数之间的距离(gap)都是
;⽽与0最近的浮点数(即最⼩的⾮规约数)也是。
特殊值
这⾥有三个特殊值需要指出:
1. 如果指数是0并且尾数的⼩数部分是0,这个数±0(和符号位相关)
2. 如果指数 = 并且尾数的⼩数部分是0,这个数是±∞(同样和符号位相关)
3. 如果指数 = 并且尾数的⼩数部分⾮0,这个数表⽰为不是⼀个数(NaN)。
以上规则,总结如下:
32位单精度
单精度⼆进制⼩数,使⽤32个⽐特存储。
1823 位长
S Exp Fraction
3130⾄23
偏正值(实际的指数⼤⼩+127)
22⾄0 位编号(从右边开始为0)
S为符号位,Exp为指数字,Fraction为有效数字。 指数部分即使⽤所谓的偏正值形式表⽰,偏正值为实际的指数⼤⼩与⼀个固定值(32位的情况是127)的和。采⽤这种⽅式表⽰的⽬的是简化⽐较。因为,指数的值可能为正也可能为负,如果采⽤补码表⽰的话,全体符号位S 和Exp⾃⾝的符号位将导致不能简单的进⾏⼤⼩⽐较。正因为如此,指数部分通常采⽤⼀个⽆符号的正数值存储。单精度的指数部分是
−126~+127加上偏移值127,指数值的⼤⼩从1~254(0和255是特殊值)。浮点⼩数计算时,指数值减去偏正值将是实际的指数⼤⼩。(⽹上很多博客中写的是-128~127,这是8位有符号数的取值范围,是严重误导!⽽且,因为错误版本的指数最⼤值也是127,在计算最⼤值时和正确版本的相同,所以⼀直纠结两个版本的正误。)
单精度浮点数各种极值情况:
类别正负号实际指数有偏移指数指数域尾数域数值
零0-12700000 0000000 0000 0000 0000 0000 00000.0
负零1-12700000 0000000 0000 0000 0000 0000 0000−0.0
1001270111 1111000 0000 0000 0000 0000 0000 1.0
-1101270111 1111000 0000 0000 0000 0000 0000−1.0
最⼩的⾮规约数*-12600000 0000000 0000 0000 0000 0000 0001±2−23 × 2−126 = ±2−149 ≈ ±1.4×10-45中间⼤⼩的⾮规约数*-12600000 0000100 0000 0000 0000 0000 0000±2−1 × 2−126 = ±2−127 ≈ ±5.88×10-39
最⼤的⾮规约数*-12600000 0000111 1111 1111 1111 1111 1111±(1−2−23) × 2−126 ≈ ±1.18×10-38最⼩的规约数*-12610000 0001000 0000 0000 0000 0000 0000±2−126 ≈ ±1.18×10-38
最⼤的规约数*1272541111 1110111 1111 1111 1111 1111 1111±(2−2−23) × 2127 ≈ ±3.4×1038
正⽆穷01282551111 1111000 0000 0000 0000 0000 0000+∞
负⽆穷11282551111 1111000 0000 0000 0000 0000 0000−∞
NaN*1282551111 1111non zero NaN
* 符号位可以为0或1 .
64位双精度
双精度⼆进制⼩数,使⽤64个⽐特存储。
11152 位长
S Exp Fraction
6362⾄52
偏正值(实际的指数⼤⼩+1023)
51⾄0 位编号(从右边开始为0)
S为符号位,Exp为指数字,Fraction为有效数字。指数部分即使⽤所谓的偏正值形式表⽰,偏正值为实际的指数⼤⼩与⼀个固定值(64位的情况是1023)的和。采⽤这种⽅式表⽰的⽬的是简化⽐较。因为,指数的值可能为正也可能为负,如果采⽤补码表⽰的话,全体符号位S和Exp⾃⾝的符号位将导致不能简单的进⾏⼤⼩⽐较。正因为如此,指数部分通常采⽤⼀个⽆符号的正数值存储。双精度的指数部分是
−1022~+1023加上1023,指数值的⼤⼩从1~2046(0(2进位全为0)和2047(2进位全为1)是特殊值)。浮点⼩数计算时,指数值减去偏正值将是实际的指数⼤⼩。
浮点数的⽐较
浮点数基本上可以按照符号位、指数域、尾数域的顺序作字典⽐较。显然,所有正数⼤于负数;正负号相同时,指数的⼆进制表⽰法更⼤的其浮点数值更⼤。
浮点数的舍⼊
任何有效数上的运算结果,通常都存放在较长的暂存器中,当结果被放回浮点格式时,必须将多出来的⽐特丢弃。 有多种⽅法可以⽤来运⾏舍⼊作业,实际上IEEE标准列出4种不同的⽅法:
舍⼊到最接近:舍⼊到最接近,在⼀样接近的情况下偶数优先(Ties To Even,这是默认的舍⼊⽅式):会将结果舍⼊为最接近且可以表⽰的值,但是当存在两个数⼀样接近的时候,则取其中的偶数(在⼆进制中式以0结尾的)。
朝+∞⽅向舍⼊:会将结果朝正⽆限⼤的⽅向舍⼊。
朝-∞⽅向舍⼊:会将结果朝负⽆限⼤的⽅向舍⼊。
朝0⽅向舍⼊:会将结果朝0的⽅向舍⼊。
浮点数的运算与函数
标准运算
下述函数必须提供:
加减乘除Add, subtract, multiply, divide.在加减运算中负零与零相等 -0.0 = 0.0
平⽅根Square root. sqrt(x) ≥ 0 (x≥0),另规定sqrt(-0.0) = -0.0
浮点余数。返回值x - (round(x / y) * y).
近似到最近的整数round(x).如果恰好在两个相邻整数之间,则近似到偶数。
⽐较运算. -Inf <;负的规约浮点数数<;负的⾮规约浮点数< -0.0 = 0.0 <;正的⾮规约浮点数<;正的规约浮点数< Inf;
特殊⽐较: -Inf = -Inf, Inf = Inf, NaN与任何浮点数(包括⾃⾝)的⽐较结果都为假,即 (NaN ≠ x) = false.
建议的函数与谓词
浮点数的基数什么意思copysign(x, y): copysign(x, y)返回的值由x的不带符号的部份和y的符号组成。因此abs(x)等于copysign(x, 1.0)。copysign可以对NaN正确操作,这是少有的⼏个可以对NaN像普通算术⼀样操作有效的函数之⼀。C99新增了copysign函数。
−x:从涵义上指将x的符号反转。当x是±0或者NaN时,其涵义可能不同于0-x.
scalb(y, N):计算y×2N(N是整数),⽆需再计算2N。C99中对应的函数名是scalbn.
logb(x):计算x = 1.a×2n(x ≠ 0, a ∈[0, 1))中的n. C99新增了logb和ilogb函数。
nextafter(x,y):沿y⽅向最邻近x的可表达浮点数。⽐如nextafter(0, 1)得到的是最⼩可表达的正数。C99新增了nextafter函数。
finite(x):判断x是否有限,即−Inf < x < Inf. C99新增了isfinite函数。
isnan(x):判断x是否是⼀个NaN,这等价于"x ≠ x". C99新增了isnan函数。
x <> y:仅当x < y或者x > y时才为True,其涵义是NOT(x = y)。注意这不同于"x ≠ x"。
unordered(x, y):当x与y⽆法⽐较⼤⼩时为True,⽐如说x或者y是⼀个NaN. C99中对应的函数名是isunordered.
class(x):区分x的浮点数类属:信号NaN、静默NaN、-Inf、负的规约浮点数,负的⾮规约浮点数,-0.0,0.0,正的⾮规约浮点数,正的规约浮点数,Inf。
精度
在⼆进制,第⼀个有效数字必定是“1”,因此这个“1”并不会存储。
单精和双精浮点数的有效数字分别是有存储的23和52个位,加上最左⼿边没有存储的第1个位,即是24和53个位。
由以上的计算,单精和双精浮点数可以保证7位和15位⼗进制有效数字。
以下的C++程序,概略地展⽰了单精和双精浮点数的精度。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论