整理自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,必要时表示为N10以与二进制的数的表示N2相区分。

对于一个数,其二进制科学计数法表示下的指数的值,下文称之为指数的实际值;而根据IEEE 754标准对指数部分的编码的值,称之为浮点数表示法指数域的编码值

整体呈现

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)。

以上规则,总结如下:

形式 指数 小数部分
0 0
非规约形式 0 非0
规约形式 任意
无穷 0
NaN 非零

32位单精度

单精度二进制小数,使用32个比特存储。

1 8 23 位长
S Exp Fraction
31 30至23

偏正值(实际的指数大小+127)
22至0 位编号(从右边开始为0)

S为符号位,Exp为指数字,Fraction为有效数字。 指数部分即使用所谓的偏正值形式表示,偏正值为实际的指数大小与一个固定值(32位的情况是127)的和。采用这种方式表示的目的是简化比较。因为,指数的值可能为正也可能为负,如果采用补码表示的话,全体符号位S和Exp自身的符号位将导致不能简单的进行大小比较。正因为如此,指数部分通常采用一个无符号的正数值存储。单精度的指数部分是−126~+127加上偏移值127,指数值的大小从1~254(0和255是特殊值)。浮点小数计算时,指数值减去偏正值将是实际的指数大小。(网上很多博客中写的是-128~127,这是8位有符号数的取值范围,是严重误导!而且,因为错误版本的指数最大值也是127,在计算最大值时和正确版本的相同,所以一直纠结两个版本的正误。)

单精度浮点数各种极值情况:

类别 正负号 实际指数 有偏移指数 指数域 尾数域 数值
0 -127 0 0000 0000 000 0000 0000 0000 0000 0000 0.0
负零 1 -127 0 0000 0000 000 0000 0000 0000 0000 0000 −0.0
1 0 0 127 0111 1111 000 0000 0000 0000 0000 0000 1.0
-1 1 0 127 0111 1111 000 0000 0000 0000 0000 0000 −1.0
最小的非规约数 * -126 0 0000 0000 000 0000 0000 0000 0000 0001 ±2−23 × 2−126 = ±2−149 ≈ ±1.4×10-45
中间大小的非规约数 * -126 0 0000 0000 100 0000 0000 0000 0000 0000 ±2−1 × 2−126 = ±2−127 ≈ ±5.88×10-39
最大的非规约数 * -126 0 0000 0000 111 1111 1111 1111 1111 1111 ±(1−2−23) × 2−126 ≈ ±1.18×10-38
最小的规约数 * -126 1 0000 0001 000 0000 0000 0000 0000 0000 ±2−126 ≈ ±1.18×10-38
最大的规约数 * 127 254 1111 1110 111 1111 1111 1111 1111 1111 ±(2−2−23) × 2127 ≈ ±3.4×1038
正无穷 0 128 255 1111 1111 000 0000 0000 0000 0000 0000 +∞
负无穷 1 128 255 1111 1111 000 0000 0000 0000 0000 0000 −∞
NaN * 128 255 1111 1111 non zero NaN
* 符号位可以为0或1 .

64位双精度

双精度二进制小数,使用64个比特存储。

1 11 52 位长
S Exp Fraction
63 62至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新增了logbilogb函数。
  • 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++程序,概略地展示了单精和双精浮点数的精度。

#include <iostream>

int main () {
std::cout.precision(20);
float a=123.45678901234567890;
double b=123.45678901234567890;
std::cout << a << std::endl;
std::cout << b << std::endl;
return 0;
} // Xcode 5.1
// Output:
// 123.456787109375
// 123.45678901234568059
// Program ended with exit code: 0

