Remove ads

模除(英語:modulo 有時也稱作 modulus),又稱模數取模取模運算等,它得出一個數除以另一個數的餘數。給定兩個正數:被除數除數(通常縮寫為),得到的是使用歐幾里得除法時的餘數所得之數被稱為小數部份。

Thumb
  商數 () 和   餘數 () 作為被除數 () 的函數時的圖像。左側是除數為正的情況,右側除數為負。從上至下分別使用了:向零取整、向下取整和歐幾里得除法

舉個例子:計算表達式得到,因為的商為而餘數為;而得到,因為的商為而餘數為;做除法不能整除時得到是有理數,平常使用的計算器採用了有限數位十進制表示法,它以小數點分隔整數部份和小數部份,比如

雖然通常情況下模除的被除數和除數都是整數,但許多計算系統允許其他類型的數值運算,比如對浮點數取模。在所有計算系統中當被除數和除數都是正數時,結果的餘數的範圍為;而經常是未定義的,在程式語言里可能會導致除以零錯誤。當被除數或除數為負數時,不同的程式語言對結果有不同的處理。

Remove ads

各種定義

在數學中,取模運算的結果就是歐幾里德除法餘數。當然也有許多其他的定義方式。計算機和計算器有許多種表示和儲存數字的方法,因此在不同的硬體環境下、不同的程式語言中,取模運算有著不同的定義。

幾乎所有的計算系統中,除以得到商和餘數均滿足以下式子:

1

即使如此,當餘數時,餘數的符號仍然是有歧義的:餘數非時,它的符號有兩種選擇,一個是正號而另一個是負號[a]。通常情況下,在數論中總是選擇正餘數。但在編程中,選擇餘數的符號依賴於程式語言和被除數或除數的符號。在程式語言所定義的整數模除中,ISO/IEC標準Pascal[1]ALGOL 68[2],在計算出的餘數r為負數時,返回正數作為結果[b];另一些程式語言如ISO/IEC C90,當被除數或除數是負數時,C90標準並沒有做具體的規定,而是留給編譯器去定義並實現[3]。在大多數系統中是未定義的,雖然有些系統定義它就等於。更多詳情參見後續章節表格。

  • 很多取模的實現都使用了「截斷除法」(英語:truncated division),商經由截尾函數來定義,商向零取整,結果等於普通除法所得的小數靠近方向的第一個整數:

    餘數和被除數符號一致:

  • 高德納定義的「下取整除法」(英語:floored division[4],商經由下取整函數來定義,商總是向負無窮取整,即使商已經是負數:

    餘數和除數符號一致:

  • Raymond T. Boute使用的歐幾里得除法定義中[5],要求滿足,在這種情況下:

    這裡的符號函數,餘數總是非負數:

  • Common Lisp的round函數和IEEE 754英語IEEE 754-1985使用「修約除法」(英語:rounded division),商經由修約函數約半成偶)來定義為:

    當商為偶數時,餘數範圍是;當商為奇數時,餘數範圍是

  • Common Lisp的ceiling函數使用「上取整除法」(英語:ceiling division),商經由上取整函數定義為:

    餘數與除數有相反的符號:

Remove ads

記號

一些計算器有取模mod()按鈕,很多程式語言里也有類似的函數,通常像mod(a, n)這樣。有些語言也支持在表達式內使用%modMod作為取模或取余操作符,比如a % na mod n

在一些沒有mod()函數的環境中或許可使用等價的:a - (n * int(a/n))。這裡的int()函數事實上等價於截斷函數。

性質及恆等式

一些取模操作,經過分解和展開可以等同於其他數學運算。這在密碼學的證明中十分有用,例如:迪菲-赫爾曼密鑰交換

  • 恆等式:
    • 對所有的正數 有:
    • 如果 是一個質數,且不為 因數,此時由費馬小定理有:
  • 逆運算:
    • .
    • 表示模反元素。若且唯若 互質時,等式左側有定義:
  • 分配律:
  • 除法定義:僅當式子右側有定義時,即 互質時有:,其他情況為未定義的。
  • 乘法逆元:.
Remove ads

程式語言實現

