浮点数表示和运算

通过学习了计算机系统的相关知识,我对浮点数的表示和算法部分格外关注,总结了一点知识点在这里。

标题浮点数的表示

在现代计算机中,为了便于软件移植,一般均采用 IEEE 754标准来表示浮点数。在介绍IEEE 754标准前,有必要先介绍一下浮 点数的表示形式。

既然尾数和阶码分别是定点小数和定点整数,即尾数和阶码都是有符号位的,那么就可以写出浮点数N的一般格式,如图所示。
浮点数N的一般格式
从图可知:
1)浮点数阶码的底r省略。

2)阶符和阶码的位数k合起来反映浮点数的表示范围及小数点的实际位置。

3)尾数M的位数n反映了浮点数的精度。

4)尾数的符号为m,它也是整个浮点数的符号位,表示了该浮点数的正负。

在大多数机器中,尾数为纯小数,常用原码或补码表示;阶码为定点整数,常用补码或移码表示。

我们采用IEEE 754标准来表示浮点数的格式可以如以下图:
在这里插入图片描述

按照IEEE 754标准,常用的浮点数有以下三种:
在这里插入图片描述
我们可以通过一个例题加深对其的理解:

[例1]若浮点数x的IEEE 754标准存储格式为41360000H,试求其浮点数的十进制数值。

解:将十六进制数先展开为二进制,可得二进制数的格式为

	0100 0001 0011 0110 0000 0000 0000 0000

	按照IEEE 754标准的格式排列一下:

	0 00000100000000000000

	指数e=阶码- 127=10000010-127=3

	包括隐藏位1的尾数1.M=1.011 0110 000 0000 0000 000=1.011011

	于是可得

x=(-1)sx1.Mx2e= +(1.011011)x23=+1011.011=(11.375)10

浮点数如何表示0:

用一种专门的位序列表示0。例如,IEEE 754单精度浮点数中,用“0000 000H"表示+0,用 “8000 00”表示-0。当运算结果出现阶码过小时,计算机将该数近似表示为0。

浮点数的阶要用移码表示:

因为在浮点数的加减运算中,要进行对阶操作,所以需要比较两个阶的大小。移码表示的实质就是把阶加上一个偏置常数,使得所有数的阶码都是一-个正整数, 比较大小时,只要按高位到低位顺序比较就行了,因而引入移码可以简化阶的比较过程。

浮点数的加法和减法运算

我们可以知道,学习了浮点数的相关知识后,我们为了提高浮点数的精度,其尾数必须为规格化的数(因为规格化数精度最高),如果不是规格化数,则要通过修改阶码并同时左右移尾数的办法使得其变成规格化数,而规格化的尾数必须满足如下条件:

假设尾数为W,且基数为2,则当1>|w|≥1/2时,此浮点数为规格化数。一般来说,浮点数的尾数常用原码和补码来表示,因此要分两种情况来分析:

1)当使用原码表示尾数时,要使得1>|w|≥1/2, 其尾数第一-位必须为 1,否则其绝对值一定小于1/2,故原码表示尾数规格化后的形式: 0.1xxX…或者 1.1Xxx…x.

2)当使用补码表示尾数时,要使得1>|w|≥1/2,当此浮点数为正数时,和原码-一样,最高位必须为1;当此浮点数为负数时,要使得1>|w|≥1/2,最高位必须为0,否则求反加1回到原码时就会造成|w|<1/2,故补码表示尾数规格化后的形式: 0.1xxX…X或者1.0XXX<… (如果采用双符号位表示,则是00.1XXX…X或者11.0XXX…X)。

注意两个特殊的数:

1)当尾数为-1/2时,尾数的补码为1.00…0.0 对于此数,它满足1>|w|≥1/2,但是不满足补码的规格化形式,故规定-1/2不是规格化的数。

2)当尾数为-1时,尾数的补码为1.00… 0,因为小数补码允许表示-1,所以特别规定-1为规格化的数。

我们可以通过计算下面的问题,加深我们对其理解:

【例】100+10=?

(100)10=( 1100100)2 ,(10)10=(1010)2

1100100转成浮点数是27 <00.1100100 (尾数采用 补码表示)

1010转成浮点数是24 x 00.1010000(尾数采用补码表示)

千万注意此处不能直接将尾数相加

浮点数的加/减运算也需要对阶。现在的问题是高阶向低阶对齐,还是低阶向高阶对齐?如果是高阶向低阶对齐,那就需要左移,这样就有可能改变符号位,肯定不行,因此对阶一定是低阶向高阶对齐。继续上面的例子,24 <00.1010000=27x00.0001010,这样就可以直接将尾数相加了。这样,两步操作(对阶、尾数求和)已完成。

尾数求和完成后,又会出现问题,求和后的尾数是规格化的吗?若不是,还得把尾数规格化。如果尾数采用补码表示,则规格化的形式应该为00.1xXxX…X或者11.0xxx…x,需要分两种情况分析:

1)当尾数求和后出现00.0xX<…X或者11.1xxX…x,如果不断往右移动,则一直都会保持不变,因为补码的算术右移是补符号位的;必须得左移,左移一位,阶码就减1。一直移到满足补码规格化的形式为止,至于要移动多少次是不确定的,以上步骤称为左规。