IEEE二进制浮点数算术标准(IEEE 754)的更多相关文章

  1. IEEE二进制浮点数算术标准学习

    看到有网上有个项目是要求将浮点数用二进制表示出来,需要用IEEE754标准,查了查维基和深入理解计算机系统,重新学习了一遍浮点数在计算机中的表示和内存中的存储, 先简单的做个笔记,后面需要更深入的理解 ...

  2. IEEE 754二进制浮点数算术标准

    可能很多人都遇到过浮点数精度丢失的问题,下面以JavaScript为例. 1 - 0.9 = 0.09999999999999998 纳尼,不应该是0.1么,怎么变成0.099999999999999 ...

  3. IEEE754二进制浮点数算术标准

    对于32位浮点数 sign: 符号,1位 exponent: 指数,8位,偏码 fraction: 分数,23位,原码 特殊值   指数域的编码值 = 指数的实际值 + 127 这样按照字典序的顺序就 ...

  4. IEEE 二进制浮点数的表示

    朋友在谈一个物流相关的项目,是以前项目的一个延续,涉及到后台的扩展,手机端的App,外加两个App的对接的蓝牙打印机.这个项目前后说了一个多月了吧,最近才草拟了协议.项目本来不复杂,但是客户却如此的拖 ...

  5. 深入理解计算机系统(2.7)---二进制浮点数,IEEE标准(重要)

    2.6我们进行了二进制整数运算的最后一役,本次LZ将和各位一起进入浮点数的世界,这里没有无符号,没有补码,但是有各种各样的惊奇.倘若你真正的进入了浮点数的世界,一定会发现它原来是这么有意思,而不是像之 ...

  6. 震惊!计算机连0.3+0.6都算不对?浅谈IEEE754浮点数算数标准

    >>> 0.3+0.6 0.8999999999999999 >>> 1-0.9 0.09999999999999998 >>> 0.1+0.1+ ...

  7. IEEE 754浮点数表示标准

    二进制数的科学计数法 C++中使用的浮点数包括采用的是IEEE标准下的浮点数表示方法.我们知道在数学中可以将任何十进制的数写成以10为底的科学计数法的形式,如下 其中显而易见,因为如果a比10大或者比 ...

  8. JTAG 标准IEEE STD 1149.1-2013学习笔记(一·)Test logic architecture、Instruction register以及Test data registers

    我是 雪天鱼,一名FPGA爱好者,研究方向是FPGA架构探索和SOC设计. 关注公众号[集成电路设计教程],拉你进"IC设计交流群". 注:转载请注明出处 一.Test logic ...

  9. [学习笔记]Javascript采用二进制浮点数和四舍五入的错误

    1.样例 var a = .3 - .2 //0.09999999999999998 var b = .2 - .1 //0.1 a == b //false 出现这样的原因在于 1.Javascri ...

随机推荐

  1. .Net设计模式_建造者模式

    引言: 建造者的特点是过程,需要建造对象的过程是一样的,如:软件项目,过程都是,POC.投标.立项.软件过程.收款,那么标准的软件项目都是这个过程,只是不同的项目在做这个过程的内容不一样.所以需要有一 ...

  2. PowerDesigner使用详解

    PowerDesign高级应用编写相关的VBS脚本在PowerDesign里自定义一些命令与操作等,具体的可以参考C:\Program Files\Sybase\PowerDesigner 9\VB ...

  3. Linux动态查看网络流量iptraf

    iptraf工具可以动态查看网络流量 yum install iptraf 查看所有网卡流量 iptraf -g

  4. 前端编辑器 之 sublime-text3

    工善欲其事,必先利其器 作为一名前端工程师,一定要有熟练,便捷的开发工具,虽然自己一直使用神一样的编辑器,但是却没有使用的像神一样,于是再次深入了解下这款工具 下载sublime-text 去官网下载 ...

  5. eclipse修改SVN下载的项目“>”变成“*”

    遇到过这样的问题,可能是因为下载的eclipse 的版本国过于高的原因. 一张图说明就行了. 将红色的地方打上勾就行了.

  6. [ lucene高级 ] 研讨如何进行Lucene的分布式应用

    http://www.cnblogs.com/huangfox/archive/2010/10/15/1852206.html Lucene是个高度优化的倒转索引搜索引擎.它将倒转的索引存储在定制的文 ...

  7. .net的 async 和 await

    async 和 await 出现在C# 5.0之后,关系是两兄弟,Task是父辈,Thread是爷爷辈,这就是.net 多线程处理的东西,具体包括 创建线程,线程结果返回,线程中止,线程中的异常处理 ...

  8. 转载---SQL Server XML基础学习<1>之--FOR XML PATH

    --> 测试数据:#tbIF OBJECT_ID('TEMPDB.DBO.#tb') IS NOT NULL    DROP TABLE #tbGO CREATE TABLE #tb      ...

  9. [python] 字符串与列表、字典的转换

    1.字符串->字典:eval(str) 2.字符串->列表:list(str)

  10. NSMutableParagraphStyle /NSParagraphStyle

    //   NSParagraphStyleAttributeName 段落的风格(设置首行,行间距,对齐方式什么的)看自己需要什么属性,写什么 NSMutableParagraphStyle *par ...