Quotient (q) and remainder (r) as functions of dividend (a), using truncated division
← ホームへ戻る

剰余演算(モジュロ)の仕組みとプログラミングにおける定義の違い

数学やコンピューティングの世界で頻繁に登場する剰余演算(Modulo Operation)とは、ある数を別の数で割った際に残る「余り」を求める操作のことです。一般的に「a mod n」と表記され、このとき割られる数(a)を被除数、割る数(n)を除数(またはモジュラス)と呼びます。

例えば、「5 mod 2」の結果は1になります。これは5を2で割ると商が2となり、1が余るためです。同様に「9 mod 3」は、割り切れるため結果は0となります。整数演算で用いられることが一般的ですが、現代の多くの計算システムでは浮動小数点数などの数値型にも対応しています。なお、整数nによる剰余演算の結果は、常に0からn-1の範囲に収まります。

Key Facts

  • 基本定義: 剰余演算は、除算後の余り(または符号付き余り)を算出する操作である。
  • 結果の範囲: 正の整数nで割った場合、結果は0からn-1の間になる。
  • 定義の多様性: 負の数が含まれる場合、言語やシステムによって「切り捨て」「床関数」「ユークリッド除法」など計算手法が異なる。
  • 最適化: 除数が2の累乗である場合、ビット演算を用いて高速に処理できることがある。

除法の定義による剰余の変動

正の数同士の計算では結果は一致しますが、負の数が関わると「どの商を選択するか」によって余りの値が変わります。このため、コンピューティングの世界には複数の定義が存在します。

切り捨て除法(Truncated Division)

商をゼロ方向へ切り捨てる方式です。多くのプログラミング言語(C言語やJavaなど)で採用されており、余りの符号は被除数(割られる数)の符号に従います。

Quotient (q) and remainder (r) as functions of dividend (a), using truncated division

床関数除法(Floored Division)

商を負の無限大方向へ丸める(床関数 $\lfloor \rfloor$ を使用する)方式です。PythonやRubyなどで採用されており、余りの符号は除数(割る数)の符号に従います。

Quotient and remainder using floored division

ユークリッド除法(Euclidean Division)

数論で一般的に用いられる定義で、余り $r$ を常に非負($0 \le r < |n|$)とする方式です。これにより、除数の符号に関わらず一貫して正の余りを得ることができます。

Quotient and remainder using Euclidean division

その他の丸め方式

IEEE 754規格やCommon Lispなどで採用されている丸め除法(Rounded Division)では、商を最も近い整数に丸めます(偶数丸め)。この場合、余りは $-n/2$ から $n/2$ の範囲に収まります。

Quotient and remainder using rounded division

また、商を正の無限大方向へ丸める天井関数除法(Ceiling Division)という定義も存在します。

Quotient and remainder using ceiling division

剰余演算の性質と計算効率

剰余演算には、数学的に便利ないくつかの恒等式が存在します。

  • 恒等性: $(a \bmod n) \bmod n = a \bmod n$
  • 逆元: $[(-a \bmod n) + (a \bmod n)] \bmod n = 0$
  • 分配律: $(a + b) \bmod n = [(a \bmod n) + (b \bmod n)] \bmod n$

パフォーマンスの最適化

計算機にとって除算はコストの高い処理です。そのため、除数が2の累乗(2, 4, 8...)である場合、コンパイラはビット演算(AND演算)に置き換えて高速化します。例えば、x % 2x & 1 と等価です。ただし、C言語のように余りの符号が被除数に依存する言語では、被除数が負の場合に結果が異なるため、単純な置換ではなく条件分岐やビット反転を組み合わせた処理が必要になります。

主要言語における実装の比較

言語によって、デフォルトの演算子(%など)がどの定義に基づいているかは異なります。以下に代表的な例をまとめます。

プログラミング言語別 剰余演算の定義
言語 演算子 定義方式 備考
C / C++ / Java % 切り捨て (Truncated) 余りの符号は被除数に依存
Python / Ruby % 床関数 (Floored) 余りの符号は除数に依存
Rust % 切り捨て (Truncated) rem_euclid() でユークリッド除法が可能
Ada mod / rem 床関数 / 切り捨て 2種類の演算子を使い分ける
Haskell mod / rem 床関数 / 切り捨て 数学的定義と計算機的定義を分離

オフセット付き剰余

通常、剰余の結果は $0$ から $n-1$ ですが、特定の用途(例:1からnまでの範囲にしたい場合)では、オフセット $d$ を導入することがあります。この場合、計算式を調整することで、結果を $d$ から $d + n - 1$ の範囲にシフトさせることが可能です。これはカレンダーの計算や、1始まりのインデックスを扱う処理などで有用です。

Frequently Asked Questions

なぜ言語によって剰余の結果が違うのですか?

それは「商」をどのように定義するかが異なるためです。ゼロに向かって丸めるか、負の無限大に向かって丸めるか、あるいは常に正の余りを維持するかという設計思想の違いが、結果として余りの符号や値の差となって現れます。

「%」演算子を使えば常に正の数が返ってきますか?

いいえ。C言語やJavaなどの「切り捨て除法」を採用している言語では、割られる数(被除数)が負の場合、結果も負になります。常に正の値を期待する場合は、言語が提供する専用の関数(例:Rustの rem_euclid)を使うか、計算後に調整が必要です。

剰余演算を高速化する方法はありますか?

除数が2の累乗である場合、ビット演算のAND(&)を使用することで劇的に高速化できます。多くの最適化コンパイラはこれを自動的に行いますが、低レイヤの最適化が必要な場合は意識して記述することがあります。

浮動小数点数に剰余演算は使えますか?

はい、可能です。ただし、整数とは挙動が異なる場合があります。C言語の fmod や IEEE 754 規格に基づいた remainder 関数などが提供されており、用途に応じて使い分ける必要があります。

References

  1. Mathematically, these two choices are but two of the infinite number of choices available for .
  2. The revised report uses boldface to distinguish between keywords and identifiers, but permits other formats, including capitalization (e.g. MOD).
  3. Argument order reverses, i.e., α|ω computes ω mod α {\displaystyle \omega {\bmod {\alpha }}} , the remainder when dividing ω by α.
  4. and define the behavior of % to be truncated. The standards before then leave the behavior implementation-defined.
  5. Divisor must be positive, otherwise undefined.
  6. As discussed by Boute, ISO Pascal's definitions of div and mod do not obey the Division Identity of D = d · (D / d) + D % d, and are thus fundamentally broken.
  7. Perl usually uses arithmetic modulo operator that is machine-independent. For examples and exceptions, see the Perl documentation on multiplicative operators.
  8. Racket defines an integer as a rational number for which round produces the same number when applied. This includes some inexact numbers, like 42.0, but excludes others, like 3.14.
  9. The expr command is defined independently of commands in the ::tcl::mathop namespace.
  10. The expr command is defined in terms of commands in the ::tcl::mathfunc namespace.

📸 フォトギャラリー

Quotient (q) and remainder (r) as functions of dividend (a), using truncated division
Quotient and remainder using floored division
Quotient and remainder using Euclidean division
Quotient and remainder using rounded division
Quotient and remainder using ceiling division