当前位置:网站首页>【C语言深度解剖】float变量在内存中存储原理&&指针变量与“零值”比较
【C语言深度解剖】float变量在内存中存储原理&&指针变量与“零值”比较
2022-06-30 12:17:00 【沐曦希】
大家好好我是沐曦希
文章目录


float 变量
浮点数在内存中的存储原理
根据国际标准IEEE(电气和电子工程协会) 754,任意一个二进制浮点数V可以表示成下面的形式:
(-1)^S * M * 2^E
(-1)^S表示符号位,当S=0,V为正数;当S=1,V为负数。
M表示有效数字,大于等于1,小于2。
2^E表示指数位。
例如:
十进制中的5.0转换成二进制是101.0,相当于1.01*2^2
(二进制中0.1表示1*2^(-1); 0.11表示1*2^(-1)+1*2^(-2))
那么,按照上面V的格式,可以得出S=0,M=1.01,E=2。
十进制的-5.0,写成二进制是 -101.0 ,相当于 -1.01×2^2 。那么,S=1,M=1.01,E=2。
V=9.5f=1001.1=(-1)^0*1.0011*2^3
相当于S=0;M=1.0011;E=3。
IEEE 754规定
对于32位的浮点数,最高的1位是符号位s,接着的8位是指数E,剩下的23位为有效数字M。
单精度浮点数存储模型:
对于64位的浮点数,最高的1位是符号位S,接着的11位是指数E,剩下的52位为有效数字M。
双精度浮点数存储模型:
IEEE 754对有效数字M和指数E,还有一些特别规定。
有效数字M的规定
前面说过, 1≤M<2 ,即M可以写成 1.xxxxxx 的形式,其中xxxxxx表示小数部分。
IEEE 754规定,在计算机内部保存M时,默认这个数的第一位总是1,因此可以被舍去,只保存后面的xxxxxx部分。比如保存1.01的时候,只保存01,等到读取的时候,再把第一位的1加上去。这样做的目的,是节省1位有效数字。以32位浮点数为例,留给M只有23位,将第一位的1舍去以后,等于可以保存24位有效数字。
指数E的规定
指数E为一个无符号整数
这意味着,如果E为8位,它的取值范围为0~255;如果E为11位,它的取值范围为0~2047。存入内存时E的真实值必须再加上一个中间数,对于8位的E,这个中间数是127;对于11位的E,这个中间数是1023。
例如: V=0.5f;
(-1)^0*1.0*2^(-1) 保存成32位浮点数时,必须保存成E+127–>126
保存成64位浮点数时,必须保存成E+1023–>1022 即float–>E(真实值)+127(中间值)–>存储值
即double–>E(真实值)+1023(中间值)–>存储值
然后,指数E从内存中取出还可以再分成三种情况:
E不全为0或不全为1
浮点数就采用下面的规则表示,即指数E的计算值减去127(或1023),得到真实值,再将有效数字M前加上第一位的1。
E全为0
当浮点数的指数E等于1-127(或者1-1023)即为真实值,
有效数字M不再加上第一位的1,而是还原为0.xxxxxx的小数。这样做是为了表示±0,以及接近于0的很小的数字。
E全为1
当有效数字M全为0,表示±无穷大(正负取决于符号位s);
float 变量与"零值"进行比较
浮点数在内存中存储,并不想我们想的,是完整存储的,在十进制转化成为二进制,是有可能有精度损失的。
注意这里的损失,不是一味的减少了,还有可能增多。浮点数本身存储的时候,在计算不尽的时候,会“四舍五入”或者其他策略
//code1
#include <stdio.h>
int main()
{
double x = 3.6;
printf("%.50f\n", x);
return 0;
}

//code2
#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;
}

结论:因为精度损失问题,两个浮点数,绝对不能使用==进行相等比较.
浮点数本身有精度损失,进而导致各种结果可能有细微差别。
浮点数比较应该进行范围精度比较
伪代码一:
if((x-y) > -精度 && (x-y) < 精度){
//TODO
}
伪代码-简洁版
if(fabs(x-y) < 精度){
//fabs是浮点数求绝对值
//TODO
}
精度可以直接定义一个(宏定义)或者使用系统精度。
宏定义精度
#define EPS 0.0000000000000001
#include<stdio.h>
#include<math.h>
int main()
{
double x = 1.0;
double y = 0.1;
if (fabs((x - 0.9) - y) < EPS)
{
printf("x==y");
}
else
{
printf("x!=y");
}
return 0;
}
输出结果:
系统精度
#include<float.h> //使用下面两个精度,需要包含该头文件
DBL_EPSILON //double 最小精度
FLT_EPSILON //float 最小精度
#include <stdio.h>
#include <math.h> //必须包含math.h,要不然无法使用fabs
#include <float.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("x==y\n");
}
else
{
printf("x!=y\n");
}
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这个单词翻译过来是’ε’的意思,数学上,就是极小的正数。
🧨x与"零值"比较
#include <stdio.h>
#include <math.h>
#include <float.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("x==y\n");
}
else
{
printf("x!=y\n");
}
return 0;
}

