浮点数
浮点数
一、基础认知:浮点数的本质与用途
1. 核心需求
整数(int)无法表示小数、极大数(如 1234567890123)、极小数(如 0.0000001),浮点数正是为解决这一问题而生。
2. 本质逻辑
借鉴「十进制科学计数法」的 “二进制版本”,核心是 “指数定范围,尾数定精度” —— 用有限的二进制位兼顾 “数值覆盖范围” 和 “小数精确程度”,遵循全球通用的 IEEE 754 标准。
二、前置铺垫:3 个关键基础
1. 进制与位权
- 十进制:逢十进一,位权 = 10^ 索引(如 123.45=1×10²+2×10¹+3×10⁰+4×10⁻¹+5×10⁻²);
- 二进制:逢二进一,位权 = 2^ 索引(如 101.101₂=1×2²+0×2¹+1×2⁰+1×2⁻¹+0×2⁻²+1×2⁻³=5.625₁₀)。
2. 科学计数法(十进制→二进制)
- 十进制:
a×10^b(1≤a<10,a = 有效数字,b = 指数),如 123.45=1.2345×10²; - 二进制:
a×2^b(1≤a<2,a = 有效数字,b = 指数),如 101.101₂=1.01101₂×2²; - 简写形式:编程中
aeb代表a×10^b(如 8e-14=8×10⁻¹⁴,即 0.00000000000008)。
3. 关键概念区分
- 符号位(S):1 位,0 = 正、1 = 负,仅管数值正负;
- 隐藏位:归尾数位,是二进制有效数字的整数部分 “1”(无需存储,提升精度),≠符号位。
三、核心结构:浮点数的二进制组成(float32/double64)
浮点数统一拆分为 符号位(S)+ 指数位(E)+ 尾数位(M),位数分配决定性能差异:
| 类型 | 总位数 | 符号位(S) | 指数位(E) | 尾数位(M) | 实际有效数字(二进制) | 十进制有效数字 |
|---|---|---|---|---|---|---|
| float32(单精度) | 32 | 1 位 | 8 位 | 23 位 | 23 位 + 1 位隐藏位 = 24 位 | 7~8 位 |
| double64(双精度) | 64 | 1 位 | 11 位 | 52 位 | 52 位 + 1 位隐藏位 = 53 位 | 15~17 位 |
1. 指数位(E):负责 “数值范围”
存储规则:用「偏移码」存储(避免符号位,简化计算),实际指数 = 存储 E 值 - 偏移量;
- 偏移量公式:
bias=2^(k-1)-1(k=E 的位数); - float32:k=8→bias=127(实际指数范围 - 126~127);
- double64:k=11→bias=1023(实际指数范围 - 1022~1023);
- 偏移量公式:
特殊值:E 全 0 = 非归一化数(接近 0),E 全 1 = 无穷大 / NaN(非数字,如 0/0)。
2. 尾数位(M):负责 “数值精度”
- 存储内容:二进制有效数字的小数部分(a=1.M,M 是尾数位数值);
- 精度本质:二进制有效数字能表示的 “不同数值种数”(float32=2²⁴≈1.6×10⁷种,double64=2⁵³≈9×10¹⁵种),种数越多,精度越高。
四、关键特性:0 附近精度高、1 附近精度低(结合 Z 缓冲区场景)
1. 核心原理:相对精度≠绝对精度
- 绝对精度:像均匀尺子,最小刻度固定(如 1 毫米),不管测大 / 小物体,间隔一致;
- 相对精度:像 “按比例缩放的尺子”,最小可区分间隔(ULP)与「当前数值大小成正比」—— 数值越小,间隔越小(精度越高);数值越大,间隔越大(精度越低)。
2. 数学拆解(float32 为例)
浮点数最小间隔公式:最小间隔≈当前数值×2^(-23)(23 是 float32 尾数位):
- 数值 = 0.000001(0 附近):最小间隔≈1e-6×8e-8=8e-14(极小,能区分 1e-6 和 2e-6);
- 数值 = 0.999999(1 附近):最小间隔≈1.0×8e-8=8e-8(比 0 附近大 1000 多倍,难以区分细微差异)。
3. 通俗类比:放大镜逻辑
- 0 附近(小数值):放大镜倍数高,画面聚焦小范围,能看清细微纹理(小间隔 = 高精度);
- 1 附近(大数值):放大镜倍数低,画面覆盖大范围,细微纹理被压缩(大间隔 = 低精度)。
4. Z 缓冲区场景(深度值 [0,1])
精度分配:[0,0.1] 占 90% 以上精度(指数小,放大倍数高),[0.1,1.0] 精度严重不足(指数大,放大倍数低);
直观例子:
- 0 附近:0.000001 和 0.000002 能轻松区分(间隔 1e-6>8e-14);
- 1 附近:0.999999 和 1.0 之间仅约 12 个可区分值,无法区分 0.9999991、0.9999992 等精细深度。
五、使用原则与避坑技巧
1. 类型选择
- 优先用 double64:误差小,兼容性好,日常开发(游戏、App)足够;
- 用 float32 场景:内存极度紧张(嵌入式、海量数据),且数值在 float32 精确范围(整数≤16777216,有效数字≤8 位)。
2. 避坑关键
- 不直接比较浮点数相等:用 “差值<极小值”(如 abs (a-b)<1e-9),避免 0.1+0.2≠0.3 的误差;
- 高精度场景(金融、科学计算):不用 float/double,用专门类型(Python 的 Decimal、Java 的 BigDecimal);
- 避免循环叠加误差:用整数替代小数计算(如 0.1×10→1×10 再 ÷10);
- Z 缓冲区优化:用 “反向 Z 缓冲区”(反转 [0,1] 为 [1,0],让远处深度值靠近 0,享受高精度)。
六、终极逻辑链
- 浮点数 = 二进制科学计数法→S(正负)+E(缩放范围)+M(细节精度);
- 精度本质:二进制有效数字的 “不同数值种数”→换算为十进制 7~8/15~17 位有效数字;
- 精度分布:相对精度导致 “0 近高精、1 近低精”,核心是间隔与数值成正比;
- 使用核心:选对类型、避开直接比较、高精度场景用专门类型。