当前位置: 首页 > news >正文

【浮点数存储】结构、精度说明

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 一、IEEE 754
      • 1. IEEE 754基本结构
      • 2. 特殊值表示
      • 3. C++中查看浮点数存储
      • 4. 浮点数精度问题
      • 总结
  • 二、举例说明
    • 1. 单精度转换
      • 步骤1:将十进制数转换为二进制
      • 步骤2:规范化二进制(科学计数法)
      • 步骤3:计算各组成部分
      • 步骤4:组合所有位
      • 验证:用C++代码解析
      • 输出结果
    • 2. 双精度转换
      • 步骤1:将十进制数转换为二进制
      • 步骤2:规范化二进制(科学计数法)
      • 步骤3:计算各组成部分
      • 步骤4:组合所有位
      • 验证:用C++代码解析
      • 输出结果
      • 双精度与单精度的核心区别
  • 三、 精度说明
      • 1. 核心公式:二进制位数 → 十进制位数
      • 2. 具体计算
      • 3. 为什么是“范围”而非固定值?
      • 4. 直观例子:用“计数能力”验证
      • 总结
  • 总结


一、IEEE 754

IEEE 754是国际电工委员会(IEEE)制定的浮点数表示标准,广泛用于计算机中表示和运算浮点数。在C++中,floatdouble类型通常分别对应IEEE 754的单精度(32位)和双精度(64位)格式。

1. IEEE 754基本结构

IEEE 754浮点数由三部分组成:符号位指数位尾数位,结构如下:

类型总位数符号位(S)指数位(E)尾数位(M)指数偏移量
单精度(float)321位(第31位)8位(30-23位)23位(22-0位)127
双精度(double)641位(第63位)11位(62-52位)52位(51-0位)1023
  1. 符号位(S)
    0表示正数,1表示负数(仅表示符号,不影响数值大小)。

  2. 指数位(E)
    用于表示浮点数的指数部分,采用"偏移码"存储(避免区分正负指数)。
    实际指数 = 存储的指数值 - 偏移量(单精度:E - 127;双精度:E - 1023)。

  3. 尾数位(M)
    表示浮点数的有效数字,采用"隐藏位"规则:默认整数部分为1(节省1位存储),因此实际有效数字为 1.M(二进制)。

2. 特殊值表示

IEEE 754定义了几种特殊值:

  • 0:指数位全0,尾数位全0(符号位可0或1,分别表示+0和-0)。
  • 无穷大(±∞):指数位全1,尾数位全0(符号位区分正负)。
  • NaN(非数值):指数位全1,尾数位非0(表示无效运算结果,如0/0)。

3. C++中查看浮点数存储

在C++中,可通过联合体(union)指针类型转换访问浮点数的二进制存储(内存级表示)。

示例代码:解析float的IEEE 754表示

#include <iostream>
#include <bitset>
using namespace std;// 用联合体解析float的存储结构
union FloatUnion {float f;uint32_t u; // 32位无符号整数,与float共享内存
};void analyzeFloat(float num) {FloatUnion u;u.f = num;uint32_t bits = u.u;// 提取符号位(第31位)bool sign = (bits >> 31) & 1;// 提取指数位(30-23位,共8位)uint8_t exp_bits = (bits >> 23) & 0xFF;// 提取尾数位(22-0位,共23位)uint32_t mantissa_bits = bits & 0x7FFFFF;// 计算实际指数int exponent = exp_bits - 127;cout << "浮点数: " << num << endl;cout << "二进制表示: " << bitset<32>(bits) << endl;cout << "符号位: " << (sign ? "1 (负数)" : "0 (正数)") << endl;cout << "指数位: " << bitset<8>(exp_bits) << " (存储值:" << (int)exp_bits << ",实际指数:" << exponent << ")" << endl;cout << "尾数位: " << bitset<23>(mantissa_bits) << " (实际有效数字:1." << bitset<23>(mantissa_bits) << ")" << endl;cout << "-------------------------" << endl;
}int main() {analyzeFloat(0.0f);       // 0analyzeFloat(1.0f);       // 正数analyzeFloat(-3.5f);      // 负数analyzeFloat(1e30f);      // 大数值analyzeFloat(1e-30f);     // 小数值return 0;
}

