携帯電話 Java で小数計算! with MathFP

Time-stamp: "2007-05-29 Tue 02:00 JST hig"

現在, 携帯電話のアプリ開発に使われている Java 2 Micro Edition (J2ME)の CLDC1.0 には, float や double などの(浮動)小数型が含まれていません. クラス Float や Double も含まれていません. (一部のキャリアの独自拡張にはこれらを含むものがあります. 下記参照) したがって, 当然 sin, cos, tan, asin, atan, exp, log, pow なども使えません. 不便ですね…やりたいことによっては.

ここでは, 固定小数点小数を使用できるようにする MathFP クラスライブラリの使い方を紹介します これを使ったアプリの例はこちらで紹介しています. そのうちのひとつのサンプルプログラムのソースを公開しています. ほぼ共通のソースからiアプリ/Vアプリ/EZアプリ用のソースを生成していますので, (MathFPとは限らない)アプリの別キャリアへの移植をしようとしている人にも役立つかもしれません.

(2004/11/18 Thu 追記) 2004.11 にリリースされた NTT DoCoMo 901i シリーズ の DoJa4.0 は J2ME CLDC1.1 に基づいています. CLDC1.1 では, double, float, およびこれを扱うための Math クラスがサポートされています. したがって, 901i専用のアプリを作る場合には, このページの扱っている問題は存在しなくなりました. よかった…

(2007-05-29 Tue 追記) Vladimir Roubtsov さんにより, 携帯で使用可能なradix 10000な浮動小数点ライブラリー dfpが公開されています. hiroto inoue 様から情報をいただきました. ありがとうございます.

(2007-05-29 Tue 追記) 3キャリアポータブルな固定小数点ライブラリMFPの開発について 報告されています.

目次

  1. 小数計算はちょっと困る…
  2. キャリア独自拡張に頼る?
  3. MathFP がお奨め!
  4. MathFP はちょっと困る…
  5. MathFP を使えるようになるまで
    1. MathFP に含まれるもの
    2. 方法1: 逆コンパイルする!
    3. 方法2: DoJa/MIDP Builderを買う!
    4. 方法3: DoJa のコマンドラインでの開発
    5. 方法4: MIDP のコマンドラインでの開発
  6. 参考文献

小数計算はちょっと困る…

携帯電話やPDA の Java である J2ME (DoJa=DoCoMoのiアプリ, MIDP=auのEZアプリ(Java),VodafoneのVアプリ,MIDP on Palm) では, 小数計算が "できません." というのも, これらの携帯 Java の採用しているバージョンの Java 2 Micro Edition(J2ME) では, 標準で double, float 型がサポートされていないからです(int はあります).

もちろん, int だけを使って 独自に固定小数点実数を実装する (三角関数とかはテーブルを持つか, テイラー展開を利用. 参照) ことにより, 小数計算を行なうことはできます. しかし, 正直面倒くさいです.

キャリア独自拡張に頼る?

キャリアもこの問題(特に, 3次元グラフィックスを用いる際には深刻になります)を認識しており, 独自に固定小数点実数を実装するのを助ける独自拡張を提供しています. たとえば, au の KDDI Profile Phase3 の com.jblend.graphics.util.Calc, com.jblend.graphics.m3d.Point3D, Vodafone の com.j_phone.amuse.j3d.Util3D, com.j_phone.amuse.j3d.Vector3D, NTT DoCoMo の DoJa2.0 の com.nttdocomo.opt.ui.j3d.Math, com.nttdocomo.opt.ui.j3d.Vector3D などです. ただし, 端末に依存する場合があります.

さらに進んで, 固定小数点クラスが提供されている場合もあります. Vodafone の Vアプリ P4,P5型の com.j_phone.util.FixedPoint, NTT DoCoMo の DoJa 2.0 以降で導入された com.nttdocomo.opt.ui.j3d2.C3DMath などです.

