プログラミング時に注意すべき点の一つに,計算精度の問題があります.
たとえばC/C++だと,doubleで計算しても有効桁数は15桁程度(10進数で)しかありません.その結果,以下のような状況で間違った計算結果が出てしまいます.
- 取り扱う数値が15桁を越える場合
- 取り扱う数値が7桁を越える場合
- たとえば8桁同士の掛け算が行われるとその結果は最大16桁になります.double型の変数には15桁程度の情報しか保存できないので,下位1桁の情報は棄てられます.
- 取り扱う数値の最大値と最小値に,15桁の以上の差がある場合
- たとえばC言語のdouble型で(1.0 + 1e-16)を計算すると結果は 1.0 になります.
これら計算精度の問題を避ける方法の一つにGMPというライブラリを使う方法があります。以下ポイントをまとめます。
GMPとは
GMP(GNU Multiple Precision library)とは任意精度計算ライブラリの一つです。GMPを使うと、変数単位で精度を指定することができ、上記のような計算精度の問題が解決できます。
ソースコード、ドキュメント等は http://gmplib.org/ で公開されています。
サンプルコードとコンパイル方法
たとえば以下のサンプルコードは、
$ gcc -lgmp hoge.c -o hoge
でコンパイルできます。
#include <stdio.h> #include <gmp.h> #define BASE 10 int main() { mpz_t a; mpz_init(a); mpz_set_str(a, "12345678901234567890", BASE); mpz_out_str (stdout, BASE, a); printf("\n"); mpz_clear(a); return 0; }
APIの概要
変数の初期化
変数は初期化が必要です.
mpz_t num; mpz_init(num);
また、精度を明示する場合は
mpz_t num;
mpz_init2(num, 512);
とします。この場合512bit分の精度が保証されます。
変数への数値の代入
代入する際は,数値を文字列で指定します.(文字列なので何桁の数値でも指定できます)
たとえば10進数の"12345"を num に保存する場合は次のように書きます。
mpz_t num; mpz_init(num); mpz_set_str(num, "12345", 10);
数値計算
大抵の演算は定義済です。
- mpz_add(z,x,y) z = x+y
- mpz_sub(z,x,y) z = x-y
- mpz_mul(z,x,y) z = x*y
関数一覧は GNU MP 6.1.2: Function Index にあります。
値の表示
変数 num の値を 10進数で stdout に出力する場合は以下のように記述します
mpz_out_str(stdout, 10, num);