news 2026/10/14 5:14:45

浮点型在内存中的存储

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
浮点型在内存中的存储

目录

摘要:

一:题目

二:浮点数存储规则

1:浮点数表示公式

2:存储规则

三:读取浮点数取

1:常规情况

2:E全为0

3:E全为1

四:讲解题目

五:浮点数的特性

1:不能精确保存

2:不能直接比较


摘要:

本质旨在介绍浮点数在内存中的存储机制,再讲解与浮点数存储相关题目,最后解析浮点数的特性


一:题目

#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("n的值为:%d\n", n); printf("*pFloat的值为:%f\n", *pFloat); return 0; }

解释:在未了解浮点数在内存中的存储规则之前,可能会觉得打印:9,9.0,9,9.0;


二:浮点数存储规则

1:浮点数表示公式

任意⼀个⼆进制浮点数V可以表示成下⾯的形式:

V = (−1) S ∗ M ∗ 2^E

•(−1) S 表示:符号位,当S=0,V为正数;当S=1,V为负数
•M 表示:有效数字,M是大于等于1,小于2的
•2^E 表示:指数位

下面以浮点数5.0为例,十进制的5.0,写成⼆进制是 101.0 ,相当于 1.01×2^2

那么,按照上⾯V的格式,可以得出S=0,M=1.01,E=2 ----> V=(-1)0*1.01*2^2

十进制的-5.0,写成⼆进制是 -101.0 ,相当于 -1.01×2^2 。那么,S=1,M=1.01,E=2。

那么,按照上⾯V的格式,可以得出S=1,M=1.01,E=2 ----> V=(-1)1*1.01*2^2

所以,现在我们就可以用V=(-1)0*1.01*2^2表示浮点数5.0,用V=(-1)1*1.01*2^2表示-5.0,而所谓的浮点数在内存中的存储,就是将浮点数对应公式中的S,E,M存储进表示浮点数的4/8个字节中

注:V = (−1) S ∗ M ∗ 2 E这个公式就类似于二进制的科学计数法,根据科学计数法的规定,M必须是大于等于1,小于2

2:存储规则

对于32位的浮点数(float),最高的1位存储符号位S,接着的8位存储指数E,剩下的23位存储有效数字M

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

对于M和指数E在内存中的存储,还有⼀些特别规定!

M的规定:

前面说过,1 ≤ M < 2,也就是说,M 可以写成 1.xxxxxx 的形式,其中 xxxxxx 表示小数部分。所以 IEEE 754 规定,在计算机内部保存 M 时,因为这个数 M 的第一位总是 1,因此可以被舍去,只保存后面的 xxxxxx 部分。

比如保存 1.01 的时候,只保存 01,等到读取的时候,再把第一位的 1 加上去。这样做的目的,是节省 1 位有效数字。以 32 位浮点数为例,留给 M 只有 23 位,将第一位的 1 舍去以后,等于可以保存 24 位有效数字,多一位保存,也会让精确度更高。

E的规定:

至于指数 E,情况就比较复杂。首先,E 为一个无符号整数(unsigned int),这意味着,如果 E 为 8 位,它的取值范围为 0~255;如果 E 为 11 位,它的取值范围为 0~2047。但是,我们知道,科学计数法中的 E 是可以出现负数的(负指数),所以 IEEE 754 规定,存入内存时 E 的真实值必须再加上一个中间数:对于 8 位的 E,这个中间数是 127;对于 11 位的 E,这个中间数是 1023。比如,2^10 的 E 是 10,所以保存成 32 位浮点数时,必须保存成 10+127=137,即 10001001。


三:读取浮点数取

所以知道了第二点中的存储规则,我们就知道怎么读取浮点数了!

1:常规情况

比如:0.5 的公式表示为:1.0*2^(-1),此时S为0,M为1.0,E为-1

存储的时候:

E:-1+127(中间值)=126,表示为01111110

M:1.0去掉整数部分为0,补齐0到23位 00000000000000000000000

