Python中如何完美避免浮点数计算的常见坑?
万万没想到
自己用 round 还是有坑
新上手 python,打算跑脚本对账用的,可这坑太大了吧
之前是写 php 的没遇到过这个问题,查了一下都说是进制的问题道理我是懂了,可是日常怎么避坑比较完美?请各位大佬指教。
Python中如何完美避免浮点数计算的常见坑?
- 用 decimal
> from decimal import *
> getcontext().rounding = ROUND_HALF_UP
> Decimal(“7.2”) * Decimal(“9.3”)
Out[3]: Decimal(‘66.96’)
> Decimal(“0.1”) + Decimal(“0.1”) + Decimal(“0.1”)
Out[4]: Decimal(‘0.3’)
> Decimal(“2.675”).quantize(Decimal(“0.00”))
Out[5]: Decimal(‘2.68’)
2. 用分作为单位使用整数
核心就一句话:用 Decimal 处理精确小数,用 math.isclose 比较浮点数。
别用 == 直接比较浮点数,也别指望 0.1 + 0.2 == 0.3 能返回 True。这是二进制表示决定的,不是 bug。
1. 需要精确十进制计算(比如钱)
直接用 decimal.Decimal,记得用字符串初始化。
from decimal import Decimal, getcontext
# 设置精度(可选)
getcontext().prec = 6
price = Decimal('19.99')
tax = Decimal('0.08')
total = price * (1 + tax) # 结果是 Decimal('21.5892')
2. 比较两个浮点数是否“足够接近”
用 math.isclose(),它能处理精度容差。
import math
a = 0.1 + 0.2
b = 0.3
print(math.isclose(a, b)) # True
# 可以自定义容差
print(math.isclose(a, b, rel_tol=1e-9, abs_tol=1e-12))
3. 科学计算或一般数值运算
用 numpy 或保持默认浮点,但比较时用 np.allclose()。
import numpy as np
arr_a = np.array([0.1, 0.2])
arr_b = np.array([0.3, 0.4])
print(np.allclose(arr_a + arr_b, np.array([0.4, 0.6]))) # True
总结:要精确用 Decimal,要比较用 isclose。
别用 float,,用 decimal
乘以 100,用 int
楼上说的对,升几位用整数
#1
#3
#4
感谢各位已决定用 decimal
PHP 没有这种情况?都是 IEE754 怎么可能没有。
#6
可能学艺不精,但是 php 里没踩到这个坑,至少以上情况 php 正常
大部分语言的浮点数都有同样或类似的问题,特别是编译型语言。 根源是浮点数在计算机中的存储格式。
方案的话,上面说的基本上就是了
楼主认为这个是 python 的坑说明基础知识太差。
所有编程语言都有这个问题,这个是计算机体系结构本身制约造成的。
浮点数运算的结果经常会出现无限循环或无限不循环小数,计算机只能用有限位来近似表示。
别的语言也是一样的,只不过他们输出的时候没输出这么多位而已。
任何浮点数,只要小数部分不能写成 2 的负整数次幂的和,就有误差。
比如 0.25 = 2^{-2},所以是精确的。但是 0.1 无法写成任何 2 的负整数次幂,所以会有误差。
一般应用不必用 decimal,因为无限不循环小数的存在的所以现在的计算机架构无法完美表示浮点数。你用的任何方式都是一定条件的取舍。
php 也是一样的
“对账用”
在 python2.x 里 decimal 性能比浮点数低 1000 倍。
python3.x 里 decimal 被改用 C 语言实现了性能要好一些。
所有还要看你对性能是不是敏感。
这是 float 的存储格式决定的,大部分语言的 float 存储格式都是这样的。 从根源上来说,是二进制数据表示浮点数必然会丢失精度的问题。
Perl6 not have this problem:
say 0.88882 - 0.22335 == 0.66547; # True
[Try it online!]( https://tio.run/##K0gtyjH7/784sVLBQM8CCIwUdIEsIyNjY1MFW1sg08zM1MTc@v9/AA “Perl 6 – Try It Online”)
记住一句话:涉及到钱的问题,一律用 decimal
确实基础差 要努力了

