Python 的 0.1 + 0.2 为什么不等于 0.3?拆开 IEEE 754 与那串死循环小数

几乎每个 Python 新手都会撞上这一幕:

python 复制代码
>>> 0.1 + 0.2
0.30000000000000004
>>> 0.1 + 0.2 == 0.3
False

这不是 Python 的 bug,也不是你代码写错了。这是二进制的宿命。

一、0.1 在内存里根本不是 0.1

浮点数按 IEEE 754 双精度(64 位)存:1 位符号 + 11 位指数 + 52 位尾数。问题出在"尾数"上------它是二进制小数。

十进制的 0.1,换成二进制是:

复制代码
0.000110011001100110011001100110011...  (0011 无限循环)

跟十进制里 1/3 = 0.333... 一样,是个无限循环小数。而 double 的尾数只有 52 位,存不下,只能截断。

验证一下(CPython 3.13):

python 复制代码
>>> float.hex(0.1)
'0x1.999999999999ap-4'
>>> float.hex(0.2)
'0x1.999999999999ap-3'
>>> struct.pack('>d', 0.1).hex()
'3fb999999999999a'

999999999999a 末尾那个 a,就是被四舍五入进去的零头。真正存进去的值是:

python 复制代码
>>> Decimal(0.1)
Decimal('0.1000000000000000055511151231257827021181583404541015625')

比 0.1 大约多 5.55e-18。所以 0.1 + 0.2 算出来是 0.30000000000000004------两个"零头"加起来溢出了最小精度位。

二、误差会累积,而且藏得很深

单次运算的误差小到看不见,攒起来就藏不住了:

python 复制代码
>>> sum([0.1] * 10) == 1.0
True          # 巧了,这回正好对齐
>>> x = 0.0
>>> for _ in range(1_000_000): x += 0.1
>>> x
100000.00000133288      # 数学上应该是 100000.0

一百万次累加,误差累积到约 1.33e-6(实测耗时 130ms)。中间每一步都在"合法地"丢精度,没有任何报错。

更极端的两种情况:

python 复制代码
>>> 1e16 + 1 == 1e16
True                    # 1 被彻底吃掉了
>>> 1e308 * 10
inf                     # 溢出成无穷,不报异常

1e16 + 1 == 1e16 特别值得记:double 在 1e16 这个量级上,相邻两个可表示数的间距已经是 2,中间的 1 根本不存在。

三、round 也不是你以为的那个 round

python 复制代码
>>> round(2.675, 2)
2.67                    # 不是 2.68
>>> round(1.005, 2)
1.0                     # 不是 1.01

原因同样是底层表示:2.675 实际存的比 2.675 略小,所以被"舍"掉了。Python 用的还是银行家舍入(四舍六入五取偶),不是我们小学学的四舍五入。

四、怎么办:三种正确的姿势

1. 比较用 math.isclose,别用 ==

python 复制代码
>>> math.isclose(0.1 + 0.2, 0.3)
True

2. 要精确就上 Decimal(注意:传字符串,别传 float)

python 复制代码
>>> Decimal('0.1') + Decimal('0.2')
Decimal('0.3')          # 干净
>>> Decimal(0.1) + Decimal(0.2)
Decimal('0.3000000000000000166533453694')   # 传 float 就白搭

金额计算必须用 Decimal('19.99') 这种字符串构造,从数据库读出来的也先转字符串。

3. 要完全精确用 Fraction

python 复制代码
>>> Fraction(1, 10) + Fraction(2, 10) == Fraction(3, 10)
True

Fraction 走整数分子分母,零误差,代价是慢。

五、一句话记住

0.1 + 0.2 != 0.3 不是 Python 的错,是"用有限的二进制位去装无限的十进制小数"这件事本身就做不到。

记住三条就够:比较用 isclose、算钱用 Decimal、别信 round 能救你。

(实测环境:CPython 3.13,Windows;文中数字均可直接复现)

相关推荐
LK_071 小时前
【Agent】工具调用的安全边界:从模型输出到程序执行
人工智能·笔记·python·agent
十二同学啊1 小时前
Python 工程化详解:项目结构、依赖管理、代码质量、测试与部署
java·开发语言·python
打工仔折腾 AI1 小时前
鸿蒙移动端直连 Windows 远程桌面配置与性能调优实践
人工智能·windows·后端·python·华为·性能优化·harmonyos
程序员飞哥1 小时前
AI 编程额度怎么用才不浪费?
java·前端·后端
二月夜1 小时前
Redis 高频面试题全解:缓存三大问题、大 key 治理与分布式锁之争(2026 招聘 JD 真题复盘)
redis·分布式·后端·缓存
IT_陈寒1 小时前
Vite开发实战:从入门到精通
前端·人工智能·后端
樱花落木兰1 小时前
Redis 消息队列的可靠性演进:从 List、Pub/Sub 到 Stream 消费者组,以及 8.x 版本改变了什么
java·开发语言·经验分享·redis·分布式·python·软件工程
学长毕设1 小时前
基于SpringBoot的文具商城在线零售系统-附源码
spring boot·后端·零售
mumu1572 小时前
快速上手大模型推理vllm
人工智能·python·大语言模型·agent·vllm