浮点数float的惊人累加误差!!

ylongwu 2013-08-15 05:35:50
代码很简单,
	float f = 0.1;
float sum = 0;
for( i=0; i<4000000; i++)
{
sum += f;
}


理论上得到的sum应该是40万,但是计算得到的结果是384524.781250!!

请问这种累加误差一般如何避免?
...全文
3324 23 打赏 收藏 转发到动态 举报
写回复
用AI写文章
23 条回复
切换为时间正序
请发表友善的回复…
发表回复
hzj200903 2013-12-17
  • 打赏
  • 举报
回复 1
我也涨知识了。牛人真多。
menzi11 2013-08-17
  • 打赏
  • 举报
回复
楼主你这么实现根本不行,ieee浮点数,为了规格化,精度每超过2的整数次幂,精度要下降一位, 你的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; } } } 懒得写了...简单写一下看得懂就行..其实也就是你说的换了个结构精度提高了的那段代码. 总之就是"浮点数,永远不要让一个很大的数去加减一个很小的数"
team79 2013-08-17
  • 打赏
  • 举报
回复
姿势涨了。。。。。
mujiok2003 2013-08-16
  • 打赏
  • 举报
回复
改进一下咯。

	printf("%d\n", (int)(4000000.0 * 0.1));

	float s = 0;
	int intpart = 0;
	for(int i=0; i< 4000000; i++)
	{
		s += (float)0.1;
	    intpart += (int)s;
		s -= (int)s;	
	}
	 printf("%d\n", (int)(s + intpart));
ken_scott 2013-08-16
  • 打赏
  • 举报
回复
涨见识了。。。。
liuduo251 2013-08-16
  • 打赏
  • 举报
回复
又涨姿势了,顶起!
边走边瞧 2013-08-16
  • 打赏
  • 举报
回复
用float做高精度计算,本身就是个错误。银行要是这么干,早都赔死了。
zhctj159 2013-08-16
  • 打赏
  • 举报
回复
两浮点数X,Y进行加减运算时,必须按以下几步执行: ①对阶,使两数的小数点位置对齐,小的阶码向大的阶码看齐。 ②尾数求和,将对阶后的两尾数按定点加减运算规则求和(差)。 ③规格化,为增加有效数字的位数,提高运算精度,必须将求和(差)后的尾数规格化。 ④舍入,为提高精度,要考虑尾数右移时丢失的数值位。 ⑤判断结果,即判断结果是否溢出。 造成这种情况的原因是对阶时的精度损失。可以想象一下最后一次加法运算时,0.1与一个大约40万的大数相加,那么它需要将阶码提升多高呀,,那么尾数就截掉了好多位、、 4楼的是个好主意!
引用 4 楼 nice_cxf 的回复:
改编译选项似乎没用 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; } 这样精度会高很多
Z-WenJiesss 2013-08-16
  • 打赏
  • 举报
回复
嗯,涨知识叻!
你怎么了熊吉 2013-08-15
  • 打赏
  • 举报
回复
非要用float累加的话,理论上这样:
int main()
{
float f = 0.1;
float sum = 0;
sum+=add(f,4000000);
cout<<sum<<endl;
return 0;
}

float add(float f,int count)
{
    if(count==1)
	return f;
    else
        return add(f,count/2)+add(f,count-count/2);
}
结果是400000 当然函数调用开销是另一回事
sky_163 2013-08-15
  • 打赏
  • 举报
回复
float 有效位只有6,7 , 要精确的当然是选择double了
jiadelin 2013-08-15
  • 打赏
  • 举报
回复
float f = 0.100001; 在浮点数后面加上个很小的小数位再去运算就好了。
qdlgdx_lsy 2013-08-15
  • 打赏
  • 举报
回复
引用 6 楼 xiangzhihappy 的回复:
[quote=引用 4 楼 nice_cxf 的回复:] 改编译选项似乎没用 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; } 这样精度会高很多
想知道为什么这样分开计算 精度会提升很多啊??求指教。。。[/quote] 顶起!!!求解释!!!!
你怎么了熊吉 2013-08-15
  • 打赏
  • 举报
回复
引用 8 楼 czarten 的回复:
[quote=引用 6 楼 xiangzhihappy 的回复:] [quote=引用 4 楼 nice_cxf 的回复:] 改编译选项似乎没用 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; } 这样精度会高很多
想知道为什么这样分开计算 精度会提升很多啊??求指教。。。[/quote] 我猜是这样: 因为float一共只有6位有效数字(10进制),如果整数部分的位数多了,相应的小数部分的位数就少了,因此当一个很大的数(如100000)去加一个很小的数(如0.1),就会形成很大的误差 用楼主的累加方式,一开始误差不大,但到后来,sum越来越大,相应的误差也就越来越大 而用这种分次累加后再相加的方式,使得相加的两数大小差距都比较小,所以每次相加产生的误差也就小[/quote] 估计直接相乘误差最小
nice_cxf 2013-08-15
  • 打赏
  • 举报
回复
引用 6 楼 xiangzhihappy 的回复:
[quote=引用 4 楼 nice_cxf 的回复:] 改编译选项似乎没用 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; } 这样精度会高很多
想知道为什么这样分开计算 精度会提升很多啊??求指教。。。[/quote] 浮点数是7位有效数字,不是小数点后7位,因此尽量要避免两个数量级差别很大的数相加,因为这样误差会变大.极端例子: float fVlaue1 =123456789; float fVlaue2 =fVlaue1+1; if (fVlaue2 ==fVlaue1) printf("same \n"); 你会发现+1根没加一样
你怎么了熊吉 2013-08-15
  • 打赏
  • 举报
回复
引用 6 楼 xiangzhihappy 的回复:
[quote=引用 4 楼 nice_cxf 的回复:] 改编译选项似乎没用 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; } 这样精度会高很多
想知道为什么这样分开计算 精度会提升很多啊??求指教。。。[/quote] 我猜是这样: 因为float一共只有6位有效数字(10进制),如果整数部分的位数多了,相应的小数部分的位数就少了,因此当一个很大的数(如100000)去加一个很小的数(如0.1),就会形成很大的误差 用楼主的累加方式,一开始误差不大,但到后来,sum越来越大,相应的误差也就越来越大 而用这种分次累加后再相加的方式,使得相加的两数大小差距都比较小,所以每次相加产生的误差也就小
qq120848369 2013-08-15
  • 打赏
  • 举报
回复
原来float误差如此之高。
xiangzhihappy 2013-08-15
  • 打赏
  • 举报
回复
引用 4 楼 nice_cxf 的回复:
改编译选项似乎没用 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; } 这样精度会高很多
想知道为什么这样分开计算 精度会提升很多啊??求指教。。。
lpcads 2013-08-15
  • 打赏
  • 举报
回复
1. 用double(到然也有误差) 2. 像#2楼一样用一些特别的数字(1.0,0.5,0.25,0.125 ……) 3. 用整型(银行系统里元、角(分)分开处理) 4. 设计出无误差的方案(至少有修正方案)。windows里的计算器的界面虽然没大变,但内核其实不断地被重写,vista起各种运算的都能精确到任意位(不会出现 1/3 * 3 != 1之类的情况了) 我记得 Bjarne Stroustrup 的书里举了一个例子,好像是海湾战争中,美军发射的一个导弹摧毁自己另一个地方的军营,原因就是因为浮点数的累积误差。。。
nice_cxf 2013-08-15
  • 打赏
  • 举报
回复
改编译选项似乎没用 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; } 这样精度会高很多
加载更多回复(3)

64,660

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