今天在提取两个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