ガウスの超幾何関数をBigdecimal、Bigintegerのみで計算する為の、複素数関数

 ガウスの超幾何関数の多倍長計算にMPArithとBigdecimal、Bigintegerを使用していましたが、Bigdecimal、Bigintegerのみ使用してプログラムを作成するために、必要な、関数を用意することにしました。

 複素数の四則演算を除き
特に必要なものは、

  ln、exp、power、sin, arctan

です。

演算は級数を利用して行います。
一般的な級数を使用すると演算は遅いのですが、多用するわけでは無いので、問題はないてす。
arctanに関しては、arg(複素数の角度)の計算に使用されるだけなので、実数の計算でも可能です。





レオンハルト・オイラーの計算式

 

  powerの計算はxyexp(ln(x)*y) となりますが、条件によって、実数値、又は虚数値がゼロに成るべき値が演算誤差によりゼロにならないので、xとyの値からゼロに補正しています。

 arctanの計算は、複素数は必要ないのですが、一応作成してみました。
arctanの計算は、一般の級数とレオンハルト・オイラーの級数を作成し、どれ位、レオンハルト・オイラーの級数の計算が早くなるかの確認用に作成しました。
また、レオンハルト・オイラーの計算でないと、arctan(1)の計算が出来ませんし、1近傍の計算に時間がかかります。

プログラム

unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, system.Math, System.VarCmplx,
  Vcl.StdCtrls, Vcl.Imaging.pngimage, Vcl.ExtCtrls, Velthuis.BigIntegers, Velthuis.Bigdecimals;

