IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言浮点数,布尔类型以及NULL与0比较 -> 正文阅读

[C++知识库]C语言浮点数,布尔类型以及NULL与0比较

一.浮点数与0比较

1.现象及原因

相信很多初学C语言的朋友一定犯过这样的错误:

	double x = 1.0;
	double y = 0.9;
	if (x - y == 0.1) {
		printf("You can see me!\n");
	}
	else {
		printf("oops!\n");
	}

理论上,1.0 - 0.9 == 0.1是毫无疑问的,但是当我们执行这段程序的时候却发现和我们预计的恰好相反,程序输出了 oops!
原因是:浮点数在计算机中存储时会有精度损失!
让我们来看一下,上例子x-y在计算机中存储结果是多少:
在这里插入图片描述
没错,计算机存储的是这样一串非常接近0.1的数字。
于是,我们得出结论:
千万不可用“==”进行浮点数的比较!!

2.解决方案

既然如此,那我们怎么进行浮点数的比较呢~
很简单,我们只需控制一个精度,当误差在精度范围内时,我们就可以认为两个浮点数相等。
就像这样:

#define EPS 0.000001	#自定义精度
int main()
{
	double x = 1.0;
	double y = 0.9;
	if (x - y > 0.1 - EPS && x - y < 0.1 + EPS) {
		printf("You can see me!\n");
	}
	else {
		printf("oops!\n");
	}
	return 0;
}

结果就符合我们的预期了。其中EPS为我们自定义精度,可根据实际需要自定义。
我们也可以使用系统帮我们定义的精度:DBL_EPSILON 或 FLT_EPSILON。只需引入float.h的头文件即可。

#include <stdio.h>
#include <float.h>
int main()
{
	double x = 1.0;
	double y = 0.9;
	if (x - y > 0.1 - DBL_EPSILON && x - y < 0.1 + DBL_EPSILON) {
		
		printf("You can see me!\n");
	}
	else {
		printf("oops!\n");
	}
	return 0;
}

这样的话,浮点数和0比较,就比较简单了。

#include <stdio.h>
#include <float.h>
#include <math.h>
int main()
{
	double x = 0.0000000000001;
	if (fabs(x) < DBL_EPSILON) {	//fabs(x): 求x的绝对值
		
		printf("You can see me!\n");
	}
	else {
		printf("oops!\n");
	}
	return 0;
}

只需要将所判断的数夹在-精度到+精度之间就可以了,这里使用绝对值函数fabs()可使代码更为简洁。
这里还有个小细节需要注意:

if (fabs(x) <= DBL_EPSILON)	// 需不需要 '=' ?

让我们来看系统对于DBL_EPSILON的定义:

#define DBL_EPSILON     2.2204460492503131e-016 /* smallest such that 1.0+DBL_EPSILON != 1.0 */

这是系统对于DBL_EPSILON的定义,我们要理解 smallest such that 1.0+DBL_EPSILON != 1.0的含义:
1.0加上大多浮点数都不等于1.0,但DBL_EPSILON是最小的那个。换言之,DBL_EPSILON是能使0.1+DBL_EPSILON被计算机识别的不等于0.1的最小值。
所以,如果我们加上了=号,上例中考虑极端情况,当x==DBL_EPSILON时,x是x+0 != 0表达式成立的最小值,也就是说,这时候x != 0 ,与条件矛盾。
结论:当使用系统精度进行浮点数判断是,应该用’>‘或’<’,而不要使用’>=‘或’<=’。

二. 布尔类型与0比较

C语言一度被认为是没有真正的布尔类型的,一般使用0表示假非0表示真,但最新标准C99中引入了布尔类型,需引入stdbool.h即可使用。
我们看定义:

#define bool	_Bool
#define false	0
#define true	1

其中_Bool是C99标准的关键字,表示布尔类型。
我们可以看到,false定义为0, true为1。
看以下3份代码:

	bool flag = true;
	if (flag){}			//1
	if (flag == true){} //2
	if (flag != 0){}	//3

