Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2007年09月02日] [更新 2016年12月24日] Delphi サンプルプログラム集
892_長い桁数の演算用ライブラリ
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
892_BigDecimal.zip [10,295 KB] 2014年10月22日版 (EXE 同梱)







長い桁数の演算用ライブラリ


ネット上で公開されている長い桁数の演算用のライブラリの紹介です。探せばこの他にもあるかも知れませんが。これらはどれも高い精度の計算が可能で、誤差をかなり抑えることができます。
ライブラリの中には、ジェネリックスを使用しているものがあります。したがって、Delphi 2009 以上でないと利用できないライブラリもあるかも知れません。

以下で紹介している他にも [Big Decimal Math] というライブラリがありますが、これは Free Pascal (FPC) 用です。修正すれば Delphi でも利用可能と思われますが、コンパイラ指令等、細かいところで違いあるので、修正は結構面倒かも知れません。



[備考]
.NetFramework 4.0 では、BigInteger というクラスが正式搭載されています。C# 言語からであれば利用可能です。Java や Android API にも、BigDecimal という任意精度 (桁数) で数値を扱う機能が実装されています。





01_長い桁数の演算用ライブラリ  -  MPArith


W.Ehrhardt: Misc で提供している MPArith というライブラリは、長い桁の整数、有理数、実数や複素数の演算を実装しています。そのサイトで提供している AMath, DAMath というライブラリは、Delphi に標準で実装されていない各種の特赦関数や超越関数を実装しています。
MPArith は、multi precision arithmetic を略したものではないかと思われます。

TPMath という同じような名前のライブラリがありますが、多倍長演算用ではありません。多くの数値計算用の関数類を実装したライブラリです。



今回は、mpa_2014-12-07.zip をダウンロードしました。ダウンロードしたファイルを解凍したフォルダにライブラリパスを通してテストしています。
下図は、添付の t_ddemo.dpr をコンパイルして実行した結果です。メルセンヌ素数の計算です。



図1
t_ddemo.dpr をコンパイルして実行

下の図とコードは、このライブラリを使用した簡単なテストプログラムです。
小数点以下が 30 桁以上ある実数値から、1 を減算します。結果の最初の 2 行が元の値です。計算結果は、単純に文字列に変換した値と浮動小数点形式の文字列で表示しています。



図2
実数演算の簡単なテスト

リスト1
MPArith ライブラリを使用した、実数演算の簡単なテスト
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  mp_types, mp_real;

{$R *.dfm}

//=============================================================================
//  長い桁数の実数演算の簡単な例
//  MPArithライブラリのmp_realユニットを使用する例
//
//  mp_stringの要素数が255なので、最大255桁までの演算が可能
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  ValueA  : mp_types.mp_float;
  ValueB  : mp_types.mp_float;
  ValueC  : mp_types.mp_float;
  StrNum  : AnsiString;
  StrText : mp_types.mp_string;
begin
  Memo1.Lines.Clear;

  //変数の初期化(必ず初期化が必要)
  //ここでは3つの変数なのでmpf_init3を使用
  mp_real.mpf_init3(ValueA, ValueB, ValueC);

  //数字の文字列を実数に変換
  StrNum := '1.000000000000000006123456789012';
  mp_real.mpf_read_decimal(ValueA, PAnsiChar(StrNum));

  //変換した実数を文字列として30桁まで取得して表示
  StrText := mp_real.mpf_decimal(ValueA, 30);
  Memo1.Lines.Add(' ' + String(StrText));

  //数字の文字列を実数に変換
  //実数に変換した後、再度文字列に変換して表示
  StrNum := '-1';
  mp_real.mpf_read_decimal(ValueB, PAnsiChar(StrNum));
  StrText := mp_real.mpf_decimal(ValueB, 30);
  Memo1.Lines.Add(String(StrText));

  //加算。実際にはValueBが-1なので減算
  mp_real.mpf_add(ValueA, ValueB, ValueC);

  //計算結果のValueCを文字列に変換して表示
  StrText := mp_real.mpf_decimal_alt(ValueC, 30);
  Memo1.Lines.Add(' ' + String(StrText));

  //計算結果のValueCを浮動小数点形式の文字列に変換して表示
  StrText := mp_real.mpf_decimal(ValueC, 30);
  Memo1.Lines.Add(' ' + String(StrText));

  //変数のクリア
  mp_real.mpf_clear3(ValueA, ValueB, ValueC);
