逆誤差関数 (Extended Double)

 Extended、Doubleの計算の計算の精度確認です。
実際の使用においての精度は、Doubleで十分だと思います。

 逆誤差関数の上記計算をそのまま実行すると、Ckのk値が大きくなり、Ckの値を求めるのに非常に時間がかかります。
C、C++に組み込まれているerfinv関数では通常のLong Doubleの関数となっており近似計算をしています。
近似値は、分子と、分母に分けて、zの値の大きさに応じて何種類かに分けて計算しています。
逆誤差関数にあるものをは、多倍長計算となっているので、Extended(Long Double) Win32(x86)とDouble Win64(x64)のプログラムに変換してみました。
Win32 と Win64の切り替えは、コンパイラをWin32にするか、Win64にするかです。

 DoubleとExtended(Long Duble)の計算精度は次の様になります。
     Double     5.0 * 10^-324 ~ 1.7 * 10^308 15~16桁
     Extended  3.6 * 10^-4951 ~ 1.1 * 10^4932 19~20桁
実際の計算では、Extendedで表示されるのは、18桁でDoubleでは16桁程度です。
通常のFloatToStrでは両方とも15桁までしか表示されません。
18桁表示する為には、FloatToStrFを使用する必要があります。

演算の精度を比較する為に、逆誤差関数のなかりC++から多倍長に変換したもの比較出来るように同じものを組み込んでみました。

ついでに、JavaScriptで組まれたプログラムを見つけたので、これも比較の為に、変換して組み込んでみました。
C++のものも、JavaScriptのものも、Long Doubleとなっているので、Win32モードの方が精度が高くなります。
近似値用のデーターはC++は20桁、JavaScriptは15~18桁程度となっています。

何桁まで正しい値を返すかは、多倍長での計算と比較すれば良いでしょう、元の近似用の値は同じですが、多倍長計算の方が精度が高い答えとなります。
参考の為に誤差関数erf(z)のExtended(long double)の計算も組み込んでみました、これは別ユニットにしてあります。
erf(z)の計算はx86モードの時 z= 4.7 が最大値とり、x64モードではdouble演算となり z= 4 が最大値となります。
実際にはもう少し大きな値を計算出来るのですが、演算誤差により正しい値を返さなくなります。

プログラム

Biginteger & Bigdecimal の組み込み方は第1種ケルビン関数を参照して下さい。

 textからDoubleに変換するとき、'0.999999・・・9'の'9'の値が続く場合、1に変換されないで、1.000000000000000222044604925031になるようです。
1より大きな値となります。
普通は、'0.999999・・・9の様な値を入れることは無いので、バグとして認識されていないのでしょう。
対策として一度 FlotToStrでTextにして再度読み込んで、1になるようにしています。

Main.pas

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, system.UITypes, Soap.TypeTrans;

