目录
IEEE‑754 基础概念
为何规格化浮点数整数位只能是 1
二进制数字特点
float 23bit 尾数,为什么得到大约 7 位十进制有效数字
阶码偏移(移码)
特殊规则:阶码两种边界情况
尾数 M,隐藏的整数位 1
非规格化数(阶码全部 bit 为 0)
实际开发 / 做题坑点
二进制浮点数不能精确表达部分十进制小数
不要用 == 判断两个浮点数相等
十进制 0.1 → float 完整演算
IEEE‑754 特殊值
1. ±0 零
2. Inf 无穷大
3. NaN(Not a Number,非数)
小结特殊值
几个经典的浮点数存储程序可视化
总结
IEEE‑754 基础概念
我们平时写的float、double,在计算机内存中遵循 IEEE‑754 标准存储,不是简单把十进制小数转二进制直接存放。
32 位 float(单精度)内存 3 部分:
——–>这只是逻辑式,实际存储不同)总位数:1+8+23 = 32bit
64 位 double(双精度):
关键知识点
- float:23 位尾数,换算十进制大约 6~7 位有效数字
- double:52 位尾数,大约 15~16 位有效数字
这就解释了你上一题:数值变大之后,float 尾部小数丢失精度。
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) 近似值不一样。

思考题
:
1.8 23
2.6~7
3.错
整数位为什么不是1就是0?为什么可以隐藏整数位置?
:
批改
为何规格化浮点数整数位只能是 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 的极小数字。
简单总结:
举个例子
十进制 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 位。
⚠️区分两件事:
举实例
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。
特殊规则:阶码两种边界情况
- 尾数全 0:无穷大(inf)
- 尾数非 0:非数字(NaN,无效计算结果,比如 0 除以 0)
- 代表非规格化数,用来表示接近 0 的极小数字;此时不再隐藏开头的1。
正常使用的普通浮点数,阶码既不全 0,也不全 1。
c
#include <cstdio>
int main()
{
float a = 1.0f / 0.0f;
printf("%f\\n", a); //输出 inf 无穷大
return 0;
}

思考题
:
1.127 1023
2.132
3.无数字
:
批改
小记忆:
- 阶码全 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;
}

思考题
:
1.(M)
2.省略了整数位
3.非规格化数
:
批改
题目陷阱:内存里存的不是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()用于整数。
区分两个概念

思考题
:
1.不能
2.做差,其差小于一定范围
3.6-7位 15-16位
0.1具体在二进制是怎么存储的呢?
:
批改
注意:浮点数求绝对值函数是fabs();整数用abs(),不要写混。
十进制 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 排布
表格
| 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不等于自己
思考题
:
1.无穷大
2.NaN != NaN
3.0/0
:
批改
小提醒:必须是浮点数 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;
}
总结
核心回顾
- 符号位:1bit;阶码:8bit;尾数:23bit;偏移量:127
- 规格化数:逻辑 \\(1.M×2^{E真实}\\);内存只存 M,隐藏整数1
- 有效数字:6‑7 位十进制有效数字
- 符号位: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)
{
//视为相等
}
网硕互联帮助中心





评论前必须登录!
注册