剰余演算

剰余演算(じょうよえんざん、リマインダーまたはモジュロとも呼ぶ)は、ある数値を別の数値で除算し、余りを取得する演算である[1][2]

アルゴリズムの違いによる商(赤線)と余り(緑線)のグラフ

概要

2つの正の整数である、被除数 a および 除数 n が与えられた場合、an による剰余a modulo n、略して a mod n とも表記される)は、ユークリッド除法における an で除算した余りである。例えば、「5 mod 2」の結果は 1 である。5を2で除算した場合、は2となり、余りは1となるからである。また、「9 mod 3」の結果は 0 である。9を3で除算した商は3となり、余りは0となる(つまり9から3を3回引くと残りがなくなる)からである[3]

通常の場合、an はともに整数であるが、多くのコンピュータシステムでは他の数値型でも処理が可能である。整数 n の剰余の取りうる範囲は、0 から n 1 までである。「n mod 1」 の場合は常に 0 となる。「n mod 0」は未定義であり、プログラミング言語によっては「ゼロ除算」エラーを引き起こす。a または n が負数の場合については、単純な定義はなく、プログラミング言語によってどのように定義されるかが異なっている。

数論における古典的な関連事項については合同算術を参照。

剰余演算による余りの算出

数学的には、剰余演算の結果はユークリッド除法における余りのことである。しかし、別の法則に従って算出されることもある。コンピュータやその他の計算機は数値の保持や処理方法がさまざまなので、剰余演算の定義はプログラミング言語や動作するハードウェアによって、それぞれ規定されている。

ほぼすべてのコンピュータシステムにおいて、an で除算した商 q および剰余 r は下記の条件を満たす。

(1)

ところが、剰余が0でない場合、その符号は不確定なものとなる。数論上は、余りは常に正の数となるが、プログラミング言語によっては a および n の符号によって剰余の符号が正となるか負となるかが定められる。標準的なPascalおよびAlgol68では、除数が負数であっても正の剰余(または0)を出力する。しかし、C90のようなプログラミング言語では、n または a が負の数の場合にはそうならないことがある。詳細は右表を参照。多くのシステムでは a の 0 での剰余は未定義だが、いくつかは a を出力するように定義されている。

  • 多くの実装においては「0への切捨て除算」が使用されている。これは商を0への切捨て関数 q = trunc(a/n) によって処理し、(1)の処理における余りは「被除数と同じ符号」となる。この場合の商は0方向に丸められる。つまり、実数での商から0の方向へ向かって直近の整数となる。
  • ドナルド・クヌースは「切り下げ除算」について言及した[4]。これは商を床関数 によって処理し、(1)の処理における余りは「除数と同じ符号」となる。床関数によって、商が負の数であっても常に負の無限大方向に丸められる。
  • Raymond T. Bouteはユークリッド除法の手法での定義について言及している[5]。この場合、余りは非負数(0 ≤ r)となる。右表では「常に正」と表記している。この定義は下記のように表すことができる。

    また、符号関数 sgn を使用すると、下記のようにも表せる。

  • Common Lispでは切り下げ除算、切り上げ除算のそれぞれについてq = round(a/n)q = ceil(a/n) で商が与えられる。
  • IEEE 754-1985 の剰余は、近接丸めを行った場合の商として定義している。したがって、剰余の符号は「0に近い側」となる。

Daan Leijenはこのように記している。

Bouteはユークリッド除法が数学的に一般的で有用なので他の方法よりも優れていることを示した。しかし、クヌースの示した切り下げ除算もまたよい定義である。「0への切捨て除算」は幅広く使われているが、他の定義よりも劣っている。
Daan Leijen、Division and Modulus for Computer Scientists[6]

剰余演算のバリエーション

// 商を求める関数
function truncated_division(divisor, dividend) {
  return Math.trunc(divisor / dividend);
}
function floored_division(divisor, dividend) {
  return Math.floor(divisor / dividend);
}
function euclidean_division(divisor, dividend) {
  return Math.sign(dividend) * Math.floor(divisor / Math.abs(dividend));
}
function ceiled_division(divisor, dividend) {
  return Math.ceil(divisor / dividend);
}
function rounded_division(divisor, dividend) {
  return Math.round(divisor / dividend);
}