end;

end.




02_ 長い桁数の演算用ライブラリ - DFF Library


DelphiForFun というサイトにあるライブラリです。非商用目的であれば自由に使用可能です。
このサイトには、この他にも多くのサンプルコードやデモプログラムがあり、ダウンロードもできるようになっています。
今回は、DFFLibV14_24Mar2014.zip をダウンロードしています。ダウンロードしたファイルを解凍したフォルダにライブラリパスを通してテストしています。



図3
ダウンロードしたファイルの構成

下図は、以下のページから Download source (Requires DFF Library source DFFLibV10 or later) をダウンロードして、そのプロジェクトをコンパイルして実行した結果です。
ただし、表示等は日本語に変更して、サイズ等も多少変更しています。



図4
テストサンプル

下の図とコードは、このライブラリを使用した簡単なテストプログラムです。
小数点以下が 30 桁以上ある実数値から、1 を減算します。結果の最初の 2 行が元の値です。計算結果は、単純に文字列に変換した値と浮動小数点形式の文字列で表示しています。



図5
実数演算の簡単なテスト

リスト2
DFFLib ライブラリを使用した、実数演算の簡単なテスト
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  UBigFloatV3;

{$R *.dfm}

//=============================================================================
//  長い桁数の実数演算の簡単な例
//  DFFLibraryライブラリのTBigFloatを使用する例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  ValueA  : TBigFloat;
  ValueB  : TBigFloat;
  StrNum  : String;
  StrText : string;
begin
  Memo1.Lines.Clear;

  //TBigFloatのインタスンスを生成
  ValueA := TBigFloat.Create;
  ValueB := TBigFloat.Create;
  try
    //35桁まで
    ValueA.SetSigDigits(35);
    ValueB.SetSigDigits(35);
    ValueB.SetSigDigits(35);

    //数字を実数に変換し、その実数を再度文字列にして表示
    StrNum := '1.00000000000000000612345678901200';
    ValueA.Assign(StrNum, 35);
    StrText := ValueA.ConvertToString(normal);
    Memo1.Lines.Add(' ' + StrText);

    //数字を実数に変換し、その実数を再度文字列にして表示
    StrNum := '-1.0';
    ValueB.Assign(StrNum, 35);
    StrText := ValueB.ConvertToString(normal);
    Memo1.Lines.Add(StrText);

    //加算
    ValueA.Add(ValueB);

    //計算結果を標準の表示形式の文字列にして表示
    StrText := ValueA.ConvertToString(normal);
    Memo1.Lines.Add(' ' + StrText);

    //計算結果を浮動小数点表示形式の文字列にして表示
    StrText := ValueA.ConvertToString(Scientific);
    Memo1.Lines.Add(' ' + StrText);
  finally
    FreeAndNil(ValueA);
    FreeAndNil(ValueB);
  end;
end;

end.




03_ 長い桁数の演算用ライブラリ - DeHL ( Delphi Helper Library )


YAPB というサイトの方が GitHub で提供している DeHL (Delphi Helper Library) に多倍長演算用のライブラリがあります。このライブラリは、演算子のオーバーロードにより、四則演算等の演算子を使用した演算ができます。導入は、

  • [DeHL-master] [Library] 内の DeHL_Package.dpk のパッケージをインストール
  • [DeHL-master] [bin] フォルダにライブラリのパスを通す

という手順で行いました。


図6
ダウンロードしたファイルの構成

下図は、[Testing] フォルダ内の DeHL_Testing.dpr をコンパイルして実行した結果です。
ベンチマークテストになっているようです。



図7
サンプルの実行結果例

下の図とコードは、このライブラリを使用した簡単なテストプログラムです。
小数点以下が 30 桁以上ある実数値から、1 を減算します。結果の最初の 2 行が元の値です。計算結果は、単純に文字列に変換した値と浮動小数点形式の文字列で表示しています。



図8
実数演算の簡単なテスト

リスト3
DeHL (Delphi Helper Library) を使用した、実数演算の簡単なテスト
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  DeHL.Math.BigDecimal;

{$R *.dfm}

//=============================================================================
//  長い桁数の実数演算の簡単な例
//  DeHL(Delphi Helper Library)ライブラリのBigDecimalを使用する例
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  ValueA  : BigDecimal;
  ValueB  : BigDecimal;
  ValueC  : BigDecimal;
  StrNum  : String;
  StrText : string;
