【C语言】浮点数在内存中的存储和读取——底层分析

🚀write in front🚀
🔎大家好,我是gugugu。希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎
🆔本文由 gugugu 原创 CSDN首发🐒 如需转载还请通知⚠
📝个人主页:gugugu—精品博客
🎁欢迎各位→点赞👍 + 收藏⭐️ + 留言📝​
📣系列专栏:gugugu的精品博客
✉️我们并非登上我们所选择的舞台,演出并非我们所选择的剧本📩

在这里插入图片描述

前言

整数在内存中以二进制补码的形式存储,这个相信大家都很熟悉,那么浮点数又是怎么在内存中存储的呢?这篇博客将给大家详细讲解。
在这里插入图片描述

一、一道有意思的题目

#include <stdio.h>
int main()
{
    int n = 9;
    float* pFloat = (float*)&n;
    printf("n的值为:%d\n", n);
    printf("*pFloat的值为:%f\n", *pFloat);
    *pFloat = 9.0;
    printf("num的值为:%d\n", n);
    printf("*pFloat的值为:%f\n", *pFloat);

    return 0;
}

首先我们从这个题目引入

仔细去思考一下,这个题目将会输出什么?

答案还是比较奇怪的

在这里插入图片描述
为什么 会输出0.000000
又为什么会输出1091567616这么大的数字呢?

在学习完本章博客之后,就能够明白这道题目。

在这里插入图片描述

二、浮点数的存储

1、存储规则

根据国际标准IEEE(电气和电子工程协会) 754,任意⼀个⼆进制浮点数V可以表示成下面的形式:
V = (−1) S * M * 2E
• (−1)S 表⽰符号位,当S=0,V为正数;当S=1,V为负数
• M 表示有效数字,M是大于等于1,小于2的
• 2E 表示指数位

看着是不是很懵圈?
在这里插入图片描述

没关系,博主先举几个例子

首先先说5.5
5.5 = 101.101?

  • 是不是很多小伙伴会这么写? 哈哈,我猜对了吧,实际上这么写是错误的,
  • 因为2进制每一位都有权重 小数点后面第一位的权重是2-1 = 0.5
  • 所以 , 5.5=101.1
  • 又M大于等于1,小于2, 所以101.1=1.011 * 102?
  • 又错了, 注意,这里是二进制浮点数,不是10进制
  • 所以 101.1=1.011 * 22
  • S呢?S怎么处理
  • 因为5.5是正数,所以S=0

至此 5.5 = (-1)0 * 1.011 * 22

所有的浮点数都可以这么写,各位小伙伴可以自己举几个例子试试吧,当然,别太复杂,否则自己会写吐的。

2、在内存中的占位

IEEE754标准还规定
对于32位的浮点数(float),最⾼的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

如下图

在这里插入图片描述

对于64位的浮点数(double),最⾼的1位存储符号位S,接着的11位存储指数E,剩下的52位存储有效数字M

如下图

在这里插入图片描述

三、浮点数在内存中存储的具体实现

1、对于S

因为-1的0次方等于1,即正数,-1的1次方等于-1,即负数
所以S中只用存储0或者1就可以完成符号的区分。

2、对于M

因为M大于等于1,小于2,所以M都是一点几,
既然都相同,那就可以不用去存储了,直接去存储小数点后面的数字就可以了。

这样做有什么好处呢???

很显然,这样子做就可以省下储存1的那个比特,可以多储存一个二进制位。
从而提高了浮点数的精度。

3、对于E

首先规定了E为无符号整形。
因为可能出现负指数,所以IEEE754标准中提到一个词——中间数,
指数加上这个中间数,就变成了非负整数。

  1. 对于32位,E的范围是0~255,中间数就是127
  2. 对于64位,E的范围是0~2047,中间数就是1023

所以,前面提到的5.5,在内存中存储的就是
S=0, E=2+127=129=10000001,M=011,因为未占满,所以后面全部补0,所以
M=01100000000000000000000

四、浮点数读取的过程的具体实现

浮点数读取有三种情况

a、第一种情况

当E中既有0又有1时,这是,就是正常情况,根据存储的过程反向操作就行。

b、第二种情况

当E中全部都是0的时候,
E的真实值会分成非常小,这个时候,这个浮点数也就是会非常非常小,非常接近于0。
编译器就会将E=1-127或者1-1023,注意是1减,而不是0减。
然后M不会在小数点前面重新加上1.了,而是直接变成0.xxxxxx
通过这些操作来表示0或者一个十分接近0的浮点数。

c、第三种情况

当E中全部都是1的时候,
这时,即使M中的数字为0时,也表示正无穷或者负无穷,即一个超级大或者超级小的数字。

