数据类型float是在计算机中是如何存储的

目前,绝大多数计算机系统都采用 IEEE 754标准 来表示浮点数。我们将以最常用的 单精度(32位,C/C++中的 float 为例进行详细讲解。

核心思想:科学计数法(二进制版)

浮点数的存储思路和我们熟悉的科学计数法(如 ( 3.14 × 102 ))一模一样,只不过它使用的是二进制科学计数法。

一个二进制浮点数可以表示为:
( N = (-1)S × M × 2E )

其中:

  • S:符号
  • M:尾数
  • E:指数

IEEE 754标准的核心工作,就是规定在固定的32位或64位空间里,如何存放S、M、E这三个部分。


内存布局(32-bit Float)

一个 float 变量占用 4字节(32位)。这32位被划分为三个部分:

| 1 bit         | 8 bits         | 23 bits               |
|---------------|----------------|-----------------------|
| 符号位 (S)    | 指数位 (E)     | 尾数位 (M)            |

我们来逐一拆解每个部分的意义。

1. 符号位
  • 位置:最高位(第31位)。
  • 长度:1 bit。
  • 含义
    • 0:表示正数。
    • 1:表示负数。
  • 这非常简单,它决定了整个数的正负。
2. 指数位
  • 位置:第30位到第23位。
  • 长度:8 bits。
  • 含义:存储指数。但这里有一个非常重要的技巧——偏移码

为什么需要偏移码?
指数E可以是正数(如 ( 210)),也可以是负数(如 ( 2-5}))。为了省去再用一个符号位来表示指数正负的麻烦,IEEE 754规定了一个偏移值

对于8位指数(float),这个偏移值是 127

实际存储的值(我们称为 e)与真实指数(E)的关系是:e = E + 127

  • 例子1:如果真实指数 E = 2,那么存储的 e = 2 + 127 = 129。129的二进制是 10000001
  • 例子2:如果真实指数 E = -3,那么存储的 e = -3 + 127 = 124。124的二进制是 01111100

这样一来,8位的指数域(e)能表示的范围是 0 ~ 255。通过减去127,真实指数E的范围就变成了 -127 ~ 128。这完美地解决了正负指数的问题。

3. 尾数位
  • 位置:第22位到第0位。
  • 长度:23 bits。
  • 含义:存储尾数 的小数部分。这里也有一个关键技巧——隐含的1

为什么有隐含的1?
在二进制科学计数法中,我们总是可以将一个数规范化,使其变成 ( 1.xxxx… × 2E) 的形式(注意,是二进制,所以整数部分只能是1)。

例如:十进制数 ( 5.0 ) 的二进制是 ( 101.0 )。科学计数法规范化后是 ( 1.01 × 22)。

请注意 1.01,整数部分的 1 是固定的!既然它永远是1,我们就没有必要浪费一个宝贵的bit去存储它。所以,在23位的尾数域中,我们只存储小数部分,也就是 .01

这个隐含的、没有存储的“1”,被称为“隐藏位”或“前导1”。

所以,当我们从内存中读取尾数位时,实际表示的尾数 M = 1 + (23位小数部分对应的十进制值)


完整转换流程:以 -6.625 为例

让我们亲手将 -6.625 这个十进制数转换为 IEEE 754 的 float 格式。

步骤1:确定符号 S
这是一个负数,所以 S = 1

步骤2:将绝对值转换为二进制

  • 整数部分:6 的二进制是 110
  • 小数部分:0.625 的二进制是多少? 0.625 × 2 = 1.25 → 取1,剩0.25;0.25 × 2 = 0.5 → 取0;0.5 × 2 = 1.0 → 取1,剩0。所以是 .101
  • 所以,( 6.62510 = 110.101{2})。

步骤3:规范化二进制数
110.101 写成 ( 1.xxxx × 2E) 的形式。
110.101 = ( 1.10101 × 22) (相当于小数点向左移动了2位)

所以:

  • 尾数部分 M = 1.10101 (记住,我们只存小数部分)
  • 真实指数 E = 2

步骤4:处理指数位(加偏移)
e = E + 127 = 2 + 127 = 129
129 的二进制是 10000001

步骤5:处理尾数位(取小数部分)
尾数 M = 1.10101
小数部分是 10101
我们需要用23位来表示它,所以在后面补0:
101011010 1000 0000 0000 0000 000 (补了18个0)

步骤6:组合所有部分
现在我们有:

  • S = 1
  • E = 10000001
  • M = 1010 1000 0000 0000 0000 000

组合起来就是:
1 10000001 10101000000000000000000

为了书写方便,通常写成16进制:
1100 0000 1101 0100 0000 0000 0000 0000 -> 0xC0D4 0000

你可以用在线工具或写一段简单的C代码(printf("%08X\n", *(int*)&yourFloat);)来验证这个结果。


特殊的指数值

指数域 e 的全0和全1被赋予了特殊含义,用来表示一些特殊情况:

指数 (e)尾数 (M)含义
00000000000...000±0 (正负由符号位S决定)
00000000非 000...000次正规数。此时隐藏位被视为0,而不是1。用于表示非常接近0的数。
0000000111111110任何值正规数。这就是我们上面讨论的正常情况。
11111111000...000无穷大 (±∞, 由符号位S决定)
11111111非 000...000NaN。表示“不是一个数字”,例如 sqrt(-1)0.0/0.0 的结果。

总结与要点

  1. 核心公式:Value = (-1)S × (1.M){2} × 2(E-127)
  2. 三个关键技巧
    • 科学计数法:将数字分解为符号、尾数和指数。
    • 偏移码:通过加一个固定值(127)来存储指数,避免为指数再设符号位。
    • 隐藏位:对于正规数,尾数的整数部分1被隐含,不存储,从而在23位中获得了24位的精度。
  3. 精度问题根源:浮点数在内存中是以离散的、有限的二进制小数表示的。很多在十进制中有限的数(如0.1),在二进制中是无限循环小数,无法被精确表示,只能存储一个近似值。这就是为什么 0.1 + 0.2 != 0.3 的根本原因。
  4. 双精度(double):64位 double 的原理完全一样,只是位数不同:
    • 1位符号位
    • 11位指数位(偏移值为1023)
    • 52位尾数位
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值