// 剰余を求める関数
function truncated_mod(divisor, dividend) {
  return divisor - dividend * truncated_division(divisor, dividend);
}
function floored_mod(divisor, dividend) {
  return divisor - dividend * floored_division(divisor, dividend);
}
function euclidean_mod(divisor, dividend) {
  return divisor - dividend * euclidean_division(divisor, dividend);
}
function ceiled_mod(divisor, dividend) {
  return divisor - dividend * ceiled_division(divisor, dividend);
}
function rounded_mod(divisor, dividend) {
  return divisor - dividend * rounded_division(divisor, dividend);
}

上記はJavaScriptでの実装例だが、これらを用いて商 (quotient) を求め、商から剰余 (remainder) を求めることができる。以下は被除数 (divisor) を ±8、除数 (dividend) を ±3 としたときの計算結果である。

剰余演算のバリエーション
関数被除数除数剰余剰余の符号
truncated_mod8322被除数と同一
-83-2-2
8-3-22
-8-32-2
floored_mod8322除数と同一
-83-31
8-3-3-1
-8-32-2
euclidean_mod8322常に正
-83-31
8-3-22
-8-331
ceiled_mod833-1-
-83-2-2
8-3-22
-8-331
rounded_mod833-10に近い側
-83-31
8-3-3-1
-8-331

剰余の符号が被除数と同一の truncated_mod はリマインダーとも呼ばれ、符号が除数と同一の floored_mod はモジュロとも呼ばれる。

なお % 演算子がリマインダーの場合、((divisor % dividend) + dividend) % dividend を計算することでモジュロを求めることができる。

ありがちなミス

被除数の符号に剰余の結果が依存する場合、失敗を引き起こす場合がある。

例えば、ある整数が奇数であるかどうかを調べたい場合、下記のC++での例のように、2 で除算した余りが 1 であるかどうかを確かめれば良いように見える。

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

しかし、使用するプログラミング言語が、剰余の符号を被除数に依存させている場合、これは正しくない。なぜなら、n が負の奇数の場合、n % 2 は 1 となるので、この関数は false を返すからである。

正しい実装のひとつは、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( ) 機能を実装し、mod(a, n) のように表記する。剰余演算子として "%"、"mod"、"Mod"などを用意しているプログラミング言語もあり、

a % n

または

a mod n

と表記する。

剰余算出の代替手段

また、mod( ) 機能がないか、正しく機能しない環境であっても、下記のように算出できる(「int( )」は小数点以下を切り捨てた値を生成する)。

a - (n * int(a/n))

このほか除数 n が2のべき乗である場合に限り、後述のように、除数より1少ない値と論理積を取って算出する方法が有効である。

ビット演算による効率化

剰余演算は、除算を行って余りを得る実装となるので、その分の処理時間を必要とする。特殊な場合においては、いくつかのハードウェア上でより高速な計算方法が存在する。

たとえば、数値の内部表現に2進法を用いているコンピュータでは、2のべき乗の剰余を計算する場合に、下記のようにビットごとのAND演算を利用することができる。

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

例を示す(x は正の整数とする)。

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

剰余演算よりもビット演算のほうが効率よく処理できるデバイスやソフトウェアでは、この変換によってより高速に計算することができる[7]

最適化をする コンパイラには、2のべき乗による剰余演算を検出し、自動的にAND演算に変換するものもある。これによって、プログラマは性能を犠牲にすることなく、読みやすいソースコードを記述することができる。ただし、AND演算による場合、出力は常に正の数となるので、C言語のように剰余演算の結果の符号が被除数によって定まる言語では同じ動作はしない。したがって、被除数が負になる場合は、特別な注意が必要である。

剰余演算の等価性

