C语言中double与float的比较

目录

目录

1.问题发现

2.原因分析

3.问题"解决"

4.何为精度损失

4.浮点数的正确比较方法

        1.问题再现

        2.最终解决方案


1.问题发现

   在编程中我们时常要判断两个数是否相等,我们先来看一段简单的代码,猜猜运行结果是什么:

#include<stdio.h>
int main()
{
	float t=3.197;
	if (t == 3.197)
	{
		printf("hehe");
	}
	else
		printf("haha");
	return 0;
}

你是否也和我一样认为t恒等于3.197,先别急,让编译器来告诉我们答案:

3c7f680042194d68af33e4de26505caa.png

2.原因分析

为什么会打印“haha呢”,难道编译器出错了?实则不然。

其实,当我们写下小数时,编译器会自动把它当做double类型:

701d9b8189d7467c8884077d9df2f659.png

而由于我们的t是float类型,占4个字节,而double类型占8个字节,由于浮点数在内存中以IEEE754标准进行存放,就会出现精度损失(下面讲解)的问题。当float与double类型进行比较时,不同类型存放数据的精度不同,编译器就会认为不相同,打印输出“haha”。

3.问题"解决"

那么,该如何解决这个问题使最后程序运行的结果为我们想要的答案?你可能会想到以下几种方法:

1.将变量t定义为double类型

double t=3.197;

2.在小数后加f使其变为float类型

float t=3.197f;

3.使用()运算符将3.197强制类型转化为double类型

float t =(float)3.197;

通过以上改进,程序成功输出“hehe”:

2770dbba1efa46b1a5bfe3bde9912eea.png

4.何为精度损失

无论单精度浮点数还是双精度浮点数,在计算机中都是以IEEE754的方式来进行存放。

我们知道float占32个比特位,其在计算机内存中存放的格式如下:

8939f8e7d28c48f18219583dce35da7f.png

而double占64个比特位,其在计算机内存中存放的格式如下:

24b5443a89fe463d9b557ee44906f51b.png

其中符号位1表示负,0表示正,阶码部分用指数部分的移码表示,而尾数部分正是决定精度的重要部分,对于不同的类型,其能表示的精度不同,从上图可看出double类型表示比用float类型表示精度更高。

当小数转化为二进制时,往往会出现无限位数的情况,而数据显然不能无限存储,这时计算机就要进行截取,而截取的位数就取决于尾数的位数

正因如此,计算机常常出现精度丢失的问题。这就是为什么上述问题会引起歧义的原因。

此外,当double隐式转化为float类型时,由于位数不同,计算机也会实行截断,造成精度丢失。值得注意的是,一个浮点数的的整数部分存储在尾数位置的高位,因此发生截断时整数部分经常可能会出现错误,而小数部分相较于整数部分影响甚微,在一些场合下可以忽略不计

4.浮点数的正确比较方法

        1.问题再现

        虽然通过我们的修改程序成功输出了hehe,但是我们的问题真的解决了吗?上例我们只是将两个以字面值出现的浮点数进行比较,但如果出现的是一个表达式呢?我们来看以下代码:
 

#include<stdio.h>
int main()
{
	double t1 = 1.0;
	double t2=  0.1;
	if (t2 == t1-0.9)
	{
		printf("hehe");
	}
	else
		printf("haha");
	return 0;
}

        按照我们之前的分析,等号两边的类型和值一样,因此会打印hehe。但是结果不以为然,运行程序,程序打印haha:

         为什么会出现错误呢?其实原因依旧逃不开精度(万恶之源)。我们通过上面分析知道浮点数存储时会出现精度丢失的问题,例如t2实际存储的并非严格的0.1,而是一个极为接近0.1的数。并且,在t1-0.9这个表达式中,0.9也是存在精度损失的,最终计算的结果也并非是0.1。因此,最后二者不等,输出haha。我们也可以通过编写程序来验证:

#include<stdio.h>
int main()
{
	double t1 = 1.0;
	double t2=  0.1;
	printf("%.50lf\n", t1 );
	printf("%.50lf\n", t2);
	printf("%.50lf\n", t1 - 0.9);
	return 0;
}

我们可以直观的看到,由于t1小数部分为0,因此不存在精度损失的问题。但是t2和t1-0.9就不一样了由于精度损失的问题,t2存储的内容并非为0.1,t1-0.9的结果也并非为0.1。我们可以看出,二者尽管非常接近0.1,但还是略有不同,依旧逃离不了编译器的法眼。

         2.最终解决方案

        "天啊,这也太折磨人了吧",看到这里,会不会有人发出感叹呢(好奇^v^)。由上分析,我们得出的结论是我们不能单纯用==来进行浮点运算的比较。我们发现t2和t1-0.9的差距十分的小,因此我们可以设定一个非常小的阈值,通过判断二者差的绝对值是否小于阈值来判断二者是否相等。其中,对于阈值,在float.h头文件中有定义,当然我们也可以自己设定阈值

#include<stdio.h>
#include<float.h>
#include<math.h>
int main()
{
	double t1 = 1.0;
	double t2=  0.1;
	//错误的,不能直接比较
	//if((t1-0.9)==t2)
	//{
	//	printf("hehe");
	//}

	//正确做法,通过阈值比较,DBL_EPSILON为浮点数阈值,使得1.0加上后不等于1.0的最小值
	if (fabs((t1 - 0.9) - t2) < DBL_EPSILON)   
	{
		printf("hehe");
	}
	else
	{
		printf("haha");
	}
	return 0;
}

对于float.h定义的阈值,其定义是使得1.0加上后的值不等于1.0的最小值。换句话说就是两个数差值如果小于这个最小值,两个数就相等。我们可以由此进行浮点数的比较。


以上,就是本期的所有内容了

制作不易,能否点个赞再走呢!

  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

忆梦初心

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

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

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

打赏作者

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

抵扣说明:

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

余额充值