浮点数的加减法运算
浮点数的加减法运算
本⽂内容参考⾃王达⽼师的《深⼊理解计算机⽹络》⼀书<;中国⽔利⽔电出版社>
浮点数与定点数相⽐较有两个⽐较明显地特点:1、⼩数点位置不固定,但是在浮点数加减法运算的时候,⼩数点必须对齐;2、存储器中存储的不是浮点数的直接值,⽽存储的是符号,尾数,移码/阶码三种要素,所以不能直接相加减,实际上,针对浮点数的加减法运算,只需要对代表浮点数有效部分的尾数进⾏加减运算。
所以,浮点数的运算相对整数运算更复杂,有关浮点数的加减法运算包括以下5个基本步骤:对阶,尾数运算、规格化处理、舍⼊处理、益出处理。
—引⾃王达⽼师《深⼊理解计算机⽹络》⼀书
⼀、浮点数运算(总共包括5个步骤):
1、对阶
对阶就是⽐较参与运算的浮点数的阶码⼤⼩,然后使他们的阶码(或移码)都⼀样,其最终⽬的是使参与运算的数的⼩数点位置对齐,以便得出正确的结果。
既然需要使参与运算的浮点数的阶码⼀样,这时就要以某⼀个浮点数的阶码为标准了,通常以⼤阶码为准,即阶码⼩的数,要向阶码⼤的数对齐,也就是将⼩阶码的值调成与⼤阶码的值⼀样,这时,为了确保⼩阶码的数值与原来的保持⼀致,肯定需要对⼩阶码的尾数的⼩数点进⾏移位,就像⼗进制数中的1.2345 * 10,如果将指数3变成5,那么尾数1.2345的⼩数点位置不可能保持不变,要向左移两位,变成0.012345,所以,最终的形式为0.012345 * 10。
因为是以⼤阶码为准,所以⼩阶码需要增⼤,那么尾数需要做相同倍数的减⼩,才能保持数值不变(即阶码⼩的尾数需要向右移对应的位数[⼩数点是向左移]),因为原则是,在尾数最⾼位前⾯加对应数量的0,然后原尾数最后的对应尾数直接丢弃,移了多少位就丢弃多少位(直接丢弃是因为存储尾数的存储器空间是固定的)。
对阶中的尾数移位可以借助⼗进制的科学计数法来帮助理解,假设⼗进制数1234.5原来表⽰成1.2345 * 10,现在需要表⽰成10的格式,则对应的格式为0.0012345 * 10(在原来的⼩数点右边最⾼位前⾯移了三位,补加了两个0),⼜假设⼩数点后⾯最多只能4位(浮点数中⽤来保存尾数的存储空间是固定的),这样⼀来,最终的存储格式是0.0012 * 10,尾数中原来
的"345"就被丢弃了,虽然这会在⼀定程度上导致与原值有区别,但还是在最⼤限度上保持了不变。
⽰例:X = 2 * 0.11000101,Y = 2 * 0.10101110。对这两个浮点数进⾏加减法运算(尾数的符号位是体现在浮点数的符号位上,阶码和尾数均已采⽤补码形式表⽰)。
X的阶码为0010,对应的⼗进制数位2,Y的阶码为0100,对应的⼗进制数为4,因此,X的阶码⽐Y的阶码⼩,此时应将X的阶码变成与Y的阶码⼀样,即都调成4,同时需要将X的尾数⼩数点左移2位(在⼩数点右边最⾼位前⾯加两个0,原来最低的2位被丢弃,这样才能使X的值与原来保持基本不变)。
X原来的尾数为11000101,向右移两位(仍要保持原来的总位数不变,则要在前⾯补相应位数的0,原来最右边的对应两位丢弃),则变成了00110001,这样⼀来,X的阶码也变成了0100,最终X在存储器中的格式是0 0100 00110001(原来为0 001011000101)。
2、尾数运算
通过对阶将尾数的⼩数点对齐之后,就能将经过移位的尾数进⾏加减运算,这⼀步很简单,可以直接参照博⽂–“⽆符号⼆进制运算”。
继续前⾯的⽰例:X = 2 * 0.11000101,Y = 2 * 0.10101110,现要求X+Y。
X对阶后的尾数为00110001,Y的尾数不变,为10101110,两者相加:
X  00110001
Y  10101110
结果  11011111
3、规格化处理
规格化处理主要是针对浮点数的尾数部分,规格化的尾数格式要求如下:
尾数采⽤原码表⽰形式时:正数的规格化格式为:0.1XXX,负数的规格化形式为1.1XXX。
尾数采⽤补码表⽰形式时,正数的规格化格式为:0.1XXX,负数的规格化形式为:1.0XXX。
以上的最⾼位均代表符号位(0代表正数,1代表负数),关键是看⼩数点后⾯的数的格式。
3536660010010000100100
对于双符号位(⽤两位来表⽰符号)的补码形式尾数,正数的规格化格式为00.1XXX,负数的规格化格式为:11.0XXX,以上最⾼两位代表符号位(00代表正数,11代表负数),关键是看⼩数点后⾯数的格式。
引⼊双符号位的设计⽬的就是为了能快速检测出运算结果是否有溢出,因为双符号位规定了"00代表正数,11代表负数",如果最⾼两位不是这两种形式,⽽是"01"或者是"10"就能快速知道有溢出了,符号位为"01"的时候称为"上溢",即最⾼真值位相加后有进1,为"10"时,称为"下溢",即最⾼真值位相减后有借1。
—引⾃王达⽼师《深⼊理解计算机⽹络》⼀书
本⽂内容仅涉及单符号位,有兴趣了解双符号位的,可以关注后续博⽂。
凡不符合以上格式要求的尾数均要进⾏规格化,对以上规格化格式进⾏总结可以得出:符号位与尾数最⾼位不⼀致才算是规格化,⼀致为⾮规格化。如1.0XXX,0.1XXX(最前⾯的为符号位)之类的尾数都是规格化的数,⽽1.1XXX,0.0XXX为⾮规格化的数,另外,如果尾数⼤余1,也是⾮规格化数。
两个负数的补码相加对于⾮规格化的尾数需要进⾏相应的处理,处理⽅式⼜分为"左规"和"右规",所谓"左规"就是尾数需要向左移位,每移1位阶码值减1,直到为规格化数为⽌,对应"右规"就是尾数要向右移位,每移1位阶码值减1,直到为规格化数为⽌。
采⽤"左规"还是"右规"的基本原则如下:
运算结果产⽣移益出(由于原来两尾数的最⾼有效位相加有进位或者相减有借位时形成的)时,必须进⾏"
右规",如双符号位的运算结果为10.XXX或01.XXX格式就是不符合规格化要求了,因为双符号位时符号位为"10"和"01"都是不正确的,右规时,最⾼有效位前补相应数量的0,此时,10.XXX格式右规后的格式为11.0XXX,⽽01.XXX格式右规后的格式为00.1XXX。
如果运算结果出现0.1XXX或者1.1XXX(即符号位与尾数最⾼有效位相同时),必须进⾏左规,左规时最低有效位后补相应位数的0。
在上⾯的例⼦中,X与Y的尾数之和是11011111,⼜因为它是正数,所以可以表⽰成0.11011111,已经是规格化,不⽤再处理了。
如果X与Y相减,得出的尾数之差为负数,如1.1101111,这时就要进⾏规格化处理了,因为符号位1与尾数最⾼位1相同,这时需要进⾏左规,即向左移位,11011111要向左移两位才能使尾数的最⾼位与符号位不⼀致,移位后的尾数为01111100(最后两个0是补上去的)同时阶码要从原值0100相应减2,得到0010。
4、舍⼊处理
在"对阶"和向左,向右规格化处理时,尾数要向左,向右移位,这样结果尾数与原先的尾数就会有⼀定的误差,因此要进⾏舍⼊处理,以尽可能减⼩这种误差。
以下提供两种减⼩误差的⽅法:
“0舍1⼊法”:
类似⼗进制中的四舍五⼊法,即如果左规或右规时丢弃的是0,则舍去不计,反之要将尾数的末尾加1。
如上⾯举例的X和Y的尾数之和为:1.11011111(最⾼位为符号位),需要左规,得到1.01111100,由此可见左移时去掉的是前⾯的两位1,这种情况下就需要在尾数的最后⼀位加1,这样进⾏舍⼊处理的结果就是:1.01111101。
‘‘恒置1法’’:
只要有数位被左规或者右规丢弃掉,就要在尾数的末尾恒置1,但是这个⽅法精确度不⾼,因为从概率上来讲,丢弃0和1的概率各占50%。
IEEE 754还有许多更复杂的舍⼊模式,有兴趣可以⾃⾏了解。
5、溢出处理
如果采⽤双符号位补码形式,出现了"01"或者是"10"的形式就表⽰有溢出,但是,如果采⽤的是单符号
位就不好判定了。这时需要根据对尾数进⾏规格化处理后的阶码是否超出了当前阶码所能表⽰的取值范围来判定了。
上⽂已经提到了,右规后阶码需要加上对应的值,尾数每向右移⼀位就加1,最终可能导致阶码超出了当前类型的阶码所能表⽰的数值范围,这称为"上溢"。
如(X+Y)补浮点数经过"对阶"和尾数相加后得到的尾数为10.101110011,显然它需要右规,结果为11.01110011,相当于尾数右移了⼀位,原来尾数的最⾼有效位"1"被丢弃了,同时阶码也需要相应加1,假设原先是⽤4位来保存阶码(算上阶码的符号位,所以,最⼤能表⽰的数是+7),原阶码为0111,尾数右移⼀位后,阶码+1就变为8了,⽽这个是⼤于+7的,所以这是就需要进⾏溢出处理了。
同时,尾数进⾏左规的时候,也有溢出的情况,因为左规后可能超出了⽤于存储阶码的存储位所能表⽰的最⼩值(称为"下溢"),如左规后得到的新阶码为-5,假设原先的阶码是⽤4为来存储的,现在要求尾数再左移3位,这样阶码也要减⼩3,得到-8,⽽-8也超出了4位阶码所能表⽰的最⼩值-7(阶码的最⾼位为符号位)。如果发现有溢出,计算机会根据以下原则进⾏处理:
如果是上溢,⽴即停⽌运算,做中断处理。
如果是下溢,整个尾数按0处理。
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博⽂中有疑义的问题做出解答。同时希望博⽂中不正确的地⽅各位加以指正!