您的问题是,尽管“/”有时被称为“真正的除法运算符”并且它的方法名称是__truediv__,但它对整数的行为并不是“真正的数学除法”。相反,它会产生一个不可避免地具有有限精度的浮点结果。
对于足够大的数字,即使数字的整数部分也会出现浮点舍入错误。当 648705536316023400 转换为 Python 浮点数(IEEE 双精度)时,它会四舍五入为 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 标准库确实提供了 Fraction 类型,并且 Fraction 除以 int 的除法运算符执行“真正的数学除法”。
math.floor(Fraction(648705536316023400) / 7) == 92672219473717628
math.floor(Fraction(648705536316123383) / 7) == 92672219473731911
但是,您应该注意使用 Fraction 类型可能对性能和内存造成严重影响。请记住,分数可以增加存储需求而不会增加数量。
为了进一步测试我的“四舍五入对二”的理论,我使用以下代码进行了测试。
#!/usr/bin/python3
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请注意,直接打印浮点数不会显示其确切值,而是显示将舍入到该值的最短十进制数(允许从浮点数到字符串并返回的无损往返转换漂浮)。