倭算数理研究所

科学・数学・学習関連の記事を、「倭マン日記」とは別に書いていくのだ!

commons-math 解読 (5) : MathUtils に定義されている static メソッド〜機械制約編〜

今回は機械制約(造語)に関するメソッドです(一覧)。

「機械制約」といって念頭にあるのはメモリのオーバーフローや double の精度に関する処理です。 扱うメソッドは以下の通り:

メソッド 返り値の型 説明
addAndCheck(int x, int y)
addAndCheck(long a, long b)
subAndCheck(int x, int y)
subAndCheck(long a, long b)
mulAndCheck(int x, int y)
mulAndCheck(long a, long b)
int
long
int
long
int
long
オーバーフローをチェックする演算
nextAfter(double d, double direction) double 機械が表現しうる次の数値を取得する
equals(double x, double y)
equals(double x, double y)
equals(double x, double y, double eps)
equals(double x, double y, int maxUlps)
boolean 精度を考慮したdouble の等値評価
compareTo(double x, double y, double eps) int 精度を考慮した double の順序評価
checkOrder(double[] val, int dir, boolean strict) void double の配列の順序評価
hash(double value)
hash(double[] value)
int double のハッシュ計算

xxxAndCheck()

オーバーフローをチェックする演算には次の3つが定義されています

  • addAndCheck()・・・足し算
  • subAndCheck()・・・引き算
  • mulAndCheck()・・・かけ算

整数値の計算なので割り算はなし。 オーバーフローが起こると ArithmeticException が投げられます。

try{
    // ∞+∞
    MathUtils.addAndCheck(Integer.MAX_VALUE, Integer.MAX_VALUE)
    assert false
}catch(ArithmeticException ex){}

try{
    // ∞-(-∞)
    MathUtils.subAndCheck(Integer.MAX_VALUE, Integer.MIN_VALUE)
    assert false
}catch(ArithmeticException ex){}

try{
    // ∞*2
    MathUtils.mulAndCheck(Integer.MAX_VALUE, 2)
    assert false
}catch(ArithmeticException ex){}

nextAfter()

nextAfter() メソッドは機械が表現できる最も近い数字を返します。 このメソッドjava.lang.Math に定義済(1.6以降)。 完全に挙動が同じかどうかは確かめてませんが。

package java.lang;

public final class Math{
    ...
    public static float nextAfter(float start, double direction) { ... }
    public static double nextAfter(double start, double direction) { ... }
}

第2引数は大小どちらに近い数字を返すかを指定します:

  • 「第1引数 < 第2引数」のとき、第1引数より小さい数字を返す
  • 「第1引数 >= 第2引数」のとき、第1引数より大きい数字を返す

ちなみに、隣の数字との差は第2引数には依存しませんが、第1引数には依存します。

assert MathUtils.nextAfter(1d, 2d) == 1.0000000000000002d
assert MathUtils.nextAfter(1d, 1d) == 1.0000000000000002d
assert MathUtils.nextAfter(1d, 0.5d) == 0.9999999999999999d
assert MathUtils.nextAfter(1d, -1d) == 0.9999999999999999d

// 増加分は第1引数によって異なる
assert MathUtils.nextAfter(2d, 2d) == 2.0000000000000004d

// java.lang.Math#nextAfter() と同じ(だと思う)
assert MathUtils.nextAfter(1d, 2d) == Math.nextAfter(1d, 2d)

equals()

equals() メソッドは double 値(もしくはその配列)の等値評価をします。 いくつかオーバーロード*1されているのでそれぞれ見ていきます。

equals(double, double)
このメソッドは、double の配列の各成分を等値評価します。

def array0 = [1d, 2d, 3d] as double[]
def array1 = [1d, 2d, 3d] as double[]
assert MathUtils.equals(array0, array1)

equals(double, double, double)
このメソッドは、第3引数で精度、つまり等しいとみなす値の範囲を指定します。 ちょっと数学っぽく書くと

    MathUtils.equals(x, y, e)

となっているとき「|x - y|≦ e」なら x と y は値が等しいと見なします(等しい場合も含めます inclusive)。

assert MathUtils.equals(0d, 0.01d, 0.001d) == false
assert MathUtils.equals(0d, 0.01d, 0.01d)
assert MathUtils.equals(0d, 0.01d, 0.1d)

equals(double, double, int)
このメソッドは、第3引数の整数で、精度が nextAfter() で返される値の何倍までを等しいと見なすかを指定します、たぶん(実際に nextAfter を呼び出してるかどうかは知りませんが)。 とりあえず、下のアサーションは通ります:

def d = MathUtils.nextAfter(0d, 1d)*100
assert d == 4.94E-322

assert MathUtils.equals(0d, d, 1000)
assert MathUtils.equals(0d, d, 100)
assert MathUtils.equals(0d, d, 10) == false
assert MathUtils.equals(0d, d, 1) == false

compareTo()

compareTo() メソッドは2つの double 値の値を精度を指定して比較します。 equals(double, double, double) と使い方は似ています。

assert MathUtils.compareTo(0d, 0.01d, 0.001d) == -1
assert MathUtils.compareTo(0d, 0.01d, 0.01d) == 0
assert MathUtils.compareTo(0d, 0.01d, 0.1d) == 0

checkOrder()

配列の要素が大小順に並んでいるかどうかをチェックします。 並んでいない場合は ArithmeticException が投げられます。

  • 第1引数は順序を評価する double の配列
  • 第2引数は小さい順(正の数)か大きい順(負の数)を指定します
  • 第3引数は順序関係が狭義かどうか(値が等しい場合を許さないか許すか)です
def array = [0.0d, 1.0d, 1.0d, 1.1d] as double[]

MathUtils.checkOrder(array, 1, false)    // 同じ値があっても OK

try{
    MathUtils.checkOrder(array, 1, true)    // 同じ値があるとダメ
    assert false
}catch(IllegalArgumentException ex){}

hash()

double 値、もしくはその配列のハッシュを計算します。

余談:同値関係と順序関係

以下、A を集合とする。
関係
A 上の二項関係もしくは関係とは、積集合 A×A の部分集合のこと。 R を A 上の関係とし、 のとき

と書く。

同値関係
同値関係とは、次の条件を満たす関係のこと:

  • A の任意の元 x に対して
  • なら
  • なら

これは JavaObject#equals() の元になってます*2

順序関係
順序関係とは、次の条件を満たす関係のこと:

  • A の任意の元 x に対して
  • なら
  • なら

通常、これを「」と書く。 これは Javajava.util.Comparator#compare() の使用の元になって(ると思い)ます。

数学の基礎―集合・数・位相 (基礎数学)

数学の基礎―集合・数・位相 (基礎数学)

Javaによるアルゴリズム事典

Javaによるアルゴリズム事典

*1:static メソッドの場合もオーバーロードっていうんだっけ?

*2:これに加えて null に関する制約なども付加されてます。