墜落日記 - 2009年3月10日の墜落
PHP で任意精度の演算を
PHP は歴史的に異常とも言えるほどデータ型が弱い。
ま、言語が既に破綻しているという噂もなきにしもあらずで、今更データ型が云々感ぬん言うのもどうかというレベルではあるが、なんにしろ異常とも言えるほどデータ型が弱い。
整数型は 32bit 整数しかない。
64bit 環境でコンパイルすれば 64bit 整数しかないことになるのだろうが、どちらにせよ固定だ。
で、小数点が入ると途端に float、浮動小数点となる。
しかも演算結果が整数型に入りきらないと勝手に浮動小数点になってしまうと言う熱い仕様まである。
熱すぎて火傷しそうである。
だから、大きな桁の計算や、計算誤差が許容されない通貨計算など、非常に難儀することになるわけだが、これを解決するために PHP では任意精度の値を計算する方法として、大別して2つの方法がある。
ひとつが BCMath 任意精度数学関数で、これは常に値を文字列として取り扱い、任意精度の計算を可能としている。
実はコレで大概のことは出来てしまうはずなのだが、データ型が文字列である関係上、不意の事故が起こりやすい。
要は任意精度数値としての文字列型なのか、単純に例えば HTTP リクエストで渡された直後故の文字列型なのか、はたまた整数型と文字列型の区別が旨く出来ない初学者故の文字列型なのか区別できないので、予期せぬ不具合の温床となりやすいわけだ。
もう一つが GNU Multiple Precision (GMP) ライブラリを利用する任意精度数学関数で、これは値をリソースとして保持しており、パース処理が少ない分だけ計算自体は速いだろうが、如何せん整数値のみである。
大別して2つの方法があるとは言え、どちらも一長一短で、コレだっ!! というソリューションが存在しない。
なので、作ってしまうことにした。
前述の GMP 関数群で利用する GMP 値で整数部と固定小数部を整数値として管理、固定小数部の精度と符号を別途で管理する、合計 4 つのパラメータで任意精度の固定小数点数を構成するクラスを作ってしまおう。
JAVA であれば丁度 BigDecimal の様な構成だ。
(ま、BigDecimal の内部がどのように実装しているのかは知らないけど)
ついでに BigInteger に相当するクラスも作ってしまおう。
思い立ったが吉日、取り敢えず昨晩にバタバタとコーディングして PHP 版の簡易 BigDecimal と BigInteger クラスを実装してしまう。
色々と計算を通して簡単なテストをして、取り敢えず使えそうなトコロまで明け方には完成した。
あとはこれをデータベース抽象化ライブラリと連携して使えるようにしてあげないといけないけど………
コメントは投稿されていません。