他の数学的演算と同様に、剰余演算についてもいくつかの演算を導出、または拡張することができる。そうすることで、ディフィー・ヘルマン鍵交換のような暗号学分野の証明が容易となるだろう。

  • 同一性:
    • 正の整数 x の場合。
    • 素数であって b の約数でない場合、フェルマーの小定理によって となる。
  • 逆数:
    • モジュラ逆数を表す。これは bn とが互いに素である場合にだけ、次式で定義される。
  • 分配則:
  • 分数(定義): 右辺が定義できる場合。そうでない場合は未定義。
  • 逆数の乗算:

実際のプログラミング言語の挙動

さまざまなプログラミング言語の整数剰余演算子
言語 演算子 符号
ActionScript%被除数と同一
Ada mod除数と同一
rem被除数と同一
ASPMod未定義
ALGOL-68÷×, mod常に正
AMPLmod被除数と同一
APL|除数と同一
AppleScriptmod被除数と同一
AWK%被除数と同一
BASICMod未定義
bash%被除数と同一
bc%被除数と同一
C (ISO 1990) %実装による
div被除数と同一
C++ (ISO 1998) %実装による[8]
div被除数と同一
C (ISO 1999)%, div被除数と同一[9]
C++ (ISO 2011)%, div被除数と同一
C#%被除数と同一
Clarion (programming language)%被除数と同一
Clojuremod除数と同一
COBOLFUNCTION MOD除数と同一
CoffeeScript %被除数と同一
%%除数と同一[10]
ColdFusion%, MOD被除数と同一
Common Lisp mod除数と同一
rem被除数と同一
D%被除数と同一[11]
Dart%常に正
remainder()被除数と同一
Eiffel\\被除数と同一
Erlangrem被除数と同一
Euphoria mod除数と同一
remainder被除数と同一
F#%被除数と同一
FileMakerMod除数と同一
Forthmod実装による
FORTRAN mod被除数と同一
modulo除数と同一
Frinkmod除数と同一
GameMaker: Studio (GML)mod被除数と同一
GDScript%被除数と同一
Go%被除数と同一
Haskell mod除数と同一
rem被除数と同一
Haxe%被除数と同一
J|~除数と同一
Java %被除数と同一
Math.floorMod除数と同一
JavaScript%被除数と同一
Julia mod除数と同一
rem被除数と同一
LibreOffice=MOD()除数と同一
Lua 5%除数と同一
Lua 4mod(x,y)除数と同一
Liberty BASICMOD被除数と同一
MathCadmod(x,y)除数と同一
Maple e mod m 常に正
MathematicaMod除数と同一
MATLAB mod除数と同一
rem被除数と同一
Maxima mod除数と同一
remainder被除数と同一
Maya Embedded Language%被除数と同一
Microsoft Excel=MOD()除数と同一
MinitabMOD除数と同一
mksh%被除数と同一
Modula-2MOD除数と同一
MUMPS#除数と同一
NASM NASMX %符号なし剰余演算子
%%符号付き剰余演算子
OberonMOD除数と同一
OCamlmod被除数と同一
Occam\被除数と同一
Pascal (Delphi)mod被除数と同一
Pascal (ISO-7185 および ISO-10206)mod常に正
Perl%除数と同一
PHP%被除数と同一
PIC Basic Pro\\被除数と同一
PL/Imod除数と同一 (ANSI PL/I)
PowerShell%被除数と同一
Progressmodulo被除数と同一
Prolog (ISO 1995) mod除数と同一
rem被除数と同一
Python %除数と同一
math.fmod被除数と同一
Racketremainder被除数と同一
RealBasicMOD被除数と同一
R%%除数と同一
REXX//被除数と同一
RPG%REM被除数と同一
Ruby %, modulo()除数と同一
remainder()被除数と同一
Rust%被除数と同一
Scala%被除数と同一
Scheme modulo除数と同一
remainder被除数と同一
Scheme R6RS mod常に正[13]
mod00に近い側[13]
Seed7 mod除数と同一
rem被除数と同一
SenseTalk modulo除数と同一
rem被除数と同一
Smalltalk \\除数と同一
rem:被除数と同一
SQL (SQL:1999)mod(x,y)被除数と同一
Standard ML mod除数と同一
Int.rem被除数と同一
Statamod(x,y)常に正
Swift%被除数と同一
Tcl%除数と同一
Torque Game Engine%被除数と同一
Turingmod除数と同一
Verilog (2001)%被除数と同一
VHDL mod除数と同一
rem被除数と同一
Visual BasicMod被除数と同一
x86 AssemblyIDIV被除数と同一
Xbase++ %被除数と同一
Mod()除数と同一
Z3 theorem proverdiv, mod常に正
さまざまなプログラミング言語の浮動小数点数剰余演算子
言語 演算子 符号
C (ISO 1990)fmod被除数と同一[14]
C (ISO 1999) fmod被除数と同一
remainder0に近い側
C++ (ISO 1998)std::fmod被除数と同一
C++ (ISO 2011) std::fmod被除数と同一
std::remainder0に近い側
C#%被除数と同一
Common Lisp mod除数と同一
rem被除数と同一
D%被除数と同一
Dart%常に正
remainder()被除数と同一
F#%被除数と同一
FORTRAN mod被除数と同一
modulo除数と同一
Gomath.Mod被除数と同一
Haskell (GHC)Data.Fixed.mod'除数と同一
Java%被除数と同一
JavaScript%被除数と同一
Microsoft Excel=MOD()除数と同一
OCamlmod_float被除数と同一
PerlPOSIX::fmod被除数と同一
Raku%除数と同一
PHPfmod被除数と同一
Python %除数と同一
math.fmod被除数と同一
REXX//被除数と同一
Ruby %, modulo()除数と同一
remainder()被除数と同一
Scheme R6RS flmod常に正
flmod00に近い側
Standard MLReal.rem被除数と同一
Swift%被除数と同一
Xbase++ %被除数と同一
Mod()除数と同一

