倭算数理研究所

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

倭マン's Math (1) : MathUtils (org.apache.commons.math.util パッケージ)

commons-mathAPI はお世辞にも使いやすいとは言えないので(Java 言語のせいもあるけど)、Groovy のメタプログラミング機能の力を借りて使いやすさを向上させちゃおう!って野望の元にあれこれ試行錯誤していきます。 パフォーマンスに関しては基本的に考慮してませんが悪しからず(一覧)。

今後のこのシリーズの記事に関して

  • Groovy:
    • コードは Groovy によって記述します。
    • 使用バージョンは 1.7.10 1.8.0 ですが、随時変更するかもしれません。
  • commons-math
    • 使用バージョンは 2.2 ですが、随時変更するかもしれません。
  • Jar ファイル
    • 拡張を行うコードを Jar ファイルにしてます。 サンプルを動かしたい場合はどうぞ。
    • 実行には Groovy が必要になります。
    • バージョンが変わってなくても内容が変わってる可能性があります(少なくともバージョンが 0.0a の場合)。
  • サンプルコード
    • import 文は、基本的に省略します。
    • サンプルコードの最初に「WamansMath.enhance(CommonsMathPackage.○○○)」という記述をつけていますが、これは Groovy によるメソッド追加処理を行うコードです。 Jar ファイルを使ってサンプルを動かす場合にも必要です。 すべてのメソッド追加を実行したい場合は「WamansMath.enhanceAll()」を実行してください。

前置きはこのくらいにして本題に。 今回定義するのは以下の2つ(一覧):

  • Gauss 記号 [x]
  • 丸めを java.math.RoundingMode で指定できる round() メソッド

Gauss 記号 [x]


「丸め方の指定」のサンプルとして、Gauss 記号で表される関数 [x] を作ってみます。 Gauss 記号 [x] とは「x を越えない最大の整数」で定義されます。 丸めを行う桁は小数第1位、丸め方は ROUND_CEILING。

Gauss 記号の処理は、MathUtils の static メソッドとして定義しましょう:

MathUtils.metaClass.'static'.gauss = { double x ->
    return MathUtils.round(x, 0, BigDecimal.ROUND_CEILING)
}

使い方は以下のようになります:

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

WamansMath.enhance(CommonsMathPackage.UTIL)

assert MathUtils.gauss(-1d) == -1d
assert MathUtils.gauss(-0.5d) == 0d
assert MathUtils.gauss(0d) == 0d
assert MathUtils.gauss(0.5d) == 1d

丸めを java.math.RoundingMode で指定できる round() メソッド


次は MathUtils#round() メソッド。 通常の round() メソッドでは丸めの指定に BigDecimal の int 型静的フィールドを使用してますが、Java 5 あたりから導入された列挙型 java.math.RoundingMode を使って丸めを指定できるようにしましょう。

import java.math.RoundingMode

MathUtils.metaClass.'static'.round = { double d, int i, RoundingMode mode ->
    switch(mode){
        case RoundingMode.CEILING:
            return round(d, i, BigDecimal.ROUND_CEILING)
        case RoundingMode.FLOOR:
            return round(d, i, BigDecimal.ROUND_FLOOR)
        case RoundingMode.UP:
            return round(d, i, BigDecimal.ROUND_UP)
        case RoundingMode.DOWN:
            return round(d, i, BigDecimal.ROUND_DOWN)
        case RoundingMode.HALF_UP:
            return round(d, i, BigDecimal.ROUND_HALF_UP)
        case RoundingMode.HALF_DOWN:
            return round(d, i, BigDecimal.ROUND_HALF_DOWN)
        case RoundingMode.HALF_EVEN:
            return round(d, i, BigDecimal.ROUND_HALF_EVEN)
        default:
            return round(d, i, BigDecimal.ROUND_UNNECESSARY)
    }
}

列挙型の order() メソッドを使えばもっと簡単に定義できるかも知れませんが、order() メソッドを直接使うのは良くないようなので頑張って switch ! 使い方は通常の round() メソッドと大して変わりません:

@GrabResolver('http://www5.ocn.ne.jp/~coast/repo/')
@Grab('org.waman.math:wamans-math:0.0.1')

WamansMath.enhance(CommonsMathPackage.UTIL)

assert MathUtils.round(123.456d, 0, RoundingMode.CEILING) == 124.0d
assert MathUtils.round(123.456d, 0, RoundingMode.FLOOR) == 123.0d
assert MathUtils.round(123.456d, 0, RoundingMode.UP) == 124.0d
assert MathUtils.round(123.456d, 0, RoundingMode.DOWN) == 123.0d

ガウスの数論 わたしのガウス (ちくま学芸文庫)

ガウスの数論 わたしのガウス (ちくま学芸文庫)