《C语言深度剖析》第一章 关键字详解 p2 C语言从入门到入土(进阶篇)(二)

简介: 1.signed、unsigned 1.1整形在内存的存储 1.2signed(有符号数) 1.3unsigned(无符号数) 2 if else 组合 3 各种变量与“零值”进行比较 3.1 bool 变量与"零值"进行比较 3.2 float 变量与"零值"进行比较 3.3 指针变量与“零值”进行比较

3.2 float 变量与"零值"进行比较

浮点数在内存中存储,并不想我们想的,是完整存储的,在十进制转化成为二进制,是有可能有精度损失的。

注意这里的损失,不是一味的减少了,还有可能增多。浮点数本身存储的时候,在计算不尽的时候,会 “ 四舍五入 ”或者其他策略


82.png83.png

结论:因为精度损失问题,两个浮点数,绝对不能使用==进行相等比较.


那么两个浮点数该如何比较呢?

应该进行范围精度比较:

84.png

#include<float.h> 
//使用下面两个精度,需要包含该头文件 
DBL_EPSILON //double 最小精度 
FLT_EPSILON //float 最小精度
//伪代码 
if((x-y) > -精度 && (x-y) < 精度){ 
//TODO 
}
//伪代码-简洁版 
if(fabs(x-y) < 精度){ 
//fabs是浮点数求绝对值
//TODO 
}

精度:

自己设置?后面如果有需要,可以试试,通常是宏定义。

使用系统精度?暂时推荐

//代码调整后 
#include <stdio.h> 
#include <math.h> 
//必须包含math.h,要不然无法使用
fabs #include <float.h> 
//必须包含,要不然无法使用系统精度 
#include <windows.h>
int main() 
{ 
double x = 1.0; 
double y = 0.1; 
printf("%.50f\n", x - 0.9);
printf("%.50f\n", y); 
if (fabs((x - 0.9) - y) < DBL_EPSILON){ //原始数据是浮点数,我们就用DBL_EPSILON 
    printf("you can see me!\n"); 
}
else{
printf("oops\n"); 
}
system("pause"); 
return 0; 
}

两个精度定义

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

#define FLT_EPSILON 1.192092896e-07F /* smallest such that 1.0+FLT_EPSILON !=

1.0 */

XXX_EPSILON 是最小误差 , 是: XXX_EPSILON + n 不等于 n 的最小的正数。

EPSILON 这个单词翻译过来是 'ε' 的意思,数学上,就是极小的正数


所以我们再回来讲float和0的比较:

#include <stdio.h> 
#include <math.h> 
#include <float.h> 
#include <windows.h> 
int main() 
{ 
double x = 0.00000000000000000000001; 
//if (fabs(x-0.0) < DBL_EPSILON){ //写法1
//if (fabs(x) < DBL_EPSILON){ //写法2 
if(x > -DBL_EPSILON && x < DBL_EPSILON){ //书中写法 
printf("you can see me!\n"); 
}
else{
printf("oops\n"); 
}
system("pause"); 
return 0; 
}

有朋友就有疑问了?

//x > -DBL_EPSILON && x < DBL_EPSILON: 为何不是 >= && <= 呢?

// 个人看法: XXX_EPSILON 是最小误差 , 是: XXX_EPSILON+n 不等于 n 的最小的正数。

//XXX_EPSILON+n 不等于 n 的最小的正数 : 有很多数字 +n 都可以不等于 n ,但是 XXX_EPSILON 是最小的, but ,

XXX_EPSILON 依旧是引起不等的一员。

// 换句话说: fabs(x) <= DBL_EPSILON( 确认 x 是否是 0 的逻辑 ) ,如果 = ,就说明 x 本身,已经能够引起其他和他 +- 的数

据本身的变化了,这个不符合 0 的概念。


3.3 指针变量与零值进行比较


其实NULL 和 '/0' 和 0 都是0。只不过因为编译器编译的时候需要相同的类型进行计算,所以就有了“各种不同的0”。


else 到底与哪个 if 配对呢?

85.png

其实并不是看着这样的理解,推荐的写法是:

86.png

总结:else 匹配采取就近原则

今天的内容就到这里了哈!!!

要是认为作者有一点帮助你的话!

就来一个点赞加关注吧!!!当然订阅是更是求之不得!

最后的最后谢谢大家的观看!!!

你们的支持是作者写作的最大动力!!!

下期见哈!!!

相关文章
|
2月前
|
C语言
|
2月前
|
存储 C语言
C语言中static关键字的作用与用法解析
C语言中static关键字的作用与用法解析
|
3月前
|
存储 C语言
C语言中的typedef关键字:为类型定义新名称
C语言中的typedef关键字:为类型定义新名称
|
3月前
|
C语言
深入探索C语言中的sizeof关键字
深入探索C语言中的sizeof关键字
|
3月前
|
存储 编译器 C语言
C语言中的关键字与标识符详解
C语言中的关键字与标识符详解
|
2天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
2天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。
|
8天前
|
Linux C语言
C语言 多进程编程(三)信号处理方式和自定义处理函数
本文详细介绍了Linux系统中进程间通信的关键机制——信号。首先解释了信号作为一种异步通知机制的特点及其主要来源,接着列举了常见的信号类型及其定义。文章进一步探讨了信号的处理流程和Linux中处理信号的方式,包括忽略信号、捕捉信号以及执行默认操作。此外,通过具体示例演示了如何创建子进程并通过信号进行控制。最后,讲解了如何通过`signal`函数自定义信号处理函数,并提供了完整的示例代码,展示了父子进程之间通过信号进行通信的过程。
|
8天前
|
C语言
C语言 字符串操作函数
本文档详细介绍了多个常用的字符串操作函数,包括 `strlen`、`strcpy`、`strncpy`、`strcat`、`strncat`、`strcmp`、`strncpy`、`sprintf`、`itoa`、`strchr`、`strspn`、`strcspn`、`strstr` 和 `strtok`。每个函数均提供了语法说明、参数解释、返回值描述及示例代码。此外,还给出了部分函数的自实现版本,帮助读者深入理解其工作原理。通过这些函数,可以轻松地进行字符串长度计算、复制、连接、比较等操作。
|
9天前
|
SQL 关系型数据库 C语言
PostgreSQL SQL扩展 ---- C语言函数(三)
可以用C(或者与C兼容,比如C++)语言编写用户自定义函数(User-defined functions)。这些函数被编译到动态可加载目标文件(也称为共享库)中并被守护进程加载到服务中。“C语言函数”与“内部函数”的区别就在于动态加载这个特性,二者的实际编码约定本质上是相同的(因此,标准的内部函数库为用户自定义C语言函数提供了丰富的示例代码)