C 语言|整数与浮点数内存存储 —— 学习笔记

📅 2026/8/26 15:49:33
C 语言|整数与浮点数内存存储 —— 学习笔记
C语言当中整数与浮点数遵循不同的内存存储规则。写代码时碰到的部分溢出、精度异常问题往往就来源于这套底层存储规则。本文就整数、浮点数的内存存储展开阐述尽力梳理清楚背后的原理。文章目录整数在内存中的存储原码、反码、补码补码的作用与数据范围C语言演示代码浮点数在内存中的存储现象引入IEEE-754 格式存储IEEE‑754 的特殊情况现象引入的解释整数在内存中的存储原码、反码、补码在计算机中数据使用二进制进行存储。对于有符号整数二进制分为符号位与数值位两部分。最高位为符号位0代表正数1代表负数剩余位用来存放数值。原码直接按照正负把数值翻译成二进制得到的就是原码。使用原码会存在一个问题会出现正零和负零两种不同编码造成编码冗余。为了解决以上的问题产生了反码。反码的换算规则如下正数反码和原码保持一致负数符号位保持不变数值位按位取反可是反码仍然没有解决正负零重复编码的问题于是就有了补码。补码的转换规则正数原码、反码、补码完全相同负数在反码的基础之上数值位再加1综上所述计算机内存存储整数时实际存放的是补码。特别说明无符号整数没有符号位全部比特位作为数值位因此不存在原码、反码、补码。补码的作用与数据范围补码两大功能将减法运算转换为加法运算简化CPU硬件电路消除原码、反码中正负0重复编码的问题充分利用二进制编码。以 char 类型8比特有符号整数举例原码‑127 ~ 127正负0占用两套编码补码‑128~ 127把多余那一份编码分配给负数值‑1288 位有符号 char 类型最小值‑128不存在对应的原码。有符号整数负数方向会多出一个数值无符号整数全部比特位存储数值仅表示非负数。C语言演示代码浮点数在内存中的存储现象引入通过前面的学习我们已经知道整数在内存中是以补码的形式存储的。那么我们很自然会产生疑问浮点数在内存中又是如何存储的下面我们借助union联合体共享一块内存的特性完成实验观察 float 的存储方式是否和 int 相同。观察实验结果可以发现同一块内存分别按照 int、float 解析得到的值完全不同。由此可知浮点数在内存中的存储规则和整数不同它并不用补码存储而是遵循一套专门的编码标准——IEEE-754。IEEE-754 格式存储那么什么是 IEEE‑754 呢这里我们以 32 位单精度float为例进行讲解。首先浮点数需要先在纸面整理为统一数学格式V ( − 1 ) S × M × 2 E \boldsymbol{V(-1)^S \times M \times 2^E}V(−1)S×M×2ES SS符号位0代表正数1代表负数M MM有效数字必须写成二进制1. x x x x \boldsymbol{1.xxxx}1.xxxx满足1 ≤ M 2 1\le M21≤M2E EE真实指数用来移动小数点例如十进制5.0 5.05.0二进制为101.0 101.0101.0。按照M MM需要写成二进制1. x x x x 1.xxxx1.xxxx的格式整理得到1.01 × 2 2 1.01\times2^21.01×22对应S 0 M 1.01 E 2 S0M1.01E2S0M1.01E2。但这只是纸上推导得到的数学形式真正存入计算机内存时还需要做相应转换。32 位 float 会把 32 个比特位分成以下三段简短拆解三段符号位 S1bit最高位保存符号0代表正数1代表负数。阶码 E8bit中间8位保存经过偏移后的指数float偏移值固定为127。尾数 M23bit低23位保存有效数字的小数部分整数的1默认省略不存。因此从内存读取规格化浮点数的复原核心公式V ( − 1 ) S × ( 1 M ) × 2 ( E − 127 ) \boldsymbol{V(-1)^S \times (1M) \times 2^{(E-127)}}V(−1)S×(1M)×2(E−127)沿用前面5.0 5.05.0的例子 纸面得到S 0 S0S0真实E 2 E2E2M 1.01 M1.01M1.01。S存0 00内存E 2 127 129 2127 1292127129内存M舍弃1.只存小数01剩余位补0凑齐23bit。补充64 位 double 逻辑完全一致。S占1bitE占11bitM占52bit偏移量固定为1023。IEEE‑754 的特殊情况阶码 E 全为 0非规格化数不再有隐藏整数位1直接使用尾数M MM不再是1 M 1M1M。计算公式V ( − 1 ) S × M × 2 ( 1 − 127 ) \boldsymbol{V(-1)^S \times M \times 2^{(1-127)}}V(−1)S×M×2(1−127)M 0 M0M0代表 0 00、− 0 -0−0IEEE‑754 区分正零、负零。M ≠ 0 M\neq0M0表示非常趋近于0的极小数值。阶码 E 全为 1尾数M 0 M0M0无穷S 0 S0S0为正无穷S 1 S1S1为负无穷。尾数M ≠ 0 M\neq0M0NaN非数代表非法运算结果。精度特殊现象0.1 0.2 ≠ 0.3 0.10.2 \neq 0.30.10.20.3部分十进制小数转换成二进制后是无限循环小数。浮点数尾数位数有限只能截断保存近似值参与运算的是近似数值计算结果就会出现误差。可以精确存储如0.5 、 0.25 0.5、0.250.5、0.25。无法精确存储如0.1 、 0.2 、 0.3 0.1、0.2、0.30.1、0.2、0.3。注意先判断阶码阶码全0、全1不能套用规格化浮点数公式。比较两个浮点数不能直接用相等判断应当判断二者差值是否小于一个极小阈值。double 的特殊情况逻辑完全一致偏移值改为 1023。现象引入的解释联合体union的多个成员共用同一块内存内存当中只存放原始二进制比特。比特本身没有意义解读方式由读取时的数据类型决定。将整数9写入联合体内存内存保存的是int的补码二进制当用float读取同一片内存时硬件会把这32位比特按照IEEE‑754浮点数S/E/M规则解析因此得到一个和9毫无关系的浮点数。同理把9.0f存入联合体内存保存的是IEEE‑754格式的浮点数编码当作int读取时直接把浮点数二进制当作整数补码翻译输出一串奇怪整数。