Jacky's blog
首页
  • Android
  • Web
  • Server
  • Python
  • iOS
  • Java
  • Vue
  • 计算机基础
  • AI
  • 全部工具
  • CLI 工具
  • 编辑器
  • 开发工具
  • 构建工具
  • 版本控制
  • 调试工具
  • 网络工具
  • 个人成长
  • 博客
  • 分类
  • 标签
  • 归档
收藏
GitHub (opens new window)

Jack Yang

编程; 随笔
首页
  • Android
  • Web
  • Server
  • Python
  • iOS
  • Java
  • Vue
  • 计算机基础
  • AI
  • 全部工具
  • CLI 工具
  • 编辑器
  • 开发工具
  • 构建工具
  • 版本控制
  • 调试工具
  • 网络工具
  • 个人成长
  • 博客
  • 分类
  • 标签
  • 归档
收藏
GitHub (opens new window)
  • 计算机基础

    • algo
    • 算法面试指南
    • toml/json/yaml/ini
    • 正则表达式(Regex)完整指南
    • float的存储
      • 📖 目录
      • 🔍 浮点数表示的基本原理
        • 二进制小数表示
        • 通用浮点数公式
        • 实例分析
      • 📊 IEEE754 标准详解
        • 单精度浮点数 (32位)
        • 双精度浮点数 (64位)
        • 🔢 数值计算公式
      • 📝 详细存储示例
        • 示例1:存储 12.375
        • 示例2:存储 -0.1875
      • ⚠️ 特殊值处理
      • 💻 编程实践与常见陷阱
        • 1. 精度损失:为什么 0.1 + 0.2 ≠ 0.3
        • 2. 浮点数比较
        • 3. 范围和精度
        • 4. 高精度计算
        • 5. 实用工具代码
        • 6. 常见浮点数的存储
        • 7. 浮点数序列化
      • 📚 总结
      • 🔗 参考资料
    • RAM ROM
    • 位运算参考文档
    • AST语法抽象树介绍
    • compress/decompress
    • encode
    • math经典公式
    • 统计学数学概念整理
    • 线性代数
  • 网络

  • 操作系统

  • Shell

  • 安全

  • 设计模式

  • cs
  • cs_base
Jacky
2024-07-07
目录

float的存储

# 浮点数的存储机制详解

# 📖 目录

  • 浮点数表示的基本原理
  • IEEE754 标准详解
  • 详细存储示例
  • 特殊值处理
  • 编程实践与常见陷阱
  • 总结

# 🔍 浮点数表示的基本原理

# 二进制小数表示

在计算机中,浮点数使用二进制科学计数法来表示。任何一个十进制小数都可以转换为二进制形式:

十进制: 1.8125
二进制: 1.1101
科学计数法: 1.1101 × 2^0
1
2
3

# 通用浮点数公式

任意一个二进制浮点数 V 可以表示为:

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

其中:

  • S (符号位):当 S=0 时,V 为正数;当 S=1 时,V 为负数
  • M (尾数/有效数字):大于等于 1,小于 2 的二进制小数
  • E (指数):指数位,用于表示数值的量级

# 实例分析

正数示例:

十进制: 5.0
二进制: 101.0
科学计数法: 1.01 × 2^2
因此: S=0, M=1.01, E=2
1
2
3
4

负数示例:

十进制: -5.0
二进制: -101.0
科学计数法: -1.01 × 2^2
因此: S=1, M=1.01, E=2
1
2
3
4

# 📊 IEEE754 标准详解

IEEE754 是国际电气电子工程师学会制定的浮点数表示标准,定义了浮点数在计算机中的存储格式。

# 单精度浮点数 (32位)

存储布局:

|  符号位  |    指数位    |        尾数位        |
|  1 bit   |   8 bits    |      23 bits        |
| 31       | 30      23  | 22               0  |
1
2
3
  • 符号位 (S):1位,表示正负
  • 指数位 (E):8位,范围 0-255,实际指数 = E - 127 (偏移量)
  • 尾数位 (M):23位,表示小数部分(隐含前导1,即尾数实际精度为 24 位)

# 双精度浮点数 (64位)

存储布局:

|  符号位  |     指数位     |           尾数位           |
|  1 bit   |   11 bits     |         52 bits           |
| 63       | 62        52  | 51                     0  |
1
2
3
  • 符号位 (S):1位
  • 指数位 (E):11位,范围 0-2047,实际指数 = E - 1023 (偏移量)
  • 尾数位 (M):52位

# 🔢 数值计算公式

规格化数值 (1 ≤ E ≤ 254 for float, 1 ≤ E ≤ 2046 for double):

