-123.625表示格式为:16位 尾...

本文主要介绍了定点数和浮点数嘚概念定点数和浮点数的加减运算(比如34.6f-34.0f),最后介绍了浮点数的特殊值

所谓定点格式即约定机器中所有数据的小数点位置是固定不變的。通常将定点数据表示成纯小数或纯整数为了将数表示成纯小数,通常把小数点固定在数值部分的最高位之前;而为了将数表示成純整数则把小数点固定在数值部分的最后面,如下图所示:

图中所标示的小数点在机器中是不表示出来的而是事先约定在固定的位置。对于一台计算机一旦确定了小数点的位置,就不再改变

假设用n位来表示一个定点数x=x0x1x2...x(n-1),其中x0用来表示数的符号位通常放在最左位置,并用数值0和1分别表示正号和负号其余位数表示它的量值。如果定点数x表示纯整数则小数点位于最低位x(n-1)的右边,数值范围是0<=|x|<=2^(n-1)-1且,例洳1111表示-7;如果定点数x表示纯小数则小数点位于x0和x1之间,数值范围是0<=|x|<=1-2^(-(n-1))且,例如1111表示-0.875.

不论操作数是正还是负在做补码加减法时,只需将苻号位和数值部分一起参与运算并且将符号位产生的进位丢掉即可。如:

A+B的补码为:1 11 0010将符号位产生的进位丢掉,因此最终结果为:

 III.定點数加减运算的溢出判断

1)用一位符号位判断溢出

对于加法只有在正数加正数和负数加负数两种情况下才可能出现溢出,符号不同的两個数相加是不会溢出的

对于减法,只有在正数减负数和负数减正数两种情况下才可能出现溢出符号相同的两个数相减是不会溢出的。

甴于减法运算在机器中是用加法器实现的因此:不论是作加法还是减法,只要实际操作数(减法时即为被减数和“求补”之后的减数)嘚补码符号位相同而结果的符号位又与操作数补码符号位不同,即为溢出如:

在4位机中,A=5B=-4,则A-B溢出推导过程如下:

A的原码为0101,补碼为0101;-B的原码为0100补码为0100; A-B的补码为1001,结果的符号位为1实际操作数的符号位为0,因此溢出

2)用两位符号位判断溢出

此时判断溢出的原則是:当2位符号位不同时,表示溢出;否则无溢出不论是否发生溢出,高位符号位永远代表真正的符号如:

注:约定整数的符号位与數值位之间用逗号隔开,小数的符号位与数值位之间用小数点隔开

定点数表示法的缺点在于其形式过于僵硬,固定的小数点位置决定了凅定位数的整数部分和小数部分不利于同时表达特别大或特别小的数,最终绝大多数现代的计算机系统采纳了浮点数表达方式,这种表达方式利用科学计数法来表达实数即用一个数(Mantissa,数有时也称为有效数字它实际上是有效数字的非正式说法),一个基数(Base)一个指数(Exponent)以忣一个表示正负的符号来表达实数,比如123.45用十进制科学计数法可以表示为1.2345x102其中1.2345为数,10为基数2为指数。浮点数利用指数达到了浮动小数點的效果从而可以灵活地表达更大范围的实数。

在IEEE标准中浮点数是将特定长度的连续字节的所有二进制位分割为特定宽度的符号域、指数域和数域这三个域,域中的值分别用于表示给定二进制浮点数中的符号、指数和数这样,通过数和可以调节的指数就可以表达给定嘚数值了

两种基本的浮点格式:单精度和双精度。其中单精度格式具有24位有效数字(即数)精度总共占用32位;双精度格式具有53位有效数字(即数)精度,总共占有64位      

两种扩展浮点格式:单精度扩展和双精度扩展。此标准并未规定这些格式的精确精度和大小但指定了最小精度囷大小,例如IEEE双精度扩展格式必须至少具有64位有效数字精度并总共占用至少79位。

具体的格式参见下面的图例:

IEEE单精度格式由三个字段组荿:23位小数f、8位偏置指数e以及1位符号s这些字段连续存储在一个32位字中,如下图所示:

0:22位包含23位小数f其中第0位是小数的最低有效位,第22位是最高有效位IEEE标准要求浮点数必须是规范的(浮点数的规范化见后文),这意味着数的小数点左侧必须位1因此我们在保存数时,可以省畧小数点前面的1从而腾出一个二进制位来保存更多的数,这样我们实际上用23位长的数域表达了24位的数

23:30位包含8位偏置指数3,第23位是偏置指数的最低有效位第30位是最高有效位。8位的指数可以表达0到255之间的256个指数值但指数可以位正数,也可以为负数因此为了处理负指数嘚情况,实际的指数值按要求需要加上一个偏置(Bias)值作为保存在指数域中的值单精度的偏置值为127(2^7-1),比如单精度的实际指数值0在指数域中保存为127(0+127)实际指数值-63在指数域中保存为64(-63+127)。偏置的引入使得对于单精度数实际可以表达的指数值的范围变为-127到128之间(包含两端),其中指数值-127(保存为全0)以及+128(保存为全1)保留用作特殊值的处理稍后介绍。如果我们分别用emin和emax来表达其它常规指数值范围的边界即最小指数和最大指数分別用emin和emax来表示,即-126和127则保留的特殊指数值可以分别表达为emin-1和emax+1;

最高的第31位包含符号位s,s为0表示数值为正数s位1表示数值为负数。

值得注意嘚是对于单精度数,由于我们只有24位的数(小数点左侧的1被隐藏)所以可以表达的最大数为2^24-1=16,777,215,因此单精度的浮点数可以表达的十进制数值Φ真正有效的数字不高于8位。

IEEE双精度格式由三个字段组成:52位小数f、11位偏置指数e以及1位符号s这些字段连续存储在两个32位字中,如下图所示:

在SPARC体系结构中较高地址的32位字包含小数的32位最低有效位,而在x86体系结构中则是较低地址的32位字包含小数的32位最低有效位。

以x86体系结构为例则f[31:0]表示小数的32位最低有效位,其中第0位是整个小数的最低有效位在另一个32位字中,0:19位表示小数的20位最高有效位f[51:32]其中第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)值作为保存在指数域中的值,单精度数的偏差值为-1)偏差的引入使得对于单精度数,实际可以表达的指数值的范围就变成 -1023到1024之間(包含两端)最小指数和最大指数分别用emin 和 emax来表达,稍后将介绍实际的指数值 -1023(保存为全0)以及 +1024(保存为全 1)保留用作特殊值的处理

最高的苐 63 位包含符号位s,s为0表示数值为正数 s为1则表示负数。

SPARC浮点环境的双精度格式符合IEEE关于双精度扩展格式的定义

SPARC浮点环境的双精度格式包含三个字段:112位小数f、15位偏置指数e以及1位符号s,这三个字段连续存储如下图所示:

地址最高的32位字包含小数的32位最低有效位,用f[31:0]表示;緊邻的两个32位字分别包含f[63:32]和f[95:64];接下来的32位字中 0:15 位包含小数的16位最高有效位f[111:96],其中第15位是整个小数的最高有效位;16:30位包含15位偏置指数e其Φ第16位是该偏置指数的最低有效位,第30位是偏置指数的最高有效位;第

上图将这四个连续的32位字按一个128位字那样进行了编号其中0:111位存储尛数f;112:126位存储15位偏置指数e而第 127 位存储符号位 s。

x86浮点环境的双精度格式符合IEEE关于双精度扩展格式的定义

要求双精度扩展参数,从而占用堆棧中三个相连地址的32位字其中地址最高字的16位最高有效位未用,如下图所示:

地址最低的32位字包含小数的32位最低有效位 f[31:0]其中第0位是整個小数的最低有效位;地址居中的 32 位字中,0:30位包含小数的31位最高有效位 f[62:32]其中第30位是整个小数的最高有效位,第31位包含显式前导有效数位 j

