模除
模除(又稱模数、取模操作、取模運算等,英語: 有时也称作 modulus)得到的是一个数除以另一个数的余数。
给定两个正整数:被除数 和除数 ,a modulo n (缩写为 )得到的是使用欧几里德除法时 的余数。 举个例子:计算表达式 "" 得到 1,因为 (5 除以 2 商 2 餘1);而 "" 得到 0,因为 ;注意:如果使用计算器做除法,不能整除时,你不会得到商,而是会得到一个小数,如:。
虽然通常情况下 和 都是整数,但许多计算系统允许其他类型的数字操作,如:对浮点数取模。一个整数对 取模的结果范围为: 0 到 ( 恒等于 0; 则是未定义的,在编程语言里可能会导致除零错误)。 有关概念在数论中的应用请参阅模算數。
当 或 为负数时,通常的定义就不适用了,不同的编程语言对结果有不同的处理。
定義与余数的计算
程式語言 | 操作符 | 结果与...同符号 |
---|---|---|
AutoLISP | (rem d n) [1] |
被除数 |
AWK | % |
被除数 |
BASIC | Mod |
未定义 |
C (ISO 1999) | % , div |
被除数[2] |
C++ (ISO 2011) | % , div |
被除数 |
C# | % |
被除数 |
Clojure | mod |
除数 |
rem |
被除数 | |
CoffeeScript | % |
被除数 |
%% |
除数[3] | |
Dart | % |
非负 |
remainder() | 被除数 | |
Erlang | rem |
被除数 |
F# | % |
被除数 |
Fortran | mod |
被除数 |
modulo |
除数 | |
Go | % |
被除数 |
Haskell | mod |
除数 |
rem |
被除数 | |
Julia | % , mod , rem |
除数 |
Kotlin | % |
被除数 |
Java | % |
被除数 |
Math.floorMod |
除数 | |
JavaScript | % |
被除数 |
Lua 5 | % |
除数 |
Mathematica | Mod[a, b] |
除数 |
MATLAB | mod |
除数 |
rem |
被除数 | |
Pascal (ISO-7185 and -10206) | mod |
非负 |
Perl | % |
除数 |
PHP | % |
被除数 |
Prolog (ISO 1995[4]) | mod |
除数 |
rem |
被除数 | |
Python | % |
除数 |
math.fmod |
被除数 | |
Racket | remainder |
被除数 |
R语言 | %% |
除数 |
Ruby | %, modulo() |
除数 |
remainder() |
被除数 | |
Rust | % |
被除数 |
Scala | % |
被除数 |
Scheme R6RS[5] | mod |
非负 |
mod0 |
最靠近0的数[5] | |
SQL (SQL:2012) | % |
被除数 |
Swift | % |
被除数 |
Verilog (2001) | % |
被除数 |
VHDL | mod |
除数 |
rem |
被除数 | |
Visual Basic | Mod |
被除数 |
WebAssembly | i32.rem_s, i64.rem_s |
被除数 |
x86 汇编 | IDIV |
被除数 |
程式語言 | 操作符 | 结果与...同符号 |
---|---|---|
C (ISO 1999) | fmod |
被除数 |
remainder |
最靠近0的数 | |
C++ (ISO 2011) | std::fmod |
被除数 |
std::remainder |
最靠近0的数 | |
C# | % |
被除数 |
Common Lisp | mod |
除数 |
rem |
被除数 | |
Dart | % |
非负 |
remainder() | 被除数 | |
F# | % |
被除数 |
Fortran | mod |
被除数 |
modulo |
除数 | |
Go | math.Mod |
被除数 |
Haskell (GHC) | Data.Fixed.mod' |
除数 |
Java | % |
被除数 |
JavaScript | % |
被除数 |
Perl6 | % |
除数 |
PHP | fmod |
被除数 |
Python | % |
除数 |
math.fmod |
被除数 | |
Ruby | %, modulo() |
除数 |
remainder() |
被除数 | |
Scheme R6RS | flmod |
非负 |
flmod0 |
最靠近0的数 | |
Swift | truncatingRemainder(dividingBy:) |
被除数 |
在数学中,取模运算的结果就是欧几里德除法的余数。当然也有许多其他的定义方式。计算机和计算器有许多种表示和储存数字的方法,因此在不同的硬件环境下、不同的编程语言中,取模运算有着不同的定义。
几乎所有的计算系统中, 除 得到商 和余数 均满足以下式子:
-
(
)
然而这样做,当余数非 0 时,余数的符号仍然是有歧义的:余数非 0 时,它的符号有两种选择,一个正、一个负。[註 2] 通常情况下,在数论中总是使用正余数。但在编程语言中,余数的符号取决于编程语言的类型和被除数 a 或除数 的符号。 标准 Pascal 和 ALGOL 68 总是使用 0 或正余数;另一些编程语言,如 C90 ,当被除数 和除数 都是负数时,C90 标准并没有做具体的规定,而是留给编译器去定义并实现[6]。 在大多数系统上 时未定义的,虽然有些系统定义它就等于 。更多详情参见表格。
- 很多取模的实现都使用了截断除法,此时商由截断函数定义定义 ,因此由等式 1 有,余数和被除数符号一致。商向零取整:结果等于普通除法所得的小数靠近 0 方向的第一个整数。
- Raymond T. Boute[8]使用的欧几里得定义中,余数总是非负的 ,这与欧几里得算法是一致的。
在这种情况下:
或者等价的:
这里的 是符号函数,因此
- Common Lisp 也定义了自己的舍入除法和进位除法,商分别定义为 和 。
- IEEE 754 定义了一个取余函数,商被定义为 ,依据舍入约定取整。因此余数的符号选定为最接近0。
常见错误
当取模的结果与被除数符号相同时,可能会导致意想不到的错误。
举个例子:如果需要判断一个整数是否为奇数,有人可能会测试这个数除 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;
}
记号
一些计算器有取模 mod() 按钮,很多编程语言里也有类似的函数,通常像 mod(a, n) 这样。 有些语言也支持在表达式内使用 "%"、"mod" 或 "Mod" 作为取模或取余操作符。
a % n
或
a mod n
或者在一些没有 mod() 函数的环境中使用等价的: (注意 'int' 事实上等价于截断函数,进行了向 0 取整)
a - (n * int(a/n))
等价性
一些取模操作,经过分解和展开可以等同于其他数学运算。这在密码学的证明中十分有用,例如:迪菲-赫爾曼密鑰交換。
性能问题
可以通过依次计算带余数的除法实现取模操作。特殊情况下,如某些硬件上,存在更快的实现。 例如:2 的 n 次幂的模,可以通过逐位与运算实现:
x % 2n == x & (2n - 1)
例子,假定 x 为正数:
x % 2 == x & 1
x % 4 == x & 3
x % 8 == x & 7
在进行位操作比取模操作效率更高的设备或软件环境中,以上形式的取模运算速度更快。[9]
编译器可以自动识别出对 2 的 n 次幂取模的表达式,自动将其优化为 expression & (constant-1)
。这样可以在兼顾效率的情况下写出更整洁的代码。这个优化在取模结果与被除数符号一致的语言中(包括 C 语言)不能使用,除非被除数是无符号整数。这是因为如果被除数是负数,则结果也是负数,但 expression & (constant-1)
总是正数,进行这样的优化就会导致错误,无符号整数则没有这个问题。
用途
參見
- 模 (消歧义)和模 (术语) —— “模数(Modulo)”这个词的许多用法,都是 1801 年卡爾·弗里德里希·高斯引入模算數时产生的。
- 模幂运算
- 同餘
脚注
- 在 Visual LISP IDE 里测试可知结果与被除数同符号。 (rem 13 3)=>1; (rem -13 3)=>-1; (rem 13 -3)=>1; (rem -13 -3)=>-1
- 从数学上讲,正和负只是满足不等式的无穷多个解中的两个
参考文献
- . Autodesk, Inc. [2018-07-12] (中文).
- ISO/IEC JTC. (PDF). open-std.org: 6.5.5: 94. 2007-09-07 [2018-07-12]. (原始内容 (pdf)存档于2018-06-24) (英语).
If the quotient a/b is representable, the expression (a/b)*b + a%b shall equal a.
- jashkenas. . coffeescript.org. [2018-07-12]. (原始内容存档于2018-07-11) (英语).
% works just like in JavaScript, while %% provides “dividend dependent modulo”
- J.P.E. Hodgson. . 1999-04-12 [2018-07-12]. (原始内容存档于2017-12-25) (英语).
A conforming processor is required to support the arithmetic operations specified by the following tables. They conform to the ISO/IEC 10967-1 Language Independent Arithmetic standard.
- ROBERT BRUCE FINDLER, JACOB MATTHEWS. . r6rs.org. 2007-09-26 [2018-07-12]. (原始内容存档于2018-03-15) (英语).
- Jones, Derek M. (PDF). Addison-Wesley. 2003 [2018-07-11]. ISBN 9780201709179. (原始内容 (PDF)存档于2018-07-11) (英语).
- Knuth, Donald. E. . Addison-Wesley. 1972 (英语).
- Boute, Raymond T. . ACM Transactions on Programming Languages and Systems (ACM Press (New York, NY, USA)). April 1992, 14 (2): 127–144. doi:10.1145/128861.128862 (英语).
- Horvath, Adam. . July 5, 2012. (原始内容存档于2018-03-05) (英语).