</pre>源程序如下:<p></p><p></p><pre name="code" class="cpp">#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
float a,b,c,x1,x2,p,q,dish;
scanf("%f%f%f",&a,&b.&c);
dish=b*b-4*a*c;
if(fabs(a)<=1e-7)
printf("不是一元二次方程")
else if(fabs(dish)<=1e-7)
printf("有两个等根:%d",(-b/2a));
else if(dish>1e-7)
{x1=(-b+sqrt(dish))/2a;
x2=(-b-sqrt(dish))/2a;
printf("两个实根:x1=%f,x2=%f",x1,x2);}
else
{p=-b/2a;
q=sqrt(-dish)/2a;
printf("两个复根为:%d+%di,%d-%di",p,q,p,q);}
return 0;
}
其中学习了两个知识点,abs函数和fabs函数以及对于浮点数不能直接进行相等比较,也不能与0比较相等。
一、abs函数与fabs函数:
abs函数的原型为 int abs(int x),而fabs的原型为double fabs(double x),为什么要把这两个函数的原型写出来,是因为abs函数是求整型数据的绝对值,fabs函数是求浮点数的绝对值,曾经想用abs函数求浮点数的绝对值,例如 定义float变量,输入1.2,输出:printf("%d",abs(i));实际结果是:1.
如下:
#include "stdio.h"
#include "math.h"
main()
{float i,j,k1,k3;
i=-1.02;
k1=abs(i);
j=-2.03;
k3=fabs(j);
printf("abs=%.2f
fabs=%.2f
",k1,k3);
}
输出结果:k1=1.00,k3=2.03。
可见abs是取绝对值后再取整,而fabs是取绝对值。vc中对fabs()定义有3种:float fabs(),double fabs(),long double fabs()
至于最新的gcc中对fabs的定义暂时不知道,请知道的朋友指点。
二、浮点数的相等比较
谭浩强的书中强调浮点数不能适用“==”来进行大小比较,因为浮点数在计算和存储时会有一些微小的误差。
常规方法都是指定一个足够小的常量,然后使用fabs(a-b)<足够小的常量,那么认为a==b;
我认为这个足够小的常量不是说随便指定的,需要参照浮点数a,b的量级来进行指定,并不是说10000.0和10001.0进行相等比较时,你指定一个1e-6的常量,那么这就失去意义了。
下面转载一篇文章,是使用相对误差和绝对误差结合对浮点数是否相等来进行判断的
相对误差:relative tolerance文章中的relerror既是。绝对误差:absolute error,文章中abserror既是。
对于整数很好处理 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定义:
<strong><span style="color:#3366ff;">/* 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</span></strong>
<strong><span style="color:#3366ff;">const double EPSILON = 1.00e-07;</span></strong>
<strong><span style="color:#3366ff;">const float FLT_EPSILON = 1.192092896e-07F;</span></strong>
<pre class="code" name="code" style="white-space: pre-wrap; word-wrap: break-word;"><p><strong><span style="color:#3366ff;">const double DBL_EPSILON = <span style="font-size: 13px;">2.2204460492503131e-016;</span></span></strong></p><span style="color:#333333;"> </span><p style="color: rgb(51, 51, 51);"><span style="font-size: 13px;"></span></p><span style="color:#333333;"> </span><span style="color: rgb(51, 51, 51); font-size: 13px;"> </span><p style="color: rgb(51, 51, 51);"> </p><span style="color:#333333;"> #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. "); else printf("They are not equal! The value of c is %13.10f,or %f",c,c);</span>