4. 浮点数精度问题

由于尾数位长度有限(单精度23位,双精度52位),很多十进制小数无法精确表示(如0.1),会产生精度误差。例如:

#include <iostream>
using namespace std;int main() {float a = 0.1f;float sum = 0.0f;for (int i = 0; i < 10; ++i) {sum += a; // 预期sum=1.0,但实际有误差}cout << "sum = " << sum << endl; // 输出可能为0.9999999//但实际输出1,后面有文章会说明return 0;
}

这是因为0.1的二进制是无限循环小数,IEEE 754只能存储近似值。

总结

  • IEEE 754通过符号位、指数位和尾数位表示浮点数,节省存储空间并统一格式。
  • C++中float(32位)和double(64位)遵循该标准。
  • 浮点数存在精度限制,实际开发中需注意误差问题(如需精确计算,可使用decimal库或整数模拟)。

二、举例说明

1. 单精度转换

下面我们以 3.75(十进制) 为例,详细演示其如何按照IEEE 754标准转换为单精度(32位)浮点数的完整过程。

步骤1:将十进制数转换为二进制

首先将3.75转换为二进制表示:

  • 整数部分(3):3 ÷ 2 = 1 余 11 ÷ 2 = 0 余 1 → 整数部分二进制为 11
  • 小数部分(0.75):0.75 × 2 = 1.5 取10.5 × 2 = 1.0 取1 → 小数部分二进制为 11

因此,3.75的二进制为:11.11

步骤2:规范化二进制(科学计数法)

IEEE 754要求将二进制数表示为 1.M × 2^E 的形式(隐藏整数位1以节省空间):

  • 11.11 可表示为 1.111 × 2^1(小数点左移2位,指数为2-1=1)
    • 其中:M = 111(尾数位,去掉整数位1)
    • 指数 E = 1

步骤3:计算各组成部分

单精度浮点数(32位)结构:1位符号位 + 8位指数位 + 23位尾数位

  1. 符号位(S)
    3.75是正数 → S = 0

  2. 指数位(E)
    单精度偏移量为127,存储的指数值 = 实际指数 + 127
    → 存储值 = 1 + 127 = 128 → 二进制为 10000000

  3. 尾数位(M)
    规范化后的尾数是111,需补足23位(不足补0)
    → 尾数位为 11100000000000000000000

步骤4:组合所有位

将符号位、指数位、尾数位拼接:

  • 符号位:0
  • 指数位:10000000
  • 尾数位:11100000000000000000000

最终32位二进制表示:
0 10000000 11100000000000000000000

验证:用C++代码解析

以下代码可验证上述计算结果:

输出结果

数字: 3.75
IEEE 754单精度表示:
符号位: 0 (0=正, 1=负)
指数位: 10000000 (存储值=128, 实际指数=1)
尾数位: 11100000000000000000000
完整32位二进制: 01000000011100000000000000000000

结果与我们手动计算的完全一致,验证了3.75的IEEE 754表示的正确性。

2. 双精度转换

下面以 3.75(十进制) 为例,详细演示其按照IEEE 754标准转换为双精度(64位)浮点数的完整过程。

步骤1:将十进制数转换为二进制

首先将3.75转换为二进制表示:

  • 整数部分(3):
    3 ÷ 2 = 1 余 11 ÷ 2 = 0 余 1 → 整数部分二进制为 11
  • 小数部分(0.75):
    0.75 × 2 = 1.5 取整数10.5 × 2 = 1.0 取整数1 → 小数部分二进制为 11

因此,3.75的二进制为:11.11

步骤2:规范化二进制(科学计数法)

IEEE 754要求将二进制数表示为 1.M × 2^E 的形式(隐藏整数位1以节省空间):

  • 11.11 可表示为 1.111 × 2^1(小数点左移2位,指数为2-1=1)
    • 其中:M = 111(尾数位,去掉整数位1)
    • 指数 E = 1

步骤3:计算各组成部分

双精度浮点数(64位)结构:1位符号位 + 11位指数位 + 52位尾数位

  1. 符号位(S)
    3.75是正数 → S = 0

  2. 指数位(E)
    双精度偏移量为1023,存储的指数值 = 实际指数 + 1023
    → 存储值 = 1 + 1023 = 1024 → 二进制为 10000000000(11位)

  3. 尾数位(M)
    规范化后的尾数是111,需补足52位(不足补0)
    → 尾数位为 1110000000000000000000000000000000000000000000000000(共52位)

步骤4:组合所有位

将符号位、指数位、尾数位拼接:

  • 符号位:0
  • 指数位:10000000000
  • 尾数位:1110000000000000000000000000000000000000000000000000

最终64位二进制表示:
0 10000000000 111000000000000000000000000000000000000000000000000000000000

验证:用C++代码解析

以下代码可验证上述计算结果:

#include <iostream>
#include <bitset>
#include <cstdint>
using namespace std;// 联合体用于访问double的64位存储
union DoubleUnion {double d;uint64_t u;
};void printIEEE754Double(double num) {DoubleUnion u;u.d = num;uint64_t bits = u.u;// 提取各部分bool sign = (bits >> 63) & 1;                     // 符号位(第63位)uint16_t exp = (bits >> 52) & 0x7FF;              // 指数位(62-52位,共11位)uint64_t mantissa = bits & 0xFFFFFFFFFFFFF;       // 尾数位(51-0位,共52位)// 输出结果cout << "数字: " << num << endl;cout << "IEEE 754双精度表示: " << endl;cout << "符号位: " << sign << " (0=正, 1=负)" << endl;cout << "指数位: " << bitset<11>(exp) << " (存储值=" << exp << ", 实际指数=" << (int(exp) - 1023) << ")" << endl;cout << "尾数位: " << bitset<52>(mantissa) << endl;cout << "完整64位二进制: " << bitset<64>(bits) << endl;
}int main() {printIEEE754Double(3.75);return 0;
}

输出结果

数字: 3.75
IEEE 754双精度表示:
符号位: 0 (0=正, 1=负)
指数位: 10000000000 (存储值=1024, 实际指数=1)
尾数位: 1110000000000000000000000000000000000000000000000000
完整64位二进制: 01000000000011100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

结果与手动计算完全一致,验证了3.75的IEEE 754双精度表示的正确性。

双精度与单精度的核心区别

对比项单精度(float)双精度(double)
总位数32位64位
指数位长度8位11位
尾数位长度23位52位
指数偏移量1271023
精度约6-7位十进制约15-17位十进制

对于3.75这类可精确表示的数,单精度和双精度的区别仅在于尾数位补0的长度;但对于无限循环二进制小数(如0.1),双精度因尾数位更长,精度会显著更高。

三、 精度说明

double的53位二进制有效数字(尾数位+隐藏位)换算为十进制约15~17位有效数字,这个换算基于不同进制下有效数字的精度对应关系,核心是通过“信息量等效”计算:二进制的n位有效数字能表达的信息量,与十进制的m位有效数字大致相当,其中m ≈ n × log10(2)

1. 核心公式:二进制位数 → 十进制位数

二进制和十进制的有效数字位数转换遵循对数关系:
十进制有效数字位数 ≈ 二进制有效数字位数 × log₁₀(2)

其中:

  • log₁₀(2) ≈ 0.3010(2的常用对数,代表二进制一位的信息量相当于十进制0.3010位的信息量);
  • double的二进制有效位数是53位(52位尾数位+1位隐藏的“1”)。

2. 具体计算

将53位二进制有效数字代入公式:
53 × log₁₀(2) ≈ 53 × 0.3010 ≈ 15.953

结果约为16,这就是为什么double的十进制有效数字位数通常描述为**1517位**(实际计算是15.95,四舍五入后在1517的范围内)。

3. 为什么是“范围”而非固定值?

  1. 有效数字的定义
    十进制有效数字的“位数”是指从第一个非零数字开始的可靠数字数量。例如,0.00123有3位有效数字,12345.67有7位。
    由于二进制到十进制的转换不是整数倍关系,53位二进制能覆盖的十进制有效数字会因数值的大小(指数部分)略有差异:

    • 对于接近1的数(如1.2345...),有效数字更接近17位;
    • 对于非常大或非常小的数(如1.2345e100),有效数字可能略少于17位,但不会低于15位。
  2. 实际存储的限制
    53位二进制最多能区分2^53个不同的数值(每个二进制位有0/1两种可能)。对应的十进制下,能区分的数值数量约为10^16(因为10^16 ≈ 2^53,两边取对数:16×log10(10)=16 ≈ 53×log10(2)≈15.95)。
    这意味着double能精确区分的数值数量,与十进制下用16位有效数字能区分的数量大致相当,因此有效数字位数在15~17位之间。

4. 直观例子:用“计数能力”验证

  • 53位二进制的计数能力:能表示2^53个不同的数值(约9.0×10^15)。
  • 16位十进制的计数能力:能表示10^16个不同的数值(约1.0×10^16)。
    两者数量级接近(9e15 vs 1e16),说明53位二进制的“分辨能力”与16位十进制相当。

如果用15位十进制,计数能力是1e15,远小于2^539e15),无法覆盖;
如果用17位十进制,计数能力是1e17,远大于2^53,但double的硬件限制达不到这么高的分辨能力。
因此,53位二进制对应的十进制有效数字只能是15~17位。

