浮点数精度问题

 一、例子

      首先我们去编译器试试  double a=1.9;通过添加监视查看a的值

    会发现a的值是1.8999999…

    二、开始今天的学习

        在最开始学c++的时候并没有对浮点数进行很深入的学习,认为浮点不就是小数嘛,首先在C++的宏里面有

FLT_MAX 和FLT_MIN的定义,float是四字节的浮点数,后面我么会发现,四字节的浮点数有效数字为6-7位,能保证的是6位,与浮点数的个数相比,四字节能表示的浮点数简直微不足道,也就是说存在小数根本就不能按照浮点数的这一套标准来进行“完美”储存,用有限的字节数去储存无限的浮点数,因此就会发生精度丢失。

C/C++采用的是IEEE浮点标准,它以“二进制的科学表示法”表示一个小数:


其中M是一个整数部分仅有一位的二进制小数,例如1.011,表示十进制下的1.375。E表示该小数以2为底时的阶数。基于以上的表示方式,小数需要对三部分进行编码:表示符号的s及阶码E尾数码M。C++中的double类型三种编码所占的位数如图所示。

现在让我们按照IEEE浮点数表示法,一步步的将float型浮点数12345.0f转换为十六进制代码。在处理这种不带小数的浮点数时,直接将整数部转化为二进制表示:

1 11100010 01000000

也可以这样表示:

11110001001000000.0

然后将小数点向左移,一直移到离最高位只有1位,就是最高位的

1:1.11100010010000000

一共移动了16位,在布耳运算中小数点每向左移一位就等于在以2为底的科学计算法表示中指数+1,所以原数就等于这样:1.11100010010000000 * ( 2 ^ 16)

好了,现在我们要的尾数和指数都出来了。显而易见,最高位永远是1,这样尾数的二进制就变成了:11100010010000000最后在尾数的后面补0,一直到补够23位:

11100010010000000000000


    再回来看指数,一共8位,可以表示范围是0 - 255的无符号整数,也可以表示-128 - 127的有符号整数。但因为指数是可以为负的,所以为了统一把十进制的整数化为二进制时,都先加上127,在这里,我们的16加上127后就变成了143,二进制表示为:10001111

12345.0f这个数是正的,所以符号位是0,那么我们按照前面讲的格式把它拼起来:

0 10001111 11100010010000000000000
  0 100011111110001 00100000 00000000
再转化为16进制为:47 F1 20 00,最后把它翻过来,就成了:00 20 F1 47。

按照IEEE浮点数表示法,将float型浮点数123.456f转换为十六进制代码。对于这种带小数的就需要把整数部和小数部分开处理。整数部直接化二进制:100100011。小数部的处理比较麻烦一些,比如有一个十进制纯小数0.57826,

5是十分位,位阶是1/10;

7是百分位,位阶是1/100;

8是千分位,位阶是1/1000……,

这些位阶分母的关系是10^1、10^2、10^3……,现假设每一位的序列是{S1、S2、S3、……、Sn},在这里就是5、7、8、2、6,而这个纯小数就可以这样表示:n = S1 * ( 1 / ( 10 ^ 1 ) ) + S2 * ( 1 / ( 10 ^ 2 ) ) + S3 * ( 1 / (10 ^ 3 ) ) + …… + Sn * ( 1 / ( 10 ^ n ) )。把这个公式推广到b进制纯小数中就是这样:

n = S1 * ( 1 / ( b ^ 1 ) ) + S2 * ( 1 / ( b ^ 2 ) ) + S3 * ( 1 / ( b ^ 3 ) ) + …… + Sn * ( 1 / ( b ^ n ) )

现在一个二进制纯小数比如0.100101011就应该比较好理解了,这个数的位阶序列就因该是1/(2^1)、1/(2^2)、1/(2^3)、1/(2^4),即0.5、0.25、0.125、0.0625……。乘以S序列中的1或着0算出每一项再相加就可以得出原数了。现在你的基础知识因该足够了,再回过头来看0.45这个十进制纯小数,化为该如何表示呢?现在你动手算一下,最好不要先看到答案,这样对你理解有好处。
    来看一下步骤:1 / 2^1位(为了方便,下面仅用2的指数来表示位),0.456小于位阶值0.5故为0;2位,0.456大于位阶值0.25,该位为1,并将0.45减去0.25得0.206进下一位;3位,0.206大于位阶值0.125,该位为1,并将0.206减去0.125得0.081进下一位;4位,0.081大于0.0625,为1,并将0.081减去0.0625得0.0185进下一位;5位0.0185小于0.03125,为0……问题出来了,即使超过尾数的最大长度23位也除不尽!这就是著名的浮点数精度问题了。

到这里就可以解释为什么最前面double a 的问题了。

        

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值