浅谈浮点数的大小比较

更新日期: 2021.8.3
本文声明: 由于小小白水平有限,内容难免有错误和不准确之处,望读者批评指正!

1.浮点数为什么不能用 == 进行比较

《深入理解计算机系统》中这样说过,浮点数普遍的作为实数运算的近似值的计算,是很有用的。这里说的是实数的近似值的计算,所以浮点数在计算机中其实是一种不精确的表示。它存在舍入误差。IEEE浮点标准用符号,尾数和阶码将浮点数的位表示划分为三个字段,单精度为32位,双精度为64位,因为表示方法限制了浮点数的范围和精度,浮点运算只能近似的表示实数运算。而 == 表示的是在计算机中的内存表示完全一样,因此使用 == 来表示两个浮点数的相等就会出现问题。

下面举两个简单的实例:
1.

#include<iostream>
using namespace std;
int main()
{
    
    
    	
    double a = (double)1.0;
    double b = (double)0.0;
    for (int i=0; i<10;++i)
        b += 0.1;
    if (a == b)
        cout<<"true"<<endl;
    else
        cout<<"false"<<endl;
	return 0;
 } 

在这里插入图片描述

10个十进制的0.1相加后,就“不等于”浮点数1.0

#include <iostream>
#include<cmath>
using namespace std;
int main()
{
    
    
	int a=3;
	if(sqrt(3)*sqrt(3)<3) puts("false");
	
	return 0;
}

在这里插入图片描述
根号3×根号3结果竟然小于3。

2.浮点数比较方法

由于计算机中采用的是有限位的二进制编码,所以浮点数在计算机中的存储不总是精确的,这种情况下会对比较操作带来极大的干扰,所以我们需要引入一个极小数 eps 来对这种误差进行修正,判断一下这两个数的绝对值,即在数轴上的距离是否小于某个精度 eps。

判定相等

如果一个数 a 在 [b-eps, b+eps] 的区间中时,就应当判断为 a==b 成立.
在这里插入图片描述
经验表明, eps 取 10-8是一个合适的数字——对大多数的情况既不会漏判,也不会误判。因此,我们可以将 eps 定义为常量 1e-8

const double eps = 1e-8;

为了使比较更加方便,我们可以将比较写成宏定义的形式:

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

使用不等于,只需要在使用时的 Equ 前面加一个非运算符 “ ! ” 即可( ! Equ(a, b))。此时在程序中就可以使用 Equ 函数来对浮点数进行比较了。
下面让我们对之前的例子重新进行比较:

#include<iostream>
using namespace std;
#define eps 1e-8
#define Equ(a, b) ((fabs((a) - (b))) < (eps))
int main()
{
    
    

    double a = (double)1.0;
    double b = (double)0.0;
    for (int i = 0; i < 10; ++i)
        b += 0.1;
    if (Equ(a, b))
        cout << "true" << endl;
    else
        cout << "false" << endl;
    return 0;
}

运行结果:
在这里插入图片描述
是不是觉得好神奇!
在这里插入图片描述

判定大于

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

宏定义:

#define More(a, b) (((a) - (b)) > (eps))

在这里插入图片描述

判定小于

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

宏定义:

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

在这里插入图片描述

同理可以写出判定大于等于和小于等于分别为:

大于等于
在这里插入图片描述

#define MoreEqu(a, b) (((a) - (b)) > (-eps))

小于等于
在这里插入图片描述

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

拓展:
判断一个浮点数是否为0是通过下面的方法来实现的。
浮点数因为存储形式的原因不能直接和0值比较,判断一个浮点数是否等于0时:
fabs(x)<=1e-6 就是认为是0了

float,double分别遵循R32-24,R64-53的标准。
所以float的精度误差在1e-6;double精度误差在1e-15
判断一个单精度浮点数:则是if( fabs(a) <= 1e-6);//fabs()是对浮点数取绝对值
判断一个双精度浮点数:则是if( fabs(b) <= 1e-15 );

关于想知道浮点数在内存如何存储,可以参考小白的一篇文章:
【C语言进阶】深度剖析数据在内存中的存储

猜你喜欢

转载自blog.csdn.net/weixin_53306029/article/details/119352531