好啦,到这里,浮点数的存储规则讲解的差不多了,现在,我们可以来分析在最开始提出的那个题目了。

在这里插入图片描述

五、解决题目

#include <stdio.h>
int main()
{
    int n = 9;
    float* pFloat = (float*)&n;
    printf("n的值为:%d\n", n);
    printf("*pFloat的值为:%f\n", *pFloat);
    *pFloat = 9.0;
    printf("num的值为:%d\n", n);
    printf("*pFloat的值为:%f\n", *pFloat);

    return 0;
}
  • 首先在一个整形里面存储了一个9。
    注意这里是按照整形去存储的,所以,内存空间中存储的是(以小端机器16进制来写)09 00 00 00
    所以打印n 的时候是9
  • 接着将n的地址强制类型转换成一个浮点数指针,赋值给了pFloat
    先写成二进制吧
    0000 1001 0000 0000 0000 0000 0000 0000
    此时,按照浮点数的读取规则,E的真实值是18-127=-109,M=1.0
    所以pFloat=1.0* 2-109,非常非常小,且按照%f去打印,%f只打印6位小数,很显然,这个数的前六个小数位全部是0,所以,最终打印的是0.000000
  • 接着,通过指针指向n的空间,将内存存储的9以浮点数的形式改写成了9.0
    9.0以浮点数的存储方式,存储的是(-1)0 * 1.001 * 23
    所以S=0,E=3+127=130,M=00100000000000000000000
    所以32位二进制里面存储的应该是
    01000001000100000000000000000000
    以%d 十进制整形的方式读取,答案就是

在这里插入图片描述
1091567616

与答案相同

  • 最后以浮点数的方式打印9.000000

所以四个答案都得到了很好的解释。



okok,今天第二更奉上,关于浮点数的存储是C语言里面的一处比较重要的底层理解,希望大家能够学到不少东西吧。


!!!!!!!!!!!!!!!!!求关注!!!!!!!!!!!!!!!!

!!!!!!!!!!!!!!!蹲个一键三连!!!!!!!!!!!!!!!

在这里插入图片描述

  • 32
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
词法分析器是编译器的一个重要组成部分,用于将输入的程序源代码转化为单词序列(Token),其单词序列是由单词(Token)组成的,每个单词代表源代码的一个基本单元,例如关键字、标识符、运算符等等。在词法分析,识别浮点数是一个重要的任务,因为浮点数C语言的基本数据类型之一。以下是一个简单的C语言浮点数词法分析器的实现: ```c #include<stdio.h> #include<ctype.h> #include<stdlib.h> #include<string.h> #define FLOAT 1 struct token { int type; char* value; }; int is_float(char* str) { int len = strlen(str); int i = 0; int flag = 0; //标记是否有小数点 while(i < len){ if(isdigit(str[i])){ i++; }else if(str[i] == '.' && !flag){ flag = 1; i++; }else{ return 0; } } return 1; } struct token* next_token(char* input) { struct token* t = (struct token*)malloc(sizeof(struct token)); int len = strlen(input); char* p = input; while (p - input < len && isspace(*p)) { // 跳过空格 p++; } if (*p == '\0') { // 到达字符串末尾 t->type = -1; t->value = NULL; return t; } if (isdigit(*p) || *p == '.') { // 找到数字或小数点,往后扫描,直到找到不是数字或小数点的字符 char* q = p + 1; while (q - input < len && (isdigit(*q) || *q == '.')) { q++; } // 将这段字符拷贝到新的字符串 int length = q - p; char* temp = (char*)malloc(length + 1); strncpy(temp, p, length); temp[length] = '\0'; // 判断是否为浮点数 if (is_float(temp)) { // 是浮点数 t->type = FLOAT; t->value = temp; } else { // 不是浮点数 free(temp); t->type = -1; t->value = NULL; } return t; } // 其他情况暂时不处理 t->type = -1; t->value = NULL; return t; } int main() { char input[100]; printf("请输入一个字符串: "); fgets(input, 100, stdin); struct token* t; while ((t = next_token(input)) != NULL && t->type != -1) { if (t->type == FLOAT) { printf("浮点数: %s\n", t->value); } free(t->value); } return 0; } ``` 该程序通过 `next_token` 函数实现了词法分析器的基本功能,每次调用该函数可以得到下一个单词,如果单词是浮点数,则输出相应的提示信息。程序的 `is_float` 函数与前面的例子的相同,用于判断一个字符串是否为浮点数。由于 `next_token` 函数每次都会动态分配内存,因此在使用完每个单词后,需要释放相应的内存

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值