V = (-1)^S × (1 + M/2^23) × 2^(E-127)    // 单精度
V = (-1)^S × (1 + M/2^52) × 2^(E-1023)   // 双精度
1
2

非规格化数值 (E = 0):

V = (-1)^S × (M/2^23) × 2^(-126)         // 单精度
V = (-1)^S × (M/2^52) × 2^(-1022)        // 双精度
1
2

注意规格化数有隐含的 1 +(隐含前导1),非规格化数没有——这是两者计算公式的关键区别。

# 📝 详细存储示例

# 示例1:存储 12.375

步骤1:转换为二进制

12.375 = 12 + 0.375

整数部分转换 (12):
12 ÷ 2 = 6  余数: 0
6  ÷ 2 = 3  余数: 0  
3  ÷ 2 = 1  余数: 1
1  ÷ 2 = 0  余数: 1
从下往上读取:1100 (二进制)

小数部分转换 (0.375):
0.375 × 2 = 0.75   → 整数部分: 0, 小数部分: 0.75
0.75  × 2 = 1.5    → 整数部分: 1, 小数部分: 0.5
0.5   × 2 = 1.0    → 整数部分: 1, 小数部分: 0.0 (结束)
从整数部分读取:0.011 (二进制)

结果:1100.011
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

步骤2:科学计数法

1100.011 = 1.100011 × 2^3

转换过程:
1. 将小数点左移3位,使整数部分为1
   1100.011 → 1.100011
2. 记录移动的位数作为指数:3
3. 标准化形式:1.100011 × 2^3
1
2
3
4
5
6
7

步骤3:提取各部分

符号位 S = 0 (正数)

指数计算:
实际指数 = 3
IEEE754偏移量 = 127 (单精度)
存储指数 = 3 + 127 = 130
130转换为二进制:10000010

尾数计算:
原始尾数:1.100011
去掉隐含的1:100011
补齐到23位:10001100000000000000000
1
2
3
4
5
6
7
8
9
10
11
12

步骤4:32位存储格式

0 10000010 10001100000000000000000
S    E                M
1
2

# 示例2:存储 -0.1875

步骤1:转换为二进制

0.1875 = 3/16 = 0.0011 (二进制)

详细转换过程:
0.1875 × 2 = 0.375  → 整数部分: 0, 小数部分: 0.375
0.375  × 2 = 0.75   → 整数部分: 0, 小数部分: 0.75
0.75   × 2 = 1.5    → 整数部分: 1, 小数部分: 0.5
0.5    × 2 = 1.0    → 整数部分: 1, 小数部分: 0.0 (结束)

从整数部分读取:0.0011 (二进制)
1
2
3
4
5
6
7
8
9

步骤2:科学计数法

0.0011 = 1.1 × 2^(-3)
1

步骤3:提取各部分

符号位 S = 1 (负数)
指数 E = -3 + 127 = 124 = 01111100 (二进制)
尾数 M = 1 (补齐23位) = 10000000000000000000000
1
2
3

步骤4:32位存储格式

1 01111100 10000000000000000000000
S    E                M
1
2

# ⚠️ 特殊值处理

特殊值 单精度位模式 (E + M) 说明
+0 S=0, E=0, M=0 正零
-0 S=1, E=0, M=0 负零,比较时与 +0 相等
+∞ S=0, E=255, M=0 正无穷,如 1.0/0.0 的结果
-∞ S=1, E=255, M=0 负无穷
NaN E=255, M≠0 非数值,如 0/0、√(-1) 的结果
非规格化数 E=0, M≠0 非常接近零的小数,用于填补最小规格化数和零之间的间隙

双精度中无穷大和 NaN 的指数为 E=2047。

# 💻 编程实践与常见陷阱

# 1. 精度损失:为什么 0.1 + 0.2 ≠ 0.3

print(0.1 + 0.2)  # 输出: 0.30000000000000004
1

根本原因:只有分母为 2 的幂的小数才能被二进制精确表示。0.375 = 3/16 可以精确转换,而 0.1 不行——用"乘2取整"转换 0.1 时永远不会结束:

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.2,进入无限循环)

0.1 = 0.00011001100110011...(0011 无限循环)
1
2
3
4
5
6
7
8

无限循环的部分只能被截断/舍入到 23/52 位尾数,因此 0.1 和 0.2 在存储时就已经是近似值,相加后误差暴露。

# 2. 浮点数比较

// 错误的比较方式
if (a == b) { ... }

// 正确的比较方式
#define EPSILON 1e-9
if (fabs(a - b) < EPSILON) { ... }
1
2
3
4
5
6