type
  TForm1 = class(TForm)
    CalcBtn: TButton;
    Memo1: TMemo;
    yEdit: TLabeledEdit;
    erfBtn: TButton;
    zEdit: TLabeledEdit;
    procedure CalcBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure erfBtnClick(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

  epsilon : extended;

implementation

{$R *.dfm}

uses system.Math, erf, Velthuis.BigIntegers, Velthuis.Bigdecimals;

const
  DPS = 50;

//--------------------------------- Ln(x)---------------------------------------
// x = m * 2^n のnの値と2^nの値を戻します
// result = n
// ta     = 2^n
// m = x / 2^n
function frexpa(x: bigdecimal; var ta: biginteger): integer;
var
  tb: biginteger;
  xi, one: bigdecimal;
  n, j, s, pcs : integer;
begin
  pcs := BigDecimal.DefaultPrecision;
  one := '1';
  one := one.RoundToPrecision(pcs);
  xi := x;                          // x保存
  if xi < one then x := one / x;    // 1より小さかったら逆数
  ta := '1';
  n := 0;
  j := 0;                           // シフト回数クリア
  ta := '1';                        // ta初期値
  s := 4096;                        // s=4^i    i = 6
  repeat
    while x > ta do begin
      biginteger.ShiftLeft(ta, s, tb);  // s分左シフト
      ta := tb;
      inc(j);                           // シフト回数インクリメント
    end;
    if (s > 1) and (j > 0) then begin   // ta > x になったら
      dec(j);                           // シフト回数デクリメント
      biginteger.Shiftright(ta, s, tb); // s分右シフト
      ta := tb;
    end;
    n := n + s * j;                     // 2^nのn計算
    j := 0;                             // シフト回数クリア
    s := s shr 2;                       // sの値を1/4にする   右へ2ビットシフト
//    s := s div 4;                       // sの値を1/4にする
  until s = 0;            // s= ...16, 4, 1, 0
  if xi < one then begin                // より小さかったら
    n := -n + 1;                        // 2^nのn値負数
    biginteger.Shiftright(ta, 1, tb);   // 上式+1により右シフト
    ta := tb;
  end;
  result := n                             // ta = 2^n
end;

// 小さい値のlog計算
// xi 実数
// dpcs 有効桁数
// ans log値
// 戻り値 Loopオーバーフロー 無し true  有り false
// loop制限の無い場合 1e-4~1e4 が計算出来る範囲
// loop制限 5.8e-3 ~1.73e2  (kk=10000の場合)
function Log_11(x: bigdecimal; dpcs: integer; var ans: bigdecimal): boolean;
const
  KK = 10000;
var
  one, two, x1, x2, i, s, last: bigdecimal;
  k : integer;
begin
  result := true;
  one := '1';
  two := '2';
  x1 := (x - one) / (x + one);                      // x1 = (x-1)/(x+1)
  x2 := x1 * x1;                                    // x1^2
  x2 := x2.RoundToPrecision(dpcs);
  i := one;                                         // i=1
  s := x1;                                          // s初期値
  k := 0;                                           // 長ループ脱出用カウンタ0
  repeat
    x1 := x1 * x2;                                  // x1^2n+1
    x1 := x1.RoundToPrecision(dpcs);
    i := i + two;                                   // 2n+1
    i := i.RoundToPrecision(dpcs);
    last := s;                                      // 判定用sの値保存
    s := s + x1 / i;                                // Σs = s + x1^2n+1 / (2n+1)
    s := s.RoundToPrecision(dpcs);                  // 収束判定の為指定有効桁数以下丸め
    inc(k);                                         // ループカウンタインクリメント
  until (last = s) or (k > KK);                     // 収束判定
  ans := two * s;
  if k > kk then result := false;
end;

// loge(x)    ln(x)
// 級数展開
// 1e±500000 程度が限度
// sm <xi< last の範囲外の場合 (0.7~xi~1.4) * 2^nに変換 xiが1に近いほど変換早い
function log_big(xi: bigdecimal): bigdecimal;
label
  EXT;
const
  KK = 10000;
var
  LOG2, SQRT2, two, last, s, sm, one, one1, k2, a: bigdecimal;
  k : integer;
  kb, ta : biginteger;
  pcsBack, dpcs : integer;
  rmback : BigDecimal.RoundingMode;
begin
  result := '0';
  if xi <= result then begin
    application.MessageBox('無効な値です。','注意',0);
    exit;
  end;
  sm := '1e-2';
  last := '5e1';
  pcsBack := BigDecimal.DefaultPrecision;             // 除算演算精度バックアップ
  rmBack := BigDecimal.DefaultRoundingMode;           // 除算丸めモードバックアップ
  dpcs := pcsBack + 5;                                // 指定精度 + α
  BigDecimal.DefaultPrecision := dpcs;
  BigDecimal.DefaultRoundingMode := rmNearesteven;
  one := '1';
  if xi = one then begin
    goto EXT;
  end;
  if (xi > sm) and (xi < last) then begin             // 0.01 < xi < 50の時はLog11計算
    if Log_11(xi, dpcs, a) then begin                 // log11
      result := a;
      goto EXT;
    end;
  end;
  one1 := '1e500000';
  if xi < one then begin                              // xi < 1
    last := one / xi;                                 // 1 / xi
    if last > one1 then begin                         // (1 / xi) > 1e500000
      application.MessageBox('値が小さすぎます。','注意',0);
      goto EXT;
    end;
  end;
  if xi > one1 then begin                             // xi > 1e500000
    application.MessageBox('値が大きすぎます。','注意',0);
    goto EXT;
  end;
  xi := xi.RoundToPrecision(dpcs);
  two := '2';
  Log_11(two, dpcs, LOG2);                            // log(2)
  SQRT2 := bigdecimal.Sqrt(two, dpcs * 2);            // √2
  k := frexpa(xi / SQRT2, ta);                        // x / √2 = m * 2^k  ta = 2^k
  kb := k;
  k2 := ta;                                           // 2^k  integer to decimal
  k2 := k2.RoundToPrecision(dpcs);
  if k < 0 then begin                                 // kが負数だったら
    xi := xi * k2;                                    // x * 2^k   x=0.707~1.414  √2
    xi := xi.RoundToPrecision(dpcs);
  end
  else begin
    xi := xi / k2;                                    // x / 2^k   x=0.707~1.414  √2
  end;
  if xi <> one then begin                             // xi <> 1 なら
    Log_11(xi, dpcs, s)                               // log(xi)
  end
  else s := '0';                                      // xi=1なら s=0
  result := LOG2 * kb + s;
EXT:
  result := result.RoundToPrecision(pcsBack);
  BigDecimal.DefaultPrecision := pcsBack;           // 除算演算精度復帰
  BigDecimal.DefaultRoundingMode := rmBack;         // 除算丸めモード復帰
end;

//----------------------------- errinv 10byte相当-------------------------------
// C++ライブラリより変換
// 元は Long Double 10byte演算でしたので多倍長演算を使用
// 近似計算用データーは20桁ありますが、誤差により18桁程度の精度となります。
// doubleでの計算でも15桁程度の精度は出ますが1に近い0.9999999999999999の場合無限大になります。
function Inverse_error_long_double(x : bigdecimal; var INF : integer): bigdecimal;
const
  DPSLD = 30;
var
  LN2 : bigdecimal;
  A : array[0..7] of bigdecimal;
  B : array[0..7] of bigdecimal;
  C : array[0..7] of bigdecimal;
  D : array[0..7] of bigdecimal;
  E : array[0..7] of bigdecimal;
  F : array[0..7] of bigdecimal;

  abs_x, r, num, den : bigdecimal;
  d1, d2 : bigdecimal;
  i : integer;

begin
  INF := 0;
  if (x < -1) or (x > 1) then begin
    result := 0;
    exit;
  end;

  if (x = -1) then begin
    INF := - 1;
    exit;
  end;
  if (x = 1) then begin
    INF := 1;
    exit;
  end;
  bigdecimal.DefaultPrecision := DPSLD;       // 除算30桁設定

  LN2 := '6.931471805599453094172321214581e-1';
  A[0] := '1.1975323115670912564578e0';
  A[1] := '4.7072688112383978012285e1';
  A[2] := '6.9706266534389598238465e2';
  A[3] := '4.8548868893843886794648e3';
  A[4] := '1.6235862515167575384252e4';
  A[5] := '2.3782041382114385731252e4';
  A[6] := '1.1819493347062294404278e4';
  A[7] := '8.8709406962545514830200e2';

  B[0] := '1.0000000000000000000e0';
  B[1] := '4.2313330701600911252e1';
  B[2] := '6.8718700749205790830e2';
  B[3] := '5.3941960214247511077e3';
  B[4] := '2.1213794301586595867e4';
  B[5] := '3.9307895800092710610e4';
  B[6] := '2.8729085735721942674e4';
  B[7] := '5.2264952788528545610e3';

  C[0] := '1.42343711074968357734e0';
  C[1] := '4.63033784615654529590e0';
  C[2] := '5.76949722146069140550e0';
  C[3] := '3.64784832476320460504e0';
  C[4] := '1.27045825245236838258e0';
  C[5] := '2.41780725177450611770e-1';
  C[6] := '2.27238449892691845833e-2';
  C[7] := '7.74545014278341407640e-4';

  D[0] := '1.4142135623730950488016887e0';
  D[1] := '2.9036514445419946173133295e0';
  D[2] := '2.3707661626024532365971225e0';
  D[3] := '9.7547832001787427186894837e-1';
  D[4] := '2.0945065210512749128288442e-1';
  D[5] := '2.1494160384252876777097297e-2';
  D[6] := '7.7441459065157709165577218e-4';
  D[7] := '1.4859850019840355905497876e-9';

  E[0] := '6.65790464350110377720e0';
  E[1] := '5.46378491116411436990e0';
  E[2] := '1.78482653991729133580e0';
  E[3] := '2.96560571828504891230e-1';
  E[4] := '2.65321895265761230930e-2';
  E[5] := '1.24266094738807843860e-3';
  E[6] := '2.71155556874348757815e-5';
  E[7] := '2.01033439929228813265e-7';

  F[0] := '1.414213562373095048801689e0';
  F[1] := '8.482908416595164588112026e-1';
  F[2] := '1.936480946950659106176712e-1';
  F[3] := '2.103693768272068968719679e-2';
  F[4] := '1.112800997078859844711555e-3';
  F[5] := '2.611088405080593625138020e-5';
  F[6] := '2.010321207683943062279931e-7';
  F[7] := '2.891024605872965461538222e-15';

  abs_x := x.Abs(x);
  if abs_x < '0.85' then begin
    d1 := '0.180625';
    d2 := '0.25';
    r := d1 - d2 * x * x;

    num := A[7];
    den := B[7];
    for i := 6 downto 0 do begin
      num := num * r + A[i];
      den := den * r + B[i];
    end;

    num := num.RoundToPrecision(DPSLD);
    den := den.RoundToPrecision(DPSLD);
    result := x * num / den;
    bigdecimal.DefaultPrecision := DPS;         // 除算有効桁数元に戻し
    exit;
  end;

  r := bigdecimal.Sqrt(LN2 - log_big(bigdecimal.One - abs_x));

  if r <= '5.0' then begin
    d1 := '1.6';
    r := r - d1;

    num := C[7];
    den := D[7];
    for i := 6 downto 0 do begin
      num := num * r + C[i];
      den := den * r + D[i];
    end;
  end 
  else begin
    d2 := '5.0';
    r := r - d2;

    num := E[7];
    den := F[7];
    for i := 6 downto 0 do begin
      num := num * r + E[i];
      den := den * r + F[i];
    end;
  end;
  num := num.RoundToPrecision(DPSLD);
  den := den.RoundToPrecision(DPSLD);

  result := num / den;
  if x < '0' then
    result := - result;
  bigdecimal.DefaultPrecision := DPS;         // 除算有効桁数元に戻し
end;


//------------------------------------------------------------------------------
// stdlib-js      Standard library for JavaScript より変換
function inverse_error_js(x : extended): extended;
const
  Y1 = 8.91314744949340820313e-2;
  Y2 = 2.249481201171875;
  Y3 = 8.07220458984375e-1;
  Y4 = 9.3995571136474609375e-1;
  Y5 = 9.8362827301025390625e-1;
var
  sign, ax, qs, q, g, r : extended;

  function rational_p1q1(x : extended): extended;
  var
    s1, s2 : extended;
  begin
    if x = 0.0 then begin
      result := -0.0005087819496582806;
      exit;
    end;
    s1 := -0.0005087819496582806 + (x * (-0.008368748197417368 + (x * (0.03348066254097446
         + (x * (-0.012692614766297404 + (x * (-0.03656379714117627 + (x * (0.02198786811111689
         + (x * (0.008226878746769157 + (x * (-0.005387729650712429 + (x * (0.0 + (x * 0.0)))))))))))))))));
    s2 := 1.0 + (x * (-0.9700050433032906 + (x * (-1.5657455823417585 + (x * (1.5622155839842302
         + (x * (0.662328840472003 + (x * (-0.7122890234154284 + (x * (-0.05273963823400997
         + (x * (0.07952836873415717 + (x * (-0.0023339375937419 + (x * 0.0008862163904564247)))))))))))))))));
    result := s1 / s2;
  end;

  function rational_p2q2(x : extended): extended;
  var
    s1, s2 : extended;
  begin
    if x = 0.0 then begin
      result :=  -0.20243350835593876;
      exit;
    end;
    s1 := -0.20243350835593876 + (x * (0.10526468069939171 + (x * (8.3705032834312
         + (x * (17.644729840837403 + (x * (-18.851064805871424 + (x * (-44.6382324441787
         + (x * (17.445385985570866 + (x * (21.12946554483405 + (x * -3.6719225470772936)))))))))))))));
    s2 := 1.0 + (x * (6.242641248542475 + (x * (3.971343795334387 + (x * (-28.66081804998
         + (x * (-20.14326346804852 + (x * (48.560921310873994 + (x * (10.826866735546016
         + (x * (-22.643693341313973 + (x * 1.7211476576120028)))))))))))))));
    result := s1 / s2;
  end;

  function rational_p3q3(x : extended): extended;
  var
    ax, ix, s1, s2 : extended;
  begin
    if x = 0.0 then begin
      result := -0.1311027816799519;
      exit;
    end;
    ax := abs(x);
    if ax <= 1.0 then begin
      s1 := -0.1311027816799519 + (x * (-0.16379404719331705 + (x * (0.11703015634199525
            + (x * (0.38707973897260434 + (x * (0.3377855389120359 + (x * (0.14286953440815717
            + (x * (0.029015791000532906 + (x * (0.0021455899538880526 + (x * (-6.794655751811263e-7
            + (x * (2.8522533178221704e-8 + (x * -6.81149956853777e-10)))))))))))))))))));
      s2 := 1.0 + (x * (3.4662540724256723 + (x * (5.381683457070069 + (x * (4.778465929458438
            + (x * (2.5930192162362027 + (x * (0.848854343457902 + (x * (0.15226433829533179
            + (x * (0.011059242293464892 + (x * (0.0 + (x * (0.0 + (x * 0.0)))))))))))))))))));
    end
    else begin
      ix := 1.0 / x;
      s1 := -6.81149956853777e-10 + (ix * (2.8522533178221704e-8 + (ix * (-6.794655751811263e-7
            + (ix * (0.0021455899538880526 + (ix * (0.029015791000532906 + (ix * (0.14286953440815717
            + (ix * (0.3377855389120359 + (ix * (0.38707973897260434 + (ix * (0.11703015634199525
            + (ix * (-0.16379404719331705 + (ix * -0.1311027816799519)))))))))))))))))));
      s2 := 0.0 + (ix * (0.0 + (ix * (0.0 + (ix * (0.011059242293464892 + (ix * (0.15226433829533179
            + (ix * (0.848854343457902 + (ix * (2.5930192162362027 + (ix * (4.778465929458438
            + (ix * (5.381683457070069 + (ix * (3.4662540724256723 + (ix * 1.0)))))))))))))))))));
    end;
    result := s1 / s2;
  end;

  function rational_p4q4(x : extended): extended;
  var
    ax, ix, s1, s2 : extended;
  begin
    if x = 0.0 then begin
      result := -0.0350353787183178;
      exit;
    end;
    ax := abs(x);
    if ax <= 1.0 then begin
      s1 := -0.0350353787183178 + (x * (-0.0022242652921344794 + (x * (0.018557330651423107
            + (x * (0.009508047013259196 + (x * (0.0018712349281955923 + (x * (0.00015754461742496055
            + (x * (0.00000460469890584318 + (x * (-2.304047769118826e-10 + (x * 2.6633922742578204e-12)))))))))))))));
      s2 := 1.0 + (x * (1.3653349817554064 + (x * (0.7620591645536234 + (x * (0.22009110576413124
            + (x * (0.03415891436709477 + (x * (0.00263861676657016 + (x * (0.00007646752923027944
            + (x * (0.0 + (x * 0.0)))))))))))))));
    end
    else begin
      ix := 1.0 / x;
      s1 := 2.6633922742578204e-12 + (ix * (-2.304047769118826e-10 + (ix * (0.00000460469890584318
            + (ix * (0.00015754461742496055 + (ix * (0.0018712349281955923 + (ix * (0.009508047013259196
            + (ix * (0.018557330651423107 + (ix * (-0.0022242652921344794 + (ix * -0.0350353787183178)))))))))))))));
      s2 := 0.0 + (ix * (0.0 + (ix * (0.00007646752923027944 + (ix * (0.00263861676657016
            + (ix * (0.03415891436709477 + (ix * (0.22009110576413124 + (ix * (0.7620591645536234
            + (ix * (1.3653349817554064 + (ix * 1.0)))))))))))))));
    end;
    result := s1 / s2;
  end;

  function rational_p5q5(x : extended): extended;
  var
    ax, ix, s1, s2 : extended;
  begin
    if x = 0.0 then begin
      result := -0.016743100507663373;
      exit;
    end;
    ax := abs(x);
    if ax <= 1.0 then begin
      s1 := -0.016743100507663373 + (x * (-0.0011295143874558028 + (x * (0.001056288621524929
            + (x * (0.00020938631748758808 + (x * (0.000014962478375834237 + (x * (4.4969678992770644e-7
            + (x * (4.625961635228786e-9 + (x * (-2.811287356288318e-14 + (x * 9.905570997331033e-17)))))))))))))));
      s2 := 1.0 + (x * (0.5914293448864175 + (x * (0.1381518657490833 + (x * (0.016074608709367652
            + (x * (0.0009640118070051656 + (x * (0.000027533547476472603 + (x * (2.82243172016108e-7
            + (x * (0.0 + (x * 0.0)))))))))))))));
    end
    else begin
      ix := 1.0 / x;
      s1 := 9.905570997331033e-17 + (ix * (-2.811287356288318e-14 + (ix * (4.625961635228786e-9
            + (ix * (4.4969678992770644e-7 + (ix * (0.000014962478375834237 + (ix * (0.00020938631748758808
            + (ix * (0.001056288621524929 + (ix * (-0.0011295143874558028 + (ix * -0.016743100507663373)))))))))))))));
      s2 := 0.0 + (ix * (0.0 + (ix * (2.82243172016108e-7 + (ix * (0.000027533547476472603
            + (ix * (0.0009640118070051656 + (ix * (0.016074608709367652 + (ix * (0.1381518657490833
            + (ix * (0.5914293448864175 + (ix * 1.0)))))))))))))));
    end;
    result := s1 / s2;
  end;

begin
  if (x < -1) or (x > 1) then begin
    result := 0;
    exit;
  end;
  if x = 1.0 then begin
    result := infinity;
    exit;
  end;
  if x = -1.0 then begin
    result := - infinity;
    exit;
  end;
  if x = 0.0 then begin
    result :=  x;
    exit;
  end;
  if  x < 0.0 then sign := -1.0
              else sign := 1.0;
   ax := abs(x);
  // |x| <= 0.5
  if ax <= 0.5 then begin
    g := ax * (ax + 10.0);
    r := rational_p1q1(ax);
    result := sign * ((g * Y1) + (g * r));
    exit;
  end;
  q := 1.0 - ax;
  // 1-|x| >= 0.25
  if q >= 0.25 then begin
    g := sqrt(-2.0 * ln(q));
    q := q - 0.25;
    r := rational_p2q2(q);
    result := sign * (g / (Y2 + r));
    exit;
  end;
  q := sqrt(-ln(q));
  // q < 3
  if q < 3.0 then begin
    qs := q - 1.125;
    r := rational_p3q3(qs);
    result := sign * ((Y3 * q) + (r * q));
    exit;
  end;
  // 3 =< q < 6
  if q < 6.0 then begin
    qs := q - 3.0;
    r := rational_p4q4(qs);
    result := sign * ((Y4 * q) + (r * q));
    exit;
  end;
  // 6 =< q < 18
  qs := q - 6.0;
  r := rational_p5q5( qs );
  result := sign * ((Y5* q) + (r * q ));
end;

//------------------------------------------------------------------------------
// C++ ライブラリより変換
// 元は Long double
function Inverse_error_double(x : extended): extended;
var
  Ln2 : extended;
  A : array[0..7] of extended;
  B : array[0..7] of extended;
  C : array[0..7] of extended;
  D : array[0..7] of extended;
  E : array[0..7] of extended;
  F : array[0..7] of extended;

  abs_x, r, num, den : extended;
  i : integer;

begin
  if (x < -1) or (x > 1) then begin
    result := 0;
    exit;
  end;

  if (x = -1) then begin
    result := -INFiNITY;
    exit;
  end;
  if (x = 1) then begin
    result := INFiNITY;
    exit;
  end;

  Ln2 := 6.931471805599453094172321214581e-1;     // ln(2)

  A[0] := 1.1975323115670912564578e0;
  A[1] := 4.7072688112383978012285e1;
  A[2] := 6.9706266534389598238465e2;
  A[3] := 4.8548868893843886794648e3;
  A[4] := 1.6235862515167575384252e4;
  A[5] := 2.3782041382114385731252e4;
  A[6] := 1.1819493347062294404278e4;
  A[7] := 8.8709406962545514830200e2;

  B[0] := 1.0000000000000000000e0;
  B[1] := 4.2313330701600911252e1;
  B[2] := 6.8718700749205790830e2;
  B[3] := 5.3941960214247511077e3;
  B[4] := 2.1213794301586595867e4;
  B[5] := 3.9307895800092710610e4;
  B[6] := 2.8729085735721942674e4;
  B[7] := 5.2264952788528545610e3;

  C[0] := 1.42343711074968357734e0;
  C[1] := 4.63033784615654529590e0;
  C[2] := 5.76949722146069140550e0;
  C[3] := 3.64784832476320460504e0;
  C[4] := 1.27045825245236838258e0;
  C[5] := 2.41780725177450611770e-1;
  C[6] := 2.27238449892691845833e-2;
  C[7] := 7.74545014278341407640e-4;

  D[0] := 1.4142135623730950488016887e0;
  D[1] := 2.9036514445419946173133295e0;
  D[2] := 2.3707661626024532365971225e0;
  D[3] := 9.7547832001787427186894837e-1;
  D[4] := 2.0945065210512749128288442e-1;
  D[5] := 2.1494160384252876777097297e-2;
  D[6] := 7.7441459065157709165577218e-4;
  D[7] := 1.4859850019840355905497876e-9;

  E[0] := 6.65790464350110377720e0;
  E[1] := 5.46378491116411436990e0;
  E[2] := 1.78482653991729133580e0;
  E[3] := 2.96560571828504891230e-1;
  E[4] := 2.65321895265761230930e-2;
  E[5] := 1.24266094738807843860e-3;
  E[6] := 2.71155556874348757815e-5;
  E[7] := 2.01033439929228813265e-7;

  F[0] := 1.414213562373095048801689e0;
  F[1] := 8.482908416595164588112026e-1;
  F[2] := 1.936480946950659106176712e-1;
  F[3] := 2.103693768272068968719679e-2;
  F[4] := 1.112800997078859844711555e-3;
  F[5] := 2.611088405080593625138020e-5;
  F[6] := 2.010321207683943062279931e-7;
  F[7] := 2.891024605872965461538222e-15;

  abs_x := abs(x);
  if abs_x < 0.85 then begin
    r := 0.180625 - 0.25 * x * x;

    num := A[7];
    den := B[7];
    for i := 6 downto 0 do begin
      num := num * r + A[i];
      den := den * r + B[i];
    end;
    result := x * num / den;
    exit;
  end;

  r := Sqrt(Ln2 - Ln(1.0 - abs_x));

  if r <= 5.0 then begin
    r := r - 1.6;

    num := C[7];
    den := D[7];
    for i := 6 downto 0 do begin
      num := num * r + C[i];
      den := den * r + D[i];
    end;
  end
  else begin
    r := r - 5.0;

    num := E[7];
    den := F[7];
    for i := 6 downto 0 do begin
      num := num * r + E[i];
      den := den * r + F[i];
    end;
  end;
  result := num / den;
  if x < 0 then
    result := - result;
end;

//------------------------------------------------------------------------------
  // '0.9999999999999999' '9'の値が長くなると正しく1に変換されません。
  //  1.000000000000000222044604925031になる
  // 一度stringに戻して再変換して切り捨てます。
  // Doubleの演算では、値が1に近づくと演算制度が4桁程度迄落ちます。
  // Double     5.0 * 10^-324 ~ 1.7 * 10^308    15~16桁
  // Extended   3.6 * 10^-4951 ~  1.1 * 10^4932   19~20桁
procedure TForm1.CalcBtnClick(Sender: TObject);
var
  y,  ans : bigdecimal;
  yd, ansd : extended;
  n : integer;
  exstr : string;
  {$IFDEF CPUX86}                                  // プラットフォーム32ビット
    L : integer;
  {$ENDIF CPUX86}
begin
  memo1.Clear;
  //----------------------------------------------------------------------------
  val(yedit.Text, yd, n);
  if n <> 0 then begin
    application.MessageBox('yの値に間違いがあります。','注意', 0);
    exit;
  end;
  {$IFDEF CPUX64}                                 // プラットフォーム64ビット
    Memo1.Lines.Add('X64モード');
    yd := strtofloat(floatTostr(yd));           // 0.9999999999999999 =>'1'-> 1
    exstr := ' Double 8byte 演算';
  {$ENDIF CPUX64}
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    Memo1.Lines.Add('X86モード');
    L := length(yedit.Text);
    if L > 30 then yd := strtofloat(Formatfloat('#0.########################',yd));       // 0.9999999999999999 =>'1'-> 1
    exstr := ' Extended 10byte 演算';
  {$ENDIF CPUX86}
//  memo1.Lines.Append(floatTostr(yd - 1));       // 2.22044604925031E-16
  if (-1 > yd) or (yd > 1) then begin
    application.MessageBox('yの値が範囲外です。','注意', 0);
    exit;
  end;
  //----------------------------------------------------------------------------
  ansd := Inverse_error_double(yd);
  memo1.Lines.Append('inverse error function');
  memo1.Lines.Append('C++から変換' + exstr);
  memo1.Lines.Append(' erfinv = ' + Formatfloat('#0.########################', ansd));
  memo1.Lines.Append(' erfinv = ' + floatTostr(ansd));

  //----------------------------------------------------------------------------
  ansd := inverse_error_js(yd);
  memo1.Lines.Append('');
  memo1.Lines.Append('jsから変換' + exstr);
  memo1.Lines.Append(' erfinv = ' + Formatfloat('#0.########################', ansd));
  memo1.Lines.Append(' erfinv = ' + floatTostr(ansd));

  //----------------------------------------------------------------------------
  memo1.Lines.Append('');
  memo1.Lines.Append('C++ から変換 多倍長演算');
  y := yedit.Text;
  ans := Inverse_error_long_double(y, n);
  if n = 1 then
    memo1.Lines.Append(' erfinv = INFINITY');
  if n = -1 then
    memo1.Lines.Append(' erfinv = -INFINITY');
  if n = 0 then begin
    ans := ans.RoundToPrecision(18);                        // 18桁丸め
    if ans = 0 then
      memo1.Lines.Append(' erfinv = 0.0')
    else
      memo1.Lines.Append(' erfinv = ' + ans.ToString);
  end;
end;

procedure TForm1.erfBtnClick(Sender: TObject);
var
  z, ans : Extended;
  ch : integer;
  exstr : string;
  {$IFDEF CPUX86}                                // プラットフォーム32ビット
    L : integer;
  {$ENDIF CPUX86}
begin
  memo1.Clear;
  //----------------------------------------------------------------------------
  val(zedit.Text, z, ch);
  if ch <> 0 then begin
    application.MessageBox('zの値に間違いがあります。','注意', 0);
    exit;
  end;
  {$IFDEF CPUX64}                                 // プラットフォーム64ビット
    Memo1.Lines.Add('X64モード');
    z := strtofloat(floatTostr(z));             // 0.9999999999999999 =>'1'-> 1
    exstr := ' Double 8byte 演算';
  {$ENDIF CPUX64}
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    Memo1.Lines.Add('X86モード');
    L := length(zedit.Text);
    if L > 30 then z := strtofloat(Formatfloat('#0.########################', z));                 // 0.9999999999999999 =>'1'-> 1
    exstr := ' Extended 10byte 演算';
  {$ENDIF CPUX86}
  memo1.Lines.Append('error function');
  ans := Error_function(z);
  memo1.Lines.Append(exstr);
  memo1.Lines.Append(' erf = ' + floatTostr(ans));
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  tmp : extended;
begin
  epsilon := 1;
  repeat
    epsilon := epsilon / 2;
    tmp := 1 - epsilon;
  until tmp = 1;
  epsilon := epsilon * 2;

  bigdecimal.DefaultPrecision := DPS;
end;

end.

erf.pas

unit erf;

interface

  function Error_function(z : Extended): Extended;

implementation

uses system.Math, system.SysUtils, main;

// erf(z)
function Error_function(z : Extended): Extended;
var
  mzh2, zhn, s, sb, tmp, zmax : Extended;
  pai2 : Extended;
  n, nl, nn : Extended;
begin
  {$IFDEF CPUX64}            // プラットフォーム64ビット
    zmax := 4;
  {$ENDIF CPUX64}
  {$IFDEF CPUX86}            // プラットフォーム64ビット
    zmax := 4.7;
  {$ENDIF CPUX86}
  if abs(z) > zmax then begin
    if z >= 0 then result := 1
              else result := -1;
    exit;
  end;
  pai2 := 2.0 / Sqrt(pi);                         // 2/√(π)
  mzh2 := -z * z;                                 // -z^2
  s := z;                                         // n = 0時  z
  nl := 1.0;                                      // 0! = 1
  zhn := z;                                       // n = 0時  z
  n := 1.0;                                       // n=1から
  repeat
    sb := s;
    nn := n + n + 1.0;                            // 2n+1
    nn := nn * nl;                                // n!(2n+1)
    zhn := zhn * mzh2;                            // ((-1)^n)(z^(2n+1))
    tmp := zhn / nn;                              // ((-1)^n)(z^(2n+1))/(n!(2n+1))
    s := s + tmp;                                 // Σ+Δ
    n := n + 1.0;                                 // inc(n)
    nl := nl * n;                                 // n!
  until (sb = s) or (abs(tmp) <= epsilon);
  result := s * pai2;                             // Σ* 2/√(π)
  if abs(result) >=1 then
    if result >=0 then result := 1.0
                  else result := -1.0;
  form1.Memo1.Lines.Append('  loops= ' + floatTostr(n));
end;

end.


download inverse_error_function.zip

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