作者:宝泉岭_白饭如霜些_350 | 来源:互联网 | 2022-11-24 09:26
对于大商,整数除(//
)似乎不一定等于常规除(math.floor(a/b)
)的最低值.
根据Python文档(https://docs.python.org/3/reference/expressions.html - 6.7),
整数的平面划分产生整数; 结果是数学除法的结果,"floor"函数应用于结果.
然而,
math.floor(648705536316023400 / 7) = 92672219473717632
648705536316023400 // 7 = 92672219473717628
'{0:.10f}'.format(648705536316023400 / 7)
收益率'92672219473717632.0000000000',但小数部分的最后两位数应为28而不是32.
1> trincot..:
测试用例中的商不相等的原因是,在这种math.floor(a/b)
情况下,结果是使用浮点运算(IEEE-754 64位)计算的,这意味着存在最大精度.你在那里的商大于2 53的限制,高于该限制浮点不再精确到单位.
但是,使用整数除法,Python使用其无限的整数范围,因此结果是正确的.
另见PEP 238中的"真正划分的语义":
请注意,对于int和long参数,真正的除法可能会丢失信息; 这是真正分裂的本质(只要理性不在语言中).有意识地使用longs的算法应该考虑使用//
,因为longs的真正划分保留不超过53位的精度(在大多数平台上).
2> interfect..:
您可能正在处理太大而不能完全表示为浮点数的整数值.您的数字明显大于2 ^ 53,这是相邻浮点数双精度之间的间隙开始大于1的位置.因此,在进行浮点除法时会失去一些精度.
另一方面,整数除法是精确计算的.
3> plugwash..:
你的问题是,尽管"/"有时被称为"真正的除法运算符"并且其方法名称是__truediv__
,但它对整数的行为不是"真正的数学除法".相反,它产生的浮点结果不可避免地具有有限的精度.
对于足够大的数字,即使数字的整数部分也可能遭受浮点舍入误差.当648705536316023400转换为Python float(IEEE double)时,它将四舍五入为648705536316023424 1.
我似乎无法找到关于当前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类型,并且除以int的Fraction的除法运算符执行"真正的数学除法".
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请注意,直接打印浮点数不会显示其精确值,而是显示将舍入到该值的最短十进制数(允许从浮点数到字符串并返回浮点数的无损往返转换).