更多資訊 語言, 算符 ...
在各種程式語言中的模除運算
語言 算符 整數 浮點數 定義
ABAP MOD 歐幾里得式
ActionScript % 截斷
Ada mod 下取整[6]
rem 截斷[6]
ALGOL 68 ÷×, mod 類似歐幾里得式[c]
AMPL mod 截斷
APL |[d] 下取整
AppleScript mod 截斷
AutoLISP (rem d n) 截斷
AWK % 截斷
bash % 截斷
BASIC Mod 實現各異
bc % 截斷
C
C++
%, div 截斷[e]
fmod (C)
std::fmod (C++)
截斷[9]
remainder (C)
std::remainder (C++)
修約
C# % 截斷
Math.IEEERemainder 修約[10]
Clarion英語Clarion (programming language) % 截斷
Clean rem 截斷
Clojure mod 下取整[11]
rem 截斷[12]
COBOL FUNCTION MOD 下取整[13]
FUNCTION REM 截斷[13]
CoffeeScript % 截斷
%% 下取整[14]
ColdFusion %, MOD 截斷
Common Intermediate Language rem (有符號) 截斷[15]
rem.un (無符號) 不適用
Common Lisp mod 下取整
rem 截斷
Crystal英語Crystal (programming language) %, modulo 下取整
remainder 截斷
D % 截斷[16]
Dart % 歐幾里得式[17]
remainder() 截斷[18]
Eiffel \\ 截斷
Elixir rem/2 截斷[19]
Integer.mod/2 下取整[20]
Elm modBy 下取整[21]
remainderBy 截斷[22]
Erlang rem 截斷
math:fmod/2 截斷 (同於C)[23]
Euphoria mod 下取整
remainder 截斷
F# % 截斷
Math.IEEERemainder 修約[10]
Factor mod 截斷
FileMaker Mod 下取整
Forth mod 實現定義
fm/mod 下取整
sm/rem 截斷
Fortran mod 截斷
modulo 下取整
Frink英語Frink (programming language) mod 下取整
Full BASIC英語Full BASIC MOD 下取整[24]
REMAINDER 截斷[25]
GLSL % 未定義[26]
mod 下取整[27]
GameMaker Studio (GML) mod, % 截斷
GDScript (Godot) % 截斷
fmod 截斷
posmod 歐幾里得式
fposmod 歐幾里得式
Go % 截斷[28]
math.Mod 截斷[29]
big.Int.Mod 歐幾里得式[30]
big.Int.Rem 截斷[31]
Groovy % 截斷
Haskell mod 下取整[32]
rem 截斷[32]
Data.Fixed.mod' (GHC) 下取整
Haxe % 截斷
HLSL % 未定義[33]
J |[d] 下取整
Java % 截斷
Math.floorMod 下取整
JavaScript
TypeScript
% 截斷
Julia mod 下取整[34]
%, rem 截斷[35]
Kotlin %, rem 截斷[36]
mod 下取整[37]
ksh % 截斷 (同於POSIX sh)
fmod 截斷
LabVIEW mod 截斷
LibreOffice =MOD() 下取整
Logo MODULO 下取整
REMAINDER 截斷
Lua 5 % 下取整
Lua 4 mod(x,y) 截斷
Liberty BASIC英語Liberty BASIC MOD 截斷
Mathcad mod(x,y) 下取整
Maple e mod m (默認), modp(e, m) 歐幾里得式
mods(e, m) 修約
frem(e, m) 修約
Mathematica Mod[a, b] 下取整
MATLAB mod 下取整
rem 截斷
Maxima mod 下取整
remainder 截斷
Maya Embedded Language英語Maya Embedded Language % 截斷
Microsoft Excel =MOD() 下取整
Minitab MOD 下取整
Modula-2 MOD 下取整
REM 截斷
MUMPS英語MUMPS # 下取整
Netwide Assembler (NASM, NASMX) %, div (無符號) 不適用
%% (有符號) 實現定義[38]
Nim mod 截斷
Oberon MOD 類似下取整[f]
Objective-C % 截斷 (同於C99)
Object Pascal, Delphi mod 截斷
OCaml mod 截斷[39]
mod_float 截斷[40]
Occam \ 截斷
Pascal (ISO-7185和ISO-10206) mod 類似歐幾里得式[c]
Perl % 下取整[g]
POSIX::fmod 截斷
PHP % 截斷[42]
fmod 截斷[43]
PIC BASIC Pro \\ 截斷
PL/I mod 下取整 (ANSI PL/I)
PowerShell % 截斷
Programming Code (PRC英語PRC (Palm OS)) MATH.OP - 'MOD; (\)' 未定義
Progress英語OpenEdge Advanced Business Language modulo 截斷
Prolog (ISO 1995[44]) mod 下取整
rem 截斷
PureBasic %, Mod(x,y) 截斷
PureScript `mod` 歐幾里得式[45]
Pure Data % 截斷 (同於C)
mod 下取整
Python % 下取整
math.fmod 截斷
math.remainder 修約
Q# % 截斷[46]
R %% 下取整[47]
Racket modulo 下取整
remainder 截斷
Raku % 下取整
RealBasic英語RealBasic MOD 截斷
Reason mod 截斷
Rexx // 截斷
RPG英語IBM RPG %REM 截斷
Ruby %, modulo() 下取整
remainder() 截斷
Rust % 截斷
rem_euclid() 歐幾里得式[48]
SAS MOD 截斷
Scala % 截斷
Scheme modulo 下取整
remainder 截斷
Scheme R6RS mod 歐幾里得式[49]
mod0 修約[49]
flmod 歐幾里得式
flmod0 修約
Scratch mod 下取整
Seed7英語Seed7 mod 下取整
rem 截斷
SenseTalk英語SenseTalk modulo 下取整
rem 截斷
Unix shell (包括bash、ash等。) % 截斷 (同於C)[50]
Smalltalk \\ 下取整
rem: 截斷
Snap! mod 下取整
Spin英語Parallax Propeller#Built in Spin byte code interpreter // 下取整
Solidity % 下取整
SQL (SQL:1999英語SQL:1999) mod(x,y) 截斷
SQL (SQL:2011英語SQL:2011) % 截斷
Standard ML mod 下取整
Int.rem 截斷
Real.rem 截斷
Stata mod(x,y) 歐幾里得式
Swift % 截斷[51]
remainder(dividingBy:) 修約[52]
truncatingRemainder(dividingBy:) 截斷[53]
Tcl % 下取整
fmod() 截斷 (同於C)
tcsh % 截斷
Torque英語Torque (game engine) % 截斷
Turing英語Turing (programming language) mod 下取整
Verilog (2001) % 截斷
VHDL mod 下取整
rem 截斷
VimL % 截斷
Visual Basic Mod 截斷
WebAssembly i32.rem_u, i64.rem_u (無符號) 不適用
i32.rem_s, i64.rem_s (有符號) 截斷[54]
x86 assembly英語x86 assembly language DIV(無符號) 不適用
IDIV(有符號) 截斷
XBase++英語XBase++ % 截斷
Mod() 下取整
Zig %, @mod, @rem 截斷[55]
Z3 theorem prover英語Z3 Theorem Prover div, mod 歐幾里得式
關閉

