目录
float 23bit 尾数,为什么得到大约 7 位十进制有效数字
IEEE‑754 基础概念
我们平时写的float、double,在计算机内存中遵循 IEEE‑754 标准存储,不是简单把十进制小数转二进制直接存放。
32 位 float(单精度)内存 3 部分:
- 符号位 S(1bit):
0代表正数,1代表负数 - 阶码 E(8bit):相当于指数,带偏移值,用来放大 / 缩小数字(规格化的二进制数是用科学计数法表示的----->
-------->这只是逻辑式,实际存储不同)
- 尾数 M(23bit):存放小数部分有效数字
总位数:1+8+23 = 32bit
64 位 double(双精度):
- 符号位 S:1bit
- 阶码 E:11bit
- 尾数 M:52bit 总位数:1+11+52 =64bit
关键知识点
- 尾数部分默认隐藏整数位
1。 例:二进制1.0101,只存小数点后面0101,开头的1不占用存储空间,节省一位。 - 有效数字不是小数点后的位数,是全部有效数字
- float:23 位尾数,换算十进制大约 6~7 位有效数字
- double:52 位尾数,大约 15~16 位有效数字
数值变大之后,float 尾部小数丢失精度。
- ❗十进制小数,很多不能用二进制浮点数精确表达 例如
0.1,内存里面是无限循环二进制小数,只能近似保存,存在微小误差。
c
#include <cstdio>
int main(void)
{
float f = 0.1f;
if(f == 0.1)
{
printf("相等");
}
else
{
printf("不相等");
}
return 0;
}
这段代码运行输出:
不相等,0.1 float 和 0.1 (double) 近似值不一样。