しかし, これらの独自拡張を利用して作成したプログラムにはキャリア間の portability がありません. また, 古い端末では走らなくなってしまいます. 最新の端末で CPU パワーを絞り出すようなアプリ以外では, これらの独自拡張を利用することには躊躇します.

なお, 次のバージョン J2ME CLDC1.1 では, double, float, およびこれを扱うための Math クラスがサポートされており, この問題は解消することが期待されます. (2004/11/18 Thu 追記. 2004.11 にリリースされた NTT DoCoMo 901i シリーズ DoJa4.0 で J2ME CLDC1.1 がサポートされました)

MathFP がお奨め!

J2MEで小数計算をするにはOnno Hommesさんの作られたクラスライブラリ MathFP を使うと便利です.

ちなみに, 浮動小数点でなく固定小数点というのは, 指数部( 1.234 ×10n の ×10n の 部分)を使わない表現方法ということです. Hommes さんは, 携帯だし計算速度が十分じゃないから, Float クラスを作ったりせずに, 固定小数点整数を利用して実装したのだと思うのですが, 誰か浮動小数点実数を作ってくれるともっとハッピーかも > 自分)

小数計算は, (ゲームの, または物理用の)物理シミュレーションには必須です. 樋口は, 物理の授業用デモを作る過程で, 実数(小数)を扱う必要に迫られました. 樋口が MathFP をもちいて作成した物理の授業用デモがありますので, ご参考までにごらんください.

MathFP はちょっと困る…

MathFP は, ソース(.java)でなくクラスファイル(.class)の形で配布されています. キャリアなどの配布している標準的なグラフィカルな開発環境

では, このようなコンパイル済クラスライブラリを使用するビルドを行なうことはできません. しかし, もちろん, command line からコマンドを実行すればできるわけです. この方法を解説したドキュメントがあまり見当たらないというのが, この文章を書こうとしたきっかけです.

なお, 文末に挙げたページ, ファイル, 書籍がたいへん勉強になりました. 著者のみなさまありがとうございます.

以下で説明しているのは, MathFP のインストールと, 開発の手順です. MathFP クラスのメソッドの説明, プログラムの書き方の説明はしていません. それについては, MathFP 付属の javadoc (英語) を参照してください. また, Web 上に 日本語の文書もあるようです. 例えば Soukou さんのページ. MathFP についての記述がある, 日本語で書かれた書籍として, Java による Palm プログラミング入門 があります.

MathFP を使えるようになるまで

下で説明する方法の意味は, compie -> prevery -> packaging(jar化) -> 必要なら JAM, ADF などの作成, KJX 化, CRC 付加 という通常の手順を理解しているとよりよく理解できるかもしれません. が, 手順だけは理解できるように書いたつもりです.

MathFP に含まれるもの

