整数在内存里直来直去,可小数(浮点数)就要「精明」得多——它住的地方,和整数完全是两套规则。

先看一个会让人愣住的实验:

int n = 9;
float* pFloat = (float*)&n;
printf("%d\n", n);        // 9
printf("%f\n", *pFloat);  // 0.000000 —— 同一个二进制却读不出 9

*pFloat = 9.0;
printf("%d\n", n);        // 一个奇怪的大数
printf("%f\n", *pFloat);  // 9.000000

结论跃然纸上:浮点数的存储方式和整数不一样!它遵循国际标准 IEEE 754。

一个万能公式:S + M + E

任意一个二进制浮点数 V 都可以写成:

V = (-1)^S × M × 2^E

其中 S 是符号位,M 是有效数字,E 是指数。存浮点数,其实就是存这三样东西的值。

拿十进制的 5.0 举例,先转成二进制 101.0,再写成科学计数法 1.01 × 2^2,于是 S=0, M=1.01, E=2。

float 和 double 的"户型"

float (32 位):  1 位 S  |  8 位 E  |  23 位 M
double (64 位): 1 位 S  | 11 位 E  |  52 位 M

存进去的三个小心机

  • M 省略第一位的 1:因为 1 ≤ M < 2,总可以写成 1.xxx,那个整数位的 1 就默认省略,省下一位给精度。
  • E 要加中间数:科学计数法的指数可能是负数,存的时候要加上一个偏移量——8 位 E 加 127,11 位 E 加 1023。

比如 2^10 的指数是 10,存成 float 时就是 10 + 127 = 137。

来看一个完整的例子,9.5f:

9.5 → 二进制 1001.1 → 1.0011 × 2^3
S = 0
E = 3 + 127 = 130 → 二进制 10000010
M = 0011(省掉开头 1,后面补零)

拼起来:0 | 10000010 | 00110000000000000000000
浮点数如落水花瓣般漂浮的可爱插画
小数点也住在自己的小房间里,安静地漂浮着

取出来的三种情况

E 不全为 0 也不全为 1(常规):真实指数 = 存储值 − 127(或 1023),M 前面补回第一位 1。

E 全为 0:表示 ±0 或非常接近 0 的数,M 还原成 0.xxx,指数为 1 − 127。

E 全为 1:若 M 全 0,表示 ±∞(正负看符号位)。

✿ 摘要

  • 浮点数遵循 IEEE 754,与整数的存储方式完全不同。
  • 核心公式:V = (-1)^S × M × 2^E。
  • float 是 1+8+23 位,double 是 1+11+52 位。
  • 存 M 时省略开头的 1,存 E 时要加中间数(127 / 1023)。
  • 取出分「常规、E 全 0、E 全 1」三种情况,分别对应普通数、接近 0 的数、无穷大。

愿当下的花如你所见般美丽