為什麼負數的取余計算各個編程語言結果不統一?

例如: -7 % 10,C++,Go和PHP得結果是-7,Python,Ruby和Lua得3

為什麼這些語言在負數取余上不統一?哪個是正確的?

實驗配圖:


帶符號整數的除法與餘數


數學上都正確,這個定義本來就有好幾個

比如按r = a - n	imes	ext{trunc}left( a/n 
ight)

或者按r=a-nleftlfloor{a/n}
ight
floor

IEEE標準是則是r=a-n	imes	ext{round}left(a/n
ight) ,然而round標準也有好幾個。。。

如果round是IEEE默認的round to the nearest,從標準的角度似乎應該是3。。。但IEEE好像也沒說其他的round標準不對。。。

PS:很多語言乾脆提供了兩個函數,rem和mod


就是商向0或負無窮方向取整的選擇,c從c99開始規定向0取整,py則規定向負無窮取整,選擇而已,無所謂對錯


就算是相同語言不同編譯器也會不一樣的。

在我發現這個問題之後,每當我更新IDE/編譯器的時候都會用我的測試程序跑一遍,看看是不是和上個版本有什麼不同。

有的時候一些小改動很坑的。

PS:有時候也不行,不能保證測試結果是唯一的。


關於負數的餘數,樓上很多網友已經闡述了純數學概念上的意義。我想從另一個角度說說我的看法。先明確一點:我們討論的是「被除數」(分子)是負整數,而除數(分母)是正整數的情況。

先看一個例子: (-17) mod 5 =?

答案一: (-17) = (-3)*5 + (-2),所以餘數是 -2 。

答案二: (-17) = (-4)*5 + (+3),所以餘數是 +3 。

首先,從「純數學」概念上說,兩個答案都「對」,就好像解方程的時候出現「複數解」一樣,也是一個解。所以各種編譯器按照各自的邏輯來產生餘數。

但是,從人類思維角度上說,「負數」的起源是因為「不足、欠缺」,使用負數是為了知道有多欠缺,然後去補救。從這個角度才能正確理解「負數的餘數」的實際意義(區別於純數學概念)。

打個比方,你和另外5個同學住在宿舍里,你很勤快,那5個人很懶,讓你幫忙買早餐,比如說,油條、豆漿 …… 你提著他們的早餐回來,對他們說:「一共17塊錢。」就是說,他們5個人一共欠你17元。平均每人給你 17/5=3.4元。

於是他們就翻箱倒櫃 —— 不是沒有錢,而是在找零錢,每人都要拿0.4元,嗯,各位看官不妨找找自己身上有沒有4毛錢 …… 結果就是,都找不到零錢,怎麼辦?於是有兩種辦法:

辦法一:每人給你3塊錢,但是總共還欠你2塊錢,餘數= -2

辦法二:每人給你4塊錢,但是你要給回他們3塊錢,餘數 = +3

現在問題是:各位作為人類,傾向於採用哪一個辦法?

上面說了,人類之所以使用負數,是為了知道「不足」,然後進行補救。現在既然欠了錢要還,我想那5位同學都會給你4塊錢,倒不是說他們是為了給你3塊錢跑腿費,而是他們想「了(liao)了(le)這筆賬」,他們不想5個人 每人都欠你0.4元的人情(何況你還真的跑腿了),既然都還錢了,為什麼還要留一筆「遺留問題」呢?

所以,從人類思維習慣和實際需要來說(再說一遍:區別於純數學概念),無論被除數(分子)是正數還是負數,餘數都應該取正數,這樣才符合人類的思維習慣和實際需要。

題外話:就好像除數(分母)通常是正數一樣,如果用一個負數來做除數(分母),人們會覺得很彆扭,很難理解。

其實,如果允許餘數可以是負數的話,那麼 23%5 同樣有兩個答案,可以看作 +3,也可以是 -2,但我相信沒有人會用 -2,第一反應肯定是用 +3 。

現在回到題主的問題: (-7)%10=? 是 +3 還是 -7 ?

如果說人話,就是:現在欠你7塊錢,由10個人來還,怎麼辦?

辦法一:每人還你一塊錢,結果多出3塊錢,餘數=+3

辦法二:每人還給你 0元,餘數 = -7 ,結果最後,一切照舊,依然欠你7塊錢 ……

數學語言說:向負無窮取值還是向0取值 ……

==========分割線==========分割線==========分割線==========分割線==========

說到這裡,可能有人說,數學不是憑情感、看習慣的,數學是嚴謹的。

我非常贊同這個觀點,對於數學這種推崇嚴密和嚴謹的科學,同樣的操作數、同樣的運算符,必然得到同樣的運算結果。那麼,餘數這種「可正可負」的「雙重標準」是致命的 Fatal Error ,會摧毀數學大殿的根基。試想一下,-7伏和+3伏是兩個截然相反的脈衝信號,足以讓太空梭掉進茫茫大海了……

還是以 (-7)%10 為例。同餘定理是數學的基礎定理之一,中學生都懂,我們來看看:

13 %10 = 3

(13+10)%10= 3

(13+20)%10= 3

(13-10)%10= 3

(13-20)%10=(-7)%10=? —— 問題來了。

顯然,根據餘數定理,答案無疑就是+3 ,而不是 -7 。這與我的上述觀點互相印證。

如果認為 (-7)%10=-7 的話,那麼連中學生都懂的餘數定理就不成立了,數學變得如此脆弱,如此不堪一擊。

餘數定理是計算機表示負數、補數的理論基礎,那些編譯器產生的混亂結果,恰恰說明這些編譯器的開發者不懂得餘數的概念和意義。畢竟,搞電腦和搞數學是不同的領域。


因為數學上一般不關心負數的餘數,數論一般討論的都是正整數。


問題的關鍵在於商是向負無窮取值還是向0取值 -7/10=0....-7

or

-7/10=-1....3

其他語言我不清楚,貌似c11規定向0取值


可能是程序員沒有區分開 取余和取模,百度一下取余,取模。僅供參考


說的好像各個編程語言內部是統一的一樣。。

請題主先把cl、gcc、clang、cmake、tc等實現的各主要版本都實驗一遍。。


推薦閱讀:

現在的編程語言已經泛濫了,為什麼google還要推出GO語言?
第二屆 Gopher China 大會影響力如何?
c++設計三種不同繼承方式的意義是什麼?
如果讓我推薦編程語言的話(一)

TAG:編程語言 | Python | PHP | C | Go語言 |