计算机的浮点数:零钱找零与粗尺子量长度的秘密(为何0.1+0.2≠0.3)

文章摘要

计算机浮点数精度问题可以用生活中的找零和测量来比喻:就像收银员只能用固定面值的硬币凑整找零,或只能以厘米为单位测量长度一样,计算机由于二进制存储空间的限制,大多数小数(如0.1、1/3)都无法被精确表示,只能近似存储。这是因为计算机只能精确存储分母为2的幂的分数(如0.5、0.25),其他十进制小数在二进制中往往是无限循环的,必须被截断处理,从而产生微小的误差。这种精度限制是由浮点数的二进制存储结构(如32位float的符号位、指数和尾数)决定的本质特性。


1. 生活中的比喻

比喻一:用零钱找钱

假设你在超市买东西,收银员只能用1元、5角、1角的硬币找零。
你买了9.37元的东西,给了10元,找你0.63元。
收银员只能给你0.6元(6角)或0.7元(7角),没法精确找出0.63元,只能“凑近”。

计算机的浮点数就像收银员手里的零钱,只有有限的“面额”(二进制位数),很多小数只能“凑近”,不能精确表示。


比喻二:用尺子量长度

你有一把只能量到厘米的尺子,想量出2.57厘米的长度。
你只能量出2.5厘米或2.6厘米,无法精确量出2.57厘米

计算机的浮点数就像这把有限精度的尺子,很多小数只能近似。


2. 计算机中的原理

二进制表示小数的局限

计算机用二进制(0和1)来存储所有数据。
有些十进制小数,在二进制里是无限循环的,没法用有限位数表示。

例子1:0.1
  • 十进制:0.1
  • 二进制:0.0001100110011001100110011…(无限循环)

计算机只能存储有限位数,比如float有23位有效数字,只能截断,所以0.1在计算机里其实是0.10000000149011612…

例子2:1/3
  • 十进制:0.333333…
  • 二进制:0.010101010101…(无限循环)

同样只能截断,存储为0.3333333…


3. 形象动画想象

想象你在画一条线,尺子只能画出1厘米、2厘米、3厘米……
你想画1.7厘米,只能画到1厘米或2厘米,总有一点点误差


4. 为什么会这样?

  • 计算机的存储空间有限(比如float是32位,double是64位)。
  • 二进制只能精确表示分母为2的分数(如1/2, 1/4, 1/8),但大多数小数不是这种形式。
  • 所以,绝大多数小数只能近似存储,不能精确。

5. 总结一句话

计算机的浮点数就像只能用零钱凑整,或者只能用粗尺子量长度,很多小数只能“差不多”,不能“刚刚好”。


下面我用形象解释+具体例子+二进制结构图解,帮你理解:


一、浮点数的二进制存储结构

以最常用的**IEEE 754标准的float(32位)**为例:

符号位指数部分(8位)尾数部分(23位)
1823
  • 符号位:0表示正数,1表示负数
  • 指数部分:用来表示数值的大小范围(2的多少次方)
  • 尾数部分:用来表示有效数字(小数部分)

float能表示的数的形式:

在这里插入图片描述


二、哪些小数能被精确表示?

1. 只能精确表示“分母是2的幂”的小数

比如:

  • 0.5 = 1/2
  • 0.25 = 1/4
  • 0.125 = 1/8
  • 0.75 = 3/4 = 0.5 + 0.25

这些都能被精确表示,因为它们在二进制下是有限位。

2. 不能精确表示的例子

  • 0.1(1/10):二进制是无限循环的
  • 0.2(1/5):二进制是无限循环的
  • 1/3:二进制是无限循环的

三、具体例子

1. 0.5 的二进制

  • 十进制:0.5
  • 二进制:0.1

float存储时:

  • 符号位:0
  • 指数部分:127(偏移量)= 01111111
  • 尾数部分:000…0(全0)

2. 0.1 的二进制

  • 十进制:0.1
  • 二进制:0.0001100110011001100110011…(无限循环)

float只能存储前23位,后面截断,所以0.1不能精确表示

3. 1/3 的二进制

  • 十进制:0.333333…
  • 二进制:0.010101010101…(无限循环)

同样只能截断,不能精确表示


四、用代码验证

你可以用C#、Python等语言查看float的二进制:

Python示例

import struct

def float_to_bin(num):
    [d] = struct.unpack(">I", struct.pack(">f", num))
    return f"{d:032b}"

print(float_to_bin(0.5))  # 00111111000000000000000000000000
print(float_to_bin(0.1))  # 00111101110011001100110011001101
  • 0.5的二进制很整齐
  • 0.1的二进制后面是“11001100…”无限循环,最后被截断

五、形象总结

  • 能精确表示的数:分母是2的幂的分数(比如1/2, 3/8, 5/16等),以及它们的整数倍。
  • 不能精确表示的数:绝大多数十进制小数(如0.1、0.2、1/3等),因为它们在二进制下是无限循环的。

六、可视化结构图

| 符号 |   指数   |                尾数(小数部分)                |
|  0   | 01111111 | 00000000000000000000000  (0.5)                 |
|  0   | 01111011 | 10011001100110011001101  (0.1, 截断)           |

七、结论

只有分母是2的幂的分数才能被浮点数精确表示,其他小数都只能近似。浮点数的二进制结构决定了这一点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你一身傲骨怎能输

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值