Python中Decimal算术不一致(舍入误差)及相等判断方法咨询
为什么Decimal的乘法与除法结果不相等,以及如何正确判断Decimal数值相等
这问题问得太到位了——很多人刚接触decimal模块时,都会默认它能彻底消除所有舍入误差,但其实这里藏着一个关于精度上下文和运算步骤的小细节!
问题根源:运算步骤中的多次舍入
decimal模块确实解决了普通浮点数的二进制舍入误差,但它的运算精度是由**当前上下文的精度(precision)**决定的(默认是28位有效数字)。当你拆分运算步骤时,每一步的中间结果都会被舍入到当前精度,这就会和一步到位的运算产生细微差异:
- 先算
Decimal('1') / Decimal('0.9323'):这个结果是一个无限循环的十进制小数,会被舍入到28位有效数字 - 再乘以
Decimal('0.3637'):这一步又会对乘积进行一次舍入 - 而直接计算
Decimal('0.3637') / Decimal('0.9323')是一步完成的除法,只做一次舍入
两次舍入和一次舍入的结果,在最后几位有效数字上会有细微差别,所以直接用==判断会返回False。
我们可以用代码验证这个差异:
from decimal import Decimal, getcontext # 查看默认精度 print(getcontext().prec) # 输出:28 a = Decimal('0.3637') * (Decimal('1') / Decimal('0.9323')) b = Decimal('0.3637') / Decimal('0.9323') print(f"a的值:{a}") print(f"b的值:{b}") print(f"a == b?{a == b}") # 输出:False
正确判断Decimal数值相等的方法
既然直接用==会因为细微舍入误差失效,我们可以用以下几种可靠的方式:
1. 使用compare()方法
Decimal类自带的compare()方法会返回三个值:Decimal('-1')(小于)、Decimal('0')(等于)、Decimal('1')(大于)。我们可以通过判断返回值是否为0来确认相等:
print(f"a和b是否相等?{a.compare(b) == Decimal(0)}") # 输出:True
2. 检查差值是否在可接受误差范围内
根据你的业务需求,设定一个极小的误差阈值(比如1e-25),判断两个数的差值绝对值是否小于这个阈值:
epsilon = Decimal('1e-25') print(f"a和b是否相等?{abs(a - b) < epsilon}") # 输出:True
3. 量化到相同的小数位数后比较
用quantize()方法把两个数统一到相同的小数位数,再进行比较:
# 统一保留20位小数 print(f"a和b是否相等?{a.quantize(Decimal('1e-20')) == b.quantize(Decimal('1e-20'))}") # 输出:True
额外提示
如果你需要更高(或更低)的精度,可以通过修改上下文的prec属性来调整:
getcontext().prec = 50 # 将精度设置为50位有效数字
不过即使提高精度,只要涉及无限循环小数的运算,还是会有舍入,所以永远不要依赖==来判断两个经过运算的Decimal是否相等,而是用上面提到的方法。
内容的提问来源于stack exchange,提问作者Aleksandr Gavrilov
相关产品推荐
相关产品推荐

