割り算の余りは?

こんにちは、masm11 です。

突然ですが、今回は、負の整数を正の整数で割った時の余りは 0 以上なのか 0 以下なのか、 という話を書きたいと思います。

具体的には、-22 % 3 はいくつなのか、というお話です。

実は、言語によって違うのです…

2 になるものたち

Ruby

Ruby は余りが 2 になります。

仕様は以下のページにあります。

https://docs.ruby-lang.org/ja/latest/method/Numeric/i/modulo.html

また、以下のページを見ると、Ruby 1.8.7 の時には既に明確に定義されていたようですね。

https://docs.ruby-lang.org/ja/1.8.7/class/Numeric.html#I_MODULO

実際に試してみました。

luna:~ % irb
irb(main):001:0> -22 % 3
=> 2
irb(main):002:0> 

Python

Python 3 は、以下の表の下の注釈 1. に記載がありました。

https://docs.python.org/ja/3/library/stdtypes.html#typesnumeric

結果(商)が負の無限大の方向に丸められるということは、余りは 0 以上です。

Python 2.7 でも、以下のページの表の下の注釈 1. に同様の記載がありました。

https://docs.python.org/ja/2.7/library/stdtypes.html#numeric-types-int-float-long-complex

Ruby と同様に実際に試してみました。

luna:~ % python
Python 3.8.2 (default, Apr  8 2020, 14:31:25) 
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> -22 % 3
2
>>>

-1 になるものたち

Java

Java では余りが -1 になります。

以下のページの floorDiv メソッドに説明がありました。

https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Math.html#floorDiv-int-int-

/ で普通に割り算すると、0 に向かって丸められるようです。 ということは余りは -1 以下ですね。

そして floorDiv メソッドを使った場合は Ruby や Python と同じ丸め方になるようです。従って floorMod メソッドを使うと余りは 2 ですね。 更に、このメソッドには「導入されたバージョン: 1.8」と記載があり、Java 8 以降にしかないようです。

Java 7 については、以下に記載がありました。

https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.17.2

Integer division rounds toward 0.

やはり 0 に向かって丸めていたようです。

実際に試してみました。

luna:~ % java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
luna:~ % cat test.java
class test {
    public static void main(String[] args) {
        System.out.println(-22 % 3);
        System.out.println(Math.floorMod(-22, 3));
    }
}
luna:~ % javac test.java       
luna:~ % java -classpath . test
-1
2
luna:~ % 

C99

以下に記載がありました。

https://kikakurui.com/x3/X3010-2003-01.html の 6.5.5

整数同士の除算の場合,/演算子の結果は,代数的な商から小数部を切り捨てた値とする(87)。

(87) には以下のように書いてあります。

これは, 0 方向への切捨てとも呼ぶ。

試してみました。

luna:~ % cat test.c
#include <stdio.h>

int main(void)
{
    printf("%d\n", -22 % 3);
    return 0;
}
luna:~ % cc test.c
luna:~ % ./a.out 
-1
luna:~ % 

処理系定義のもの

C89

C99 登場以前の C は C89 と呼ばれる規格に従っていました。 C89 の規格は簡単には手に入らなさそうなので、stackoverflow.com で見つけた情報 をご紹介します。

https://ja.stackoverflow.com/questions/12649/

C89 では処理系定義だったようです。つまり、結果がコンパイラや CPU に依存していた、ということです。

そんな処理系を持っていないので、残念ながら実際に試すことはできませんでした。

まとめ

C89 のコンパイラを使っていた時、ふと、余りが負であることに気付き、 私は衝撃を受けました。 それからずーっと気になっていて、今回ようやく各言語の状況をまとめることが できました。

昔はコスト的に 0 以下にすることを選んだ CPU が多く、C ではそれがそのまま 結果として現れているようです。しかし現在は CPU の処理能力も向上し、 少しくらい処理が増えても問題ないため、現代的な言語では 0 以上になっている のでしょう。

Java について言うと、私は Java は CPU 能力が潤沢にある環境で使うものだと 思っているので、そういう意味では意外な結果でした。

ではまた!