地址最高的32位字中,0:14位包含15位偏置指数e其中第0位是该偏置指数的最低有效位,而第14位是最高有效位;第15位包含符号位s

同样的数值可鉯有多种浮点数表达方式,比如上面例子中的123.45可以表达为12.345x10^10.或者1.,因为这种多样性有必要对其加以规范化以达到统一表达的目标。规范嘚(Normalized)浮点数表达方式具有如下形式:

其中d.dd...d为数β为基数,e为指数。数中数字的个数称为精度用 p 来表示,每个数字d介于0和基数之间包括0,小数点左侧的数字不为0

基于规范表达的浮点数对应的具体值可由下面的表达式计算得到:

对于十进制的浮点数,即基数β等于10的浮点數而言上面的表达式非常容易理解,也很直白而计算机内部的数值表达是基于二进制的,从上面的表达式我们可以知道二进制数同樣可以有小数点,也同样具有类似于十进制的表达方式只是此时β等于2,而每个数字d只能在0和1之间取值比如二进制数

6) 实数和浮点数之間的转换

假定我们有一个32位的数据,它是一个单精度浮点数十六进制表示为0xC0B40000,为了得到该浮点数实际表达的实数我们首先将其转换为②进制形式:

接着按照浮点数的格式切分为相应的域:

符号位1表示这是一个负数,指数域为129意味着实际值为2,数域为01101意味着实际的二进淛数为1.01101所以实际的实数为:

从实数向浮点数变换稍微麻烦一点,假定我们需要将实数-9.625表达为单精度的浮点数格式方式是首先将它用二進制浮点数表示,然后变换为相应的浮点数格式

首先,整数部分即9的二进制形式为1001,小数部分的算法则是将小数部分连续乘以基数2並记录结果的整数部分:

当最后的小数部分为0时,结束该过程因此小数部分的二进制表达为0.101,这样我们就得到了完整的二进制形式用規范浮点数表示为:

因为是负数,因此符号位为1指数为3,因此指数域为3+127=130即二进制的,数域省掉小数点左侧的1右侧用零补齐,得到最終结果为:1 11_00_最后可以将浮点数表示为十六进制的数据如下:01 00 0000

这里需要注意一个问题,在上面我们有意选择的示例中不断地将产生的小數部分乘以2的过程掩盖了一个事实,该过程结束的标志是小数部分乘以2的结果为1但实际上,很多小数根本不能经过有限次这样的过程而嘚到结果(比如0.1)但浮点数数域的位数是有限的,为此浮点数的处理方法是持续该过程直到由此得到的数足以填满数域,之后对多余的位進行舍入换句话说,除了我们之前讲到的精度问题之外十进制到二进制的变换也并不能保证总是精确的,而只能是近似值事实上,呮有很少一部分十进制小数具有精确的二进制浮点数表达再加上浮点数运算过程中的误差累积,结果是很多我们看来非常简单的十进制運算在计算机上却往往出人意料这就是最常见的浮点运算的"不准确"问题,比如:34.6f-34.0f=0.599998产生这个误差的原因是34.6f无法精确的表达为相应的浮点數,而只能保存为经过舍入的近似值这个近似值与34.0f之间的运算自然无法产生精确的结果(具体过程后面会讲)。

根据标准要求无法精确保存的值必须向最接近可保存的值进行舍入,这有点像我们熟悉的十进制的四舍五入即不足一半则舍,一半以上(包括一半)则进不过对于②进制浮点数而言,则是0就舍但1不一定进,而是在前后两个等距接近的可保存的值中取其中最后一位有效数字为零的值进行保存,即采取向偶数舍入比如0.5要舍到0,1.5要入到2(即先试着进1会得到最后结果,如果这个结果的数的最后位为0则进位成功;否则进位失败,直接舍去)看下面几个例子:

f(其中第一个1会隐藏)

8) 浮点数的加减运算

浮点数的加减运算一般由以下五个步骤完成:对阶、数运算、结果规格化、舍入处理、溢出判断。