# 3. 范围和精度

类型 范围 有效数字 推导
float (32位) 约 ±1.4 × 10^(-45) 到 ±3.4 × 10^38 约 7 位 23 bit 尾数: log10(2^23) ≈ 6.9
double (64位) 约 ±4.9 × 10^(-324) 到 ±1.8 × 10^308 约 15-17 位 52 bit 尾数: log10(2^52) ≈ 15.65

# 4. 高精度计算

from decimal import Decimal, getcontext

# 设置高精度
getcontext().prec = 50

# 使用 Decimal 避免浮点数精度问题
a = Decimal('0.1')
b = Decimal('0.2')
result = a + b  # 精确结果:0.3
1
2
3
4
5
6
7
8
9

注意:Decimal('0.1') 必须用字符串构造。Decimal(0.1) 会先把 0.1 转成二进制浮点近似值,精度已经丢失。

# 5. 实用工具代码

十进制小数转二进制(乘2取整):

def decimal_to_binary_fraction(decimal_part, precision=23):
    """将十进制小数部分转换为二进制(precision 位)"""
    binary = ""
    for _ in range(precision):
        decimal_part *= 2
        if decimal_part >= 1:
            binary += "1"
            decimal_part -= 1
        else:
            binary += "0"
    return binary

print(decimal_to_binary_fraction(0.375))
# 输出: "01100000000000000000000"(23位,前 3 位 011 是有效位)
1
2
3
4
5
6
7
8
9
10
11
12
13
14

IEEE754 格式解析器:

import struct

def analyze_float(f):
    """分析浮点数的IEEE754表示"""
    # 获取32位二进制表示
    bits = struct.unpack('I', struct.pack('f', f))[0]
    
    # 提取各部分
    sign = (bits >> 31) & 1
    exponent = (bits >> 23) & 0xFF
    mantissa = bits & 0x7FFFFF
    
    print(f"数值: {f}")
    print(f"符号位: {sign}")
    print(f"指数位: {exponent} (实际指数: {exponent - 127})")
    print(f"尾数位: {mantissa:023b}")
    print(f"完整二进制: {bits:032b}")

analyze_float(12.375)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

运行输出(与上文示例1的步骤4互相印证):

数值: 12.375
符号位: 0
指数位: 130 (实际指数: 3)
尾数位: 10001100000000000000000
完整二进制: 01000001010001100000000000000000
1
2
3
4
5

# 6. 常见浮点数的存储

十进制 单精度IEEE754 说明
0.0 00000000 00000000 00000000 00000000 正零
-0.0 10000000 00000000 00000000 00000000 负零
1.0 00111111 10000000 00000000 00000000 1(E=127, M=0)
-1.0 10111111 10000000 00000000 00000000 -1
+∞ 01111111 10000000 00000000 00000000 正无穷(E=255, M=0)
-∞ 11111111 10000000 00000000 00000000 负无穷
NaN 0 11111111 1xxxxxx...(M≠0) 非数值,M 任意非零

# 7. 浮点数序列化

#include <string.h>

// 将浮点数转换为字节数组
union FloatBytes {
    float f;
    unsigned char bytes[4];
};

void serialize_float(float value, unsigned char* buffer) {
    union FloatBytes fb;
    fb.f = value;
    memcpy(buffer, fb.bytes, 4);
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# 📚 总结

IEEE754 浮点数存储标准是现代计算机处理实数的基础,理解其原理有助于:

  • 避免精度陷阱:只有分母为 2 的幂的小数才能被精确表示,0.1 这类数天然是近似值
  • 优化数值计算:选择合适的数据类型(float/double/Decimal)和比较方式(epsilon)
  • 调试浮点数问题:用位模式解析工具快速定位浮点数相关的 bug
  • 跨平台数据交换:IEEE754 保证浮点数在不同系统间的一致性

# 🔗 参考资料

  • IEEE754标准官方文档 (opens new window)
  • 浮点数在计算机中是如何存储的? (opens new window)
  • What Every Programmer Should Know About Floating-Point Arithmetic (opens new window)
  • IEEE754 在线转换器 (opens new window)
#cs-fundamentals#algorithm
上次更新: 2026/10/18, 22:14:48
正则表达式(Regex)完整指南
RAM ROM

← 正则表达式(Regex)完整指南 RAM ROM→

最近更新
01
Codex 使用与会话管理指南
10-17
02
Tailscale 入门:让自己的设备跨网络互联
10-15
03
GitHub CLI(gh)工作流与 CI 排查
10-08
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Jacky | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式