python中的Decimal与float计算踩坑记录

549 阅读2分钟

前置场景

在工作中,遇到需要从mysql数据库中查询两个字段,一个为int类型,一个为Decimal类型,计算两者相乘的结果

select a, b from t_data;
# 查询结果{'a': 12346, 'b': Decimal('0.00001')}

踩坑

之前都是用的基础类型int,float啥的直接进行计算,所以也想着转换成float进行计算。

b = float(b)
c = a * b
print(c)

然后一看结果,一脸懵:0.1234600001

分析

试着去写了个demo去复现这个问题,但并没有成功,输出结果无误。

f = 1e-05
i = 12345
print(f*i)

突然想到浮点数计算可能会存在精度损失,就去查了一下float与Decimal互相转换的问题。

Decimal对象是 Python 中用于高精度十进制运算的类。它位于decimal模块中,并提供了精确的十进制计算功能。 Decimal对象和浮点数(float)是两种不同的数据类型,它们之间的转换可能会导致精度丢失。float类型是基于二进制浮点数表示,而Decimal对象使用十进制表示。 如果你需要将Decimal对象转换为浮点数,可以使用float函数进行转换。但是请注意,在转换过程中可能会发生精度丢失。 以下是一个示例代码,演示如何将Decimal对象转换为浮点数:

python from decimal import Decimal 
# 创建一个 Decimal 对象 
decimal_num = Decimal('3.141592653589793') 
# 将 Decimal 对象转换为浮点数 
float_num = float(decimal_num) 
print(decimal_num) 
print(float_num) 

在上述示例中,我们首先创建了一个Decimal对象decimal_num,然后使用float函数将其转换为浮点数float_num

请注意,由于十进制和二进制的表示方式不同,转换为浮点数可能会导致精度损失。在进行需要高精度计算的任务时,建议尽量保持使用Decimal对象进行计算,以避免精度丢失。

解决

虽然没办法复现,但大致可以定位为Decimal对象转为float后参与计算导致了精度损失。 我试了一下直接用Decimal参与计算,计算结果果然恢复正常

引申问题

计算问题是解决了,但又引入了新的问题。 计算结果为Decimal类型,无法实现json序列化。 解决方法是将Decimal类型转换为其他可json序列化的类型,如float(已经计算完成,不用担心精度损失),str等