关于浮点数计算的误差

浮点数float的惊人累加误差!!-CSDN论坛-CSDN.NET-中国最大的IT技术社区
http://bbs.csdn.net/topics/390549664

两段进行浮点数累加的代码。如果无误差的话,输出应该是40000.

============

浮点数的精度由其尾数的最低位决定,比如float类型,小数点后有23位二进制位,最右边一位可以表示的十进制数据的值为2^(-23).

所以有下面的结论:

float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字;

double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。

============ 

    double ans1=0;
    for (int i = 0; i < 4000000; i++){
        ans1 += 0.1;
    }
    printf("%f\n", ans1);  //399999.999979,如果换成float类型呢? 

// note:虽然double的精度有16位,但输出时总是只有6位小数,这里的总有效位数12。

    float ans2 = 0;
    for (int i = 0; i < 4000000; i++){
        ans2 += 0.1;
    }
    printf("%f\n", ans2);   //384524.781250,看来float还真是误差挺大的。

================讨论=================

计算机中的计算总是会有误差的,但如何减少误差呢?

下面是一些好的建议:

1。用double类型不用float。

2。对于类似上面这种情况每5次加0.5,下面是运行的结果:

float ans3 = -0.5;
for (int i = 0; i < 4000000; i++){
    if (i % 5 == 0)ans3 += 0.5;    //399999.500000,虽然还是有误差,但误差小了许多。
}
printf("%f\n", ans3);

3. 用整型(银行系统里元、角(分)分开处理)
4. 设计出无误差的方案(至少有修正方案)。windows里的计算器的界面虽然没大变,但内核其实不断地被重写,vista起各种运算的都能精确到任意位(不会出现 1/3 * 3 != 1之类的情况了)

我记得 Bjarne Stroustrup 的书里举了一个例子,好像是海湾战争中,美军发射的一个导弹摧毁自己另一个地方的军营,原因就是因为浮点数的累积误差。。。

5。按照下面这种方式精度会提高许多。解释见下文。

float f = 0.1;
float sum = 0;
for( int i=0; i<2000; i++)
{
float tmp_sum =0;
for (int j = 0;j<2000;j++)
tmp_sum+=f;
    sum += tmp_sum;
}
我猜是这样:
因为float一共只有6位有效数字(10进制),如果整数部分的位数多了,相应的小数部分的位数就少了,因此当一个很大的数(如100000)去加一个很小的数(如0.1),就会形成很大的误差
用楼主的累加方式,一开始误差不大,但到后来,sum越来越大,相应的误差也就越来越大
而用这种分次累加后再相加的方式,使得相加的两数大小差距都比较小,所以每次相加产生的误差也就小。

tips:

浮点数是7位有效数字,不是小数点后7位,因此尽量要避免两个数量级差别很大的数相加,因为这样误差会变大.极端例子:
float fValue1 = 123456789;  //本身这个输出就不准确,float的精度(有效数字)只有6-7位,正如你所看到的第7位后面的数据,计算完全靠猜了。
printf("%f\n", fValue1);   //123456792.000000,
printf("%f\n", (double)fValue1);  //123456792.000000

//1.23457e+008 对于浮点数,double或float类型如果由于数据过大,默认使用了科学计数法,有效数字是6位。看来还是cout输出float类型时靠谱,有一说一啊!!!,虽然最后一位是四舍五入得来的。
cout << fValue1 << endl;  

float fVlaue2 = fValue1 + 1;  //1.23457e+008
    if (fVlaue2 == fValue1)    printf("same \n"); //真的是这样哦!!!!
你会发现+1根没加一样

6。关于一个很大的数加一个很小的数会发生什么???

你的f是0.1,float位数是23,当sum足够大的时候,会出现 sum+f==sum 的情况,这个是ieee标准,
和C++没关系,事实上编译器应该已经做了浮点精度调整了,你这结果误差算小的了.
避免这种误差的方法就是浮点数,永远不要让一个很大的数去加上一个很小的数.

不知你这段代码的目的是什么,但如果你改成这样,误差会小很多:
float f = 0.1;
float sum = 0;
for( i=0; i<100; i++)
{
int sumEachBig=0;
for(....k<400....)
int sumEachSmall=0;
for(....j<100.....)
    sumEachSmall += f;
}
}
}
懒得写了...简单写一下看得懂就行..其实也就是你说的换了个结构精度提高了的那段代码.
总之就是"浮点数,永远不要让一个很大的数去加减一个很小的数"

转载于:https://my.oschina.net/ray1421/blog/702982

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值