MPCライブラリ

目次

  1. TL;DR
  2. MPC 概要
  3. インストール
  4. 基本
  5. 使い方 一例
C言語で計算を行う時,誤差が生じることがあります. 例えば有理数(有比数)や無理数を扱う場合です. この誤差は,コンピュータが2進数で演算を行っていること,また表現できる数値が有限であることに由来するため,決して取り除くことは出来ません. しかし対策をすることは出来ます. 例えば使っている変数がfloat型ならば,それらをdouble型に変更することで精度の向上が見込めます. 使えるbit数が文字通り倍(double)に増えるため,より真の答え(真値)に近い値を得ることが出来るようになるからです. www.multiprecision.org しかし...もしdouble型でも精度が足りないような計算を実行したい場合は? もしくは...複素数も計算したい場合は? そんな時にはMPCライブラリを使いましょう!

TL;DR (じっくり読みたい方はこの章を飛ばしましょう)

MPCライブラリを使えば任意長のbit数を持つ変数に加え,複素数も扱えます. MPCを使うにはGMPとMPFRライブラリも必要になりますが,最近のgccがインストールされていれば全てインストールされているでしょう. ご利用の際はヘッダファイルmpc.hをインクルードします. 次のコマンドで,MPCを使ったCプログラムをコンパイルできます.
gcc ファイル名 -lmpc -lmpfr -lgmp -o 出力名
任意長bit数または複素数となるmpc_t型の変数は"定義(宣言) → 初期化 → 使用 → 解放(クリア)"の順に利用します. ちなみに,MPCライブラリで定義されている函数名はすべてmpc_で始まります. また,ほとんどの函数の戻り値は丸め誤差を次に示すようなint型で返します.
  • +1 : 丸めた結果が真値よりも大きくなった場合.
  • 0 : 丸めた結果と真値が等しい場合(誤差ゼロ).
  • -1 : 丸めた結果が真値よりも小さくなった場合.
丸め処理の方法はN,Z,U,Dの4種類をMPC_RNDに続けて入力することで指定出来ます.
  • N (to nearest) : 近似値
  • Z (towards zero) : ゼロ方向へ丸め
  • U (towards plus infinity) : 正無限大方向へ丸め
  • D (towards minus infinity): 負無限大方向へ丸め

MPC 概要

MPCはおそらくMulti-Precision Complexの略です. 高精度複素数の意味でしょう. このライブラリの特徴は次の通りです.
  • 任意のbit数を持つ変数を定義することが出来る.
  • 複素計算を実行できる.
  • 4種類の丸め処理が用意されている.
MPCライブラリを使えばfloat型やdouble型に加え,mpc_tという型の変数も定義することが出来るようになります. あなたがお使いのコンピュータに積まれたメモリが許す限りのbit数を,このmpc_t型の変数へ割り当てることが出来ます. またmpc_t型変数へ実部と虚部の2つを持つ,言わば複素数を代入することも出来ます. ある数値を表現するにはbit数が不足している場合や,無理数の場合はその少数部が無限に続きますが,mpc_t型変数へ代入する際には適切な丸め処理(端数処理)が施されます. 丸め処理には次の4つを選択出来ます.
  • N (to nearest) : 近似値
  • Z (towards zero) : ゼロ方向へ丸め
  • U (towards plus infinity) : 正無限大方向へ丸め
  • D (towards minus infinity): 負無限大方向へ丸め
N(近似値)はIEEE P754標準に従った近似をするモードで,数値を2進数で考えた場合の最下位桁(LSB)を0にして近似します. 例えば5は2進数で(101)2です. この場合LSBの1を0へと変えた(100)2,つまり4へと丸めます.

インストール

MPCは,GMP(高精度計算)とMPFR(高精度浮動小数計算)という2つのライブラリを用いて開発されています. そのため,MPCを組み込む前にGMPとMPFRをインストールする必要があります. もし,UnixやLinuxでgccを用いてC言語をコンパイルしている場合,すでにこれら3つのライブラリはインストールされているかも知れません. というのも,最近のgccはGMP/MPFR/MPCがインストールされていないとインストール出来ないようになっているためです. もしこれらがインストールされていなければ,homebrewやpacman,apt-getなどのパッケージ管理ツールを利用してインストールするか,あるいは下記リンクよりDownloadページへ飛び,最新のGMP/MPFR/MPCライブラリをダウンロード/インストールしましょう. ダウンロードした圧縮ファイルを任意の場所へ展開します. ターミナルを起動して解凍したフォルダ(例:gmp-3.1.2)までcdコマンドで移動します.
cd ~/gmp-3.1.2
そこで次のコマンドを一行づつ実行します.
./configure
make
make check
make install
GMP,MPFR,MPCのそれぞれのディレクトリで実行すれば全てのインストール作業は完了です.

基本

