3 回答

TA貢獻1887條經驗 獲得超5個贊
您的測試用例中的商不相等的原因是,在這種math.floor(a/b)
情況下,結果是使用浮點算法(IEEE-754 64 位)計算的,這意味著存在最大精度。您擁有的商大于 2 53限制,高于該限制的浮點不再精確到單位。
然而,對于整數除法,Python 使用其無限的整數范圍,因此結果是正確的。
請注意,對于 int 和 long 參數,真正的除法可能會丟失信息;這是真正除法的本質(只要語言中沒有有理數)。有意識地使用 long 的算法應該考慮使用
//
,因為真正的 long 劃分保留不超過 53 位的精度(在大多數平臺上)。

TA貢獻1847條經驗 獲得超11個贊
您可能正在處理太大而無法準確表達為浮點數的整數值。您的數字明顯大于 2^53,這是相鄰浮點雙精度之間的差距開始大于 1 的地方。因此,在進行浮點除法時會損失一些精度。
另一方面,整數除法是精確計算的。

TA貢獻2065條經驗 獲得超14個贊
你的問題是,盡管“/”有時被稱為“真正的除法運算符”并且它的方法名稱是__truediv__,但它對整數的行為不是“真正的數學除法”。相反,它產生的浮點結果不可避免地具有有限的精度。
對于足夠大的數字,即使是數字的整數部分也會出現浮點舍入誤差。當 648705536316023400 轉換為 Python 浮點數(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請注意,直接打印浮點數不會顯示其確切值,而是顯示將舍入到該值的最短十進制數(允許從浮點數到字符串再到浮點數的無損往返轉換)。
添加回答
舉報