计算机中浮点数的编码方式/浮点数的运算

文章详细介绍了浮点数在计算机中的编码方式,包括规格化和非规格化的浮点数表示,以及浮点数的加法和乘法运算规则。讨论了浮点数溢出、舍入模式以及C语言中浮点数类型转换的影响。还提供了一组与浮点数转换和比较相关的练习题。
摘要由CSDN通过智能技术生成

来稍微谈一谈计算机中浮点数的编码方式/浮点数的运算叭

摘要:浮点数的编码方式、浮点数的加法/乘法运算,float类型与int、double类型相互转换会带来的一些问题

十进制浮点数编码方式(适用于可表示为x/(2^k)的数)

编码公式:

v=(-1)^s*M*(2^E)

E=Exp-Bias

浮点数在计算机中按照s-Exp-Frac的方式进行编码。

单精度系统的位数分布为1-8-23,双精度为1-11-52(64位)。

其中s为符号位,正数为0,负数为1。

公式中的E为浮点数的阶数。如1.011*(2^13)这里的E就是13。

Bias为系统的偏差值,为2^(k-1)-1。如单精度系统中k=8,Bias就是127,双精度系统的Bias就是1023。

这个时候Exp就等于13+127=140。我们要把140转换为二进制数编为Exp中的八位,即10001100。

M类似于科学计数法中的底数,只不过把10换成了2,取值范围为[1.0,2.0)。

规格化浮点数的编码:

首先把整数和小数部分分别编码成二进制。

然后移动二进制小数点使得首位为一,得到M。M的取值范围为[1.0,2.0)。

既然M的首位为1是恒定的,我们就可以“白嫖”一个表示位,不用把首位的1放在内存中。把这个“1”扔掉,之后把剩下的数字存进frac中,尾部补上0直到满23位。

那当我们要表示0或者接近于零的数时,规格化浮点数的编码方式就不适合,因为首位总有一个隐含的“1”。这个时候就要用到非规格化的浮点数编码方式。

下面以1-4-3编码方式来介绍非规格化浮点数编码方式。

(1位符号位,4位Exp,3位frac,1-4-3是我瞎说的一个名词hhhh应该没有这种叫法)

当Exp的各个位数全都是0的时候,这就是非规格化(Denormalized)的浮点数编码方式。此时的E≠Exp-Bias,而是=1-Bias。在1-4-3的表示方法中,Bia等于7,此时E=-6。最小表示的非零浮点数位0-0000-001。001前面有一个隐含的“0.”,即0.001;表示的十进制浮点数为1/512。当frac加1,变成010时,表示的十进制浮点数则为2/512,以此类推至7/512,这是非规格化编码方式中所能表示的最大浮点数。

Exp不为0时,比如Exp=0001,这个时候就为规格化浮点数编码方式,满足公式E=Exp-Bias,Bias还是7(2的(4-1)次方减1)。注意到此时E=1-7=-6,能编码的最小非负浮点数为0-0001-000,表示的十进制浮点数为8/512  (frac部分000前有一个隐含的“1”,即1.000)。

有没有发现连起来了?不得不说IEEE的那帮人真的有点东西(划掉

当exp=111……,frac不全为0时,这就是浮点数溢出的编码形式,比如1.0/0.0=+∞。值得一提的是,浮点数溢出是直接溢出到正无穷。当exp=111……,frac=000……时,表示的数为NaN(Not A Number),比如-1的平方根,∞-∞,0*∞之类的。

当exp=000……的时候,就是非规格化浮点数的编码方式。当exp=000……,frac=000……,表示的浮点数0就是整数0。当exp=000……,frac不全为0的时候,表示的数为接近于0的数。

当需要表示的浮点数的精度大于计算机系统所能表示的最大精度的时候,就需要进行舍入。一共有四种舍入方式:(1)向零舍入;(2)向下舍入;(3)向上舍入;(4)向偶数舍入。我们可以通过汇编语言更改机器的舍入方式。第(4)种舍入方式为IEEE所采用的标准舍入方式。一言以蔽之,当大于中间数的时候进位,当小于中间数的时候截断,当处于中间位的时候向偶数位舍入。(二进制的中间位为100……,十进制的中间位为500……)

    然后我发现我之前学的地方好像哪里不对。比如说2.50001舍入到整数位,不仅仅看小数点后的第一位,而是小数点后的所有位都要看!如果只看小数点后第一位的话,就舍入到2(向偶数);然而正确的舍入方式是进一位到3,因为小数点后的所有位都是要看的,.5001是大于一半的!(哪怕就大了那么一丢丢)

下面来谈一谈浮点数的乘法。浮点数在表示为科学计数法之后(2为底数)的符号位、尾数、阶数分别设为s1,M1,E1;s2,M2,E2。新的浮点数设为s、M、E。s=s1^s2(^为异或运算符,Xor,当s1和s2相同的时候s=0,反之s=1,很好理解吧)

不出意外的话,M=M1*M2,E=E1+E2。当然如果M>2的话,小数点就移一位,E加1。如果E溢出的话,直接溢出到+∞,M的位数太多以至于需要截断的话,还是遵循找中间位舍入的原则。

浮点数的加法也是一样的。小数点对齐然后相加即可,然后把尾数M规格化至[1.0,2.0)。

值得一提的是,浮点数的加法和乘法都是不可结合的。(3.14+1e10)-1e10=0。为什么呢?因为3.14+1e10相加得到一个新的浮点数的时候,系统经过编码、将M规格化之后没有足够多的位来表示3.14,于是3.14就被截断了。3.14+(1e10-1e10)=3.14,没什么说的。乘法也是一样的道理(1e10*1e10)/1e10=inf。为什么?因为1e10*1e10溢出了,直接溢出到+∞(前面也有提到)。不管对inf进行什么操作,得到的结果都是inf。但是1e10*(1e10/1e10)=1e10。

最后讨论一下C语言中的浮点数。C语言中的浮点数分为单精度、双精度两种,都是IEEE表示法的(1-8-23、1-11-52)类型转换会对位的值产生实际的影响。从double/float转换到int会截取小数部分。从float到int不会发生舍入,因为int的32位能容纳下float的23位,从int到double同样的道理,因为double的52位能容纳下int的32位。从int到float要舍入,M规格化之后先无视精度得到确切结果,然后向偶数舍入(这一步就不能无视精度了hhhh)。

已知x为int,f为float,d为double来看一些练习,下面哪些式子真值为1:

  1. x==(int)(float)x
  2. x==(int)(double)x
  3. f==(float)(double)f
  4. d==(double)(float)d
  5. f==(-f)
  6. 2/3==2.0/3.0
  7. d<0.0 -> ((d*2)<0.0)
  8. d>f -> -f>-d
  9. d*d>0.0
  10. (d+f)-d=f

    1是错的,因为把32位的int转换为23位的float不能保证不丢失位数,相应的2、3是对的,4是错的。5是对的。6是错的,2/3表示整数0,2.0/3.0表示浮点数三分之二。7是对的,就算你d*2溢出,也是溢出到-∞,也满足小于0;8是对的。9是对的,d*d就算溢出,也是溢出到+∞;10是错的,之前的(3.14+1e10)-1e10的例子已经说明了。

    萌新第一次尝试写博客,居然写(水)了将近2000字。水平有限,如果没有写到的或者写得不准确甚至错误的,欢迎各位不吝赐教。参考资料:CMU 15213_15513课程。See you next time!

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值