関連項目

脚注

注釈

  • ^ Perlでは剰余の算術演算子は機種依存となる。例示とその例外についてはPerl documentationを参照。
  • ^ 右項は正の数でなければならない。左項は定義されていない。
  • ^ ACUCOBOL、Micro Focus COBOLなどの実装。
  • ^ たとえばファミリーベーシックのROMバージョン2.0Aでは、除数と被除数が等しい場合にMODの演算が正しく行われない。

出典

  1. MSDN 剰余演算子 (%)”. マイクロソフト. 2015年12月9日閲覧。
  2. ASCII.jpデジタル用語辞典 モジュロ”. http://ascii.jp/.+2015年12月9日閲覧。
  3. 一般的な電卓を使用して除算を行う場合、商が小数点表記で出力されるので、剰余演算は直接行えないことに注意する。
  4. Knuth, Donald. E. (1972). The Art of Computer Programming. Addison-Wesley
  5. Boute, Raymond T. (April 1992). “The Euclidean definition of the functions div and mod”. ACM Transactions on Programming Languages and Systems (TOPLAS) (ACM Press (New York, NY, USA)) 14 (2): 127–144. doi:10.1145/128861.128862. http://portal.acm.org/citation.cfm?id=128862&coll=portal&dl=ACM.
  6. Leijen, Daan (2001年12月3日). Division and Modulus for Computer Scientists (PDF). 2014年12月25日閲覧。
  7. Horvath, Adam (2012年7月5日). Faster division and modulo operation - the power of two”. 2015年12月9日閲覧。
  8. "ISO/IEC 14882:2003 : Programming languages -- C++" (Document). 5.6.4: ISO, IEC. 2003.. "二項演算子 % 左項を右項で割った余りを算出する。.... オペランドが共に負数でない場合は余りも負数ではない。しかし、そうでない場合の剰余の符号は実装によって定義される。"
  9. open-std.org, section 6.5.5
  10. CoffeeScript operators
  11. Expressions”. D Programming Language 2.0. Digital Mars. 2010年7月29日閲覧。
  12. Mod()”. PureBasic. Fantaisie Software. 2015年12月9日閲覧。
  13. r6rs.org
  14. "ISO/IEC 9899:1990 : Programming languages -- C" (Document). 7.5.6.4: ISO, IEC. 1990. fmod関数は、yが0でない場合、iが整数となるx - i * yの結果を返す。結果はxと同じ符号となり、yよりも小さな絶対値をとる.
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.