x > -DBL_EPSILON && x < DBL_EPSILON: 为何不是>= && <= 呢?
XXX_EPSILON是最小误差,是:
XXX_EPSILON+n不等于n的最小的正数。
XXX_EPSILON+n不等于n的最小的正数: 有很多数字+n都可以不等于n,但是XXX_EPSILON是最小的,但是XXX_EPSILON依旧是引起不等的一员。
换句话说:fabs(x) <= DBL_EPSILON(确认x是否是0的逻辑),如果=,就说明x本身,已经能够引起其他和他±的数据本身的变化了,这个不符合0的概念。
fabs库函数

fabs是用来计算浮点参数的绝对值的库函数,头文件是math.h。
Example
/* ABS.C: This program computes and displays * the absolute values of several numbers. */
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
void main( void )
{
int ix = -4, iy;
long lx = -41567L, ly;
double dx = -3.141593, dy;
iy = abs( ix );
printf( "The absolute value of %d is %d\n", ix, iy);
ly = labs( lx );
printf( "The absolute value of %ld is %ld\n", lx, ly);
dy = fabs( dx );
printf( "The absolute value of %f is %f\n", dx, dy );
}
Output
The absolute value of -4 is 4
The absolute value of -41567 is 41567
The absolute value of -3.141593 is 3.141593
指针变量与“零值”进行比较
#include<stdio.h>
int main()
{
printf("%d\n", 0);//0
printf("%d\n", '0');//字符0,ASCII码值48
printf("%d\n", '\0');//字符串结束标'\0',ASCII码值0
printf("%d\n", NULL);//0
//#define NULL ((void *)0)
return 0;
}

真实转化:
字符串A为“123456”转换成int类型,此时字符串A的字节数为7,而int类型为4个字节,要进行真实转化,需要我们编写算法,使用相关库函数来进行转化。
强制类型转化:
不改变内存中的数据,只改变对应的类型。
例如:
float a = 3.14;//3.14类型为double,存储在float类型中需要经过强制类型转化
//float a =(float)3.14;
int* p = NULL;//定义指针一定要同时初始化,否则为野指针
//if(p==0); if(p!=0);//不推荐
//if(p); if(!p);//不推荐
if(NULL==p); if(NULL!=p);//推荐
🧵else 到底与哪个if配对呢?
//code1
#include<stdio.h>
int main()
{
int x = 0;
int y = 1;
if (10 == x)
if (11 == y)
printf("hello\n");
else
printf("world!\n");
return 0;
}
无打印结果。
//code2
#include<stdio.h>
int main()
{
int x = 0;
int y = 1;
if (10 == x)
{
if (11 == y)
{
printf("hello\n");
}
}
else
{
printf("world!\n");
}
return 0;
}
打印结果:world!
结论:else 匹配if采取就近原则
写在最后
友友们觉得不错的可以给个关注,点赞或者收藏哦!感谢各位友友们的支持。
你的️点赞是我创作的动力的源泉
你的收藏是我奋斗的方向
你的关注是对我最大的支持
你的️评论是我前进的明灯
创作不易,希望大佬你支持一下小沐吧
边栏推荐
- Flink sql控制台,不识别group_concat函数吗?
- grep匹配查找
- MySQL判断执行条件为NULL时,返回0,出错问题解决 Incorrect parameter count in the call to native function ‘ISNULL‘,
- Solve numpy core._ exceptions. Ufunctypeerror: UFUNC 'Add' did not contain a loop with signature matching
- MySQL built-in functions
- Vision based robot grasping: from object localization, object pose estimation to parallel gripper grasping estimation
- 【OpenGL】OpenGL Examples
- 药店管理系统
- Substrate 源码追新导读: 波卡系波卡权重计算全面更新, Governance 2.0 版本的优化和调整
- SuperMap 3D SDKs_ Unity plug-in development - connect data services for SQL queries
猜你喜欢

【一天学awk】数组的使用

Some commonly used hardware information of the server (constantly updated)

How do different types of variables compare with zero

【MySQL】MySQL的安装与配置

Determining the subject area of data warehouse construction

NoSQL - redis configuration and optimization

电机控制Clarke(α/β)等幅值变换推导

Redis configuration files and new data types

Basic interview questions for Software Test Engineers (required for fresh students and test dishes) the most basic interview questions

Apple executives openly "open the connection": Samsung copied the iPhone and only added a large screen
随机推荐
Building of Hisilicon 3559 universal platform: obtaining the modified code of data frame
Mysql判断计算结果,除以100
Visual studio configures QT and implements project packaging through NSIS
ECDSA signature verification in crypt
【300+精选大厂面试题持续分享】大数据运维尖刀面试题专栏(二)
How to use AI technology to optimize the independent station customer service system? Listen to the experts!
Analysis of the whole process of common tilt data processing in SuperMap idesktop
Layout of pyqt5 interface and loading of resource files
21. Notes on WPF binding
Substrate 源码追新导读: 修复BEEFY的gossip引擎内存泄漏问题, 智能合约删除队列优化
Dataworks synchronizes maxcomputer to sqlserver. Chinese characters become garbled. How can I solve it
Redis的配置文件及新数据类型
【一天学awk】内置变量的使用
力扣之螺旋矩阵,一起旋转起来(都能看懂)
第十三章 信号(三)- 示例演示
Redis - problèmes de cache
Splitting e-commerce systems into micro services
Questionnaire star questionnaire packet capturing analysis
SuperMap iClient3D for WebGL 加载TMS瓦片
[qnx hypervisor 2.2 user manual]6.2.3 communication between guest and external