当我们进行布尔类型判断时,应使用那种写法较好呢?
答案是第一种!
因为布尔类型本身就是真假结果,而if判断正好判断的是真假结果,所以无需画蛇添足写成 flag==true,更不要写成flag!=0这样还会让人误解flag为整形变量。
结论:
使用布尔值判断时应使用如下格式:

	bool flag = false;
	if (flag)
	{ }
	else 
	{}
	//或者
	if (!flag)
	{ }
	else 
	{ }

三. NULL 与 0 比较

NULL是C语言的空指针,一般指针变量初始化时可以赋值为NULL。
我们来看NULL的定义:

#define NULL    ((void *)0)

0强制转化为void*类型。
这里要正确的理解强制类型转化。
比如想通过字符串"123456"得到整形123456,你可千万不能写出int i = (int)"123456"这样的代码来,因为强制类型转化只是对于同一个二进制序列换了一种解释方式,而原二进制序列在内存中的存储形式是不发生任何改变的。
比如这样一段代码:

	char c = '0';
	int a = (int)c;
	printf("%c %d\n", c, a);

输出结果为 0 48,其实不管是c还是a在内存的存储的二进制序列是一样的,只不过前者将这段序列理解为字符,后者理解为整形而已。
所以,我们的NULL在本质上,其实就是0。
那么我们就有着相同的问题。

	int* p = NULL;
	if (p) {}			//1
	if (NULL == p) {}	//2
	if (0 == p) {}		//3

那种判断形式好?
答案是:第二种
原因是:第一种会让人以为p为布尔类型变量,而第三种会让人误解p为整形变量。
结论:
使用NULL判断时应使用如下格式:

	if (NULL == p) 
	{}
	else
	{}
	//或者
	if (NULL != p)
	{}
	else
	{}

之所以将NULL写在等式左侧,是为了避免写出 if (p = NULL) 而造成程序错误(p会被赋值为NULL)。

四. 各色各异的0值

C语言中有着各种各样的零值,比如0, ‘\0’, 以及上面说的NULL。
首先要阐述很重要一点 char c = ‘0’,这个被单引号括起来的0,以及char* s = “abcdef0”,这个在双引号里的零,是字符0,你可以把它们理解为是假装的0,就像披着狼皮的羊一样,它们只是看起来和0一样罢了,其实他们对于的整形是48,就像

	char c = '0';
	int a = (int)c;
	printf("%c %d\n", c, a);	//输出结果  0  48

这个例子一样。大家可以自行去了解一下ASCll值的知识。
除了这个‘0’伪装的零以外,0,’\0’,NULL可都是货真价实的零,下面逐一介绍。
0:就是最本质的整形 0。
‘\0’: 常作为字符串的结束标志,大家可以理解为这个伪装的‘0’,被转义字符 \ 转义为真正的0,用途是放在字符串末尾做结束标志。当你定义 char* s = "abcdef"时系统会自动在后面添加’\0’标记字符串的结束。切记,’\0’本质就是0
NULL: 上面已经讲过,是0被强转为void * 类型,本质也是0。
既然如此,可能大家会问,既然他们的本质都是0,那为什么还要搞出这么多花样,直接全部用 0 不就行了吗?
我的答案是:
在计算机底层实现的角度是是完全没有任何问题的,你可以把你代码中的’\0’,NULL全部换成0,但是那样对于程序员或者编译器来说实在是太过不友好。
比如:

int a = 0;
int a = '\0';
int a = NULL;

上面三份代码在计算机实现时没有任何区别,都是将一串二进制全0的序列放在了整形变量a中,但是后两种写法,但凡学过计算机语言的人都会觉得无比怪异,甚至让编译器也捉摸不透眼前的这个程序员到底想搞啥子,从而可能有一些完全没有必要的警告。
结论:
我们在使用零值时应尽量保证对应使用,整形就用0,字符串就用’\0’,指针就用NULL。但我们要清楚,这三者的本质其实都是零。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-07-30 12:34:13  更:2021-07-30 12:36:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 11:08:32-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码