

项目背景
最近主要测试一个字段逻辑项目,基本逻辑就是实现数据表某一字段的数据总和作为结果来输出,由于总体逻辑较为复杂只能通过脚本来实现逻辑的细节,主要使用的是python语言,pandas第三方库来处理逻辑部分,在出现问题的地方使用了pandas的sum()函数求和,同时采用Decimal方法实现数据精度控制。



问题描述
输入数据运行程序时可以返回结果,但是在距离结果的最后一步出现了意外,输出结果明显和实际数据表不一致,导致与开发运行结果不一致,接下来手工复现一下问题场景,来看一下pycharm调试图:

从图中可以看到调试界面最下方输出显示var_result=21354.079999999998,继续查看我们会发现new_refund_info_df这张表中的repay_principal字段数据没有这些小数呀,可能你会猜想是由于后面paid_principal字段的数据含小数,那么我们再看一下paid_principal字段的数据

我们会发现此字段值都是1000,基本不含这种特殊小数。那么结果返回的特殊小数位应该是明显不对的,但是用法摆在那里计算的列中不存在异常的数据,应该来说结果一致才对。



初步研究
问题产生的几种可能情况:
1、是否在使用sum方法上有什么误解呢?
2、数据库中原始数据的数据类型是否会对结果有所影响
第一种情况的探究如下:
新建一个dataframe格式的数据表:

通过sum()方法对A列进行求和

运行结果一致,官网上关于sum的用法

那么到此为止暂且当成和sum的使用方法无关,进而需要考虑到数据层面上了
第二种情况的探究如下:
在形如mysql数据库的中数据类型主要有以下几种:

先来看一下,在数据类型中分为整型和浮点型,先来看一下int型和bigyint型数据,首先建立一张表字段类型如下

运行pandas的sum方法对int型数据求和结果如下

从结果中可以看出数据运行的结果无异常,接下来继续对bigint数据进行求和运算,结果如下

从图中可以看出结果依然是正常的,那么我们暂且认为在使用pandas的sum方法来计算int型和bigyint型数据时,不会出现特殊的小数情况。接下里继续看一下float类型数据是否运行正常,先建立一张数据表字段数据类型有float,具体如下

数据写入情况见下图

同样对float类型数据进行sum运算,运行结果如下:

可以看出结果依然和实际是相同的,没发现任何异常情况,那么我们暂且认为产生特殊小数位的问题和float类型数据无关
接下来我们对double类型的数据进行计算。结果如下

至此神奇的特殊小数问题复现了,通过和实际对比我们会发现跟实际结果相比计算结果在最后一位是有3这样一个误差,但是总体的实际结果是正常。到这里本来是可以告一段落了,但是抱着求是的态度我将数据做了些许的修改,将第三行的数据置为0运行结果如下:

从最后运行出来的结果中可以发现这个时候是正常了。
综上所述,pandas中使用sum方法运行double数据求和时可能会产生特殊小数



解决方法
通过对上面初步探究我们会发现实际在对double数据进行求和时算出的结果和实际是相差不大的,那么如何处理才能在最后得出一个和实际结果越相近的数值呢。这个时候我想到了是否可以使用四舍五入的方法来使的数据更加精准,继续对数据进行分析我们会发现dble字段的类型为double(10,5)表示的是5位小数,那么看sum的计算结果小数位达到16位,从第5位开始出现9999...这类数值知道第15位结束9,第16位变为7,可以这么猜想刚刚好三行数据3*5+1=16.那么在运行结果上只保留5位小数,第6位为9,四舍五入,最后结果就和实际相符了。可以使用这个方法达到这个效果:Decimal(sum).quantize(Decimal('0.00000'))



问题思考
本次问题的出现主要还是在数据类型不够清晰,double类型数据属于双精度类型,此次问题的出现可以设想到在mysql中用mysql的sum语句运行得出的结果依然可能是会和实际结果有偏差的。








