• 浮点数比较大小 不及格的程序员


    在数学运算当中经常会涉及到判断两个数是否相等的情况 
    对于整数很好处理 A==B这样的一个语句就可以解决全部的问题 
    但是对于浮点数是不同的 

    首先,浮点数在计算机当中的二进制表达方式就决定了大多数浮点数都是无法精确的表达的 
    现在的计算机大部分都是数字计算机,不是模拟机,数字机的离散化的数据表示方法自然无法精确表达大部分的数据量的。 

    其次计算机浮点数的精度在单精度float类型下,只有7位,在进行浮点运算的时候,这个精度往往会导致运算的结果和实际期望的结果之间有误差 

    因为前两个原因,我们很难用 A==B来判定两个浮点数是否相同 

    很自然,我们可以想到 fabs(A-B) < epsilon 这样的一种判别方法 
    但是这种判别方法稳妥吗? 
    它也不稳妥。 

    首先, epsilon是一个绝对的数据,也就是误差分析当中说说的绝对误差 
    使用一个固定的数值,对于float类型可以表达的整个数域来说是不可以的 
    比如epsilon取值为0.0001,而a和b的数值大小也是0.0001附近的,那么显然不合适 
    另外对于a和b大小是10000这样的数据的时候,它也不合适,因为10000和10001也可以认为是相等的呢 
    适合它的情况只是a或者b在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, relError ) 
    {   

       if (fabs(a)<fabs(b)) return ( fabs((a-b)/a)    >     relError ) ? true     :    false;   
         return ( fabs((a-b)/b)     >     relError ) ? true     :    false; 
    }; 

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

    这样才相对完整  

    参照MSDN定义:

    /* Compile options needed: none. Value of c is printed with a decimal
    point precision of 10 and 6 (printf rounded value by default) to
    show the difference
    */
    #include 
    // Define your own tolerance
    const double EPSILON = 1.00e-07;
    const float   FLT_EPSILON  = 1.192092896e-07F;

    const double  DBL_EPSILON = 2.2204460492503131e-016;

    #define FLOAT_EQ(x,v) (((v - EPSILON) < x) && (x <( v + EPSILON)))
    int main()
    {
        float a, b, c;
        a = 1.345f;
        b = 1.123f;
        c = a + b;
        // if (FLOAT_EQ(c, 2.468))   // Remove comment for correct result
        if (c == 2.468)              // Comment this line for correct result
        printf("They are equal.\n");
      else
        printf("They are not equal! The value of c is %13.10f,or %f",c,c);
    }

    1.基本概念

    实型变量

    又称浮点型变量,是指用来存储实型数值的变量,其中实型数值是由整数部分和小数两个部分组成的。

    实型变量根据实型的精度可分为3种类型,分别是单精度类型、双精度类型和长双精度类型,其对应的关键字分别为float、double、long double。

    单精度类型

    使用的关键字是float,在内存中占4个字节(32位),取值范围为-3.4x10^-38~3.4x10^38。

    双精度类型

    使用的关键字是double,在内存中占8个字节(64位),取值范围为-1.7x10^-308 ~ 1.7x10^308。

    长双精度类型

    使用的关键字是long double,在内存中占8个字节(64位),取值范围为-1,7x10^-308 ~ 1.7x10^308。

    使用sizeof运算符,很容易知道任何数据类型的长度。

    如sizeof(int)的值是一个整型的长度等等。

    运算符用于执行程序代码运算,会针对一个以上操作数项目来进行运算。

    由于不同的编译器为变量分配的内存空间不同,所以没有明确规定实型变量在内存中到底占多少个字节,但却有float型的长度小于等于double型长度,同时double型长度又小于等于long double型长度的规定。

     2.浮点数在内存中的表示

    单精度类型变量,其有效位数为7位,如果大于7位,以后的位都是无效数字;

    双精度类型变量,其有效位数为16位,但系统规定,小数点后最多可以保留6位,其余部分四舍五入。

    float和double的精度是由尾数的位数来决定的。

    浮点数在内存中是按科学计数法来存储的,其整数部分始终是一个隐含着的“1”,

    由于它是不变的,故不能对精度造成影响。

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

    也即float的精度为6~7位有效数字;

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

    C/C++中浮点数的表示遵循IEEE754标准。

    一个浮点数由三部分组成:符号位S、指数部分E(阶码)以及尾数部分M(如下)。

      Floating

    S--------E-------M

    1位-----8位-----23位

      Double

    S--------E-------M

    1位-----11位----52位

    浮点数的精度取决于尾数部分。尾数部分的位数越多,能够表示的有效数字越多。

    单精度数的尾数用23位存储,加上默认的小数点前的1位1,2^(23+1) = 16777216。因为 10^7 < 16777216 < 10^8,所以说单精度浮点数的有效位数是7位。

    由于组成实型变量的存储单元有限,所以能提供的有效数字也是有限的,因有效位外的数字将被舍去,这样就会造成误差。

    为了避免这样的误差,尽量在给变量赋值时,将赋的值控制在有效位以内且不要将一个很大的数和一个很小的数进行加减计算。

  • 相关阅读:
    uva10341
    android_定义多个Activity及跳转
    阿里巴巴2014年校园招聘(秋季招聘)在线笔试--測试研发project师
    关于程序猿的几个阶段!
    【Spring】Spring学习笔记-01-入门级实例
    感知器算法(二分类问题)
    Ubuntu14.04下安装ZendStudio10.6.1+SVN出现Failed to load JavaHL Library
    EF架构~关系表插入应该写在事务里,但不应该是分布式事务
    EF架构~在global.asax里写了一个异常跳转,不错!
    EF架构~为导航属性赋值时ToList()的替换方案
  • 原文地址:https://www.cnblogs.com/ioriwellings/p/4276045.html
Copyright © 2020-2023  润新知