type
  // 複素数構造体
  CBig = record
    r : bigdecimal;
    i : bigdecimal;
  end;

  TForm1 = class(TForm)
    zEdit: TLabeledEdit;
    Memo1: TMemo;
    epsEdit: TLabeledEdit;
    yEdit: TLabeledEdit;
    yiEdit: TLabeledEdit;
    Image1: TImage;
    Button1: TButton;
    CheckBox1: TCheckBox;
    CheckBox2: TCheckBox;
    iedit: TLabeledEdit;
    procedure Button1Click(Sender: TObject);
    procedure epsEditChange(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

// CBig複素数の加算
function cadd(a, b: CBig): CBig;
begin
  result.r := a.r + b.r;
  result.i := a.i + b.i;
end;

// CBig複素数の減算
function csub(a, b: CBig): CBig;
begin
  result.r := a.r - b.r;
  result.i := a.i - b.i;
end;

// Cbig複素数の乗算
function cmul(a, b: Cbig): CBig;
begin
  result.r := a.r * b.r - a.i * b.i;
  result.i := a.r * b.i + a.i * b.r;
end;

// Cbig複素数の除算
function cdiv(a, b: Cbig): Cbig;
var
  bb, arbraibi, aibrarbi: Bigdecimal;
  dpcs : integer;
begin
  dpcs := bigdecimal.DefaultPrecision;
  bb := b.r * b.r + b.i * b.i;
  arbraibi := a.r * b.r + a.i * b.i;
  aibrarbi := a.i * b.r - a.r * b.i;
  bb := bb.RoundToPrecision(dpcs);
  arbraibi := arbraibi.RoundToPrecision(dpcs);
  aibrarbi := aibrarbi.RoundToPrecision(dpcs);

  if bb <> Bigdecimal.Zero then begin
    result.r := arbraibi / bb;
    result.i := aibrarbi / bb;
  end
  else begin
    result.r := Bigdecimal.Zero;
    result.i := Bigdecimal.Zero;
  end;
end;

// Cbigの絶対値
function cabs(a : cbig; dpcs : integer): bigdecimal;
var
  arh2, aih2 : bigdecimal;
  tmp : bigdecimal;
begin
  arh2 := a.r * a.r;
  aih2 := a.i * a.i;
  tmp := arh2 + aih2;
  result := bigdecimal.Sqrt(tmp, dpcs);
end;

// Comp/deci
function cdivdec(a: Cbig; b: bigdecimal): cbig;
begin
  result.r := a.r / b;
  result.i := a.i / b;
end;

// exp(x)
function exp_big(x: Cbig; dpcs: integer; eps : bigdecimal): Cbig;
var
  xh, s, sb, si, i, ih, ss: Cbig;
  one, two ,ass : bigdecimal;
begin
  one := bigdecimal.One;
  two := bigdecimal.Two;
  two := two + two + two + two;
  two := two.RoundToPrecision(dpcs);

  i.r := One;
  i.i := bigdecimal.Zero;
  ih := i;
  x.r := x.r.RoundToPrecision(dpcs);
  x.i := x.i.RoundToPrecision(dpcs);

  x.r := x.r / two;
  x.i := x.i / two;
  xh := x;
  s := x;
  repeat
    sb := s;
    i.r := i.r + one;
    ih := cmul(ih,  i);     // i!
    xh := cmul(xh, x);      // x^n
    xh.r := xh.r.RoundToPrecision(dpcs);
    xh.i := xh.i.RoundToPrecision(dpcs);
    si := cdiv(xh, ih);   // x^n/i!
    s := cadd(s, si);
    ss := csub(s, sb);
    ass := cabs(ss, dpcs);
  until (ass < eps);

  form1.Memo1.Lines.Append('exp n= ' + i.r.ToString);

  s.r := s.r + one;
  s := cmul(s, s);
  s := cmul(s, s);
  result := cmul(s, s);
  result.r := result.r.RoundToPrecision(dpcs);
  result.i := result.i.RoundToPrecision(dpcs);
end;

// π
// https://www.kurims.kyoto-u.ac.jp/~ooura/pi_fft-j.html
// T.Ooura AGM アルゴリズ
function pi_big: Bigdecimal;
var
  n, dpcs, pcsBack : integer;
  rmback : BigDecimal.RoundingMode;
  SQRT_SQRT_EPSILON, c, b, e : BigDecimal;
  npow : Bigdecimal;
  a, one, two, four, five, eight : Bigdecimal;
begin
  pcsBack := BigDecimal.DefaultPrecision;             // 除算演算精度バックアップ
  rmBack := BigDecimal.DefaultRoundingMode;           // 除算丸めモードバックアップ
  dpcs := pcsBack + 5;                                   // 指定精度 + α
  BigDecimal.DefaultPrecision := dpcs;
  BigDecimal.DefaultRoundingMode := rmNearesteven;

  one := '1';
  one := one.RoundToPrecision(dpcs);                  // oneの有効桁数をdpcsに設定
  two := '2';
  four := '4';
  five := '5';
  eight := '8';
  SQRT_SQRT_EPSILON := one / BigDecimal.IntPower(two, dpcs shl 0); // 収束判定値
  c := BigDecimal.Sqrt(one / eight, dpcs * 2);
  a := one + c + c + c;
  b := BigDecimal.Sqrt(a, dpcs * 2);
  e := b - five / eight;
  b := b + b;
  c := e - c;
  a := a + e;
  npow := '4';
  n := 0;
  while (e > SQRT_SQRT_EPSILON) and (n < 100) do begin
    npow := npow + npow;
    e := (a + b) / two;                               // 平均値 除算有効桁数での丸め
    b := a * b;
    b := b.RoundToPrecision(dpcs);                    // pcs + α 丸め
    b := BigDecimal.Sqrt(b, dpcs * 2);                // 平方根有効桁数での丸め
    e := e - b;
    b := b + b;
    c := c - e;
    a := e + b;
    inc(n);
  end;
  e := e * e;
  e := e.RoundToPrecision(dpcs);                      // pcs + α 丸め
  e := e / four;                                      // e = e * e / 4
  a := a + b;
  result := (a * a - e - e / two) / (a * c - e) / npow;   // 除算の順番に注意
  result := result.RoundToPrecision(pcsBack);             // 指定の精度に丸め

  BigDecimal.DefaultPrecision := pcsBack;             // 除算演算精度復帰
  BigDecimal.DefaultRoundingMode := rmBack;           // 除算丸めモード復帰
end;

// variant double 計算
// Ln(z) = 2arctanh{(z-1)/z+1)}   対数計算
function lnd(dz, di : double): variant;
var
  zi: variant;
  c10, c10c : variant;
  pc, mc : integer;
  max : double;
  argf : boolean;
begin
  result := varascomplex(result);
  c10 := varascomplex(c10);
  c10c := varascomplex(c10c);
  zi := varascomplex(zi);
  c10.real := 10;
  c10.Imaginary := 0;
  c10c := 2 * VarComplexArcTanH((c10 - 1) / (c10 + 1));   // len(10+0i)
  zi.real := dz;
  zi.Imaginary := di;
  max := VarComplexAbs(zi);                               // 絶対値
  pc := 0;
  mc := 0;
  repeat
    if max >= 10 then begin
      max := max / 10;
      zi.real := zi.real / 10;
      zi.Imaginary := zi.Imaginary / 10;
      inc(pc);
    end;
    if max < 1 then begin
      max := max * 10;
      zi.real := zi.real * 10;
      zi.Imaginary := zi.Imaginary * 10;
      inc(mc);
    end;
  until (max < 10) and (max >= 1);

  // real < 0 の時 分母がゼロになり計算出来ない事があるので符号を反転します。
  argf := false;
  if zi.real < 0 then begin
    zi.real := -zi.real;
    zi.Imaginary := -zi.Imaginary;
    argf := true;
  end;
  result := 2 * VarComplexArcTanH((zi - 1) / (zi + 1));
  if pc > 0 then result.real := result.real + c10c * pc;
  if mc > 0 then result.real := result.real - c10c * mc;
  // real < 0 時符号を反転して計算しているので虚数部にπを加算して補正します。
  if argf then result.Imaginary := result.Imaginary + pi;
end;

// sin(z);
function sin_big(z : Cbig; eps : bigdecimal; dpcs: integer): Cbig;
var
  fa2n1, fn, zh2n1, zh2: cbig;
  cone, s, tmp : cbig;
  abss: bigdecimal;
  FG : boolean;
  n : integer;
begin
  n := 0;
  // n = 0
  cone.r := bigdecimal.One;
  cone.i := bigdecimal.Zero;
  zh2n1 := z;                     // z^(2n+1)
  zh2 := cmul(z,z);               // z^2
  fa2n1 := cone;                  //(2n+1)!
  fn := cone;                     // (2*0+1)!
  s := z;                         // z
  FG := false;
  // n = 1 ~
  repeat
    zh2n1 := cmul(zh2n1, zh2);    // z^(2n+1)
    fn := cadd(fn, cone) ;        // fn = fn + 1
    fa2n1 := cmul(fa2n1, fn);
    fn := cadd(fn, cone) ;
    fa2n1 := cmul(fa2n1, fn);     // (2n+1)!
    fa2n1.r := fa2n1.r.RoundToPrecision(dpcs);
    fa2n1.i := fa2n1.i.RoundToPrecision(dpcs);
    zh2n1.r := zh2n1.r.RoundToPrecision(dpcs);
    zh2n1.i := zh2n1.i.RoundToPrecision(dpcs);
    tmp := cdiv(zh2n1, fa2n1);    // z^(2n+1) / (2n+1)!
    if FG then begin
      s := cadd(s, tmp);
      FG := false;
    end
    else begin
      s := csub(s, tmp);
      FG := true;
    end;
    inc(n);
    abss := cabs(tmp, dpcs);
  until abss < eps;
  form1.Memo1.Lines.Append(' sin n= ' + intTostr(n));
  result := s;
end;

// arctan(z)
// 1 を越えたら反対側の角度を計算
function arctan_big(z: cbig; eps : bigdecimal; dpcs: integer): cbig;
var
  cone, s, z2, z2n, pi2, tmp: cbig;
  absz, abst, nbig  : bigdecimal;
  n : integer;
begin
  if (z.r = bigdecimal.Zero) and (z.i = bigdecimal.One) then begin
    result.r := bigdecimal.Zero;
    result.i := bigdecimal.Zero;
    exit;
  end;
  cone.r := bigdecimal.One;
  cone.i := bigdecimal.Zero;
  n := 0;
  absz := cabs(z, dpcs);
  if absz > bigdecimal.One then begin
    z.r := z.r.RoundToPrecision(dpcs);
    z.i := z.i.RoundToPrecision(dpcs);
    cone.r := cone.r.RoundToPrecision(dpcs);
    cone.i := cone.i.RoundToPrecision(dpcs);
    z := cdiv(cone, z);
  end;
  z2 := cmul(z, z);
  z2.r := z2.r.RoundToPrecision(dpcs);
  z2.i := z2.i.RoundToPrecision(dpcs);
  s := z;
  z2n := z;
  nbig := bigdecimal.One + bigdecimal.Two;

  // z2 := -z2;
  z2.r := -z2.r;
  z2.i := -z2.i;
  repeat
    z2n := cmul(z2n, z2);
    z2n.r := z2n.r.RoundToPrecision(dpcs);
    z2n.i := z2n.i.RoundToPrecision(dpcs);
    nbig := nbig.RoundToPrecision(dpcs);
    tmp := cdivdec(z2n, nbig);
    s := cadd(s, tmp);
    abst := cabs(tmp, dpcs);
    nbig := nbig + bigdecimal.Two;
    inc(n);
  until (abst <= eps) or (n > 20000);
  if absz > bigdecimal.One then
  begin
    pi2.r := pi_big;
    pi2.i := bigdecimal.Zero;
    pi2.r := pi2.r / bigdecimal.Two;    // π/2
    s := csub(pi2, s);
  end;
  form1.Memo1.Lines.Append(' arctan n= ' + intTostr(n));
  result := s;
end;

// arctan
// 1 を越えたら反対側の角度を計算
// レオンハルト・オイラーの計算
function arctan_Leonhard_Euler(x: cbig; eps : bigdecimal; dpcs: integer): cbig;
var
  n : integer;
  ax, ax2, y, ans, bans, f, cone, i, tmp, tmpy : cbig;
  absz, abst : bigdecimal;
begin
  if (x.r = bigdecimal.Zero) and (x.i = bigdecimal.One) then begin
    result.r := bigdecimal.Zero;
    result.i := bigdecimal.Zero;
    exit;
  end;
  ax := x;
  cone.r := bigdecimal.One;
  cone.i := bigdecimal.Zero;
  absz := cabs(ax, dpcs);

  if absz > bigdecimal.One then begin
    ax.r := ax.r.RoundToPrecision(dpcs);
    ax.i := ax.i.RoundToPrecision(dpcs);
    cone.r := cone.r.RoundToPrecision(dpcs);
    cone.i := cone.i.RoundToPrecision(dpcs);
    ax := cdiv(cone, ax);
  end;
  ax2 := cmul(ax, ax);
  tmp := cadd(ax2, cone);
  ax2.r := ax2.r.RoundToPrecision(dpcs);
  ax2.i := ax2.i.RoundToPrecision(dpcs);
  tmp.r := tmp.r.RoundToPrecision(dpcs);
  tmp.i := tmp.i.RoundToPrecision(dpcs);
  y := cdiv(ax2, tmp);
  i := cone;
  ans := cone;
  f := cone;
  tmpy.r := bigdecimal.Zero;
  tmpy.i := bigdecimal.Zero;
  n := 0;
  repeat
    bans := ans;
    tmp := cmul(f, i);                        // f * i
    tmp.r := tmp.r * bigdecimal.Two;          // f * i * 2
    tmp.i := tmp.i * bigdecimal.Two;
    tmpy.r := (i.r + i.r) + bigdecimal.One;   // i * 2 + 1
    tmp.r := tmp.r.RoundToPrecision(dpcs);
    tmp.i := tmp.i.RoundToPrecision(dpcs);
    tmpy.r := tmpy.r.RoundToPrecision(dpcs);
    tmp := cdivdec(tmp, tmpy.r);
    f := cmul(tmp, y);                        // f := f * i * 2 / (i * 2 + 1) * y;
    ans := cadd(ans, f);
    i.r := i.r + bigdecimal.One;
    inc(n);
    abst := cabs(f, dpcs);
  until (abst < eps) or (n > 10000);
  y.r := y.r.RoundToPrecision(dpcs);
  y.i := y.i.RoundToPrecision(dpcs);
  ax.r := ax.r.RoundToPrecision(dpcs);
  ax.i := ax.i.RoundToPrecision(dpcs);
  tmp := cdiv(y, ax);
  ans := cmul(ans, tmp);                      //  ans := ans  * y / ax;
  if absz > bigdecimal.One then begin
    tmpy.r := pi_big / bigdecimal.two;
    tmpy.i := bigdecimal.Zero;
    ans := csub(tmpy, ans);
  end;
  result := ans;
  form1.Memo1.Lines.Append(' arctanR n = ' + intTostr(n));
end;

// z 入力値 複素数
// eps 収束判定値
// dpcs 有効桁数
// ndis 収束表示 true 表示 false 非表示
function log_big_sub(z : Cbig; eps : bigdecimal; dpcs: integer; ndis: boolean): Cbig;
const
  NN = 1000000;
var
  s, x, zm1, zp1, xh2, xg, n2p1 : CBig;
  one, two : Cbig;
  xgsn : Cbig;
  asabs : bigdecimal;
  n : integer;
begin
  one.r := Bigdecimal.One;      // 1
  one.r := one.r.RoundToPrecision(dpcs);
  one.i := Bigdecimal.Zero;     //
  two := cadd(one, one);        // 2
  zm1 := csub(z, one);          // z - 1
  zp1 := cadd(z, one);          // z + 1
  x := cdiv(zm1, zp1);          // x = (z-1)/(z+1)
  s.r := Bigdecimal.Zero;
  s.i := Bigdecimal.Zero;
  xh2 := cmul(x, x);            // x^2
  xg := x;                      // n = 0;    x^(2n+1)
  n2p1 := one;                  // 2n+1 = 1
  n := 0;
  repeat
    xg.r := xg.r.RoundToPrecision(dpcs);
    xg.i := xg.i.RoundToPrecision(dpcs);
//    n2p1.r := n2p1.r.RoundToPrecision(dpcs);
//    n2p1.i := n2p1.i.RoundToPrecision(dpcs);
    xgsn := cdiv(xg, n2p1);     // x^(2n+1) / (2n+1)
    s := cadd(s, xgsn);         // Σ
    xg := cmul(xg, xh2);        // x^(2n+1)
    n2p1 := cadd(n2p1, two);    // 2n + 1
    inc(n);
    asabs := cabs(xgsn, dpcs);
  until  (asabs < eps) or (n > NN);
  form1.Memo1.Lines.Append('ln n = ' + intTostr(n));
  s := cmul(s, two);
  result := s;
end;

var
  z10 : cbig;
  Z10F : boolean = false;

// z   因数
// eps 収束判定値
// dpcs 有効桁数
// 返り値 対数複素数
function log_big(z : Cbig; eps : bigdecimal; dpcs: integer): Cbig;
var
  zb : Cbig;
  z10n : cbig;
  Zmax, ten, one, bn : bigdecimal;
  pi_big2: bigdecimal;
begin
  if (z.r = 0) and (z.i = 0) then begin
    result.r := 0;
    result.i := 0;
    exit;
  end;

//  pi_big := pi_bigs;
  pi_big2 := pi_big / bigdecimal.Two;

  zb := z;

  // 虚数と実数両方ともゼロでないなら 実数部側を大きな値にして演算誤差を小さくします
  if (zb.r <> bigdecimal.Zero) and (zb.i <> bigdecimal.Zero) then begin
    if zb.i.Abs(zb.i) > zb.r.Abs(zb.r) then begin     // 虚数の絶対値が実数の絶対値より大きい時入れ替え
      z.r := zb.i;
      z.i := zb.r;
      if zb.i < bigdecimal.Zero then begin            // 虚数が負数だったら符号反転
        z.r := -z.r;
        z.i := -z.i;
      end;
    end
    else begin                                        // 虚数の絶対値が実数の絶対値と等しいか小さい時
      if zb.r < bigdecimal.Zero then begin            // 実数の値が負数だったら符号反転
        z.r := -zb.r;
        z.i := -zb.i;
      end;
    end;
  end;

  // z.rが0でz.iがゼロでない時  虚数部と実数部入れ替え
  if (zb.r = bigdecimal.Zero) and (zb.i <> bigdecimal.Zero) then begin
    z.r := zb.i;
    z.i := zb.r;
    if z.r < bigdecimal.Zero then z.r := -z.r;      // 実数部が負数だったら符号反転 虚数部ゼロなのでそのまま
  end;

  // 実数部が負数ので虚数部ゼロの時符号を反転 虚数部ゼロなのでそのまま
  if (zb.r <> bigdecimal.Zero) and (zb.i = bigdecimal.Zero) then
    if zb.r < bigdecimal.Zero then z.r := -zb.r;

  // 10の対数を利用して大きな値の計算を速くします
  ten := '10';
  ten := ten.RoundToPrecision(dpcs);               // dpcs桁合わせ
  one := bigdecimal.One;
  bn := bigdecimal.Zero;

  if not Z10F then begin
    z10.r := ten;
    z10.i := bigdecimal.Zero;
    z10 := log_big_sub(z10, eps, dpcs, false);          // 10の対数
    Z10F := true;
  end;
  zmax := cabs(z, dpcs);                              // zの絶対値
  zmax := zmax.RoundToPrecision(dpcs);             // dpcs桁合わせ
  z.r := z.r.RoundToPrecision(dpcs);               // dpcs桁合わせ
  z.i := z.i.RoundToPrecision(dpcs);               // dpcs桁合わせ
  // 10で除算又は乗算して値がの値が1~10の範囲に入るようにします。
  repeat
    if zmax > ten then begin        // zmaxが10より大きい時10で除算
      z.r := z.r / ten;
      z.i := z.i / ten;
      zmax := zmax / ten;
      bn := bn + one;               // 10で除算した回数 +になります。
    end;
    if zmax < one then begin        // zmaxが1より小さい時10を乗算
      z.r := z.r * ten;
      z.i := z.i * ten;
      zmax := zmax * ten;
      bn := bn - one;               // 10を乗算した回数 -になります。
    end;
  until (zmax <= ten) and (zmax >= one);
  //----------------------------------------

  // 修正された値で対数計算
  result := log_big_sub(z, eps, dpcs, true);

  // 10の対数補正
  z10n.r := z10.r * bn;             // 加算する対数値計算10の対数値のn倍
  z10n.i := z10.i * bn;

  result.r := result.r + z10n.r;          // 10の対数値のn倍加算
  result.i := result.i + z10n.i;
  //----------------------------------------

  // z.rがゼロでz.iがゼロでない時 虚数部π/2補正
  if (zb.r = bigdecimal.Zero) and (zb.i <> bigdecimal.Zero) then begin
    if zb.i >= bigdecimal.Zero then result.i := result.i + pi_big2
                               else result.i := result.i - pi_big2;
  end;

  // z.rの値が負数のきと符号を変更して計算しているの虚数部π補正。
  if (zb.r < bigdecimal.Zero) and (zb.i = bigdecimal.Zero) then
    result.i := result.i + pi_big;

  // 虚数と実数両方ともゼロでない時
  if (zb.r <> bigdecimal.Zero) and (zb.i <> bigdecimal.Zero) then begin
    // 次数部絶対値より虚数部の絶対値が大きい場合
    if zb.i.Abs(zb.i) > zb.r.Abs(zb.r) then begin
      if zb.i > bigdecimal.Zero then result.i := -result.i + pi_big2    // π/2補正
                                else result.i := -result.i - pi_big2;
    end
    // 実数部が負数の時
    else begin
      if zb.r < bigdecimal.Zero then
        if zb.i > bigdecimal.Zero then result.i := result.i + pi_big    // π補正
                                  else result.i := result.i - pi_big
    end;
  end;
end;

// ans = x^y
function pow_big(x, y : cbig; eps : bigdecimal): cbig;
var
  dpcs : integer;
  harf, absi, absy, four : bigdecimal;
  lnx, tmp : cbig;
begin
  dpcs := BigDecimal.DefaultPrecision;
  if (x.r = bigdecimal.zero) and (x.i = bigdecimal.zero) then begin
    result.r := bigdecimal.Zero;
    result.i := bigdecimal.Zero;
    exit;
  end;
  lnx := log_big(x, eps, dpcs);         // ln(x)
  tmp := cmul(lnx, y);                  // ln(x) * y
  result := exp_big(tmp, dpcs, eps);
  // xの虚数部とyの虚数部0なら
  if (x.i = bigdecimal.zero) and (y.i = bigdecimal.Zero) then begin
    harf := bigdecimal.One / bigdecimal.Two;          // 0.5
    absy := y.r.Abs(y.r);       // yの実数部の絶対値
    absi := absy.Frac;          // yの実数部の絶対値の小数部
    absi := absi - harf;        // yの実数部の絶対値の小数部と0.5の差分
    // 差分がゼロ(yの実数部の小数部が0.5)
    if absi = bigdecimal.Zero then
      // xの実数部が0と等しいか0より大きいなら
      if x.r >= bigdecimal.zero then result.i := bigdecimal.zero   // 答えの虚数部0
                                // 0より小さいなら
                                else result.r := bigdecimal.zero;  // 答えの実数部0
    absi := y.r.Frac;           // 乗数の小数部
    if absi = bigdecimal.Zero then result.i := bigdecimal.Zero;    // 整数なら答えの虚数部0
  end;
  // xの整数部とyの虚数部が0なら
  if (x.r = bigdecimal.Zero) and (y.i = bigdecimal.Zero) then begin
    absi := y.r.Frac;                                     // 乗数の小数部
    if absi = bigdecimal.Zero then begin                  // 整数なら
      absy := y.r / bigdecimal.Two;                       // 偶数奇数確認
      absi := absy.Frac;                                  // 小数部
      if absi = bigdecimal.Zero then result.i := bigdecimal.Zero             // 偶数なら虚数部0
                                else result.r := bigdecimal.Zero;            // 奇数なら実数部0
    end;
  end;
  absy := x.r.Abs(x.r);                                     // |x.r|
  absi := x.i.Abs(x.i);                                     // |x.i|
  // xの実数部と虚数部の絶対値が等しくてy.rがゼロでなかったら
  if (absy = absi) and (y.i = bigdecimal.Zero) and not (y.r = bigdecimal.Zero) then begin
    absi := y.r.Frac;                                       // 乗数の小数部
    if absi = bigdecimal.Zero then  begin                   // 整数なら
      absy := y.r / bigdecimal.Two;                         // 偶数奇数確認
      absi := absy.Frac;                                    // 小数部
      if absi = bigdecimal.Zero then begin                  // 偶数なら
        four := bigdecimal.Two + bigdecimal.Two;            // 4
        absy := y.r / four;                                 // 4偶数奇数確認
        absi := absy.Frac;                                  // 小数部
        if absi = bigdecimal.Zero then result.i := bigdecimal.Zero      // 偶数なら 虚数部0  y= 4 8
                                  else result.r := bigdecimal.Zero;     // 奇数なら 虚数部0  y= 2 6 10
      end;
    end;
  end;
end;

// 複素数の対数計算
// 50桁の精度の為 除算有効桁数100桁
procedure TForm1.Button1Click(Sender: TObject);
label
  VA;
const
  dpcs = 244;                 // 除算有効桁数
var
  ch : integer;
  zd, id, yd, yi, epsd : double;
  z, ans, anse, y, anssin, arctan,arctanR: cbig;
  eps : bigdecimal;
  dans, xc : variant;
begin
  val(zedit.Text, zd, ch);
  if ch <> 0 then begin
    application.MessageBox('zの値に間違いがあります。','注意',0);
    exit;
  end;
  val(iedit.Text, id, ch);
  if ch <> 0 then begin
    application.MessageBox('iの値に間違いがあります。','注意',0);
    exit;
  end;
  val(epsedit.Text, epsd, ch);
  if ch <> 0 then begin
    application.MessageBox('収束判定値に間違いがあります。','注意',0);
    exit;
  end;
  if (epsd > 1e-1) then begin
    application.MessageBox('収束判定値は。0.01より小さくし下さい。','注意',0);
    exit;
  end;
  if (epsd <= 0) then begin
    application.MessageBox('収束判定値は。0 あるいは、負数ではいけません。','注意',0);
    exit;
  end;
  if checkbox2.Checked = true then begin
    val(yedit.Text, yd, ch);
    if ch <> 0 then begin
      application.MessageBox('yの値に間違いがあります。','注意',0);
      exit;
    end;
    val(yiedit.Text, yi, ch);
    if ch <> 0 then begin
      application.MessageBox('yの値に間違いがあります。','注意',0);
      exit;
    end;
  end;


  bigdecimal.DefaultPrecision := dpcs;

  // 入力値をテキスト形式で読み込み多倍長数値に変換します、変換誤差防止です。
  z.r := zedit.Text;

  z.i := iedit.Text;
  eps := epsedit.Text;
//  eps := 1e-300;

  memo1.Clear;
  application.ProcessMessages;
//------------------------------------------------------------------------------

  memo1.Lines.Append('z= ' + z.r.ToString + '  i= ' + z.i.ToString +' i');

//---------------- power -------------------------------------------------------
  if checkbox2.Checked = true then begin
    y.r := yedit.Text;
    y.i := yiedit.Text;
    memo1.Lines.Append('y= ' + y.r.ToString + '  i= ' + y.i.ToString +' i');

    ans := pow_big(z, y, eps);

    ans.r := ans.r.RoundToPrecision(80);
    ans.i := ans.i.RoundToPrecision(80);
    memo1.Lines.Append('x^y = exp(ln(z)*y) = ');

    memo1.Lines.Append('  ' + ans.r.ToString);
    if ans.i >= bigdecimal.Zero then
      memo1.Lines.Append('  +' + ans.i.ToString + ' i')
    else
      memo1.Lines.Append('  ' + ans.i.ToString + ' i');
    exit;
  end;

//---------------------- sin ---------------------------------------------------
  memo1.Lines.Append('sin(z) = ');
  anssin := sin_big(z, eps, dpcs);
  anssin.r := anssin.r.RoundToPrecision(50);               // 50桁に丸め
  anssin.i := anssin.i.RoundToPrecision(50);
  anssin.r := anssin.r.RemoveTrailingZeros(3);
  anssin.i := anssin.i.RemoveTrailingZeros(3);
  memo1.Lines.Append('  ' + anssin.r.ToString);
  if anse.i >= bigdecimal.Zero then
    memo1.Lines.Append('  +' + anssin.i.ToString + ' i')
  else
    memo1.Lines.Append('  ' + anssin.i.ToString + ' i');

//---------------------- arctan-------------------------------------------------
  memo1.Lines.Append('arctan(z) = ');

  if (z.r = bigdecimal.Zero) and (z.i.Abs(z.i) = bigdecimal.One) then begin
    memo1.Lines.Append('  ' + '0');
    if z.i > bigdecimal.Zero then
      memo1.Lines.Append('  ' + '∞i')
    else
      memo1.Lines.Append('  ' + '-∞i');
  end
  else begin
    arctan := arctan_big(z, eps, dpcs);
    arctan.r := arctan.r.RoundToPrecision(50);               // 50桁に丸め
    arctan.i := arctan.i.RoundToPrecision(50);
    arctan.r := arctan.r.RemoveTrailingZeros(3);
    arctan.i := arctan.i.RemoveTrailingZeros(3);
    memo1.Lines.Append('  ' + arctan.r.ToString);
    if arctan.i >= bigdecimal.Zero then
      memo1.Lines.Append('  +' + arctan.i.ToString + ' i')
    else
      memo1.Lines.Append('  ' + arctan.i.ToString + ' i');
  end;
//---------------------- arctan Leonhard Euler -------------------------
  memo1.Lines.Append('arctan_Leonhard_Euler(z) = ');

  if (z.r = bigdecimal.Zero) and (z.i.Abs(z.i) = bigdecimal.One) then begin
    memo1.Lines.Append('  ' + '0');
    if z.i > bigdecimal.Zero then
      memo1.Lines.Append('  ' + '∞i')
    else
      memo1.Lines.Append('  ' + '-∞i');
  end
  else begin
    arctanR := arctan_Leonhard_Euler(z, eps, dpcs);
    arctanR.r := arctanR.r.RoundToPrecision(50);               // 50桁に丸め
    arctanR.i := arctanR.i.RoundToPrecision(50);
    arctanR.r := arctanR.r.RemoveTrailingZeros(3);
    arctanR.i := arctanR.i.RemoveTrailingZeros(3);
    memo1.Lines.Append('  ' + arctanR.r.ToString);
    if arctanR.i >= bigdecimal.Zero then
      memo1.Lines.Append('  +' + arctanR.i.ToString + ' i')
    else
      memo1.Lines.Append('  ' + arctanR.i.ToString + ' i');
  end;
//------------------------- Ln exp --------------------------------
  // 虚数と実数両方ともゼロの場合-∞
  if (z.r = bigdecimal.Zero) and (z.i = bigdecimal.Zero) then begin
    memo1.Lines.Append('loop = 0  演算出来ません。');
    memo1.Lines.Append('Ln(0 + 0i) =');
    memo1.Lines.Append('  -∞');
    memo1.Lines.Append('    0 i');
    goto VA;
  end;

  // 対数計算
  ans := log_big(z, eps ,dpcs);

  // exp
  anse := exp_big(ans, dpcs, eps);

  // 計算結果表示
  ans.r := ans.r.RoundToPrecision(50);               // 50桁に丸め
  ans.i := ans.i.RoundToPrecision(50);
  if ans.r = bigdecimal.Zero then ans.r := '0';
  if ans.i = bigdecimal.Zero then ans.i := '0';

  memo1.Lines.Append('Ln(z) = ');

  memo1.Lines.Append('  ' + ans.r.ToString);
  if ans.i >= bigdecimal.Zero then
    memo1.Lines.Append('  +' + ans.i.ToString + ' i')
  else
    memo1.Lines.Append('  ' + ans.i.ToString + ' i');

  memo1.Lines.Append('exp(Ln(z)) = ');

  anse.r := anse.r.RoundToPrecision(50);               // 50桁に丸め
  anse.i := anse.i.RoundToPrecision(50);
  anse.r := anse.r.RemoveTrailingZeros(3);
  anse.i := anse.i.RemoveTrailingZeros(3);
  memo1.Lines.Append('  ' + anse.r.ToString);
  if anse.i >= bigdecimal.Zero then
    memo1.Lines.Append('  +' + anse.i.ToString + ' i')
  else
    memo1.Lines.Append('  ' + anse.i.ToString + ' i');

// ---------------- variant Ln exp ------------------------------------------
VA:
  // variant double 計算
  xc := varascomplex(xc);
  xc.real := zd;
  xc.Imaginary := id;
  if (xc.real = 0) and (xc.Imaginary = 0) then
    if (zd <> 0) or (id <> 0) then begin
      memo1.Lines.Append('variant計算');
      memo1.Lines.Append('  variantの有効桁数不足で演算不可');
      exit;
    end;
  if (xc.real <> 0) or (xc.Imaginary <> 0) then begin
    if checkbox1.Checked then begin
      memo1.Lines.Append('variant計算  Ln(z) = ');
      dans := VarComplexLn(xc);           // complexLn計算
    end
    else begin
      memo1.Lines.Append('variant計算  2*arctanh((z-1)/(z-1)) = ');
//      dans := VarComplexarctanh((xc * xc - 1)/(xc * xc + 1));
      dans := lnd(zd, id);                // 2arctanh{(z-1)/z+1)} 計算
    end;
    memo1.Lines.Append('  ' + floattostr(dans.real));
    if dans.Imaginary >= 0 then
      memo1.Lines.Append('  +' + floattostr(dans.Imaginary) + ' i')
    else
      memo1.Lines.Append('  ' + floattostr(dans.Imaginary) + ' i');
  end
  else begin
    memo1.Lines.Append('variant計算 = ');
    memo1.Lines.Append('  -∞');
    memo1.Lines.Append('   0 i');
  end;
end;

procedure TForm1.epsEditChange(Sender: TObject);
begin
  Z10F := false;
end;

end.

 
download log_exp_function_big.zip



  三角関数、逆三角関数、その他関数 に戻る/pre