您的问题是,尽管“/”有时被称为“真除法运算符”,其方法名称为__truediv__
,但它在整数上的行为并不是“真正的数学除法”。相反,它会产生一个浮点结果,这个结果不可避免地具有有限的精度。
对于足够大的数字,甚至数字的整数部分都可能受到浮点舍入误差的影响。当648705536316023400转换为Python浮点数(IEEE double)时,它会被舍入为6487055363160234241。
我似乎找不到关于当前Python内置类型操作符确切行为的权威文档。最初引入该功能的PEP声明,"/"等价于将整数转换为浮点数,然后执行浮点除法。然而,Python 3.5中的快速测试表明情况并非如此。如果是,则以下代码将不会产生任何输出。
for i in range(648705536316023400,648705536316123400):
if math.floor(i/7) != math.floor(float(i)/7):
print(i)
但是至少对我来说,它确实会产生输出。
相反,我觉得 Python 是按照呈现的数字执行除法,并将结果四舍五入以适应浮点数。从该程序的输出中取一个例子。
648705536316123383 // 7 == 92672219473731911
math.floor(648705536316123383 / 7) == 92672219473731904
math.floor(float(648705536316123383) / 7) == 92672219473731920
int(float(92672219473731911)) == 92672219473731904
Python标准库提供了一个分数类型和分数除以整数的除法运算符,执行的是“真正的数学除法”。
math.floor(Fraction(648705536316023400) / 7) == 92672219473717628
math.floor(Fraction(648705536316123383) / 7) == 92672219473731911
然而,您应该意识到使用分数类型可能会带来严重的性能和内存影响。请记住,分数可以在不增加数量级的情况下增加存储要求。
为了进一步测试我的“一次四舍五入 vs 两次四舍五入”理论,我使用了以下代码进行测试。
from fractions import Fraction
edt = 0
eft = 0
base = 1000000000010000000000
top = base + 1000000
for i in range(base,top):
ex = (Fraction(i)/7)
di = (i/7)
fl = (float(i)/7)
ed = abs(ex-Fraction(di))
ef = abs(ex-Fraction(fl))
edt += ed
eft += ef
print(edt/10000000000)
print(eft/10000000000)
直接执行除法的平均误差幅度明显小于先转换为浮点数再执行除法,支持单舍入与双舍入理论。
1请注意,直接打印浮点数不会显示其精确值,而是显示最短的十进制数,该数将四舍五入为该值(允许从浮点数到字符串再到浮点数的无损往返转换)。
//
)时,“数学除法”并不是指Python中的“除法”(/
)。相反,“除法”(/
)和“地板除法”(//
)是对真正(“数学上的”)除法的两种不同近似。 - ruakh