Chinaunix首页 | 论坛 | 博客
  • 博客访问: 608753
  • 博文数量: 72
  • 博客积分: 1177
  • 博客等级: 少尉
  • 技术积分: 856
  • 用 户 组: 普通用户
  • 注册时间: 2011-12-23 23:03
文章分类

全部博文(72)

文章存档

2015年(13)

2014年(5)

2013年(7)

2012年(39)

2011年(8)

分类: C/C++

2014-12-30 10:27:40

浮点数在内存中的存储机制和整型数不同,其有舍入误差,在计算机中用近似表示任意某个实数。具体的说,这个实数由一个整数或定点数(即尾数)乘以某个基数(计算机中通常是2)的整数次幂得到,这种表示方法类似于基数为10的科学记数法。所以浮点数在运算过程中通常伴随着因为无法精确表示而进行的近似或舍入。但是这种设计的好处是可以在固定的长度上存储更大范围的数。
1、将字符串转换为float、double过程存在精度损失,只是float、double各自损失的精度不相同而已
    std::stringstr="8.2";
    floatcc=atof(str.c_str());  //cc的值为8.1999998
    std::stringstr="8.2";
    doublecc=atof(str.c_str());  //cc的值为8.1999999999999993
2、将float、double转换为字符过程中可能存在精度损失,但是通过%.8lf可以规避,因此建议在系统开发过程中设计到字符转换建议采用double类型,精度设置为%.8lf即可
(1)float小数点前后加起来有效数字只有6位。当给定的float有效数在6位以内转换为字符不会丢失精度,当有效位数大于6位就会存在精度丢失
    //精度没有丢失
    charbuf[100]={'\0'};
    floataa=8000.25;
    sprintf(buf,"%f",aa);   //8000.250000
    //精度没有丢失
    charbuf[100]={'\0'};
    floataa=8.00025;
    sprintf(buf,"%f",aa);   buf = 8.000250
    //精度丢失,存在误差
    charbuf[100]={'\0'};
    floataa=8000.251;
    sprintf(buf,"%f",aa);  //8000.250977
    //精度丢失,存在误差此时使用.8lf也无效
    charbuf[100]={'\0'};
    floataa=8000.251;
    sprintf(buf,"%.8lf",aa);//8000.25097656
    (2)double小数前后加起来的有效数字只有16位,当给定的double有效数在16位以内转换为字符串不会丢失精度,当有效位数大于16位湖影公寓存在精度丢失
    存在误差
    charbuf[100]={'\0'};
    doubleaa=121.437565871234012;
    sprintf(buf,"%.20lf",aa);//121.43756587123401000000
    //没有误差
    charbuf[100]={'\0'};
    doubleaa=8000.256165;
    sprintf(buf,"%.8lf",aa);
    std::cout< 3 浮点数的相等比较

    整数的相等比较可以直接使用==来判断,但是浮点数的比较不能简单的用==来比较。用"=="来比较两个double应该相等的类型,返回真值完全是不确定的。计算机对浮点数的进行计算的原理是只保证必要精度内正确即可。我们在判断浮点数相等时,推荐用范围来确定,若x在某一范围内,我们就认为相等,至于范围怎么定义,要看实际情况而已了,float,和double 各有不同。那怎么对浮点数是否相等进行判断呢?-- 利用差值的绝对值的精度来判断。具体就是:f1和f2是两个浮点数,precision是我们自己设置的精度,比如1e-6。则可以用 fabs(f1-f2)<=precision 来判断f1和f2是否相等。如果要求更高的精度,则把precision定得更小就行了。


    这个方法还存在问题:首先,precision是一个绝对的数据,也就是误差分析当中所说的绝对误差,使用一个固定的数值,对于float类型可以表达的整个数域来说是不可以的。比如precision取值为0.0001,而f1和f2的数值大小也是0.0001附近的,那么显然不合适。另外对于f1和f2大小是10000这样的数据的时候,它也不合适,因为10000和10001也可以真伪是相等的呢。适合它的情况只是f1或者f2在1或者0附近的时候。

    解决方法:既然绝对误差不可以,那么自然的我们就会想到了相对误差。
    bool IsEqual(float a, float b, float relError )
    {
        return ( fabs ( (a-b)/a ) < relError ) ? true : false;
    }

    这样写还不完善,因为是拿固定的第一个参数做比较的,那么在调用IsEqual(a, b, relError ) 和 IsEqual(b, a, relError ) 的时候,可能得到不同的结果。同时如果第一个参数是0的话,就有可能是除0溢出。这个可以改造:把除数选取为a和b当中绝对数值较大的即可
    bool IsEqual(float a, float b, float relError )
    {
        if (fabs(a)>fabs(b)) return ( fabs((a-b)/a) > relError ) ? true : false;
        else return (fabs( (a-b)/b) > relError ) ? true : false;
    }

    使用相对误差就很完善吗?也不是。当a和b都为0时,将出现除数为0的情况;并且在某些特殊情况下,相对误差也不能代表全部。比如在判断空间三点是否共线的时候,使用判断点到另外两个点形成的线段的距离的方法的时候,只用相对误差是不够的,因为线段距离可能很段,也可能很长,点到线段的距离,以及线段的长度做综合比较的时候,需要相对误差和绝对误差结合的方式才可以。相对完整的比较算法应该如下:
    bool IsEqual(float a, float b, float absError, float relError )
    {
        if (a==b) return true ;
        if (fabs(a-b)         if (fabs(a)>fabs(b)) return (fabs((a-b)/a>relError ) ? true : false;
        else return (fabs((a-b)/b>relError ) ? true : false;
    }

阅读(2406) | 评论(0) | 转发(0) |
0

上一篇:PLY文件

下一篇:关于typedef的用法总结

给主人留下些什么吧!~~