mathfp-2.0.6.zip の場合, 次のようになります.
mathfp-2.0.6/bin/  (Palm の MIDP用 および WABA 用デモプログラム)
mathfp-2.0.6/docs/*    ドキュメント(Javadoc)
mathfp-2.0.6/classes/net/jscience/math/MathFP.class (MIDP用64ビット *2)
mathfp-2.0.6/classes/net/jscience/math/waba/MathFP.class (WABA用32ビット)
mathfp-2.0.6/classes/net/jscience/math/kvm/MathFP.class (KVM用32ビット *1)
mathfp-2.0.6/classes/calcMathFP.class (J2SE 用デモプログラム)
mathfp-2.0.6/classes/j2seMathFP.class (J2SE 用デモプログラム)
mathfp-2.0.6/classes/midpMathFP$midpCanvas.class (デモプログラム用のクラス)
mathfp-2.0.6/classes/midpMathFP.class (MIDP 用デモプログラム)
mathfp-2.0.6/classes/wabaMathFP.class (WABA 用デモプログラム)
mathfp-2.0.6/README   

ドキュメントを除けば, 携帯 Java の開発に必要なのは *1 (とその上の階層構造)だけです. なぜなら, 現在の携帯 Java はすべて J2ME CLDC を用いており, 使用する Java VM は KVM に限られる CLDC でない J2ME において, long 型を用いて64ビット固定小数点計算を行なうためのクラスが *2 です.

なお, WABA とは, Wabasoft社の, Palm, PocketPC 用のプログラミングプラットフォームです.

方法1: 逆コンパイルする!

樋口はやったことはないのですが, 原理的には, クラスライブラリとして配布されていても, JADなどの逆コンパイラを使って 逆コンパイルしてしまえば, 自分で書いたソースと同様に扱えます. すると, i-appli development tool などで開発できます. Eclipse + DoJa-3.5 eclipse plugin の組みあわせでも開発できます.

方法2: DoJa/MIDP Builderを買う!

SkyArts の開発ツール MIDP Builder, DoJa Builder では, MathFP のような 外部ライブラリ を指定できます. \apps\(プロジェクト名)\libclasses\net\jscience\math\kvm\MathFP.class という位置に MathFP.class を置くだけです(プロジェクトの数だけコピーしなくちゃいけないけど). Preverify, jar へのパッケージングも自動でやってくれます. classpath に上の path を追加する必要はありません.

方法3: DoJa のコマンドラインでの開発

準備

J2ME Wireless SDK for DoJa をインストールします (以下は, これに含まれる build.bat, run.bat を参考にさせていただいています). J2SE をインストールします. これらの開発環境の path を示す環境変数が

set JAVA_HOME=C:\jdk1.3.1_06
set KVEM_HOME=C:\J2MEWSDK4DOJA
と設定されているとします.

次にプロジェクトのディレクトリとして, 次のようなディレクトリ構造を考えます.

カレント-+ src         Class1.java Class2.java などのソースが置かれる.
         + tmpclasses  preverify 前のクラスファイルが置かれる一時ディレクトリ
         + tmpclasses - net - jscience - math - kvm - MathFP.class
         + classes     preverify 後のクラスファイルが置かれる
         + bin         最終的な jar が置かれる.
MathFP を, ディレクトリ階層構造を保ってインストールします. classes\net\jscience\math\kvm\MathFP.class だけを tmpclasses\net\jscience\math\kvm\MathFP.class としてインストールします(preverify しておく必要はありません). この段階では, tmpclasses, classes, bin は (MathFP.class は除いて)空にしてあるとします.

ビルドの方法

実際には, 以下の手順を batch file または Makefile にして繰り返し実行するのが現実的でしょう.
  1. ソースを作ります. import 文は

          import net.jscience.math.kvm.MathFP;
    
    となります.

  2. 次にコンパイルします. 得られた class ファイルは tmpclasses に置かれます(-d).

    %JAVA_HOME%\bin\javac -g:none -encoding SJIS_i -J-Xbootclasspath/a:%KVEM_HOME%\lib\dojaconv.jar -bootclasspath %KVEM_HOME%\lib\dojaapi.jar -sourcepath src -classpath src;tmpclasses -d tmpclasses src/Class1.java
    ... Class2.java などに対しても繰り返す.
    
    classpath に tmpclasses を含めているため, ここに置いてある MathFP.class が 参照されます.

  3. 次にディレクトリ tmpclasses の中の class ファイルを事前検証(preverify)して, classes ディレクトリに出力します(実機でなく, i-JADE Light で実行するだけならこのステップは不要です)

    %KVEM_HOME%\bin\preverify.exe -classpath tmpclasses;%KVEM_HOME%\lib\dojaapi.jar -d classes  tmpclasses
    

  4. Jar ファイルにパッケージします.

    %JAVA_HOME%\bin\jar cMf bin\vector.jar -C classes .
    

  5. jam ファイルを記述します. 特に vector.jar のサイズを, jam ファイルに反映させます.

    start notepad ..\bin\vector.jam
    
    何度も手で直すのが面倒だったら, jam.template のようなものをプロジェクト(Vector)ごとに作っておいて, ディレクトリ bin 内で
    copy jam.template vector.jam
    echo PackageURL= Vector.jar >> vector.jam
    date +"LastModified= %a, %d %b %Y %T" >> vector.jam
    echo AppSize= `wc --bytes Vector.jar | cut -c 1,2,3,4,5,6,7` >> vector.jam
    
    のようにすれば自動化できます. ただし, date, wc, cut などは標準ではついてないので, cygwin などからもってくる必要があります.

  6. エミュレータで実行してみます.

    %JAVA_HOME%\bin\java -classpath %KVEM_HOME%\lib\dojatest.zip;%KVEM_HOME%\lib\kenv.zip -Dkvem.home=%KVEM_HOME% com.nttdocomo.dojaemu.tests.Tester jam phone1color
    

Preverify は一度だけにしたい

なお, 上の手順では, (内容の変化しない) MathFP.class を何度も preverify し直していることになります. 大した CPU 時間ではありませんが, これを避けるには, あらかじめ preverify した MathFP.class をディレクトリ class に置いておくということが考えられます. Preverify するには, MathFP のアーカイブを展開したディレクトリ classes をカレントとして,

%KVEM_HOME%\bin\preverify -classpath %KVEM_HOME%\lib\dojaapi.jar -d preverified .
のようにします. また, この場合, コンパイル時には, classpath で MathFP を指しておく必要があります. たとえば, net 以下を階層構造を保って zip ファイルにしたものを mathfp.zip などとして %KVEM_HOME%\lib においたとすると, classpath に
%KVEM_HOME%\lib\mathfp.zip
を追加します.

Unix(Linux)で開発したい!

以上は Windows 上での手順です. Linux 上でもとりあえず, なお, Zentek technology の i-JADE Light のクラスライブラリを使うと, コンパイルとエミュレータでの実行はできます (preverify ができないので, 携帯用の jar は作れませんが). そのための Makefile を書いてみました. あるディレクトリに, ソースVector.java と, MathFP のディレクトリ net (preverifyしてなくてよい)を置いてるとして, そこで make します. Makefile jam.template

Unix(Linux)で開発したい人にとっては, 増井俊之さんのezplusのUnix風開発環境構築がたいへん勉強になります. ezplus という表題ですが, J2ME に共通する内容もあります.

方法4: MIDP のコマンドラインでの開発

EZアプリ(Java)の場合を主に説明して, Vアプリ(Vodafone) と Palm の場合についてもコメントします.

準備

J2ME Wireless Toolkit をインストールします (以下は, これに含まれる make.bat を参考にさせていただいています). ezPlus Tools もインストールします. J2SE もインストールします. これらの開発環境の path を示す環境変数が

set JAVA_HOME=C:\jdk1.3.1_06
set KVEM_HOME=C:\WTK104
set EZ_HOME=C:\ezplusTools
と設定されているとします.

次にプロジェクトのディレクトリとして, 次のようなディレクトリ構造を考えます.

カレント-+ src         Class1.java Class2.java などのソースが置かれる.
         + tmpclasses  preverify 前のクラスファイルが置かれる一時ディレクトリ
         + tmpclasses - net - jscience - math - kvm - MathFP.class
         + classes     preverify 後のクラスファイルが置かれる
         + bin         最終的な jar が置かれる.
MathFP を, ディレクトリ階層構造を保ってインストールします. classes\net\jscience\math\kvm\MathFP.class だけを tmpclasses\net\jscience\math\kvm\MathFP.class としてインストールします(preverify しておく必要はありません). この段階では, tmpclasses, classes, bin は (MathFP.class は除いて)空にしてあるとします.

ビルドの方法

実際には, 以下の手順を batch file または Makefile にして繰り返し実行するのが現実的でしょう.
  1. ソースを作ります. 上に記した DoJa の場合と同様です.

  2. コンパイルします. 得られた class ファイルは tmpclasses に置かれます(-d).

    %JAVA_HOME%\bin\javac -encoding SJIS -g:none  -bootclasspath %KVEM_HOME%\lib\midpapi.zip -classpath tmpclasses -sourcepath src -d tmpclasses src/Class1.java
    ... Class2.java などに対しても繰り返す.
    
    classpath に tmpclasses を含めているため, ここに置いてある MathFP.class が 参照されます. もし, J-Phone や AU の拡張クラスを使うなら, この段階で, classpath にセミコロンで区切って追加します.

  3. 次にディレクトリ tmpclasses の中の class ファイルを事前検証(preverify)して, classes ディレクトリに出力します.

    %KVEM_HOME%\bin\preverify.exe -classpath %KVEM_HOME%\lib\midpapi.zip -d classes  tmpclasses
    

  4. Jar ファイルにパッケージします.

    %JAVA_HOME%\bin\jar cmf bin\Vector.jar bin\MANIFEST.MF -C classes .
    
    ここで, MANIFEST.MF はプロジェクトに応じて
    MIDlet-1: Vector, Vector.png , Vector
    MIDlet-Name: Vector
    MIDlet-Vendor: Saburo HIGUCHI
    MIDlet-Version: 1.0
    MicroEdition-Configuration: CLDC-1.0
    MicroEdition-Profile: MIDP-1.0
    
    のような感じで bin 内に用意しておきます.

  5. jad ファイルを記述します. 特に Vector.jar のサイズを, jad ファイルに反映させます.

    start notepad ..\bin\vector.jad
    
    何度も手で直すのが面倒だったら, bin をカレントにして
    copy MANIFEST.MF Vector.jad
    echo MIDlet-Jar-URL: Vector.jar >> Vector.jad
    echo -n MIDlet-Jar-Size:  >> Vector.jad
    wc --bytes Vector.jar | cut -c "1,2,3,4,5,6,7"  >> Vector.jad
    
    を実行し, MANIFEST.MF から自動的に生成してもいいでしょう. ただし, echo, date, wc, cut などは cygwin などからもってくる必要があります.

  6. なお, J-Phone の場合, jad と jar だけがあればいいので, これで完成です. エミュレータによる実行に飛びましょう. Palm の場合, この後 prc ファイルに変換する必要があります. この辺のことは, 書籍 MIDP Java ゲームプログラミング に大変詳しく書いてあります.

  7. EZアプリの場合, まだ続きがあります. まず, KJX ファイルにパッケージします.

    %JAVA_HOME%\bin\java -jar %EZ_HOME%\Tools\CmdTool\KJXArchiver.jar -c bin\Vector.jad bin\Vector.jar bin\Vector.kjx
    

  8. エミュレータで実行してみます.

  9. EZアプリの場合で, download cgi を用いて実機にダウンロードする場合には, あらかじめ kjx に CRC を付加しておく必要があります.

    %JAVA_HOME%\bin\java -classpath .;%EZ_HOME%\Tools\CRC CRC bin\Vector.kjx
    

Unix(Linux)で開発したい!

Unix(Linux)で開発したい人にとっては, 増井俊之さんのezplusのUnix風開発環境構築がたいへん勉強になります. ezplus という表題ですが, J2ME に共通する内容もあります.

参考文献


樋口は携帯 Java によるプログラミング教育, 携帯で動くプログラムを用いた物理/数学教育も行なっています. 概要は オープンキャンパスの際の説明ページ をご覧ください.
誤り, 改善すべき点などご指摘いただければ幸いです.
Copyright © 2003,2004 Saburo Higuchi. All rights reserved.
樋口三郎, http://www.math.ryukoku.ac.jp/~hig/ hig mail address