一、除法:
除法的取整分為三類:向上取整、向下取整、向零取整。
1.向上取整:向+∞方向取最接近精確值的整數。在這種取整方式下,5 / 3 = 2, -5 / -3 = 2, -5 / 3 = -1, 5 / -3 = -1
2.向下取整:向-∞方向取最接近精確值的整數。在這種取整方式下,5 / 3 = 1, -5 / -3 = 1, -5 / 3 = -2, 5 / -3 = -2
3.向零取整:向0方向取最接近精確值的整數,換言之就是舍去小數部分,因此又稱截斷取整。在這種取整方式下,5 / 3 = 1, -5 / -3 = 1, -5 / 3 = -1, 5 / -3 = -1通過觀察可以發現,無論是向上取整還是向下取整,(-a)/b==-(a/b)都不一定成立。這給程序設計者帶來了極大的麻煩。而對於向零取整,(-a)/b==-(a/b)是成立的,以此,C/C++(包括Java)采用這種取整方式。
而Python采用的是向下取整的方式,具體原因得結合取模運算分析。
二、取模:
如果 a/b = q, a%b = r (即a除以b 模q 余r,可表示為a/b=q … r)
那么 a = b*q + r
所以,根據C語言中除法結果向零取整的規則,5 % 3 = 2, -5 % -3 = -2, -5 % 3 = 1, 5 % -3 = –1
即:
a b q r 5 3 1 2 -5 -3 1 -2 -5 3 -1 -2 5 -3 -1 2
那么,為何Python整除運算采用向下取整的規則,詳細內容在Why Python's Integer Division Floors?,簡單地來講就是:
因為python認為余數r用到的機會會更大,采用向下取整的規則可以保證余數r與除數b的符號相同(同正或者同負)。以下為重點內容的摘抄:
假設a和b都>=0時,b * q + r = a, 0 <= r < b
如果希望將這一關系擴展到a為負(b仍為正)的情況,有兩個選擇:一是q向0取整,r取負值,這時約束關系變為 0 <= abs(r) < b,另一種選擇是q向下(負無窮方向)取整,約束關系不變,依然是 0 <= r < b。
在數學的數論中,數學家總是傾向於第二種選擇(參見如下Wikipedia鏈接)。
在Python語言中也做了同樣選擇,因為在某些取模操作應用中被除數a取什么符號並不重要。
例如從POSIX時間戳(從1970年初開始的秒數)得到其對應當天的時間。因為一天有24*3600 = 86400秒,這一操作就是簡單的t % 86400。但是當表達1970年之前的時間,這時是一個負數,向0取整規則得到的是一個毫無意義的結果!而向下取整規則得到的結果仍然是正確的。
另外一個我能想到的應用是計算機圖形學中計算像素的位置。我相信這樣的應用還有更多。順便說一下,b取負值時,僅需要把符號取反,約束關系變為:
0 >= r > b
那么,現在的問題變成,C為啥不采取(Python)這樣的選擇呢?可能是設計C時硬件不適合這樣做,所謂硬件不適合這樣做是說指那些最老式的硬件把負數表示為“符號+大小”而不是像現在的硬件用二進制補碼表示(至少對整數是用二進制補碼)。我的第一台計算機是一台Control Data大型機,它用1的補碼來表示整數和浮點數。60個1的序列表示負0!
Tim Peters對Python的浮點數部分洞若觀火,對於我想把這一規則推廣到浮點數取模運算有些擔心。可能他是對的,因為向負無窮取整的規則有可能導致當x是絕對值特別小的負數時x%1.0會丟失精度。但是這還不足以讓我對整數取模,也就是//進行修改。
附言:注意我用了//而不是/,這是一個Python 3 語法,而且在Python 2 中也是有效的,它強調了使用者是要進行整除操作。Python 2 中的 / 有可能產生歧義,因為對兩個操作數都是整數時或者一個整數一個浮點數或者兩個都是浮點數時,返回的結果類型不同。當然,這是另外的故事,詳情參見PEP238。
參考:
[1]http://yangyou230.iteye.com/blog/1315426
[2]http://tieba.baidu.com/p/1881961036
[3]http://blog.chinaunix.net/uid-26898698-id-3269779.html
[4]http://python3.blogspot.kr/2010/08/why-pythons-integer-division-floors.html