MPCライブラリを使うにはヘッダファイルmpc.hをインクルードします. gccコンパイラではlmpc,lmpfr,lgmpオプションを付加してコンパイルしましょう. 任意長のbit数を割り当てたい変数,もしくは複素数として扱いたい変数,またはその両方でも,mpc_t型変数として定義します. mpc_t型変数を使うには,定義→初期化→使用→クリアの手順を踏む必要があります. mpc_t型変数を定義(宣言)した後,mpc_init2もしくはmpc_init3函数を用いて初期化します. この初期化処理で必要なbit数をmpc_t型変数へ割り当てるのです.
mpc_init2(初期化したいmpc_t型変数, bit数);
と2つの引数を渡して初期化します. これで実部と虚部に任意のbit数が割り当てられます. もし,実部と虚部に異なるbit数を割り当てたい場合は,
mpc_init3(初期化したいmpc_t型変数, 実部のbit数, 虚部のbit数);
を実行しましょう. 初期化された直後の変数は実部虚部ともにNaN(Not a Number: 非数)が代入されています. mpc_set函数を利用して実際に数値を代入します. 符号無し整数(ui: unsigned integer)を代入したければ,mpc_set_ui_ui函数を利用できます.
mpc_set_ui_ui(代入先変数, 実部の値, 虚部の値, 丸め処理方法);
丸め処理方法とは"MPCライブラリ 概要"で説明した4つの丸め処理方法のことです. MPC_RNDに続けて,実部の丸め処理方法,虚部の丸め処理方法と入力します. 例えば,丸め処理としてMPC_RNDZUを渡すと,実部はゼロ方向へ丸められ,虚部は正無限大方向へと丸められた値が変数へ代入されます. MPCライブラリで定義されている函数は名前の頭がmpc_で始まっています. 例えば乗算はmpc_mul函数,加算はmpc_add函数といった具合です. MPCライブラリで定義されている函数を利用することでmpc_t型変数の計算が出来ます. これは高精度な計算が行えるという意味です. もちろん複素計算も実行可能です. mpc_mul函数の場合,第二引数と第三引数を掛けあわせた結果を第一引数へ代入します. この時必要に応じて第四引数で指定された丸め処理が施されてから第一引数へ代入されます. MPCライブラリのほとんどの函数は,その戻り値として丸め誤差の影響をint型で返してくれます. この戻り値はMPC_INEX_RE(int型の戻り値)マクロを利用することで実部の,MPC_INEX_IM(int型の戻り値)マクロで虚部の丸め誤差を得られます. この丸め誤差は次の3種類の値を持ち得ます.
  • +1 : 丸めた結果が真値よりも大きくなった場合.
  • 0 : 丸めた結果と真値が等しい場合(誤差ゼロ).
  • -1 : 丸めた結果が真値よりも小さくなった場合.
このように,計算結果が丸められたために生じた誤差を確かめる手段も,MPCライブラリには用意されています. 使い終えたmpc_t型変数はmpc_clear函数で解放する必要があります. 忘れずに解放しましょう.

使い方 一例

以下にMPCを用いたCプログラム例を載せます. これはSep 2013の記録で扱った問題で,虚数の虚数乗を計算するプログラムです. ヘッダファイルmpc.hのインクルードを忘れずに...

pow.c (虚数の虚数乗を求めるプログラム)

#include <stdio.h>
#include <mpc.h>

int main() {
  mpc_t z;
  int inex;
  mpc_init2(z, 128);
  mpc_set_ui_ui(z, 0, 1, MPC_RNDNN);

  inex = mpc_pow(z, z, z, MPC_RNDNN);
  pc_out_str(stdout, 10, 10, z, MPC_RNDNN);
  printf("\n%i %i\n", MPC_INEX_RE(inex), MPC_INEX_IM(inex));

  mpc_clear(z);
  return 0;
}
C言語で解くことが出来るのか不安になるような問題ですが,次のコマンドを入力してコンパイル/実行しましょう.
gcc pow.c -lmpc -lmpfr -lgmp -o pow
./pow
さて,次のように出力されたかと思います.
(2.078795764e-1 0)
-1 0
1行目はmpc_out_str函数によって出力されたmpc_t型変数の値です. (実部 虚部)となっているので実部の値が"2.07879...",最後がe-1ですから10のマイナス1乗をすると結局答えは,実部"0.207..."で虚部"0". 確かに解けています! 次の行には丸め誤差を出力しました. 確認すると,実部は丸められた影響で真値よりも小さな値となったこと(-1)が分かります.

プログラムについてもっと詳しく...

mpc_t型変数のべき乗を計算する函数は11行目のmpc_pow函数です. またint型の変数inexに代入している戻り値はmpc_powが丸めたことによって生じたその丸め誤差です. MPC_INEX_RE(inex)で実部の,MPC_INEX_IM(inex)で虚部の丸め誤差を出力しました. ちなみに,inexはinexactの略で"不正確な"という意味です. 6行目はmpc_t型変数zの定義,8行目はmpc_init2函数による変数zの初期化です. 今回はzの実部と虚部に128bitを割り当てました. 9行目でmpc_set_ui_ui函数を用い,変数zに値を代入しています. 実部に符号無し整数0,虚部に符号なし整数1を代入しているのでzには虚数iが代入されたことになります. mpc_set_ui_ui函数を利用しましたが,もしdouble型の値を代入したいのならばmpc_set_d_d函数を利用できます. 第四引数の丸め処理は本プログラムではMPC_RNDNNとし,実部虚部ともにN(to nearest)での丸め処理としました. もちろんZやUやDも使えます. mpc_pow函数でべき乗の計算して,mpc_out_str函数で変数zの値をstdout(標準出力)へ出力. 正しい答えが得られました. mpc_out_strの第三引数は,出力桁数です. ここでは10桁を指定していますが,この値を0にすると自動的に桁数を決定し出力してくれます. そして最後に忘れてはいけないことはmpc_t型変数をクリアすることです. mpc_t型変数を使い終えたら,必ずmpc_clear(クリアしたい変数)を実行しましょう.
861553264840121533 http://www.storange.jp/2014/03/mpc.html http://www.storange.jp/2014/03/mpc.html MPCライブラリ 2014-03-06T11:17:00+09:00 http://www.storange.jp/2014/03/mpc.html Hideyuki Tabata 200 200 72 72