则其二进制表示形式为:

此时我们读取也很容易:即指数E的计算值减去127,得到真实值-1,再将有效数字M前加上第⼀位的1。就得到了1.0*2^(-1),也就是0.5

但是除了常规情况,还有两种特殊情况,特殊情况不做深究,本博客只需理解常规浮点数即可

2:E全为0

E全为0,代表原本的E值为-126(或者-1023),当指数都是-126的时候,此浮点数一定是无限接近于0的浮点数!所以,M不再加上第⼀位的1,舍弃了1,直接还原为0.xxxxxx的⼩数。这样做是为了表示±0,以及接近于0的很小的数字。

0 00000000 00100000000000000000000

3:E全为1

同理,不过当E全为1的时候,E已经很大了,表示无限大的数字,这时,如果有效数字M全为0,表示±⽆穷大(正负取决于符号位s)

0 11111111 00010000000000000000000

注:有效数字M全为0且E全为1 ,直接就是无穷大,而当E全为1但M不全为0的时候,则为NaN

可以把 E 全为 1 想象成 一张“特殊情况卡”:

你看到这张卡,就知道:接下来的规则完全变了。

  • 如果卡片背面(M)干干净净(全 0) → 意思是“无穷大”

  • 如果卡片背面(M)写了点东西(不全 0) → 意思是“NaN”(里面甚至可以存具体是哪种 NaN,但通常不关心)

NaN 是 Not a Number 的缩写,直译就是“不是一个数”。一句话解释:NaN 用来表示“无效的、未定义的、或者无法用实数表示”的计算结果。


四:讲解题目

#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("n的值为:%d\n", n); printf("*pFloat的值为:%f\n", *pFloat); return 0; }

解释:

①:*pFloat的值为:0.000000

首先,9是一个整形,那其在内存中的32位就是:0000 0000 0000 0000 0000 0000 0000 1001,此时我们*pFloat,其是浮点数指针类型,所以此时系统会以浮点数的角度去看待这32位,会以浮点数的形式去读取这32位

所以此时E就是全0,2^E中的E为-126,按照特殊情况的规定,此数判定为无限小,其次M为0000 0000 0000 0000 0000 1001,须知,M没有23位是向后补0到23位,这里的0是前导0,代表这不是补的0,而是小数的位数,小数点后有23位,并且E为全0时,此时M不再加上第一位的1,而是还原为0.xxxxxx的小数。所以为0.00000000000000000001001

所以浮点数的值为:(-1)^0 * 0.00000000000000000001001 * 2^-126

这个数无限趋近于0了,而%f打印浮点数,最多展示小数点后6位,所以打印结果为0.000000

②:n的值为:1091567616

*pFloat = 9.0;此时32位是按照浮点数的存储规则存储进去的,然后%d打印n,此时会以解读整形4个字节的视角去看待这32位

9.0的公式表示为:1001.0-------->(-1)^0 * 1.001 * 2^3,所以s=0,E=3,M=1.001,此时存入:E=3,要加上127 = 130;所以存入的32位: 0 10000010 00100000000000000000000,此时%d的形式去打印 ,这32位补码符号位为0,视为整数,原反补相同,所以补码对应的值就是结果,1091567616。


五:浮点数的特性

1:不能精确保存

浮点数是无法在内存中精确保存的!

二进制小数每一位是:

  • 第1位 = 1/2 = 0.5

  • 第2位 = 1/4 = 0.25

  • 第3位 = 1/8 = 0.125

  • 第4位 = 1/16 = 0.0625

  • 第5位 = 1/32 = 0.03125

所以一个浮点数,我们只能用这些位去组合表示,这就导致:

十进制二进制小数是否精确
0.50.1✅ 精确
0.250.01✅ 精确
0.1250.001✅ 精确
0.10.0001100110011...❌ 无限循环

故:3.14在内存中会多保存一点,一般为3.14000010

