負数の剰余を計算してはならない
負数が含まれる剰余を計算した場合、言語に跨がって一意な結果が得られない。
| -5 % 3 | 5 % -3 | |
|---|---|---|
| C | -2 | 2 |
| C++ | -2 | 2 |
| Java | -2 | 2 |
| Ruby | 1 | -1 |
| Python | 1 | -1 |
| Common Lisp | 1 | -1 |
さて、なぜこんなことが起きるのかというと、剰余には複数の定義が存在するからである。
m ÷ n = q … r
この r を剰余と言うが、 r の範囲が
- 0 ≤ r < n
- 最小非負剰余
- -n/2 ≤ r < n/2
- 絶対値最小剰余
の二つの定義があり、一般的には前者の「最小非負剰余」を用いるようである。
m が負数、 n が正数の場合は、先程の表にあるプログラミング言語は以下のように分類される。
- 絶対値最小剰余
- C
- C++
- Java
- 最小非負剰余
- Ruby
- Python
- Common Lisp
しかし、最小非負剰余では r が正数になる必要があり、剰余の結果が -1 になるのは最小非負剰余の定義から外れている。
これは、除数 n が負数になる場合、プログラミング言語では以下の定義になる理由によるものである。
m % -n == -(m % n)
ただし、数学的に最小非負剰余は以下が真になるため、 0 ≤ r < n が常に成り立つ。
m % (-n) = m % n
まとめ
除数、被除数に負数が入る剰余演算はプログラミング言語により結果が変わるので使うのを辞めましょう。
/* 負数は扱わないようにする */
int mod(int m, int n) {
if (m < 0 || n < 0) {
return ERROR_NEGATIVE_MOD;
}
return (m % n);
}
負数の剰余について、明確に違いが分かっていれば使う分には自己責任だと思いますが、他人が理解できない (予想した動作にならない) 可能性が高いのでチーム開発では利用するべきではないでしょう。