Chinaunix

标题: 关于c语言中的float和double运算误差问题 [打印本页]

作者: faku    时间: 2018-01-16 15:08
标题: 关于c语言中的float和double运算误差问题
操作系统:rhel7.0
gnu c lib: 2.17
项目是一个代理服务,将服务器的数据转发给客户,从客户获取数据转发给服务器, 除了glibc没有用到其它库
有这样一个问题:
服务器发送过来一个字符串“2277.24”, 代理将该串  atof(“2277.24”)  插入数据库,然后  atof(“2277.24”)*100  转发给客户端(代理运算时只处理整数,因此需要乘以100),客户端再除以100
问题来了, 数据库里是2277.24,客户端将该数字除以100后,得到的是2277.23

其实有一个很简单的方法测试该问题
printf(“%d\n”, (int)(2277.24*100)); //2277.24是double, 输出227723
printf(“%d\n”, (int)(2277.24f*100)); //2277.24f是float,输出227724

printf(“%d\n”, (int)(atof("2277.24")*100)); //atof("2277.24")返回的是double, 输出227723
printf(“%d\n”, (int)((float)atof("2277.24")*100)); //转换成float输出227724


我知道浮点数运算有误差,但最近项目里出现误差的客户端实在太多,我想用gmp或mpfr来解决精度问题, 但总感觉没有必要用一个庞大的库来解决这个问题

请高手指点






作者: sxcong    时间: 2018-01-16 15:14
*100 再/100小了。
你要的是两位小数,需要多留出一位进行四舍五入。所以应该*/1000。另外,c++ 有两个函数:ceil(),floor(),配合着用进行取整。

在一个项目中,我用了一个笨方法:先把float按位转成string,再根据最后一位数进行四舍五入。不想费脑子的话也可跟我学。

作者: dorodaloo    时间: 2018-01-16 15:26
回复 2# sxcong

*/1000
这是一个好主意
作者: jason680    时间: 2018-01-16 15:58
回复 1# faku

连printf的打印都知四舍五入...
你怎能...

  double a=2277.24;
  printf("%.2f\n", a*100);
  printf("%.12f\n",a*100);
  printf("%d  \n",(int)(a*100));
  printf("%d  \n",(int)(a*100+.5));

227724.00
227723.999999999971
227723  
227724  

作者: sxcong    时间: 2018-01-16 16:04
回复 4# jason680
内存数据不是想象的,比如1.5,很可能是1.499999999,这样,如果四舍五入保留整数,1.5应该是2,但这种情况下就是1,因为小数点后第一位是4。 浮点数在内存这个表现还是很头痛的。


作者: pandaiam    时间: 2018-01-16 16:05
前段时间刚碰到
系统内部是double  1.23  ,对方需要 乘以10000 ,也就是 12300

int32(d_price*10000.0 + 0.5)
作者: jason680    时间: 2018-01-16 16:09
回复 5# sxcong

what's your example ...

  double b=3/2.0;

  printf("%d  \n",(int)(b));
  printf("%d  \n",(int)(b+.5));

1  
2  



作者: bruceteen    时间: 2018-01-16 16:36
2277.24 实际值 2277.239999999999781721271574497222900390625
2277.24*100 实际值 227723.99999999997089616954326629638671875

2277.24f 实际值 2277.239990234375
2277.24f*100 实际值 227724.0

作者: q1208c    时间: 2018-01-17 12:19
为啥数据库里要存小数?

如果是钱,直接 x 1000存成 分以下的整数就好了。
作者: dorodaloo    时间: 2018-01-17 19:45
回复 9# q1208c

为啥数据库里要存小数?
大牛请教一个问题
数据库里为啥不能不存小数?
这是什么原因?
该怎么理解呢?
作者: jason680    时间: 2018-01-17 22:27
本帖最后由 jason680 于 2018-01-17 22:30 编辑

回复 10# dorodaloo

可存,有偏差...

[数学]
0.1 * 0.1 = 0.01
0.01 * 10 = 0.1
0.1 * 0.1 = 0.01
0.01 * 10 = 0.1
...
反复算100次,会不会改变?

[XX程序]
double num = 0.1;
num = num * num;  // num=0.01
num = num * 10;    // num = 0.1
num = num * num;  // num=0.01
num = num * 10;    // num = 0.1
...
反复算100次,num会不会改变?


作者: q1208c    时间: 2018-01-18 12:56
回复 10# dorodaloo

目前所有的系统在存储浮点数的时候,都是按 IEEE-754 的方式处理。
所以,一定有误差。
而整数的存储,在很多系统中是没有误差的。
我个人的建议,最好不用浮点数。
除非是必须是浮点数的内容。



作者: yulihua49    时间: 2018-01-18 21:50
本帖最后由 yulihua49 于 2018-01-18 22:06 编辑
faku 发表于 2018-01-16 15:08
操作系统:rhel7.0
gnu c lib: 2.17
项目是一个代理服务,将服务器的数据转发给客户,从客户获取数据转 ...

进来的数乘100进数据库,以分为单位。出库时转成元。永远不错。
我们的统计系统都是以分为单位的整数处理。报表时才转成元。
记住,金钱不要用浮点数!
整数不够的就用int64!要不2000多万元就溢出了。
这个统计系统工作了20多年没有发生错误。
系统计算除法必须按照四舍五入,开发了四舍五入的整数运算函数(在分一级四舍五入是合乎财务规定的)。一般的整数除法只舍,是不可以的。
浮点数除法也不是四舍五入,而是科学舎入法,即四舍六入,5看前边,奇入偶舍。平均舍入误差最小法。





欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2