总结:所以浮点数无法精确保存,有可能少一点,有可能多一点

2:不能直接比较

既然无法精确保存,那么禁止使用浮点数进行比较!

#include <stdio.h> int main() { if (0.1 + 0.2 == 0.3) { printf("相等\n"); } else { printf("不相等\n"); } // 打印实际值 printf("0.1 + 0.2 = %.20f\n", 0.1 + 0.2); printf("0.3 = %.20f\n", 0.3); return 0; }

解释:0.1、0.2、0.3 在二进制浮点数中都是无限循环小数,只能近似存储,所以 a + b 的近似值与 c 的近似值不完全相等

📌 [ 作者 ] shylyly
📃 [ 首次发布 ] 2024.3.6
❌ [ 最新修改 ] 2026.10.13
📜 [ 声明 ] 由于笔者水平有限,文中难免有疏漏或不妥之处,还望读者不吝赐教。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 5:14:34

联想昭阳N4620国产化平台Windows 10驱动安装全攻略

简介&#xff1a;这份驱动包专为联想昭阳 N4620 KX-6640MA 笔记本在 Windows 10 环境下的硬件适配而整理&#xff0c;适合遇到 USB 设备无法识别、显卡显示异常、安全模块或指纹功能失效等问题的用户&#xff0c;也便于装机与运维人员一次性补齐所需驱动。压缩包共 86 个文件&a…

作者头像 李华
网站建设 2026/10/14 5:14:33

AI系统网络架构演进:从需求到调优的完整实践指南

1. 从一场技术大会的议题设置看AI系统网络架构的演进逻辑每年到了大型技术峰会密集举办的季节&#xff0c;圈子里讨论最多的往往不是某个单点技术突破&#xff0c;而是那些反复出现在主论坛和分论坛标题里的关键词。今年有个议题让我印象很深&#xff0c;就是围绕AI系统网络架构…

作者头像 李华
网站建设 2026/10/14 5:14:33

Kafka接入AI推理服务:实时特征链路搭建与踩坑指南

开篇直接聊干货&#xff1a;最近把一条线上数据链路里最折腾的那段——Kafka这一侧&#xff0c;正式接进AI推理服务了。熟悉实时数仓的朋友应该能立刻明白我在说什么&#xff1a;Kafka接AI&#xff0c;意味着事件流不再需要落地数仓、跑批处理、再拿结果去推断&#xff0c;而是…

作者头像 李华
网站建设 2026/10/14 5:14:32

Electron+Web+iOS三端协同开发实战指南

1. “t3code”不是工具名&#xff0c;而是开发者圈里一个正在成型的技术信号最近在几个前端技术群和 Electron 社区的讨论帖里&#xff0c;频繁看到“t3code”这个词被零散提起——不是作为某个开源库的正式名称&#xff0c;也不是某家公司的产品代号&#xff0c;而更像一种隐性…

作者头像 李华
网站建设 2026/10/14 5:14:16

MMC模块化多电平换流器PQ并网功率控制:从拓扑原理到调试实战

第一次打开MMC模型的桥臂波形时&#xff0c;我盯着那条接近正弦的阶梯波愣了好一会儿——它跟传统两电平逆变器那种又窄又尖的PWM脉冲完全是两回事。后来在PQ并网功率控制项目里跟模块化多电平换流器打了两年交道&#xff0c;我才逐渐摸清这套拓扑真正的脾气&#xff1a;子模块…

作者头像 李华
网站建设 2026/10/14 5:14:02

React Router 7全局路由保护实战:组件守卫、Loader与Middleware详解

手头这个中后台项目&#xff0c;第一版是没做登录的&#xff0c;页面全裸在外网。等到业务数据跑起来&#xff0c;需求方轻飘飘一句话&#xff1a;"所有页面都要登录才能看&#xff0c;管理员页面只能管理员进"。这种活儿干过的人都知道&#xff0c;代码放不对位置&a…

作者头像 李华