2)当尾数求和后出现01.xxx… X或者10.xxx…x,我们可以想到变形补码判断溢出的方式,两位符号位不同,表示溢出,可见尾数溢出了。但是,这在浮点数中不算溢出,可以通过右移来纠正。右移一位, 阶码加1。这种形式只要右移一次就可以变成规格化数,01.XXX… X右移一位变成00.1XX X…<; 10.xxx…右移一位变成11.0XXX…x ,以上步骤称为右规。规格化问题解决了,浮点数的第3步操作(规格化)也就完成了。但是在对阶和右移的过程中,很有可能会导致尾数的低位丢失,这样就会引起误差,影响精度。可以使用舍入法来提高尾数的精度,常用的舍入法有以下两种:

1)“0舍1入”法。“0舍1入”法类似于十进制中的“四舍五入”法,即在尾数右移时,被移去的末位为0,则舍去;被移去的末位为1,则在尾数的末位加1.但是这样又很有可能导致尾数溢出,因此此时需要做一次右规,例如,011111 末尾加1,就变成0.0000,此时需要右规。

2)恒置“1”法。尾数右移时,不论丢掉的最高数值位是“1”或“0”,都使右移后的尾数末位恒置“1”。

对于浮点数的加/减运算,可以总结为以下4个步骤:

①对阶,使两数的小数点位置对齐。

②尾数求和,将对阶后的两尾数按定点加/减运算规则求和或者求差。

③规格化,为增加有效数字的位数,提高运算精度,必须将求和或求差后的尾数规格化

④舍入,为提高精度,要考虑尾数右移时丢失的数值位。

当然,以上4个步骤完成后,还需要加上一步,即检查一下 最后的结果是否溢出,由于浮点数的溢出完全是用阶码来判断的,假设阶码采用补码来表示,溢出就可以使用双符号位判断溢出的方式来判断此浮点数是否溢出,过程如下:

			if (阶符= =01)
						
				上溢,需做中断处理;
						
			else if (阶符= =10)
						
				下溢,按机器零处理;
						
			else :
						
				结果正确;

理论知识可能不是很懂,我们下面来看个题就会容易理解一点的

【例】已知十进制数 x= -5/256, y=+59/1024, 按机器补码浮点数运算规则计算x-y,结果用二进制表示。其中,浮点数格式如下:数的阶符取两位,阶码取3位,数符取两位,尾数取9位(舍入时采用0舍1入法)。

解:首先将x和y转换成浮点数,如下:

x=-5/256=2-101x(11.101000000)

y= 59/1024 =2-100x(00.111011000)

由于jx=11101,因此[jx]补=11011,同理[-jy]补+ =00100.

所以:
[x]补=11011,11.011000000

[y]补=11100,00.111011000

下面可以按照浮点数加/减运算的5个步骤来做:

1)对阶。求阶差: [△j]补=[jx]补-[jy]补=[jx]补+[-jy]补

=11011+00100

=111111 (补码全1表示-1)

因此,x的阶码要低1,故应该x向y对齐,x尾数需要右移一位,阶码加1,如下:

[x]补=11100,11.101100000

2)尾数求差。

 11.101100000

+11.000101000 (这里加的是y尾数的负数补码)

 =10.110001000

即[x-y]补=11100,10.110001000
3)规格化。尾数出现10.XXx…x形式,说明需要右规一次即可, 阶码加1,最后可得

[x-y]补=11101,11.010001000 (加了下划线的0为右规丢弃的0)

4)舍入处理。由于右规低位丢0,因此直接舍去。

5)溢出判断。最后阶符为11,没有溢出,应将[x-y]补=11101,11.011000100转换为真值。阶码为11101,换成原码为11011,说明真实值为-3;同理,尾数为-0.1001111故最后的结果为2-3x(-0.1001111)。

进一步,在C语言程序中,为什么关系表达式“123456789= =(int)(float)123456789”的结果为“假”,而关系表达式“123456= =(int)(loat)123456”和“123456789==(int)(double)123456789”的结果都为“真”?

首先应该明白,在C语言中,float 类型对应IEEE 754单精度浮点数格式,即float型数据的有效位数只有24位(相当于有7位十进制有效数); double 类型对应IEEE 754双精度浮点数格式,有效位数有53位(相当于有17位十进制有效数); int 类型为32位整数,有31位有效位数(最大数为231-1=2147483647)。

整数123456789的有效位数为9位,转换为float型数据后肯定发生了有效位数丢失,再转换成int型数据时,已经不是123456789了。所以,关系表达式“123456789= =(int)(float)123456789”的结果为“假”。

整数改为123456后,有效位数只有6位,转换为float型数据后有效位数没有丢失,因而数值没变,再转换为int型数据时,还是123456。所以,关系表达式“123456= =(int)(float)123456”的结果为“真”。

整数123456789的有效位数为9位,转换为double型数据后,不会发生有效位数丢失,再转换成int 型数据时,还是123456789。所以,关系表达式“123456789= =(int)(double)123456789”的结果为“真”。

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值