(CSAPP) – 二进制小数表示

二进制表示小数

使用乘二取整发可以求小数的二进制表示,注意:有的小数不是能够用二进制有限表示,需要保留位数。

IEEE 754表示浮点数

首先几种浮点数类型

来自参考1

浮点数的计算公式为:(-1)^s * M * 2^E。对于对于浮点数,frac(尾数)代表M,exp(阶码)则代表E。注意frac和M,exp和E有时不是相等的,因为浮点数的表示有位数的限制。

举例:单精度浮点数蕴含的几种情况:

  • 规格化的,exp部分为不等于0且不等于255(即2^8-1)
  • 非规格化的,exp部分全为0
  • 无穷大,exp部分全部为1,frac部分全为0
  • NaN,exp部分全为1,frac不都为0

情况一:

值的计算公式就是:v=(-1)^s * M * 2^E。

E = exp - Bias。Bias是一个偏置的值,exp是阶码表示的十进制的数字,E是阶码的十进制的值。Bias = 2 ^ (k - 1) -1(k就是exp的二进制位的长度)。

设f表示小数的值。f = frac的值,所以frac实际上保存的是二进制的科学记数法的尾数的部分。令

令M = f + 1,E = exp - Bias。带入v的公式就可以得出浮点数表示的值

情况二:非规格化的值

阶码exp为0,如果尾数全部为0。如果是规格化的,M>=1是成立的。所以使用非规格化的值,M = f = 0(也就是说frac的前面不再是1,而是0了). 当s=-1、0,分别表示的就是-0.0和+0.0。非规格化的数还可以表示非常接近0.0的数,他提供一种属性称为逐渐溢出。

特殊值:无穷大和NaN

当阶码全部为1时,若frac全为0则表示无穷大. 无穷大的方向取决于符号位s. 当frac不为0时,表示NaN. 一些运算会产生无穷大或者NaN.

浮点数的分布:越靠近原点他们越稠密。

来自参考1

例子:

s  exp   frac E 值
------------------------------------------------------------------
0 0000 000 -6 0 # 这部分是非规范化数值,下一部分是规范化值
0 0000 001 -6 1/8 * 1/64 = 1/512 # 能表示的最接近零的值
0 0000 010 -6 2/8 * 1/64 = 2/512
...
0 0000 110 -6 6/8 * 1/64 = 6/512
0 0000 111 -6 7/8 * 1/64 = 7/512 # 能表示的最大非规范化值
------------------------------------------------------------------
0 0001 000 -6 8/8 * 1/64 = 8/512 # 能表示的最小规范化值
0 0001 001 -6 9/8 * 1/64 = 9/512
...
0 0110 110 -1 14/8 * 1/2 = 14/16
0 0110 111 -1 15/8 * 1/2 = 15/16 # 最接近且小于 1 的值
0 0111 000 0 8/8 * 1 = 1
0 0111 001 0 9/8 * 1 = 9/8 # 最接近且大于 1 的值
0 0111 010 0 10/8 * 1 = 10/8
...
0 1110 110 7 14/8 * 128 = 224
0 1110 111 7 15/8 * 128 = 240 # 能表示的最大规范化值
------------------------------------------------------------------
0 1111 000 n/a 无穷 # 特殊值

表的总结

  • 对于exp = 0000, 即非规格化的数据, 间距都是1/8
  • 由于间隔的存在0~1之间的小数只能以 1/8 为最小单位来表示,且相邻数字间间距一样.
  • 最大的非规格化数7/512和最小的规格化数8/512之间平滑过渡得益于非规格化的frac前面没有1
  • frac继续+1且exp为1110直到exp溢出为1111, 即无穷

舍入

在二进制中,最低为0被认为偶数, 为1为奇数(毕竟时2^0次方+2^x的和).  加粗的部分为需要确保为偶数的位.

正中间值类似于XXX.YYY1000类型.最右侧的Y时要被省去的序列的第一位, 如果保留的最低位(加粗的部分)为0, 且之后的序列不为都为0...0则不到一半; 如果为1且之后的都不为0...0则时超过了一半.

