Python精确四舍五入/保留小数位数问题及Excel数据自动对比的优化处理

今天在提取两个Excel中数据的时候,准备做自动对比判断两个值是否相等,数据在保留小数位数时,用了python的round函数来四舍五入,但是发现计算的结果和自己预期的不一样,于是查了很多的资料,算是了解了。
openpyxl库提取数字时候的单元格数据,为float浮点型数据,那么就会存在大大小小的精度问题。

小白慢慢踏全栈之路,多多点赞关注收藏支持哈~~

1、round函数,精度低,如果需要考虑精度,尽量避免使用

print(round(0.55,1))
>>> 0.6
print(round(1.55,1))
>>> 1.6
print(round(2.55,1))
>>> 2.5
print(round(3.55,1))
>>> 3.5

在python官方文档中,有这样提到:

Note: The behavior of round() for floats can be surprising: for example, round(2.675, 2) gives 2.67 instead of the expected 2.68. This is not a bug: it’s a result of the fact that most decimal fractions can’t be represented exactly as a float. See Floating Point Arithmetic: Issues and Limitations for more information.

注意:对于浮点数来说,round()的行为可能令人惊讶,例如,round(2.675, 2)给出了2.67,而不是预期的2.68。这并不是一个bug,它是大多数小数部分不能精确表示为浮点数的结果。有关更多信息,请参阅浮点算法:问题和限制。

那么如何精确的四舍五入呢?往下看!

2、Decimal函数,精度高,推荐使用

Decima(num)中的 num 需要是字符串形式的数字,而不能直接是浮点型数字,因为浮点型数据本身就不是精确的,在计算机存储中是以二进制存储的,会有精度的损失,你肉眼看到的是0.55,但计算机中并不是实际的0.55,它实际的值可以通过decimal.Decimal模块可以看出。
使用字符串存储输入的数据就可以精确存储输入的数据,即输入是多少输出就是多少。

from decimal import Decimal

#浮点数形式的数字
print(Decimal(0.55))
>>> 0.5500000000000000444089209850062616169452667236328125
print(Decimal(1.55))
>>> 1.5500000000000000444089209850062616169452667236328125
print(Decimal(2.55))
>>> 2.54999999999999982236431605997495353221893310546875
print(Decimal(3.55))
>>> 3.54999999999999982236431605997495353221893310546875

#字符串形式的数字
print(Decimal('0.55'))
>>> 0.55
print(Decimal('1.55'))
>>> 1.55
print(Decimal('2.55'))
>>> 2.55
print(Decimal('3.55'))
>>> 3.55

使用decimal模块的 Decimal() + quantize() 实现舍五入
四舍六入五成双式舍入法 :rounding = ROUND_HALF_EVEN,decimal模块默认
百度百科:四舍六入五成双式舍入法
四舍五入法:rounding = ROUND_HALF_UP
百度百科:四舍五入法

from decimal import Decimal

num1 = '0.55'
num2 = '1.55'
num3 = '2.55'
num4 = '3.55'
num1 = Decimal(num1).quantize(Decimal('0.0'), rounding = "ROUND_HALF_UP")
num2 = Decimal(num2).quantize(Decimal('0.0'), rounding = "ROUND_HALF_UP")
num3 = Decimal(num3).quantize(Decimal('0.0'), rounding = "ROUND_HALF_UP")
num4 = Decimal(num4).quantize(Decimal('0.0'), rounding = "ROUND_HALF_UP")
print(num1)
>>> 0.6
print(num2)
>>> 1.6
print(num3)
>>> 2.6
print(num4)
>>> 3.6

3、ceil(num)函数,向上取整

from math import ceil

print(ceil(1.3))
>>> 2
print(ceil(1.6))
>>> 2

4、floor(num)函数,向下取整

from math import floor

print(floor(1.3))
>>> 1
print(floor(1.6))
>>> 1

问题:
表格1有4位同学,表格2有3位同学,对比姓名,成绩是否一致,如果不存在,则写入不存在。在面对数据量很大的时候,手动Excel肯定是不可取的,耗时又费力。
在这里插入图片描述
在这里插入图片描述
思路: python提取数据写入,但python提取出来的实际浮点数,存在精度问题,既然Excel中可以用IF语句来进行判断可见的数据是否相等,那么就往Excel中写入语句啊!这就完美的解决了我遇到的问题。
代码:

# -*- coding:utf-8 -*-
import openpyxl
import time
from openpyxl.styles import Font

start_time = time.time()

wb1 = openpyxl.load_workbook('表格1.xlsx',data_only=True)
wb2 = openpyxl.load_workbook('表格2.xlsx',data_only=True)
ws1 = wb1['Sheet1']
ws2 = wb2['Sheet1']

ls2 = list(ws2.values)[1:]
d_tb2 = {}
#以姓名为字典建立关系
for i in range(0,len(ls2)):
    name = ls2[i][1] #姓名
    d_tb2[name] = ls2[i]

nwb=openpyxl.Workbook()
nwb.remove(nwb.active)
nws=nwb.create_sheet('结果')
title = ['序号','姓名','成绩']

# 插入标题
newTitle = []
for i in range(0,len(title)):
    if i == 0 :newTitle.append(title[i])
    else:
        val = title[i] + '--' + '表格1'
        val2 = title[i] + '--' + '表格2'
        val3 = title[i] + '--' + '是否相等'
        newTitle.append(val)
        newTitle.append(val2)
        newTitle.append(val3)
nws.append(newTitle)

ls1 = list(ws1.values)[1:]
n = 2
for i in range(0,len(ls1)):
    name = ls1[i][1]
    for j in range(1,len(title)):
        nws.cell(n, 1).value = ls1[i][0] #序号
        nws.cell(n, 3*j-1).value = ls1[i][j]  # 表格1数据
        # 如果表格2学生在表格1中存在,则写入,否则不存在
        if name in d_tb2.keys():
            nws.cell(n, 3 * j).value = d_tb2[name][j]
        else:
            nws.cell(n, 3 * j).value = '不存在'
        # 如果两个数都是浮点数,则写入Excel公式,IF(round(num1,1),round(num2,1),TRUE,FALSE)
        # isinstance判断是否是某个类型的值
        if isinstance(nws.cell(n, 3 * j - 1).value, float) and isinstance(nws.cell(n, 3 * j).value, float):
            nws.cell(n, 3 * j + 1).value = '=IF(round('+nws.cell(n, 3 * j - 1).coordinate+',1)=round('+nws.cell(n, 3 * j).coordinate+',1),TRUE,FALSE)'
            nws.cell(n, 3 * j + 1).font = Font(color='FF69B4')
        else:
            nws.cell(n, 3 * j + 1).value = '=IF(' + nws.cell(n, 3 * j - 1).coordinate + '=' + nws.cell(n,3 * j).coordinate + ',TRUE,FALSE)'
            nws.cell(n, 3 * j + 1).font = Font(color='FF69B4')
    n = n + 1

nwb.save('结果.xlsx')

end_time = time.time()
all_time = end_time - start_time
print('耗时:%s s'%all_time)

结果:耗时:0.0668189525604248 s
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值