begin
  Memo1.Lines.Clear;

  //数字を実数に変換し、その実数を再度文字列にして表示
  StrNum  := '1.00000000000000000612345678901200';
  ValueA  := BigDecimal.Parse(StrNum);
  StrText := ValueA.ToString(False);
  Memo1.Lines.Add(StrText);

  //数字を実数に変換し、その実数を再度文字列にして表示
  StrNum  := '1.0';
  ValueB  := BigDecimal.Parse(StrNum);
  StrText := ValueB.ToString(True);
  Memo1.Lines.Add(StrText);

  //減算
  ValueC := ValueA - ValueB;

  //計算結果を標準の表示形式の文字列にして表示
  StrText := ValueC.ToString(False);
  Memo1.Lines.Add(StrText);

  //計算結果を浮動小数点表示形式の文字列にして表示
  StrText := ValueC.ToString(True);
  Memo1.Lines.Add(StrText);
end;

end.




04_ 長い桁数の演算用ライブラリ - biginteger


Sergey Kasandrov 氏が提供しているライブラリです。整数演算専用です。
ダウンロードしたファイルに含まれている numerics32.dll と Numerics.pas を使用します。これらのファイルをプロジェクトのフォルダにコピーしてテストしました。このライブラリは、演算子のオーバーロードにより、四則演算等の演算子を使用した演算ができます。

Sergey Kasandrov 氏は、kstools というシリアル通信コンポーネント TksComPort を含むツール類も提供しています。



図9
ダウンロードしたファイルの構成

下図は、添付のデモプログラム PiBench の実行結果です。マチンの公式を使用して、π の値を 1 万桁計算します。
筆者の環境では約 14 秒かかりました。あまり速いとは言えないようです。



図10
サンプルの実行

下の図とコードは、このライブラリを使用した簡単なテストプログラムです。
整数演算専用ですので、整数の加算のテストです。1 行目と 2 行目の値を加算します。



図11
整数演算の簡単なテスト

リスト4
numerics32.dll と Numerics.pas 使用した整数演算の簡単なテスト
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  Numerics;

{$R *.dfm}

//=============================================================================
//  長い桁数の整数演算の簡単な例
//  The Programming WorksのBigIntegerを使用する例
//
//  ライブラリに付属しているDLLを使用する
//  numerics32.dll : 32ビットアプリ用
//  numerics64.dll : 64ビットアプリ用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  ValueA  : BigInteger;
  ValueB  : BigInteger;
  ValueC  : BigInteger;
  StrNum  : String;
  StrText : string;
begin
  Memo1.Lines.Clear;

  //BigIntegerで使用するDLLのロード関係の関数を実行
  if LoadNumerics('numerics32.dll') < 0 then
    raise Exception.Create('DLL load error');


  //数字をBigIntegerの整数に変換し、その値を再度文字列にして表示
  StrNum  := '10000000000000000061234567890120';
  ValueA  := BigInteger(StrNum);
  StrText := ValueA.ToString;
  Memo1.Lines.Add(StrText);

  //数字をBigIntegerの整数に変換し、その値を再度文字列にして表示
  StrNum  := '10000000000000000000000000000080';
  ValueB  := BigInteger(StrNum);
  StrText := ValueB.ToString;
  Memo1.Lines.Add(StrText);

  //加算
  ValueC := ValueA + ValueB;

  //計算結果を文字列で取得して表示
  StrText := ValueC.ToString;
  Memo1.Lines.Add(StrText);
end;

end.




05_長い桁数の演算用ライブラリ  -  GMP_for_Delphi


The GNU MPFR Library で提供されているライブラリです。
このライブラリを Delphi で使用可能にしたライブラリがネット上で提供されています。今回はそのライブラリを使用しています。
ダウンロードしたファイルに含まれている mpir.dll, gmp_lib.pas, gmp_obj.pas を使用します。これらのファイルをプロジェクトのフォルダにコピーしてテストしました。このライブラリは、演算子のオーバーロードにより、四則演算等の演算子を使用した演算ができます。



図12
ダウンロードしたファイルの構成

下図は、添付のプロジェクトをコンパイルして実行した画面です。元の中国語簡体字を日本語に変更して、フォントも変更しています。それ以外は元のままとなっています。
GMP, MPIR, MPFR では演算精度をビット数で指定します。