十进制      二进制   舍入结果 十进制     原因
2 又 3/32 10.00011 10.00           2          不到一半,正常四舍五入(011 )
2 又 3/16 10.00110 10.01       2 又 1/4   超过一半,正常四舍五入(110)
2 又 7/8 10.11100 11.00              3         刚好在一半时,保证最后一位是偶数,所以向上舍入(100)
2 又 5/8 10.10100 10.10        2 又 1/2    刚好在一半时,保证最后一位是偶数,所以向下舍入(100)
可能的中间值向偶数舍入在统计的时候有50%的时间里发生,所以能够平均统计的误差.

浮点运算

①加法

假设两个浮点数:

=2Ex·M

=2Ey·M

两浮点数进行加法和减法的运算规则是

    x±=(M2ExEy±M)2Ey,  E<=E

运算的步骤是:

1.检查0操作数

浮点加减运算过程比定点运算过程复杂。如果判知两个操作数xy中有一个数为0,即可得知运算结果而没有必要再进行后续的一系列操作,以节省时间。0操作数检查步骤则用来完成这一功能。

2. 比较阶码大小并完成对阶

两浮点数进行加减,首先要看两数的阶码是否相同,即小数点位置是否对齐。若两数阶码相同,表示小数点是对齐的,就可以进行尾数的加减运算。反之,若两数阶码不同,表示小数点位置没有对齐,此时必须使两数的阶码相同,这个过程叫做对阶

要对阶,首先应求出两数阶码Ex和Ey之差,即:△x = Ex - Ey

若Ex = Ey,表示两数阶码相等,不需改变两数的阶码;若Ex ≠ Ey,要通过尾数的移位以改变Ex或Ey,使之相等。由于浮点表示的数多是规格化的,尾数左移会引起最高有产位的丢失,造成很大误差;而尾数右移虽引起最低有效位的丢失,但造成的误差较小因此,对阶操作规定使尾数右移,尾数右移后使阶码作相应增加,其数值保持不变。很显然,一个增加后的阶码与另一个相等,所增加的阶码一定是小阶。因此在对阶时,总是使小阶向大阶看齐,即小阶的尾数向右移位(相当于小数点左移),每右移一位,其阶码加1,直到两数的阶码相等为止,右移的位数等于阶差△E。

3. 尾数进行加或减运算

对阶完毕后就可对尾数求和。不论是加法运算还是减法运算,都按加法进行操作,其方法与定点加减运算完全一样。

4. 结果规格化并进行舍入处理。

需要满足的性质,尾数M, 1<= M < 2.  对尾数的和需要进行规格化处理,

在对阶或向右规格化时,尾数要向右移位,这样,被右移的尾数的低位部分会被丢掉,从而造成一定误差,因此要进行舍入处理。

常用的舍入方法有两种:一种是“0舍1入”法,即如果右移时被丢掉数位的最高位为0则舍去,为1则将尾数的末位加“1”,另一种是“恒置1”,即只要数位被移掉,就在尾数的末位恒置“1”。

浮点数加法的性质:

相加可能产生 infinity 或者 NaN
满足交换率
不满足结合律(因为舍入会造成精度损失,如 (3.14+1e10)-1e10=0,但 3.14+(1e10-1e10)=3.14)
加上 0 等于原来的数
除了 infinity 和 NaN,每个元素都有对应的倒数
除了 infinity 和 NaN,满足单调性,即 a≥b→a+c≥b+c

②乘法

假设两个浮点数

=2Ex·S 

=2Ey·S

则浮点乘法运算的规则是:

x×y = 2(Ex + Ey)·( Sx × Sy )               (2.7.2)

可见,乘积的尾数是相乘两数的尾数之积,乘积的阶码是相乘两数的阶码之和。当然,这里也有规格化与舍入等步骤。

浮点除法运算的规则是:

x÷y = 2(Ex Ey)·( Sx ÷ Sy )               (2.7.3)

可见,商的尾数是相除两数的尾数之商,商的阶码是相除两数的阶码之差。当然也有规格化和舍入等步骤。

浮点数的乘除运算大体分为四步:

第一步,0 操作数检查;第二步,阶码加/减操作;第三步,尾数乘/除操作;第四步,结果规格化及舍入处理。

