Boost.Multiprecision を調べてみた

Faith and Brave - C++で遊ぼう

<2012年4月6日に調査>

Boost.Multiprecision が、なんとなく良さげなライブラリに見えてきたので、落として調べてみました。

ただの調査メモなので、使い方的な入門は書いてないです。

gcc で使いたい

どうもテスト環境は Windows Vista 32bit, MSVC-10.0 らしいので、gcc とかで使うための設定は若干爪が甘い感じがある。

gcc の場合は __int128 を必要とするが、多分 gcc4.6 からなので、gcc4.5 以前だとコードそのままでは使えない。多分いじれば使えるのかもしれない。(ちなみに僕のところでは 4.4 , 4.5 では動かなかった)

もちろん Boost.MPL などとべったりなのでほかの Boost ライブラリもパス通す

ヘッダオンリ

Boost.Multiprecision 自体はヘッダオンリだが、下に記載したほかの多倍長整数ライブラリなどを使おうとするとそれらのライブラリのリンクが必要になる。

ほかの多倍長整数ライブラリのサポート

デフォルトでは、Boost.Multiprecision による多倍長実装がある。cpp_int と cpp_dec_float がそれなので普通はそれを使いたい。
しかし、ほかの多倍長整数系ライブラリで、以下のようなライブラリが利用できる。

  • GMP, libTomMath による多倍長整数と、独自実装による多倍長整数
  • GMP, MPFR による多倍長実数と、独自実装の多倍長実数
  • GMP, libTomMath による有理数と、独自実装の有理数、boost/rational.hpp と有理数として使用するためのアダプタが含まれる

Expression Templates のサポート

Boost.Multiprecision では、Expression Templates をサポートし、その有効無効も設定できる。

mp_number について

mp_number がクラスの基底らしい。テンプレートパラメータが Backend と呼ばれるポリシークラスで、ポリシーベースなクラス設計がされているらしい。これに GMP 用のポリシー等を設定した typedef が用意されている。

// boost/multiprecision/mp_number.hpp

namespace boost { namespace multiprecision {

/*
  Backend - ポリシークラス
  ExpressionTemplates - ET を使うか否か
*/
template<class Backend, bool ExpressionTemplates = true>
{
  ...
};

} };

数値の出力

Constructing and Interconverting Between Number Types

iostream で数値を出力可能、その際は std::numeric_limits が特殊化されているのでこれで出力桁数などを調整できる。

// 最大50桁の実数
cpp_dec_float_50 df = "3.14159265358979323846264338327950288419716939937510";
// すべての桁を出力する
std::cout <<
  std::setprecision(std::numeric_limits<cpp_dec_float_50>::max_digits10) 
  << df << std::endl

cpp_int i = 1;
i <<= 256;
// 16進数で整数を出力
std::cout << std::hex << std::showbase << i << std::endl;

変換

Boost.Multiprecision 内で、int to float, int to rational, rational to float ならば変換可能。

それ以外はコンパイルエラーになるようだ。

// 多倍長整数
cpp_int cppi(2);
// GMP を使った多倍長整数
mpz_int z = cppi;
// 以下の変換は利用可能
cpp_rational     cppr = cppi;
cpp_dec_float_50 df =   cppi;
df                  =   cppr;
// これはコンパイルエラー
cppi                =   df;

可変長と固定長

mp_number にはアロケータを指定できる。アロケータはデフォルトで std::allocator が使用されるが、アロケータ指定を void にすることで、固定長の多倍長整数(実数)として利用できる。固定長は 128, 256, 512, 1024 ビットなら typedef で用意されている。

cpp_int

#include <boost/multiprecision/cpp_int.hpp>

namespace mp = boost::multiprecision;

// 2048 ビット、符号なし固定長整数(ET無効)
mp::mp_number<mp::cpp_int_backend<2048, false, void>, false> mp_uint_2048;

// ビット数は可変の多倍長整数(ET無効)
mp::mp_number<mp::cpp_int_backend<0, false>, false> mp_int_arbitrary;

パフォーマンスに関して

色々簡単なテストでパフォーマンスを測ったらしきものが色々おいてある。ある程度の参考に。

Performance Comparison

できれば調べたいこと

  1. cpp_int, cpp_dec_float で、 ET を有効にしたときとしなかった時のパフォーマンス差