此外,很多計算機系統提供divmod功能,它同時產生商和餘數。例子x86架構IDIV指令,C程式語言的div()函數,和Pythondivmod()函數。

Remove ads

常見錯誤

當取模的結果與被除數符號相同時,可能會導致意想不到的錯誤。

舉個例子:如果需要判斷一個整數是否為奇數,有人可能會測試這個數除 2 的餘數是否為 1:

bool is_odd(int n) {
    return n % 2 == 1;
}

但在一個取模結果與被除數符號相同的程式語言里,這樣做是錯的。因為當被除數 n 是奇數且為負數時, 得到 −1,此時函數返回「假」。

一種正確的實現是測試取模結果是否為 0,因為餘數為 0 時沒有符號的問題:

bool is_odd(int n) {
    return n % 2 != 0;
}

或者考慮餘數的符號,有兩種情況:餘數可能為 1 或 -1。

bool is_odd(int n) {
    return n % 2 == 1 || n % 2 == -1;
}
Remove ads

性能問題

可以通過依次計算帶餘數的除法實現取模操作。特殊情況下,如某些硬體上,存在更快的實現。 例如:2 的 n 次冪的模,可以通過逐位與運算實現:

x % 2n == x & (2n - 1)

例子,假定 x 為正數:

x % 2 == x & 1
x % 4 == x & 3
x % 8 == x & 7

在進行位操作比取模操作效率更高的設備或軟體環境中,以上形式的取模運算速度更快。[56]

編譯器可以自動識別出對 2 的 n 次冪取模的表達式,自動將其優化為 expression & (constant-1)。這樣可以在兼顧效率的情況下寫出更整潔的代碼。這個優化在取模結果與被除數符號一致的語言中(包括 C 語言)不能使用,除非被除數是無符號整數。這是因為如果被除數是負數,則結果也是負數,但 expression & (constant-1) 總是正數,進行這樣的優化就會導致錯誤,無符號整數則沒有這個問題。

用途

  • 取模運算可用於判斷一個數是否能被另一個數整除。對 2 取模即可判斷整數的奇偶性;從 2 到 取模則可判斷一個數是否為質數。
  • 進制之間的轉換。
  • 用於求取最大公約數的輾轉相除法使用取模運算。
  • 密碼學中的應用:從古老的凱撒密碼到現代常用的RSA橢圓曲線密碼,它們的實現過程均使用了取模運算。

參見

腳註

參考文獻

Wikiwand in your browser!

Seamless Wikipedia browsing. On steroids.

Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.

Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.

Remove ads