思考题
- float 一共 32bit,符号位占 1bit,阶码占多少 bit?尾数占多少 bit?
- float 的十进制有效数字大约是多少位?double 呢?
- 判断:
0.1可以在 float 中被完全精确存储(对 / 错)
:
1.8 23
2.6~7
3.错
整数位为什么不是1就是0?为什么可以隐藏整数位置?
:
批改
- float 阶码8bit,尾数23bit ✔(符号位 1bit)
- float:6‑7 位有效数字;double:15‑16 位 ✔
- 错 ✔,0.1 是十进制小数,二进制下是无限循环小数,只能近似存储。
为何规格化浮点数整数位只能是 1
IEEE‑754 规格化浮点数,要求二进制科学计数法强制写成:
二进制科学计数法,小数点左边只能是 1,不能是 0,也不能是 2。
二进制数字特点
二进制每一位只有两种取值:0、1。
- 如果整数位写
0:0.101 × 2^3,这个可以改写为1.01 × 2^2
把小数点向右挪一位,指数减 1,整数位就变成 1。
👉规格化的定义:把数字调整成「整数部分严格等于 1」的形式。 所以规格化数整数位永远固定为1,不会出现 0、2 之类。
既然永远固定是1,计算机就不需要把这个 1 存入内存,直接省略,只存小数点后面 M。 这就是那个隐藏位 1的来源,凭空多出来一位精度。
那什么时候整数位可以是 0?
只有非规格化数(阶码全部 bit 为 0)。 此时不再强制要求整数位为 1
格式变成:
用途:表示无限接近 0 的极小数字。
简单总结:
- ✅普通正常浮点数(规格化):整数位固定二进制
1,隐藏,不占内存。 - ✅阶码全 0(非规格化):整数位是
0,不再隐藏。
举个例子
十进制 5.0
十进制 5 →二进制 101.0
科学计数法改写:
整数部分是1,内存只存小数点后010。
:
有效数字是怎么算出来的?
:
float 23bit 尾数,为什么得到大约 7 位十进制有效数字
规格化 float 实际尾数:23bit 内存存储 M + 硬件隐藏的 1bit = 24bit 有效二进制位。
问题转化:24 位二进制,等价多少位十进制有效数字?
公式:
位数(十进制) ≈ 二进制位数 ×
≈ 0.3010
代入: 24 * 0.3010 约等于 7.44
👉所以 float 大约 6‑7 位十进制有效数字。
同理 double: 尾数内存 52bit,加上隐藏 1bit,一共 53bit 二进制有效位 53 ×0.3010≈15.95
👉double 约 15‑16 位十进制有效数字。
通俗解释
二进制每增加 1bit,能区分 2 倍大小; 十进制每增加 1 位,能区分 10 倍大小。
log₁₀2 ≈ 0.3010含义:1 个二进制 bit ≈ 0.301 位十进制有效数字。
- float:24bit ×0.301 ≈7.2 位;
- double:53bit ×0.301≈16 位。
⚠️区分两件事:
- 有效数字:可靠的数字位数,不是小数点后面多少位。 例:
16777217.0f,整数 8 位,float 只有 7 位有效精度,最低位直接丢失。 printf("%.20f")打印很多小数,后面一大串是虚假的,只有前 6‑7 位可信。
举实例
c
float x = 12345678.0f;
printf("%f",x);
真实数字:12345678 float 输出:12345678?实际会发生偏差。
一共 8 位有效十进制数字,超出 float≈7.2 位极限,最低位不准。
阶码偏移(移码)
IEEE‑754 的阶码不直接存真实指数,要加上一个偏移量。
- float(32 位):偏移量 = 127
- double(64 位):偏移量 = 1023
公式:
存储的阶码 = 真实指数 + 偏移量 真实指数 = 存储阶码 − 偏移量
举例:
二进制科学计数
真实指数 =3;float 存入阶码字段的值 = 3+127=130。
特殊规则:阶码两种边界情况
- 阶码全部比特为 1
- 尾数全 0:无穷大(
inf) - 尾数非 0:非数字(
NaN,无效计算结果,比如 0 除以 0)
- 尾数全 0:无穷大(
- 阶码全部比特为 0
- 代表非规格化数,用来表示接近 0 的极小数字;此时不再隐藏开头的
1。
- 代表非规格化数,用来表示接近 0 的极小数字;此时不再隐藏开头的
正常使用的普通浮点数,阶码既不全 0,也不全 1。
c
#include <cstdio>
int main()
{
float a = 1.0f / 0.0f;
printf("%f\n", a); //输出 inf 无穷大
return 0;
}

思考题
- float 的阶码偏移量是多少?double 的偏移量?
- 若 float 真实指数是 5,那么存入阶码字段的数值是多少?
- float 阶码所有 bit 全为 1,尾数不为 0,表示什么?
:
1.127 1023
2.132
3.无数字
:
批改
- float 偏移:127;double 偏移:1023 ✅正确
- 5 + 127 = 130 ✅正确
- 表述小问题:标准名称是 NaN(Not a Number,非数字),不是 “无数字”。代表无效运算结果,例如 0.0/0.0。
小记忆:
- 阶码全 1,尾数 = 0 →无穷大 inf
- 阶码全 1,尾数≠0 → NaN
尾数 M,隐藏的整数位 1
IEEE‑754 规格化浮点数(日常绝大多数浮点数) 二进制科学计数法统一写成
(E为真实的E,除去偏移量)
重点:开头这个整数 1,不存入内存,是隐藏位。 内存里面 23bit 尾数 M,只存小数点后面的部分。
示例:
二进制
- 整数位
1丢掉不存 - 尾数 M 内存只保存:
1010,后面补 0 填满 23bit。 读取数据的时候硬件自动把1补回来。
收益:白白多出 1 位有效精度,这就是 float23bit 尾数却拥有接近 7 位十进制有效数字的原因。
非规格化数(阶码全部 bit 为 0)
当阶码全 0,不再使用隐藏的1,数字格式变为:
用途:表示无限趋近 0 的极小数字,填补 0 附近的数值空隙。
区分:
- ✅正常数字:阶码不全 0 不全 1 →规格化数,隐藏
1.M - ✅阶码全 0:非规格化数,没有隐藏的 1
- ✅阶码全 1:特殊值 inf / NaN
c
#include <cstdio>
int main(void)
{
float x = 1.0f / 3.0f;
printf("%f\n",x);
return 0;
}

思考题
- 规格化 float,尾数部分内存保存的是
1.M还是小数点后的M? - float 为什么 23 位尾数,却能实现约 7 位有效十进制数字?
- 什么时候浮点数不再隐藏整数位 1?
:
1.(M)
2.省略了整数位
3.非规格化数
:
批改
- ✅正确:内存尾数只保存小数点后面的 M,不会存前面的 1。 逻辑:实际内存存 M;读取的时候硬件自动拼接成
1.M。
题目陷阱:内存里存的不是
1.M,只存小数部分M。
-
✅正确:省略隐藏整数位 1,凭空多出 1bit 有效精度。
-
✅正确:阶码全 0,进入非规格化数,不再隐藏整数位 1。
再复述一遍加深印象: 计算:
内存尾数 23bit,
只存:
101,前面的1不写入内存。
实际开发 / 做题坑点
二进制浮点数不能精确表达部分十进制小数
十进制0.1,转二进制是无限循环小数。 但是 float 内存只有有限比特,只能截断保存近似值。 所以内存中0.1并不是真正等于 0.1。
cpp
#include <cstdio>
#include <cmath>
int main()
{
float a = 0.1f;
if(a == 0.1)
{
printf("相等");
}
else
{
printf("不相等");
}
return 0;
}
输出结果:不相等
0.1字面量默认是 double 类型;a是 float,两者近似值不一样。
不要用 == 判断两个浮点数相等
浮点数有微小误差,不能直接 if(x == y)。
✅正确写法:判断两者差值的绝对值小于一个极小允许误差
cpp
#include <cmath>
// 允许误差 1e‑6
if(fabs(a - b) < 1e‑6)
{
//视为两个浮点数近似相等
}
fabs():求浮点数绝对值;abs()用于整数。
区分两个概念
- 表示范围:float 可以存很大的数字;
- 有效精度:数字位数一多,尾部小数不准。

思考题
- 为什么
0.1不能在 float 中精确存储? - 浮点数比较相等,为什么不能直接用
==?应该怎么写? - float、double 有效数字分别大约多少位?
:
1.不能
2.做差,其差小于一定范围
3.6-7位 15-16位
0.1具体在二进制是怎么存储的呢?
:
批改
- ✅正确。十进制
0.1转二进制是无限循环小数,float 存储空间有限,只能保存近似值,无法精确存放。 - ✅大体正确;完整表述:计算两者差值绝对值,
fabs(a‑b) < 极小值(例如1e‑6),就认为近似相等。
注意:浮点数求绝对值函数是
fabs();整数用abs(),不要写混。
- ✅正确:float:6‑7 位有效数字;double:15‑16 位有效数字。
十进制 0.1 → float 完整演算
十进制 \(0.1_{10}\)
第一步:转二进制
用乘 2 取整法:
plaintext
0.1 ×2 =0.2 取0
0.2 ×2 =0.4 取0
0.4 ×2 =0.8 取0
0.8 ×2 =1.6 取1
0.6 ×2 =1.2 取1
0.2 ×2 =0.4 取0
0.4 ×2 =0.8 取0
0.8 ×2 =1.6 取1
……循环往复
得到二进制:0.0001100110011......
0011无限循环,永远写不完。
第二步:写成规格化科学计数法(IEEE‑754)
把小数点右移 4 位:
- 符号位:正数 → 0
- 真实指数 E 真实 = -4
- float 存储阶码 = 真实指数 +127 = -4+127 = 123
第三步:尾数 M(内存只存小数点后面部分)
逻辑数值:1.100110011 0011 0011……
前面
1.隐藏,内存尾数 23bit 只保存小数点之后:10011001100110011001101
因为只能存 23 位,无限循环的比特直接截断,末尾做舍入。 👉所以内存里面存的,并不是真正的 0.1,是一个非常接近 0.1 的近似值。
完整 float 内存 32bit 排布
表格
| 符号 (1bit) | 阶码 (8bit) | 尾数 M (23bit) |
|---|---|---|
| 0 | 01111011(123) | 10011001100110011001101 |
对应代码现象
c
float f = 0.1f;
printf("%.20f",f);
输出: 0.10000000149011611938
可以看见,并不是严格等于 0.1。
IEEE‑754 特殊值
除普通规格化浮点数,IEEE‑754 定义了几组特殊数值,由阶码的边界状态决定。
1. ±0 零
阶码全部 bit 为 0,尾数 M 全部 bit 为 0。 符号位可以 0 或者 1,于是存在+0.0和‑0.0。 绝大多数场景下两者运算效果几乎一样。
2. Inf 无穷大
阶码全部 bit 为 1,尾数 M=0。
- 符号位 0:正无穷
+inf - 符号位 1:负无穷
-inf产生场景:正数除以 0,得到正无穷。
3. NaN(Not a Number,非数)
阶码全部 bit 为 1,尾数 M 不为 0。
代表无效数学运算:
0.0 / 0.0、sqrt(-1.0)负数开根号。
⭐NaN 最独特性质:NaN 不等于任何数字,甚至 NaN == NaN 的结果也是假。 不能用 x == NaN 判断是否是非数;C 语言用isnan(x)函数判断。
c
#include <cstdio>
#include <cmath>
int main(void)
{
float a = 0.0f / 0.0f;
printf("%f\n", a);
if(a == a)
{
printf("a等于自己");
}
else
{
printf("a不等于自己");
}
return 0;
}
运行输出:
plaintext
nan
a不等于自己
思考题
- float 阶码全 1,尾数 M 等于 0,代表什么?
- NaN 有一个很特殊的判断特性是什么?
- 什么运算会产生 NaN?举一个例子。
:
1.无穷大
2.NaN != NaN
3.0/0
:
批改
- ✅无穷大(Inf),符号位决定是正无穷还是负无穷。
- ✅
NaN != NaN,NaN 连自己都不等于自己,不能用==判断 NaN。 - ✅
0.0 / 0.0会产生 NaN;补充:负数开平方sqrt(-1)也会得到 NaN。
小提醒:必须是浮点数 0 相除;整数
0/0程序直接崩溃,不是 NaN。

小结特殊值
表格
| 条件 | 结果 |
|---|---|
| 阶码全 0,尾数全 0 | ±0 |
| 阶码全 1,尾数全 0 | ±Inf 无穷大 |
| 阶码全 1,尾数非 0 | NaN 非数 |
几个经典的浮点数存储程序可视化
#include <cstdio>
#include <cstdint>
//打印float内存原始比特:符号|8阶码|23尾数
void printFloatBits(float num)
{
uint32_t bits = *(uint32_t*)#
printf("float = %.20f\n", num);
printf("32bit内存:");
for (int i = 31; i >= 0; i--)
{
uint32_t mask = 1U << i;
printf("%c", (bits & mask) ? '1' : '0');
if (i == 31) printf(" "); //符号位分隔
if (i == 23) printf(" "); //阶码、尾数位分隔
}
printf("\n----------------------------------------\n");
}
int main(void)
{
// 典型案例
printFloatBits(0.1f); //案例1:经典无法精确存储,二进制无限循环
printFloatBits(5.0f); //案例2:可以精确存储的正数 101.0 → 1.01 ×2^2
printFloatBits(1.0f / 3.0f); //案例3:1/3无限循环小数
printFloatBits(-2.0f); //案例4:负数,观察符号位变为1
printFloatBits(0.0f); //案例5:正0,阶码全0,尾数全0
//printFloatBits(1.0f / 0.0f); //案例6:正无穷Inf,阶码全部置1 了解,真正代码不可执行
return 0;
}
总结
核心回顾
- 32 位 float
- 符号位:1bit;阶码:8bit;尾数:23bit;偏移量:127
- 规格化数:逻辑 \(1.M×2^{E真实}\);内存只存 M,隐藏整数
1 - 有效数字:6‑7 位十进制有效数字
- 64 位 double
- 符号位:1bit;阶码:11bit;尾数:52bit;偏移量:1023
- 有效数字:15‑16 位十进制有效数字
- 边界特殊情况
- 阶码全 0:非规格化数,无隐藏 1,表示极小数字,可得到 ±0
- 阶码全 1,尾数 0:无穷大 Inf
- 阶码全 1,尾数≠0:NaN,非数,NaN 不等于自身
- 工程做题铁律
- 很多十进制小数(0.1)二进制无法精确存储,只能近似;
- ❌严禁直接使用
==比较浮点数;✅采用fabs(a‑b) < 1e‑6近似判断; - OJ 做题浮点计算优先选择
double,减少精度丢失。
c
//浮点数近似相等模板
#include <cmath>
if(fabs(a - b) < 1e-6)
{
//视为相等
}

1314

被折叠的 条评论
为什么被折叠?