总结

double的53位二进制有效数字换算为十进制15~17位,本质是信息量等效

  • 公式:53 × log₁₀(2) ≈ 16
  • 范围:因数值大小和计数能力的匹配,最终落在15~17位;
  • 意义:这是double精度的上限——超过17位的十进制数字是不可靠的,因为53位二进制无法提供更多的信息量。

简单来说:53位二进制能“记住”的细节,大致相当于十进制下16位有效数字能“记住”的细节,这就是精度换算的核心。


总结

以上是浮点数存储基本知识,接下来有一个下篇介绍一些注意点与自己的误区

http://www.dtcms.com/a/324455.html

相关文章:

  • 联邦学习之------VT合谋
  • Pico+unity VR入门开发超详细笔记2025
  • 人形机器人强化学习入门实践1part
  • stm32没有CMSIS文件
  • Redis如何实现一个分布式锁?
  • 第4章 程序段的反复执行3 do-whiile语句P139练习(题及答案)
  • [Linux]学习笔记系列 -- [arm][lib]
  • C++的嵌套结构体
  • Deep Learning MNIST手写数字识别 Mac
  • 【从源码角度深度理解 CPython 的垃圾回收机制】:第2课循环引用:标记清除-分代回收
  • 7.企业级AD活动目录的备份与恢复策略
  • 【celeba】-数据集的介绍
  • 驱动电路设计
  • Ollama+Deepseek+Docker+RAGFlow打造自己的私人AI知识库
  • 【软件测试】性能测试 —— 工具篇 JMeter 介绍与使用
  • AI质检数据准备利器:基于Qt/QML 5.14的图像批量裁剪工具开发实战
  • 升级 JDK 17 碰到的请求 https 问题
  • 从0开始的中后台管理系统-5(userList页面功能实现)
  • 自测电脑有没有木马
  • 深度学习周报(8.4~8.10)
  • 使用binutils工具解析目标文件符号表(叁)
  • Datawhale AI夏令营 多模态RAG环境问题
  • 海关 瑞数 失信企业 逆向 分析 后缀 rs
  • es查询小结
  • CSS优先级、HTTP响应状态码
  • BGP综合大实验
  • 人工智能-python-机器学习-模型选择与调优实战指南:从交叉验证到朴素贝叶斯分类
  • 为wordpress顶部header.php文件中调用不同的标题和摘要
  • 学习中的杂项知识
  • 在Word和WPS文字一页中实现一栏与多栏混排