浮点数1.什么是浮点数在计算机系统的发展过程中,曾经提出过多种方法表达实数。典型的比如相对于浮点数的定点数(FixedPointNumber)。在这种表达方式中,小数点固定的位于实数所有数字中间的某个位置。货币的表达就可以使用这种方式,比如99.00或者00.99可以用于表达具有四位精度(Precision),小数点后有两位的货币值。由于小数点位置固定,所以可以直接用四位数值来表达相应的数值。SQL中的NUMBER数据类型就是利用定点数来定义的。还有一种提议的表达方式为有理数表达方式,即用两个整数的比值来表达实数。定点数表达法的缺点在于其形式过于僵硬,固定的小数点位置决定了固定位数的整数部分和小数部分,不利于同时表达特别大的数或者特别小的数。最终,绝大多数现代的计算机系统采纳了所谓的浮点数表达方式。这种表达方式利用科学计数法来表达实数,即用一个尾数(Mantissa,尾数有时也称为有效数字——Significand;尾数实际上是有效数字的非正式说法),一个基数(Base),一个指数(Exponent)以及一个表示正负的符号来表达实数。比如123.45用十进制科学计数法可以表达为1.2345×102,其中1.2345为尾数,10为基数,2为指数。浮点数利用指数达到了浮动小数点的效果,从而可以灵活地表达更大范围的实数。2.IEEE浮点数计算机中是用有限的连续字节保存浮点数的。在IEEE标准中,浮点数是将特定长度的连续字节的所有二进制位分割为特定宽度的符号域,指数域和尾数域三个域,其中保存的值分别用于表示给定二进制浮点数中的符号,指数和尾数。这样,通过尾数和可以调节的指数(所以称为浮点)就可以表达给定的数值了。IEEE754指定:n两种基本的浮点格式:单精度和双精度。ØIEEE单精度格式具有24位有效数字精度,并总共占用32位。ØIEEE双精度格式具有53位有效数字精度,并总共占用64位。n两种扩展浮点格式:单精度扩展和双精度扩展。此标准并未规定这些格式的精确精度和和大小,但它指定了最小精度和大小。例如,IEEE双精度扩展格式必须至少具有64位有效数字精度,并总共占用至少79位。具体的格式参见下面的图例:3.浮点格式浮点格式是一种数据结构,用于指定包含浮点数的字段、这些字段的布局及其算术解释。浮点存储格式指定如何将浮点格式存储在内存中。IEEE标准定义了这些格式,但具体选择哪种存储格式由实现工具决定。汇编语言软件有时取决于所使用的存储格式,但更高级别的语言通常仅处理浮点数据类型的语言概念。这些类型在不同的高级语言中具有不同的名称,并且与表中所示的IEEE格式相对应。IEEE精度C、C++Fortran(仅限SPARC)单精度floatREAL或REAL*4双精度doubleDOUBLEPRECISION或REAL*8双精度扩展longdoubleREAL*16IEEE754明确规定了单精度浮点格式和双精度浮点格式,并为这两种基本格式分别定义了一组扩展格式。表中显示的longdouble和REAL*16类型适用于IEEE标准定义的一种双精度扩展格式。3.1.单精度格式IEEE单精度格式由三个字段组成:23位小数f;8位偏置指数e;以及1位符号s。这些字段连续存储在一个32位字中(如下图所示)。Ø0:22位包含23位小数f,其中第0位是小数的最低有效位,第22位是最高有效位。IEEE标准要求浮点数必须是规范的。这意味着尾数的小数点左侧必须为1,因此我们在保存尾数的时候,可以省略小数点前面这个1,从而腾出一个二进制位来保存更多的尾数。这样我们实际上用23位长的尾数域表达了24位的尾数。Ø23:30位包含8位偏置指数e,第23位是偏置指数的最低有效位,第30位是最高有效位。8位的指数为可以表达0到255之间的256个指数值。但是,指数可以为正数,也可以为负数。为了处理负指数的情况,实际的指数值按要求需要加上一个偏差(Bias)值作为保存在指数域中的值,单精度数的偏差值为127;偏差的引入使得对于单精度数,实际可以表达的指数值的范围就变成-127到128之间(包含两端)。在本文中,最小指数和最大指数分别用emin和emax来表达。稍后将介绍实际的指数值-127(保存为全0)以及+128(保存为全1)保留用作特殊值的处理。Ø最高的第31位包含符号位s。s为0表示数值为正数,而s为1则表示负数。3.2.双精度格式IEEE双精度格式由三个字段组成:52位小数f;11位偏置指数e;以及1位符号s。这些字段连续存储在两个32位字中(如下图所示)。在SPARC体系结构中,较高地址的32位字包含小数的32位最低有效位,而在x86体系结构中,则较低地址的32-位字包含小数的32位最低有效位。如果用f[31:0]表示小数的32位最低有效位,则在这32位最低有效位中,第0位是整个小数的最低有效位,而第31位则是最高有效位。在另一个32位字中,0:19位包含20位小数的最高有效位f[51:32],其中第0位是这20位最高有效位中的最低有效位,而第19位是整个小数的最高有效位;20:30位包含11位偏置指数e,其中第20位是偏置指数的最低有效位,而第30位是最高有效位;最高的第31位包含符号位s。上图将这两个连续的32位字按一个64位字那样进行了编号,其中Ø0:51位包含52位小数f,其中第0位是小数的最低有效位,第51位是最高有效位。IEEE标准要求浮点数必须是规范的。这意味着尾数的小数点左侧必须为1,因此我们在保存尾数的时候,可以省略小数点前面这个1,从而腾出一个二进制位来保存更多的尾数。这样我们实际上用52位长的尾数域表达了53位的尾数。Ø52:62位包含11位偏置指数e,第52位是偏置指数的最低有效位,第62位是最高有效位。11位的指数为可以表达0到2047之间的2048个指数值。但是,指数可以为正数,也可以为负数。为了处理负指数的情况,实际的指数值按要求需要加上一个偏差(Bias)值作为保存在指数域中的值,单精度数的偏差值为1023;偏差的引入使得对于单精度数,实际可以表达的指数值的范围就变成-1023到1024之间(包含两端)。在本文中,最小指数和最大指数分别用emin和emax来表达。稍后将介绍实际的指数值-1023(保存为全0)以及+1024(保存为全1)保留用作特殊值的处理。Ø最高的第63位包含符号位s。s为0表示数值为正数,而s为1则表示负数。3.3.双精度扩展格式(SPARC)SPARC浮点环境的四倍精度格式符合双精度扩展格式的IEEE定义。四倍精度格式占用32位字并包含以下三个字段:112位小数f、15位偏置指数e和1位符号s。这三个字段连续存储,如图2-3所示。地址最高的32位字包含小数的32位最低有效位,用f[31:0]表示。紧邻的两个32位字分别包含f[63:32]和f[95:64]。下面的0:15位包含小数的16位最高有效位f[111:96],其中第0位是这16位的最低有效位,而第15位是整个小数的最高有效位。16:30位包含15位偏置指数e,其中第16位是该偏置指数的最低有效位,而第30位是最高有效位;第31位包含符号位s。下图将这四个连续的32位字按一个128位字那样进行了编号,其中0:111位存储小数f;112:126位存储15位偏置指数e;而第127位存储符号位s。3.4.双精度扩展格式(x86)该浮点环境双精度扩展格式符合双精度扩展格式的IEEE定义。它包含四个字段:63位小数f、1位显式前导有效数位j、15位偏置指数e以及1位符号s。在x86体系结构系列中,这些字段连续存储在十个相连地址的8位字节中。由于UNIXSystemVApplicationBinaryInterfaceIntel386ProcessorSupplement(IntelABI)要求双精度扩展参数,从而占用堆栈中三个相连地址的32位字,其中地址最高字的16位最高有效位未用,如下图所示。地址最低的32位字包含小数的32位最低有效位f[31:0],其中第0位是整个小数的最低有效位,而第31位则是32位最低有效位的最高有效位。地址居中的32位字中,0:30位包含小数的31位最高有效位f[62:32](其中第0位是这31位最高有效位的最低有效位,而第30位是整个小数的最高有效位);地址居中32位字的第31位包含显式前导有效数位j。地址最高的32位字中,0:14位包含15位偏置指数e,其中第0位是该偏置指数的最低有效位,而第14位是最高有效位;第15位包含符号位s。虽然地址最高的32位字的最高16位未被x86体系结构系列使用,但如上所述,它们对于符合IntelABI规定是至关重要的。4.将实数转换成浮点数4.1浮点数的规范化同样的数值可以有多种浮点数表达方式,比如上面例子中的123.45可以表达为12.345×101,0.12345×103或者1.2345×102。因为这种多样性,有必要对其加以规范化以达到统一表达的目标。规范的(Normalized)浮点数表达方式具有如下形式:±d.dd...d×βe,(0≤diβ)其中d.dd...d即尾数,β为基数,e为指数。尾数中数字的个数称为精度,在本文中用p来表示。每个数字d介于0和基数之间,包括0。小数点左侧的数字不为0。基于规范表达的浮点数对应的具体值可由下面的表达式计算而得:±(d0+d1β-1+...+dp-1β-(p-1))βe,(0≤diβ)对于十进制的浮点数,即基数β等于10的浮点数而言,上面的表达式非常容易理解,也很直白。计算机内部的数值表达是基于二进制的。从上面的表达式,我们可以知道,二进制数同样可以有小数点,也同样具有类似于十进制的表达方式。只是此时β等于2,而每个数字d只能在0和1之间取值。比如二进制数1001.101相当于1×23+0×22+0×21+1×20+1×2-1+0×2-2+1×2-3,对应于十进制的9.625。其规范浮点数表达为1.001101×23。4.2根据精度表示浮点数以上面的9.625为例,其规范浮点数表达为1.001101×23,因此按单精度格式表示为:11000001000110100000000000000000同理按双精度格式表示为:11000000001000110100000000000000000000000000000000000000000000005.特殊值通过前面的介绍,你应该已经了解的浮点数的基本知识,这些知识对于一个不接触浮点数应用的人应该足够了。不过,如果你兴趣正浓,或者面对着一个棘手的浮点数应用,可以通过本节了解到关于浮点数的一些值得注意的特殊之处。我们已经知道,单精度浮点数指数域实际可以表达的指数值的范围为-127到128之间(包含两端)。其中,值-127(保存为全0)以及+128(保存为全1)保留用作特殊值的处理。本节将详细IEEE标准中所定义的这些特殊值。浮点数中的特殊值主要用于特殊情况或者错误的处理。比如在程序对一个负数进行开平方时,一个特殊的返回值将用于标记这种错误,该值为NaN(NotaNumber)。没有这样的特殊值,对于此类错误只能粗暴地终止计算。除了NaN之外,IEEE标准还定义了±0,±∞以及非规范化数(DenormalizedNumber)。对于单精度浮点数,所有这些特殊值都由保留的特殊指数值-127和128来编码。如果我们分别用emin和emax来表达其它常规指数值范围的边界,即-126和127,则保留的特殊指数值可以分别表达为emin-1和emax+1;。基于这个表达方式,IEEE标准的特殊值如下所示:其中f表示尾数中的小数点右侧的(Fraction)部分。第一行即我们之前介绍的普通的规范化浮点数。随后我们将分别对余下的特殊值加以介绍。5.1NaNNaN用于处理计算中出现的错误情况,比如0.0除以0.0或者求负数的平方根。由上面的表中可以看出,对于单精度浮点数,NaN表示为指数为emax+1=128(指数域全为1),且尾数域不等于零的浮点数。IEEE标准没有要求具体的尾数域,所以NaN实际上不是一