浮点数的比较

浮点数的比较

       由于计算机中采用有限位的二进制编码,因此浮点数在计算机中的存储并不总是精确的。
例如在经过大量计算后,一个浮点型的数3.14在计算机中就可能存储成3.1400000000001,
也有可能存储成3.1399999999999,这种情况下会对比较操作带来极大的干扰(因为C/C++
中的操作是完全相同才能判定为true)。于是需要引入一个极小数eps来对这种误差进行修正。

1.等于运算符(=)

  如右图所示,如果一个数a落在了[b-eps, b+eps]的区间中时,就应当判断为a = b成立。那么eps应当取多少呢?经验表明,eps 取 KF8 是一个合适的数字——对大多数的情况既不会漏 判,也不会误判。因此可以将eps定义为常量le-8:
 const double eps = le-8;

为了使比较更加方便,把比较操作写成宏定义的形式:

#define Equ(a,b) ((fabs((a)-(b)))< (eps))  

.
  正如上面的代码,将a和b相减,如果差的绝对值小于极小量eps,那么就返回true,加上如此多的括号也是为了防止宏定义可能带来的错误。注意:如果想要使用不等于,只需要在使用时的 Equ 前面加一个非运算符“!”即可(!Equ(a, b))。于是在程序中就可以使用 Equ 函数来对浮点数进行比较了:

  #include <stdio.h>
  #include <math.h>
  const double eps = le-8;
  #define Equ(a'b) ((fabs((a)-(b)))<(epsj)
  int main () {
  	double db = 1.23;                         /
  	if(Equ(db,1.23)) {
  		printf("true’*);
  	} else{
  		printf("false");
  	}
  	return 0;
  }

       有读者可能会改写成 db = 1.23 的写法,发现同样输 true。其实像这种比较简单的比较情况下是可以忽视误差的(事实上,如果没有经过容易损失精度的计算,就不需要考虑误差,可以直接比较X但是当一个变量进行了误差较大的运算后,精度的损失就不可忽视了。例如下面的代码中,dbl和db2的精确值都应该是it,但是却输出了 false;而使用了 Equ函 数就会输出true。

  #include <stdio.h>
  #include <math.h>
  int main () {
  	double dbl = 4*asin (sqrt (2.0) / 2);
  	double db2 = 3 * asin(sqrt(3.0) / 2);
  	if(dbl == db2) {
  		printf("true”);
  	} else {
  		printf("false”);
  	return 0;
  }

       显然,由于这种误差的影响,其他比较运算符也会出现差错,因此必须进行修正。

2.大于运算符(>)

       类比下面 < .

3.小于运算符(<)

b-eps           b        b+eps

       如果一个数a要小于b,那么就必须在误差eps的扰动范围之外小于b,因此只有小于b-eps的数才能判定为小午b (也即a减b小于-eps)。

 #define Less(afb) {((a)-(b))<(-eps))

4. 大于等于算符(>=)

      类比下面小于等于。

5. 小于等于运算符(<=)

           b-eps         b        b+eps
  小于等于运算符可以理解为小于运算符和等于运算符的结合,于是需要让一个数a在误差扰动范围内能够判定其为小于或者等于b,因此小于b+eps的数都应当判定为小于等于b (也即a减b小于eps)。

#define LessEqu(arb) (((a)-(b)) < (eps))

6.圆周率Π

       圆周率Π不需要死记,因为由cos(Π) = -1可知 Π = arccos(-l)。因此只需要把 Π 写成常量 acos(-1.0)即可。

const double Pi=acos(-1.0);

把上面的所有核心部分汇总起来就是下面这些代码:

  const double eps=le-8;
  const double Pi=acos (-1,0);
  #define Equ(a,b) ((fabs((a)-(b)))<(eps))
  #define More(a'b) (((a)-(b))>(eps))
  #define Less(azb) (((a)-(b))<(-eps))
  #define MoreEqu (a,b)  ( ( (a) - (b) ) > (-eps))
  #define LessEqu(a,b) (((a)-(b))<(eps))
  

       这些部分不必死记硬背,因为懂了上面的原理之后很容易推断出来。
最后需要指出几点:

  1. 由于精度问题,在经过大量运算后,可能一个变量中存储的 0 是个很小的负数,这时如果对其开根号sqrt,就会因不在定义域内而出错。同样的问题还出现在asin(x)当x存放+1、acos(x)当x存放-1时。这种情况需要用eps使变量保证在定义域内。
  2. 在某些由编译环境产生的原因下,本应为0.00的变量在输出时会成-0.00。这个问题是编译环境本身的bug,只能把结果存放到字符串中,然后与-0.00进行没较,如果比对成功,则加上eps来修正为0.00。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Drdajie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值