C语言深度剖析——bool类型,浮点数相等比较。

简介: C语言深度剖析——bool类型,浮点数相等比较。

序言:

C语言中有很多类型,但是bool我们至今也没有见过。那到底C语言中到底有没有bool类型呢?


我们之前聊过浮点数的在内存中的存储问题,由于浮点数在内存中比较特殊的存储结构,也造成了浮点数在判断相等的时候,有着比较特殊的比较方法。今天我们也就聊一聊这两个问题。


一.bool类型

(1)C语言中到底有没有bool类型?

c99之前,主要是c90标准是没有的,目前大部分书,都是认为没有的。因为书,一般都要落后于行业。

但是c99引入了_Bool类型(你没有看错,_Bool就是一个类型,不过在新增头文件stdbool.h中,被重新用宏写成了bool,为了保证C/C++兼容性)。

演示代码:

#include<stdio.h>
#include<stdbool.h>
int main()
{
  _Bool ret1 = false;
  _Bool ret2 = true;
  bool ret3 = false;
  bool ret4 = true;
  return 0;
}



这两种写法当然都是正确的,都能编译通过。只不过bool是_Bool宏定义。




那么我们就可以来看一看bool类型占用几个字节,




我们查看 true 和 false 底层也是用 0 和 1 宏定义的。


占用一个字节,理论上也就不难理解,表示真假一个bit就够了,不过这个问题还得取决于编译器,VS2013,VS2017,VS2019,VS2022,都认为是一个字节。


但是因为,目前编译器对C99特性的支持并不全面,所以我们在编码的时候,还是以C90的认识去编码就可以了,使用整形的方式表示真假,0表示假,非0表示真。


(2)VS中的BOOL类型

我们来看一段代码:

#include<stdio.h>
#include<stdbool.h>
#include<Windows.h>
int main()
{
  BOOL ret = true;
  printf("%d\n", sizeof(BOOL));
  return 0;
}


BOOL类型包含在我们的头文件 windows.h 里面。


259c4c7f0c224fa489388a9df30420a0.png


我们发现,竟然也能编过。。。什么鬼??

这都是Microsoft自己搞的一套BOOL值。在vs中转到BOOL对应的头文件,翻到最上面,就能看到微软的版权信息。好了,该听谁的??微软?强烈不推荐,用微软的标准微软也不能给我们发offer,一个好的习惯是:一定要保证代码的跨平台性,微软定义的专属类型,其他平台不支持。为了保证代码跨平台性,以后如果有使用的话,还是选择使用C99标准。


二.浮点数与 0 比较

(1)浮点数的精度缺失

我们之前讲过浮点数的存储问题了,我们知道浮点数在往计算机的存储的时候都是二进制的存储方式,但是有一些浮点数并不像我们想的,是完整存储的,在十进制转化成为二进制,是有可能有精度损失的。注意这里的损失,不是一味的减少了,还有可能增多。浮点数本身存储的时候,在计算不尽的时候,会“四舍五入”或者其他策略。

#include<stdio.h>
#include<stdbool.h>
#include<Windows.h>
int main()
{
  double x = 3.6;
  printf("%.50f\n", x);
  system("pause");
  return 0;
}


b5f6790a692c4d6482277a3b4dd617a2.png


看一段代码:

大家可以猜一下结果,结果一定会让你大吃一惊!!

#include <stdio.h>
int main()
{
  double x = 1.0;
  double y = 0.1;
  printf("%.50f\n", x - 0.9);
  printf("%.50f\n", y);
  if ((x - 0.9) == y) {
    printf("you can see me!\n");
  }
  else {
    printf("oops\n");
  }
  return 0;
}

运行结果:



所以使用 == 直接对浮点数经行比较是强烈不建议的。

(2)浮点数判断相等解决方案

那么两个浮点数该如何比较呢?应该进行范围精度比较。

//伪代码
if((x-y) > -精度 && (x-y) < 精度)
{
  //TODO
}
//伪代码-简洁版
if(fabs(x-y) < 精度)
{ //fabs是浮点数求绝对值,在头文件 math.h 里面。
  //TODO
}

精度:

自己设置?后面如果有需要,可以试试,通常是宏定义。
使用系统精度?暂时推荐

 

#include<float.h> //使用下面两个精度,需要包含该头文件
DBL_EPSILON //double 最小精度
FLT_EPSILON //float 最小精度

代码调整后:



这里的 DBL_EPSILON 和 FLT_EPSILON可以理解为微积分中的一个概念——无穷小量,他是改变一个数大小的最小值。那自然当我们需要判断两个数是否相等时,只要判断他们差的绝对值是否小于FLT_EPSILON或者DBL_EPSILON;


(3)判断浮点数是否为 0

只要判断是否小于最小精度

#include <stdio.h>
#include <math.h>  //必须包含math.h,要不然无法使用fabs
#include <float.h>  //必须包含,要不然无法使用系统精度
int main()
{
  double x = 0.000001;
  double y = 0.00000000000000000000000000000001;
  if (fabs(x) < DBL_EPSILON)
  {
    printf("you can see me!\n");
  }
  if(fabs(y)<DBL_EPSILON)
  {
    printf("bye\n");
  }
  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的概念。


最后:

把意念深潜得下,何理不可得,把志气奋发起,何事不可做。击败焦虑的最好的办法就是学习,加油,诸君,山顶见!!


相关文章
|
1月前
|
存储 编译器 C语言
C语言:数组名作为类型、作为地址、对数组名取地址的区别
在C语言中,数组名可以作为类型、地址和取地址使用。数组名本身代表数组的首地址,作为地址时可以直接使用;作为类型时,用于声明指针或函数参数;取地址时,使用取地址符 (&),得到的是整个数组的地址,类型为指向该类型的指针。
|
1月前
|
C语言 C++
【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)
【C语言】指针篇-一篇搞定不同类型指针变量-必读指南(3/5)
|
1月前
|
存储 C语言
C语言中的浮点数存储:深入探讨
C语言中的浮点数存储:深入探讨
|
2月前
|
存储 缓存 程序员
c语言的存储类型-存储类
本文详细介绍了C语言中的存储类型及其分类,包括基本类型(如整型、浮点型)和复合类型(如数组、结构体)。重点讲解了不同存储类别(`auto`、`static`、`register`、`extern`、`typedef`、`volatile`、`const`)的特点及应用场景,并展示了C11/C99引入的新关键字(如`_Alignas`、`_Atomic`等)。通过示例代码解释了每个存储类别的具体用法,帮助读者更好地理解和运用这些概念。
|
3月前
|
存储 自然语言处理 编译器
C语言中的char类型
C语言中的char类型
239 1
|
3月前
|
编译器 C语言
C语言中的浮点数:深入探索与应用
C语言中的浮点数:深入探索与应用
137 1
|
3月前
|
算法 编译器 API
C语言中的longdouble类型
C语言中的longdouble类型
|
5月前
|
Java 程序员 Linux
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
探索C语言宝库:从基础到进阶的干货知识(类型变量+条件循环+函数模块+指针+内存+文件)
49 0
|
5月前
|
存储 C语言
C语言中的typedef关键字:为类型定义新名称
C语言中的typedef关键字:为类型定义新名称
|
5月前
|
C语言
C语言---输入n科成绩(浮点数表示),统计其中的最高分,最低分以及平均分。
C语言---输入n科成绩(浮点数表示),统计其中的最高分,最低分以及平均分。