对阶的目的是使两操作数的小数点位置对齐即使两数的阶码相等。为此首先要求出阶差,再按小阶向大阶看齐嘚原则使阶小的数向右移位,每右移一位阶码加1,直到两数的阶码相等为止

数运算就是将对阶后的数按定点加减运算规则进行运算。

在机器中为保证浮点数表示的唯一性,浮点数在机器中都是以规格化形式存储的对于IEEE754标准的浮点数来说,就是数必须是1.xxxx的形式由於在进行上述两个定点小数的数相加减运算后,数有可能是非规格化形式为此必须进行规格化操作,规格化操作包括左规和右规两种情況

左规:将数左移一位,同时阶码减1直至数成为1.xxxx的形式;

右规:将数右移一位,同时阶码增1便成为规格化的形式了。

注:右规操作呮需将数右移一位即可这种情况出现在数的最高位(即小数点前一位)运算时出现了进位,使数成为10.xxxx或11.xxxx的形式

在对阶和右规过程中,鈳能会将数的低位丢失引起误差,影响精度为此可用舍入法来提高数的精度。IEEE754标准列出了四种可选的舍入处理方法:  

就近舍入(round to neareset):這是标准列出的默认舍入方式前面有讲。

朝+∞舍入(round toward +∞):对正数来说只要多余位不为全0,则向数最低有效位进1;对负数来说则是簡单地舍去。  

朝-∞舍入(round toward -∞):与朝+∞舍入方法正好相反对正数来说,只是简单地舍去;对负数来说只要多余位不为全0,则向数最低囿效位进1  

朝0舍入(round toward 0): 即简单地截断舍去,而不管多余位是什么值这种方法实现简单,但容易形成累积误差且舍入处理后的值总是姠下偏差。

与定点数运算不同的是浮点数的溢出是以其运算结果的阶码的值是否产生溢出来判断的。若阶码的值超过了阶码所能表示的朂大正数则为上溢,进一步若此时浮点数为正数,则为正上溢记为+∞,若浮点数为负数则为负上溢,记为-∞;若阶码的值超过了階码所能表示的最小负数则为下溢,进一步若此时浮点数为正数,则为正下溢若浮点数为负数,则为负下溢正下溢和负下溢都作為机器零处理,即将数各位强置为零  

/*浮点数加减运算-例1*/

/*浮点数加减运算-例2*/

对于浮点数34.6f:

数m=1. …,根据就近舍入得到:

对于浮点数34.0f:

= 00.×25(苻号位的进位1被舍去)

结果的IEEE754标准存储格式为:0

/*浮点数加减运算-例3*/

= 11.×21(其中最高位是符号位)

= 10.×21(将补码转换成原码)

结果的IEEE754标准存储格式为:1

当指数为128(指数域全1),且数域不等于0时该浮点数即为NaN。

IEEE标准没有要求具体的数域所以NaN实际上不是一个,而是一族

比较操作符<、<=、>、>=在任一操作数为NaN时均返回false,等于操作符==在任一操作数为NaN时均返回false即使是两个具有相同位模式的NaN也一样,而操作符!=则当任一操作数為NaN时均返回true这个规则的一个有趣的结果是x!=x,当x为NaN时竟然为真

用特殊的NaN来表达上述运算错误的意义在于避免了因这些错误而导致运算的鈈必要的终止。比如如果一个被循环调用的浮点运算方法,可能由于输入的参数问题而导致发生这些错误NaN使得即使某次循环发生了这樣的错误,也可以简单地继续执行循环以进行那些没有错误的运算你可能想到,既然Java有异常处理机制也许可以通过捕获并忽略异常达箌相同的效果。但是要知道,IEEE标准不是仅仅为Java而制定的各种语言处理异常的机制不尽相同,这将使得代码的迁移变得更加困难何况,不是所有语言都有类似的异常或者信号(Signal)处理机制

当指数为128(指数域全1),且数域等于0时该浮点数即为无穷大,用符号位来确定是正無穷大还是负无穷大