(1) 浮点数的阶码运算

对阶码的运算有+1、-1、两阶码求和、两阶码求差四种,运算时还必须检查结果是否溢出。在计算机中,阶码通常用补码或移码形式表示。补码运算规则和溢出的方法,前面已经讲过。这里只对移码的运算规则和判定溢出的方法进行讲解。

移码的定义为:

                     [x] = 2n + x     2n > x ≥2 -n

按此定义,有

      [x]+[y]移    = 2n + x + 2n + y

                                   = 2n + (2n + (x + y))

                                   = 2n + [x + y]

即直接用移码实现求阶码之和时,结果的最高位多加了个1,要得到移码形式的结果,必须对结果的符号再执行一次求反。

考虑到移码和补码的关系:对同一个数值,其数值位完全相同,而符号位正好相反。而[y]的定义为

               [y]补 = 2n+1 + y

则求阶码和用如下方式完成:

               [x]移 + [y]补    = 2n + + 2n+1 + y    = 2n+1 +(2n + (x + y))

[x + y]=[x]移 + [y]补                               (mod  2n+1)

同理

[x - y]=[x]移 + [-y]                                                     

上二式表明执行阶码加减时,对加数或减数y应送移码符号位正常值的反码。

如果阶码运算的结果溢出,上述条件则不成立。此时,使用双符号位的阶码加法器,工规定移码的第二个符号位,即最高符号位恒用0参加加减运算,则溢出条件是结果的最高符号位为1。此时,当低位符号位为0时,表明结果上溢;为1时,表明结果下溢。当最高符号位为0时,表明没有溢出;低位符号位为1,表明结果为正;为0时,表明结果为负。

【例 】 x = +011,y = +110,求[x + y]和[x - y],并判断是否溢出。

    [解:]           [x]= 01,011,[y]= 00,110,[-y]=11,010

                [x + y]=[x]移 + [y]=10,001,结果上溢。

                [x - y]=[x]移 + [-y]=00,101,结果正确,为-3。

(2) 尾数处理

浮点加减法对结果的规格化及舍入处理也适用于浮点乘除法。

第一种简单的办法是,无条件地丢掉正常尾数最低位之后的全部数值。这种办法被称为截断处理,其好处是处理简单,缺点是影响结果的精度。

第二种简单办法是,运算过程中保留中移出的若干高位的值,最后再按某种规则用这些位上的值修正尾数,这种处理方法被称为舍入处理。

当尾数用原码表示时,舍入规则比较简单。最简便的方法,是只要尾数最低位为1,或移出的几位中有为1的数值位,就使最低位的值为1。另一种是0舍1入法,即当丢失的最高位的值为1时,把这个1加到最低数值位上进行修正,否则舍去丢失的各位的值。这样处理时,舍入效果对负数是相同的,入将使数的绝对值变大,舍则使数的绝对值变小。

当尾数是用补码表示时,所用的舍入规则,应该与用原码表示时产生相同的处理效果,具体规则是:

当丢失的各位均为0时,不必舍入;

当丢失的最高位为0,以下各位不全为0时,或者丢失的最高位为1,以下各位均为0时,则舍去丢失位上的值;

当丢失的最高位为1,以下各位不全为0时,则执行在尾数最低位入1的修正操作。

【例 】 设[x1] = 11.0110,[x2] = 11.01100001,[x3] = 11.01101000,[x4] = 11.01111001,求执行只保留小数点后4位有效数字的舍入操作值。

    [解:]    执行舍入操作后,其结果值分别为

          [x1] = 11.0110(不舍不入)

          [x2] = 11.0110(舍)

          [x3] = 11.0110(舍)

          [x4] = 11.1000(入)

基本性质

  • 相乘可能产生 infinity 或者 NaN
  • 满足交换率
  • 不满足结合律(因为舍入会造成精度损失)
  • 乘以 1 等于原来的数
  • 不满足分配率 1e20*(1e20-1e20)=0.0 但 1e20*1e20-1e20*1e20=NaN
  • 除了 infinity 和 NaN,满足单调性,即 aba×ca×b

C语言中的浮点数

引用&参考

 

1条评论