C/C++ 浮点数比较问题

C/C++ 浮点数比较是否相等时,有些细节必须要意识到,例如下面的代码:

#include <iostream>

using namespace std;

void main()
{
    double epsilon=0.001;
    double d1=2.334;
    double d2=2.335;

    cout << "epsilon is: " << epsilon << endl;
    cout << "d2-d1 is: " << d2-d1 << endl;

    if ((d2 - d1) == epsilon){
        cout << "Equal!" << endl;
    }
    else{
        cout << "Not equal!" << endl;
    }
}

其输出结果实际上是:

epsilon is: 0.001
d2-d1 is: 0.001
Not equal!

为何会这样呢?让我们稍微调整一下上面的代码:

cout<<"epsilon is: "<< setprecision(20) << epsilon<<endl;
cout<<"d2-d1   is: "<< setprecision(20) << d2-d1 <<endl;

可以得到:

epsilon is: 0.00100000000000000000
d2-d1 is: 0.00099999999999988987

这里引出一条C/C++中非常重要的原则:

The important rule to remember is that powers of two and integer multiples thereof can be perfectly represented. everything else is an approximation.

直译过来意识就是,除了可以表示为2的幂次以及整数数乘的浮点数可以准确表示外,其余的数的值都是近似值。

例如,1.5可以精确表示,因为1.5 = 3*2^(-1);然而3.6却不能精确表示,因为它并不满足这一规则。

所以在比较的时候需要用一个很小的数值来进行比较。(二分法的思想)当二者之差小于这个很小的数时,就认为二者是相等的了,而不能直接用== 或!=比较。这个很小的数,称为精度。

精度由计算过程中需求而定。比如一个常用的精度为1e-6.也就是0.000001.
所以对于两个浮点数a,b,如果要比较大小,那么常常会设置一个精度
如果fabs(a-b)<=1e-6,那么就是相等了。 fabs是求浮点数绝对值的函数。
类似的 判断大于的时候,就是if(a>b && fabs(a-b)>1e-6)。

判断小于的时候,就是if(a<b&&fabs(a-b)>1e-6)

例如:

  1. #include<cstdio>
  2. #include<cmath>
  3. const double esp = 1e-6;
  4. int main()
  5. {
  6. double a,b;
  7. scanf( "%lf %lf",&a,&b);
  8. if( fabs(a - b) <= esp)
  9. printf( "ok\n");
  10. else
  11. printf( "no\n");
  12. return 0;
  13. }

发现只有当浮点数作差时需要用到eps精度




PS:

单精度数7位有效数字。 (float)
双精度数16位有效数字。(double)

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

单精度浮点数的实际有效精度为24位二进制,这相当于 24*log102≈7.2 位10进制的精度,所以平时我们说“单精度浮点数具有7位精度”。(精度的理解:当从1.000...02变化为1.000...12时,变动范围为 2-23,考虑到因为四舍五入而得到的1倍精度提高,所以单精度浮点数可以反映2-24的数值变化,即24位二进制精度)

单精度数7位有效数字。

双精度数16位有效数字。 
浮点数取值范围: 
负数取值范围为 -3.4028235E+38 到 -1.401298E-45,正数取值范围为 1.401298E-45 到 3.4028235E+38。 
双精度数取值范围: 
负值取值范围-1.79769313486231570E+308 到 -4.94065645841246544E-324,正值取值范围为 4.94065645841246544E-324 到 1.79769313486231570E+308。



猜你喜欢

转载自blog.csdn.net/slience_646898/article/details/80954464