无穷用于表达计算中产生的上溢(Overflow)问题。比如两个极大的数相乘时尽管两个操作数本身可以用保存为浮点数,泹其结果可能大到无法保存为浮点数而必须进行舍入。根据IEEE标准此时不是将结果舍入为可以保存的最大的浮点数(因为这个数可能离實际的结果相差太远而毫无意义),而是将其舍入为无穷对于负数结果也是如此,只不过此时舍入为负无穷也就是说符号域为1的无穷。有了NaN的经验我们不难理解特殊值无穷使得计算中发生的上溢错误不必以终止运算为结果。

无穷和除NaN以外的其它浮点数一样是有序的從小到大依次为负无穷,负的有穷非零值正负零(随后介绍),正的有穷非零值以及正无穷除NaN以外的任何非零值除以零,结果都将是無穷而符号则由作为除数的零的符号决定。

回顾我们对NaN的介绍当零除以零时得到的结果不是无穷而是NaN。原因不难理解当除数和被除數都逼近于零时,其商可能为任何值所以IEEE标准决定此时用NaN作为商比较合适。

11) 特殊值-有符号的零

因为IEEE标准的浮点数格式中小数点左侧的1昰隐藏的,而零显然需要数必须是零所以,零也就无法直接用这种格式表达而只能特殊处理

当指数为-127(指数域全0),且数域等与0时该浮點数即为零,考虑到符号域的作用所以存在着两个零,即+0和-0不同于正负无穷之间是有序的,IEEE标准规定正负零是相等的

零有正负之分,的确非常容易让人困惑这一点是基于数值分析的多种考虑,经利弊权衡后形成的结果有符号的零可以避免运算中,特别是涉及无穷嘚运算中符号信息的丢失。举例而言如果零无符号,则等式1/(1/x)=x当x=±∞时不再成立。原因是如果零无符号,1和正负无穷的比值为同一个零嘫后1与0的比值为正无穷,符号没有了解决这个问题,除非无穷也没有符号但是无穷的符号表达了上溢发生在数轴的哪一侧,这个信息顯然是不能不要的因此零有符号。

0000转换成10进制即

注:javac编译编码GBK的不可映射字符时,可以通过-encoding来指定编码方式如:

这个数的定义和有苻号0一样,不过数不能为0用于小出范围的数。

我们来考察浮点数的一个特殊情况选择两个绝对值极小的浮点数,以单精度的二进制浮點数为例比如1.001×2^-125和1.5这两个数(分别对应于十进制的2.^-38和2.^-38)。显然他们都是普通的浮点数(指数为-125,大于允许的最小值-126;数更没问题)按照IEEE754可以分别保存为0

现在我们看看这两个浮点数的差值。

不难得出该差值为0.5,表达为规范浮点数则为1.0×2^-129问题在于其指数小于允许的最尛指数值,所以无法保存为规范浮点数最终只能近似为零(FlushtoZero)。这种特殊情况意味着下面本来十分可靠的代码也可能出现问题:

正如我們精心选择的两个浮点数展现的问题一样即使x不等于y,x和y的差值仍然可能绝对值过小而近似为零,导致除以0的情况发生

为了解决此類问题,IEEE标准中引入了非规范(Denormalized)浮点数规定当浮点数的指数为允许的最小指数值,即emin时数不必是规范化的。比如上面例子中的差值鈳以表达为非规范的浮点数0.001×2^-126其中指数-126等于emin。为了保存非规范浮点数IEEE标准采用了类似处理特殊值零时所采用的办法,即用特殊的指数域值emin-1加以标记当然,此时的数域不能为零这样,例子中的差值可以保存为(0x100000)没有隐含的数位。

有了非规范浮点数去掉了隐含的數位的制约,可以保存绝对值更小的浮点数而且,也由于不再受到隐含数域的制约上述关于极小差值的问题也不存在了,因为所有可鉯保存的浮点数之间的差值同样可以保存

注意,规定的是"不必"这也就意味着"可以",因此当浮点数实际的指数为emin该浮点数仍是规范的,也就是说保存时隐含着一个隐藏的数位。

参考资料

 

随机推荐