参考までに、精度 (ビット) を 4000 にして π の値を計算すると、12,042 桁まで計算できます。筆者の環境ではほとんど一瞬で計算できました。
演算の処理速度は、演算の種類や関数の種類、CPU の機能にもよるので、π の計算結果だけでは比較になるとは言えないと思いますが、何かの参考にはなるかも知れません。



図13
サンプルの実行画面

下の図とコードは、このライブラリを使用した簡単なテストプログラムです。

小数点以下が 30 桁以上ある実数値から 1 を減算します。結果の最初の 2 行が元の値です。計算結果は、標準形式 (または固定小数点形式) と浮動小数点形式の文字列で表示しています。
末尾の 0 は無視されていますが、入力した桁数分までは正確に計算できています。
GMP_for_Delphi では、gmp_printf, gmp_scanf を使用して書式を指定できます。書式についてはライブラリに添付されている PDF 文書で確認してください。



図14
実数演算の簡単なテスト
図15
書式を指定した文字列で表示

リスト5
GMP_for_Delphi ライブラリを使用した、実数演算の簡単なテスト
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    Button2: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  gmp_lib,
  gmp_obj;

{$R *.dfm}

//=============================================================================
//  長い桁数の実数演算の簡単な例
//  GMP_for_Delphiライブラリを使用する例
//
//  ライブラリに付属しているDLLを使用する
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  ValueA  : gmpFloat;
  ValueB  : gmpFloat;
  ValueC  : gmpFloat;
  StrNum  : AnsiString;
  StrText : AnsiString;
begin
  Memo1.Lines.Clear;

  //デフォルトの計算精度を128ビットに設定
  SetDefaultFloatPrecision(128);
  //gmpFloatの変数を生成
  ValueA.Create(0);
  ValueB.Create(0);
  ValueC.Create(0);

  try
    //数字を実数に変換し、その実数を再度文字列にして表示
    StrNum := '1.00000000000000000612345678901200';
    ValueA.Assign(StrNum);
    StrText := ValueA.ToString;
    Memo1.Lines.Add(StrText);

    //数字を実数に変換し、その実数を再度文字列にして表示
    StrNum := '1.0';
    ValueB.Assign(StrNum);
    StrText := ValueB.ToString;
    Memo1.Lines.Add(StrText);

    //減算(Assignを使用する)
    ValueC.Assign(ValueA - ValueB);

    //計算結果を標準の表示形式の文字列にして表示
    StrText := ValueC.ToString;
    Memo1.Lines.Add(StrText);

    //計算結果を浮動小数点表示形式の文字列にして表示
    StrText := ValueC.ToStringExp(10, 20);
    Memo1.Lines.Add(StrText);
  finally
    ValueA.Free;
    ValueB.Free;
    ValueC.Free;
  end;
end;

//=============================================================================
//  gmp_printf(書式指定出力)の使用例
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  buff   : AnsiString;
  ValueA : gmpFloat;
  ValueB : gmpFloat;
  ValueC : gmpFloat;
  StrNum : AnsiString;
begin
  Memo1.Lines.Clear;
  SetLength(buff, 1000);

  //デフォルトの計算精度を128ビットに設定
  SetDefaultFloatPrecision(128);
  //gmpFloatの変数を生成
  ValueA.Create(0);
  ValueB.Create(0);
  ValueC.Create(0);

  try
    //数字を実数に変換し、その実数を再度文字列にして表示
    StrNum := '1.00000000000000000612345678901200';
    ValueA.Assign(StrNum);
    gmp_printf(PAnsiChar(buff), '%F.32f', ValueA.NativeObject);
    Memo1.Lines.Add(String(buff));

    //数字を実数に変換し、その実数を再度文字列にして表示
    StrNum := '1.0';
    ValueB.Assign(StrNum);
    gmp_printf(PAnsiChar(buff), '%F.3f', ValueB.NativeObject);
    Memo1.Lines.Add(String(buff));

    //減算(Assignを使用する)
    ValueC.Assign(ValueA - ValueB);

    //計算結果を固定小数点形式の文字列にして表示
    gmp_printf(PAnsiChar(buff), '%F.32f', ValueC.NativeObject);
    Memo1.Lines.Add(String(buff));

    //計算結果を浮動小数点表示形式の文字列にして表示
    gmp_printf(PAnsiChar(buff), '%F.30E', ValueC.NativeObject);
    Memo1.Lines.Add(String(buff));
  finally
    ValueA.Free;
    ValueB.Free;
    ValueC.Free;
  end;
end;

end.