逆誤差関数、誤差関数

 逆誤差関数(erfinv)、java script から変換したものが判りづらいのと、c++より変換したものより精度が良さそうなので、演算に配列を使用した、extended(long double)と多倍長のプログラムを作成してみました。
多倍長演算では、元データーが18桁程度なので、Doubleに併せて18桁表示としています、演算精度は、18:桁を維持します。
Doubleの演算では、0.99・・の場合、直ぐに1になってしまい、1直近では3桁程度しか精度が出ません。

 誤差関数(erf)のextended、doubleでの計算時、計算結果が1に近づくと、精度が悪くなるので、これの対策も検討しました。
誤差関数をテーラー展開した計算のみで行うと、収束判定を、値が変化しなくなった時点で収束したと判定するのですが、この方法だと、有効桁数より小さい値なのだが本来、加算されるべき値が加算されず桁落ちすることになります。
特に、erf(z)の値が1に近づくと顕著になります。
対策として、1に近づくような値の場合は、連分数を使用して計算するようにします。
多倍長演算を使用して、表示桁数に対して、数桁以上演算桁数を多くすれば桁落ちの心配はありませんが、演算数が増えます。

連分数

 連分数の計算は、1から減算する値を計算するので、桁落ちの様な問題は発生しません。
但し、連分数のでの計算は、zの値が小さくなると、連分数の深度が深くなり、計算数が増大します、そこで、zの値が小さい範囲では、テーラー展開の計算で行い、ある程度大きくなったら連分数の計算を行います。
此処での、プログラムは、2.4以下と、以上で切り分けています。
 テーラー展開の場合extended演算でerf(z)のzの値が最大で、4.7程度ですが、連分数を使うと6.1程度まで広がります。

プログラム

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

Big_erfinvJs.pas

unit Big_erfinvJs;


interface

uses system.math, Vcl.Forms, Vcl.Dialogs, Velthuis.BigIntegers, Velthuis.Bigdecimals;

function erfinvjs_function_big(x : bigdecimal): bigdecimal;

implementation

uses main;

var
  p1 : array[0..1] of array[0..9] of bigdecimal;
  p2 : array[0..1] of array[0..9] of bigdecimal;
  p3 : array[0..3] of array[0..10] of bigdecimal;
  p4 : array[0..3] of array[0..8] of bigdecimal;
  p5 : array[0..3] of array[0..8] of bigdecimal;

  // Bigdecimal に 直接設定出来ないので、一度文字配列にします。
  p1s: array[0..1] of array[0..9] of string =
    (('-0.0005087819496582806', '-0.008368748197417368', '0.03348066254097446', '-0.012692614766297404',
      '-0.03656379714117627', '0.02198786811111689', '0.008226878746769157', '-0.005387729650712429', '0.0', '0.0'),
     ('1.0', '-0.9700050433032906', '-1.5657455823417585', '1.5622155839842302', '0.662328840472003', '-0.7122890234154284',
      '-0.05273963823400997', '0.07952836873415717', '-0.0023339375937419', '0.0008862163904564247'));
  p2s: array[0..1] of array[0..8] of string =
    (('-0.20243350835593876', '0.10526468069939171', '8.3705032834312', '17.644729840837403', '-18.851064805871424',
      '-44.6382324441787', '17.445385985570866', '21.12946554483405', '-3.6719225470772936'),
     ('1.0', '6.242641248542475', '3.971343795334387', '-28.66081804998', '-20.14326346804852',
     '48.560921310873994', '10.826866735546016', '-22.643693341313973', '1.7211476576120028'));
  p3s: array[0..3] of array[0..10] of string =
    (('-0.1311027816799519', '-0.16379404719331705', '0.11703015634199525', '0.38707973897260434', '0.3377855389120359', '0.14286953440815717',
      '0.029015791000532906', '0.0021455899538880526','-6.794655751811263e-7', '2.8522533178221704e-8','-6.81149956853777e-10'),
     ('1.0', '3.4662540724256723', '5.381683457070069', '4.778465929458438', '2.5930192162362027', '0.848854343457902',
      '0.15226433829533179', '0.011059242293464892', '0.0', '0.0', '0.0'),
     ('-6.81149956853777e-10', '2.8522533178221704e-8', '-6.794655751811263e-7', '0.0021455899538880526', '0.029015791000532906', '0.14286953440815717',
      '0.3377855389120359', '0.38707973897260434', '0.11703015634199525', '-0.16379404719331705', '-0.1311027816799519'),
     ('0.0', '0.0', '0.0', '0.011059242293464892', '0.15226433829533179', '0.848854343457902',
      '2.5930192162362027', '4.778465929458438', '5.381683457070069', '3.4662540724256723', '1.0'));
  p4s: array[0..3] of array[0..8] of string =
    (('-0.0350353787183178', '-0.0022242652921344794', '0.018557330651423107', '0.009508047013259196', '0.0018712349281955923',
      '0.00015754461742496055', '0.00000460469890584318', '-2.304047769118826e-10', '2.6633922742578204e-12'),
     ('1.0', '1.3653349817554064', '0.7620591645536234', '0.22009110576413124', '0.03415891436709477',
      '0.00263861676657016', '0.00007646752923027944', '0.0', '0.0'),
     ('2.6633922742578204e-12', '-2.304047769118826e-10', '0.00000460469890584318', '0.00015754461742496055', '0.0018712349281955923',
      '0.009508047013259196', '0.018557330651423107', '-0.0022242652921344794', '-0.0350353787183178'),
     ('0.0', '0.0', '0.00007646752923027944', '0.00263861676657016', '0.03415891436709477',
      '0.22009110576413124', '0.7620591645536234', '1.3653349817554064', '1.0'));
  p5s: array[0..3] of array[0..8] of string =
    (('-0.016743100507663373', '-0.0011295143874558028', '0.001056288621524929', '0.00020938631748758808', '0.000014962478375834237',
      '4.4969678992770644e-7', '4.625961635228786e-9', '-2.811287356288318e-14', '9.905570997331033e-17'),
     ('1.0', '0.5914293448864175', '0.1381518657490833', '0.016074608709367652', '0.0009640118070051656',
      '0.000027533547476472603', '2.82243172016108e-7', '0.0', '0.0'),
     ('9.905570997331033e-17', '-2.811287356288318e-14', '4.625961635228786e-9', '4.4969678992770644e-7', '0.000014962478375834237',
      '0.00020938631748758808', '0.001056288621524929', '-0.0011295143874558028', '-0.016743100507663373'),
     ('0.0', '0.0', '2.82243172016108e-7', '0.000027533547476472603', '0.0009640118070051656',
      '0.016074608709367652', '0.1381518657490833', '0.5914293448864175', '1.0'));

//--------------------------------- 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;
const
  KK = 10000;
var
  LOG2, SQRT2, two, last, s, sm, one, one1, k2, a: bigdecimal;
  k, dpcs : integer;
  kb, ta : biginteger;
begin
  dpcs := bigdecimal.DefaultPrecision;
  result := '0';
  if xi <= result then begin
    application.MessageBox('無効な値です。','注意',0);
    exit;
  end;
  sm := '1e-2';
  last := '5e1';
  one := '1';
  if xi = one then begin
    exit;
  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;
      exit;
    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);
      exit;
    end;
  end;
  if xi > one1 then begin                             // xi > 1e500000
    application.MessageBox('値が大きすぎます。','注意',0);
    exit;
  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;
end;

// 文字配列からBigdecimal値に読み込みます
//  p1 : array [0..1] of array[0..9] of bigdecimal;
//  p2 : array [0..1] of array[0..9] of bigdecimal;
//  p3 : array [0..3] of array[0..10] of bigdecimal;
//  p4 : array [0..3] of array[0..8] of bigdecimal;
//  p5 : array [0..3] of array[0..8] of bigdecimal;
procedure pnread;
var
  i, j : integer;
begin
  for i := 0 to 1 do
    for j := 0 to 9 do  p1[i, j] := p1s[i, j];
  for i := 0 to 1 do
    for j := 0 to 9 do  p2[i, j] := p2s[i, j];
  for i := 0 to 3 do
    for j := 0 to 10 do  p3[i, j] := p3s[i, j];
  for i := 0 to 3 do
    for j := 0 to 8 do  p4[i, j] := p4s[i, j];
  for i := 0 to 3 do
    for j := 0 to 8 do  p5[i, j] := p5s[i, j];
end;

function erfinvjs_function_big(x :  bigdecimal):  bigdecimal;
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, tmp : bigdecimal;
  dpcs : integer;

  function rational_p1q1(x : bigdecimal): bigdecimal;
  var
    s1, s2 : bigdecimal;
    i : integer;
  begin
  if x = '0.0'  then begin
      result := '-0.0005087819496582806';
      exit;
    end;
    s1 := '0.0';
    for i := 9 downto 1 do s1 := x * (p1[0, i] + s1);
    s1 := s1 + p1[0, 0];
    s2 := '0.0';
    for I := 9 downto 1 do s2 := x * (p1[1, i] + s2);
    s2 := s2 + p1[1, 0];
    s1 := s1.RoundToPrecision(dpcs);
    s2 := s2.RoundToPrecision(dpcs);
    result := s1 / s2;
  end;

  function rational_p2q2(x : bigdecimal): bigdecimal;
  var
    s1, s2 : bigdecimal;
    i : integer;
  begin
    if x = '0.0' then begin
      result :=  '-0.20243350835593876';
      exit;
    end;
    s1 := '0.0';
    for i := 8 downto 1 do s1 := x * (p2[0, i] + s1);
    s1 := s1 + p2[0, 0];
    s2 := '0.0';
    for I := 8 downto 1 do s2 := x * (p2[1, i] + s2);
    s2 := s2 + p2[1, 0];
    s1 := s1.RoundToPrecision(dpcs);
    s2 := s2.RoundToPrecision(dpcs);
    result := s1 / s2;
  end;

  function rational_p3q3(x : bigdecimal): bigdecimal;
  var
    ax, ix, s1, s2 : bigdecimal;
    i : integer;
  begin
    if x = '0.0' then begin
      result := '-0.1311027816799519';
      exit;
    end;
    ax := x.Abs(x);
    if ax <= '1.0' then begin
      s1 := '0.0';
      for i := 10 downto 1 do s1 := x * (p3[0, i] + s1);
      s1 := s1 + p3[0, 0];
      s2 := '0.0';
      for I := 10 downto 1 do s2 := x * (p3[1, i] + s2);
      s2 := s2 + p3[1, 0];
    end
    else begin
      ix := '1.0' / x;
      s1 := '0.0';
      for i := 10 downto 1 do s1 := ix * (p3[2, i] + s1);
      s1 := s1 + p3[2, 0];
      s2 := '0.0';
      for I := 10 downto 1 do s2 := ix * (p3[3, i] + s2);
      s2 := s2 + p3[3, 0];
    end;
    s1 := s1.RoundToPrecision(dpcs);
    s2 := s2.RoundToPrecision(dpcs);
    result := s1 / s2;
  end;

  function rational_p4q4(x : bigdecimal): bigdecimal;
  var
    ax, ix, s1, s2 : bigdecimal;
    i : integer;
  begin
    if x = '0.0' then begin
      result := '-0.0350353787183178';
      exit;
    end;
    ax := x.Abs(x);
    if ax <= '1.0' then begin
      s1 := '0.0';
      for i := 8 downto 1 do s1 := x * (p4[0, i] + s1);
      s1 := s1 + p4[0, 0];
      s2 := '0.0';
      for I := 8 downto 1 do s2 := x * (p4[1, i] + s2);
      s2 := s2 + p4[1, 0];
    end
    else begin
      ix := '1.0' / x;
      s1 := '0.0';
      for i := 8 downto 1 do s1 := ix * (p4[2, i] + s1);
      s1 := s1 + p4[2, 0];
      s2 := '0.0';
      for I := 8 downto 1 do s2 := ix * (p4[3, i] + s2);
      s2 := s2 + p4[3, 0];
    end;
    s1 := s1.RoundToPrecision(dpcs);
    s2 := s2.RoundToPrecision(dpcs);
    result := s1 / s2;
  end;

  function rational_p5q5(x : bigdecimal): bigdecimal;
  var
    ax, ix, s1, s2 : bigdecimal;
    i : integer;
  begin
    if x = '0.0' then begin
      result := '-0.016743100507663373';
      exit;
    end;
    ax := x.Abs(x);
    if ax <= '1.0' then begin
      s1 := '0.0';
      for i := 8 downto 1 do s1 := x * (p5[0, i] + s1);
      s1 := s1 + p5[0, 0];
      s2 := '0.0';
      for I := 8 downto 1 do s2 := x * (p5[1, i] + s2);
      s2 := s2 + p5[1, 0];
    end
    // 精度10byte以上でないと使用されない
    else begin
      ix := '1.0' / x;
      s1 := '0.0';
      for i := 8 downto 1 do s1 := ix * (p5[2, i] + s1);
      s1 := s1 + p5[2, 0];
      s2 := '0.0';
      for I := 8 downto 1 do s2 := ix * (p5[3, i] + s2);
      s2 := s2 + p5[3, 0];
    end;
    s1 := s1.RoundToPrecision(dpcs);
    s2 := s2.RoundToPrecision(dpcs);
    result := s1 / s2;
  end;

begin
  if (x < '-1') or (x > '1') then begin
    result := '0.0';
    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 := x.Abs(x);
  dpcs := bigdecimal.DefaultPrecision;
  // |x| <= 0.5
  if ax <= '0.5' then begin                   // 0<|x|<=0.5
    g := ax * (ax + '10.0');
    r := rational_p1q1(ax);                   // ax = 0~0.5
    result := sign * ((g * Y1) + (g * r));
    exit;
  end;
  q := '1.0' - ax;
  // 1-|x| >= 0.25
  if q >= '0.25' then begin                   // 0.5<|x|<=0.75
    g := g.Sqrt('-2.0' * log_big(q));
    q := q - '0.25';
    r := rational_p2q2(q);                    // q = 0~0.25
    tmp := Y2 + r;
    g := g.RoundToPrecision(dpcs);
    tmp := tmp.RoundToPrecision(dpcs);
    result := sign * (g / tmp);
    exit;
  end;
  // |x| > 0.75
  q := q.sqrt(-log_big(q), dpcs);
  // q < 3
  if q < '3.0' then begin                     // 0.75<|x|<0.999876
    qs := q - '1.125';
    r := rational_p3q3(qs);                   // qs =  0.05241~3
    result := sign * ((Y3 * q) + (r * q));
    exit;
  end;
  // 3 =< q < 6
  if q < '6.0' then begin                     // 0.999876<|x|<0.999999999999999768
    qs := q - '3.0';
    r := rational_p4q4(qs);                   // qs = 0~3
    result := sign * ((Y4 * q) + (r * q));
    exit;
  end;
  // 6 =< q < 18                              // 0.999999999999999768<|x|<1
  qs := q - '6.0';                            // qs = 0~10
  r := rational_p5q5(qs);
  result := sign * ((Y5* q) + (r * q));
end;

initialization
  pnread;                 // string -> bigdecimal

end.

erfinvJs.pas

unit erfinvJs;

interface

function erfinvjs_function(x : extended): extended;

implementation

uses system.math, main, system.SysUtils;

var
  p1: array[0..1] of array[0..9] of extended =
    ((-0.0005087819496582806, -0.008368748197417368, 0.03348066254097446, -0.012692614766297404,
      -0.03656379714117627, 0.02198786811111689, 0.008226878746769157, -0.005387729650712429, 0.0, 0.0),
     ( 1.0, -0.9700050433032906, -1.5657455823417585, 1.5622155839842302, 0.662328840472003, -0.7122890234154284,
      -0.05273963823400997, 0.07952836873415717, -0.0023339375937419, 0.0008862163904564247));
  p2: array[0..1] of array[0..8] of extended =
    ((-0.20243350835593876, 0.10526468069939171, 8.3705032834312, 17.644729840837403, -18.851064805871424,
      -44.6382324441787, 17.445385985570866, 21.12946554483405, -3.6719225470772936),
     ( 1.0, 6.242641248542475, 3.971343795334387, -28.66081804998, -20.14326346804852,
       48.560921310873994, 10.826866735546016, -22.643693341313973, 1.7211476576120028));
  p3: array[0..3] of array[0..10] of extended =
    (( -0.1311027816799519, -0.16379404719331705, 0.11703015634199525, 0.38707973897260434, 0.3377855389120359, 0.14286953440815717,
       0.029015791000532906, 0.0021455899538880526, -6.794655751811263e-7, 2.8522533178221704e-8,-6.81149956853777e-10),
     ( 1.0, 3.4662540724256723, 5.381683457070069, 4.778465929458438, 2.5930192162362027, 0.848854343457902,
       0.15226433829533179, 0.011059242293464892, 0.0, 0.0, 0.0),
     (-6.81149956853777e-10, 2.8522533178221704e-8, -6.794655751811263e-7, 0.0021455899538880526, 0.029015791000532906, 0.14286953440815717,
       0.3377855389120359, 0.38707973897260434, 0.11703015634199525, -0.16379404719331705, -0.1311027816799519),
     ( 0.0, 0.0, 0.0, 0.011059242293464892, 0.15226433829533179, 0.848854343457902,
       2.5930192162362027, 4.778465929458438, 5.381683457070069, 3.4662540724256723, 1.0));
  p4: array[0..3] of array[0..8] of extended =
    ((-0.0350353787183178, -0.0022242652921344794, 0.018557330651423107, 0.009508047013259196, 0.0018712349281955923,
       0.00015754461742496055, 0.00000460469890584318, -2.304047769118826e-10, 2.6633922742578204e-12),
     ( 1.0, 1.3653349817554064, 0.7620591645536234, 0.22009110576413124, 0.03415891436709477,
       0.00263861676657016, 0.00007646752923027944, 0.0, 0.0),
     ( 2.6633922742578204e-12, -2.304047769118826e-10, 0.00000460469890584318, 0.00015754461742496055, 0.0018712349281955923,
       0.009508047013259196, 0.018557330651423107, -0.0022242652921344794, -0.0350353787183178),
     ( 0.0, 0.0, 0.00007646752923027944, 0.00263861676657016, 0.03415891436709477,
       0.22009110576413124, 0.7620591645536234, 1.3653349817554064, 1.0));
  p5: array[0..3] of array[0..8] of extended =
    ((-0.016743100507663373, -0.0011295143874558028, 0.001056288621524929, 0.00020938631748758808, 0.000014962478375834237,
       4.4969678992770644e-7, 4.625961635228786e-9, -2.811287356288318e-14, 9.905570997331033e-17),
     ( 1.0, 0.5914293448864175, 0.1381518657490833, 0.016074608709367652, 0.0009640118070051656,
       0.000027533547476472603, 2.82243172016108e-7, 0.0, 0.0),
     ( 9.905570997331033e-17, -2.811287356288318e-14, 4.625961635228786e-9, 4.4969678992770644e-7, 0.000014962478375834237,
       0.00020938631748758808, 0.001056288621524929, -0.0011295143874558028, -0.016743100507663373),
     ( 0.0, 0.0, 2.82243172016108e-7, 0.000027533547476472603, 0.0009640118070051656,
       0.016074608709367652, 0.1381518657490833, 0.5914293448864175, 1.0));

function erfinvjs_function(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;
    i : integer;
  begin
    if x = 0.0  then begin
      result := -0.0005087819496582806;
      exit;
    end;
    s1 := 0;
    for i := 9 downto 1 do s1 := x * (p1[0, i] + s1);
    s1 := s1 + p1[0, 0];
    s2 := 0;
    for I := 9 downto 1 do s2 := x * (p1[1, i] + s2);
    s2 := s2 + p1[1, 0];
    result := s1 / s2;
  end;

  function rational_p2q2(x : extended): extended;
  var
    s1, s2 : extended;
    i : integer;
  begin
    if x = 0.0 then begin
      result :=  -0.20243350835593876;
      exit;
    end;
    s1 := 0;
    for i := 8 downto 1 do s1 := x * (p2[0, i] + s1);
    s1 := s1 + p2[0, 0];
    s2 := 0;
    for I := 8 downto 1 do s2 := x * (p2[1, i] + s2);
    s2 := s2 + p2[1, 0];
    result := s1 / s2;
  end;

  function rational_p3q3(x : extended): extended;
  var
    ax, ix, s1, s2 : extended;
    i : integer;
  begin
    if x = 0.0 then begin
      result := -0.1311027816799519;
      exit;
    end;
    ax := abs(x);
    if ax <= 1.0 then begin
      s1 := 0;
      for i := 10 downto 1 do s1 := x * (p3[0, i] + s1);
      s1 := s1 + p3[0, 0];
      s2 := 0;
      for I := 10 downto 1 do s2 := x * (p3[1, i] + s2);
      s2 := s2 + p3[1, 0];
    end
    else begin
      ix := 1.0 / x;
      s1 := 0;
      for i := 10 downto 1 do s1 := ix * (p3[2, i] + s1);
      s1 := s1 + p3[2, 0];
      s2 := 0;
      for I := 10 downto 1 do s2 := ix * (p3[3, i] + s2);
      s2 := s2 + p3[3, 0];
    end;
    result := s1 / s2;
  end;

  function rational_p4q4(x : extended): extended;
  var
    ax, ix, s1, s2 : extended;
    i : integer;
  begin
    if x = 0.0 then begin
      result := -0.0350353787183178;
      exit;
    end;
    ax := abs(x);
    if ax <= 1.0 then begin
      s1 := 0;
      for i := 8 downto 1 do s1 := x * (p4[0, i] + s1);
      s1 := s1 + p4[0, 0];
      s2 := 0;
      for I := 8 downto 1 do s2 := x * (p4[1, i] + s2);
      s2 := s2 + p4[1, 0];
    end
    else begin
      ix := 1.0 / x;
      s1 := 0;
      for i := 8 downto 1 do s1 := ix * (p4[2, i] + s1);
      s1 := s1 + p4[2, 0];
      s2 := 0;
      for I := 8 downto 1 do s2 := ix * (p4[3, i] + s2);
      s2 := s2 + p4[3, 0];
    end;
    result := s1 / s2;
  end;

  function rational_p5q5(x : extended): extended;
  var
    ax, ix, s1, s2 : extended;
    i : integer;
  begin
    if x = 0.0 then begin
      result := -0.016743100507663373;
      exit;
    end;
    ax := abs(x);
    if ax <= 1.0 then begin
      s1 := 0;
      for i := 8 downto 1 do s1 := x * (p5[0, i] + s1);
      s1 := s1 + p5[0, 0];
      s2 := 0;
      for I := 8 downto 1 do s2 := x * (p5[1, i] + s2);
      s2 := s2 + p5[1, 0];
    end
    // 精度10byte以上でないと使用されない
    else begin
      ix := 1.0 / x;
      s1 := 0;
      for i := 8 downto 1 do s1 := ix * (p5[2, i] + s1);
      s1 := s1 + p5[2, 0];
      s2 := 0;
      for I := 8 downto 1 do s2 := ix * (p5[3, i] + s2);
      s2 := s2 + p5[3, 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                   // 0<|x|<=0.5
    g := ax * (ax + 10.0);
    r := rational_p1q1(ax);                 // ax = 0~0.5
    result := sign * ((g * Y1) + (g * r));
    exit;
  end;
  q := 1.0 - ax;
  // 1-|x| >= 0.25
  if q >= 0.25 then begin                   // 0.5<|x|<=0.75
    g := sqrt(-2.0 * ln(q));
    q := q - 0.25;
    r := rational_p2q2(q);                  // q = 0~0.25
    result := sign * (g / (Y2 + r));
    exit;
  end;
  // |x| > 0.75
  q := sqrt(-ln(q));
  // q < 3
  if q < 3.0 then begin                     // 0.75<|x|<0.999876
    qs := q - 1.125;
    r := rational_p3q3(qs);                 // qs =  0.05241~3
    result := sign * ((Y3 * q) + (r * q));
    exit;
  end;
  // 3 =< q < 6
  if q < 6.0 then begin                     // 0.999876<|x|<0.999999999999999768
    qs := q - 3.0;
    r := rational_p4q4(qs);                 // qs = 0~3
    result := sign * ((Y4 * q) + (r * q));
    exit;
  end;
  // 6 =< q < 18                            // 0.999999999999999768<|x|<1
  qs := q - 6.0;                            // qs = 0~10
  r := rational_p5q5(qs);
  result := sign * ((Y5* q) + (r * q));
end;

end.

erf.pas

unit erf;

interface

function erf_function(x : extended): extended;

implementation

uses system.math;

// 誤差関数
// テーラー展開と連分数
function erf_function(x : extended): extended;
var
  n: integer;
  absx: extended;
  a, c, y, ab : extended;
begin
  absx := abs(x);
  if absx < 2.4 then begin                          // テーラー展開
    c := 1;
    a := 0;
    n := 1;
    repeat
      ab := a;
      a := a + x / (2 * n - 1) * c;
      c := -c * x * x / n;
      inc(n);
    until ab = a;
    result :=  a * 2 / sqrt(pi);
    exit;
  end;
  y := absx * 2;                                    // 連分数
  a := 0;
  for n := 40 downto 1 do a := n * 2 / (y + a);
  a := 1 - exp(-x * x) / (y + a) * 2 / sqrt(pi);
  if x < 0 then result := -a
           else result := a;
end;

end.

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;

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses system.math, system.UITypes, erfinvJs, erf, Big_erfinvJs, Velthuis.BigIntegers, Velthuis.Bigdecimals;

procedure TForm1.erfBtnClick(Sender: TObject);
var
  z, ans : extended;
  i : integer;
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    L : integer;
  {$ENDIF CPUX86}
begin
  val(zedit.Text, z, i);
  if i <> 0 then begin
    application.MessageBox('yの値に間違いがあります。','注意',0);
    exit;
  end;
  memo1.Clear;
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    L := length(zedit.Text);
    if L > 30 then z := strtofloat(Formatfloat('#0.########################', z));  // 0.9999999999999999 =>'1'-> 1
    memo1.Lines.Append('x86 mode');
  {$ENDIF CPUX86}
  {$IFDEF CPUX64}                                 // プラットフォーム32ビット
    z := strTofloat(floattostr(z));
    memo1.Lines.Append('x64 mode');
  {$ENDIF CPUX64}
  ans := erf_function(z);
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    memo1.Lines.Append('Extended 演算');
  {$ENDIF CPUX86}
  {$IFDEF CPUX64}                                 // プラットフォーム32ビット
    memo1.Lines.Append('double 演算');
  {$ENDIF CPUX64}
  memo1.Lines.Append(' erf= ' + Formatfloat('#0.########################', ans));
end;

procedure TForm1.erfinvBtnClick(Sender: TObject);
var
  x, ans: extended;
  xb, ansb : bigdecimal;
  i : integer;
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    L : integer;
  {$ENDIF CPUX86}
begin
  val(yedit.Text, x, i);
  if i <> 0 then begin
    application.MessageBox('yの値に間違いがあります。','注意',0);
    exit;
  end;

  try
    xb := yedit.Text;
  except
    on EConverterror do begin
      MessageDlg('x の値に間違いがあります。', mtError, [mbOK], 0);
      exit;
    end;
  end;
  memo1.Clear;
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    L := length(yedit.Text);
    if L > 30 then x := strtofloat(Formatfloat('#0.########################', x));  // 0.9999999999999999 =>'1'-> 1
    memo1.Lines.Append('x86 mode');
  {$ENDIF CPUX86}
  {$IFDEF CPUX64}                                 // プラットフォーム32ビット
    x := strTofloat(floattostr(x));
    memo1.Lines.Append('x64 mode');
  {$ENDIF CPUX64}

  memo1.Lines.Append('inverse error function');
  ans := erfinvjs_function(x);
  {$IFDEF CPUX86}                                 // プラットフォーム32ビット
    memo1.Lines.Append('Extended js 演算');
  {$ENDIF CPUX86}
  {$IFDEF CPUX64}                                 // プラットフォーム32ビット
    memo1.Lines.Append('double js 演算');
  {$ENDIF CPUX64}
//  memo1.Lines.Append(' erfinv= ' + floattostr(ans));
  memo1.Lines.Append(' erfinv= ' + Formatfloat('#0.########################', ans));

  memo1.Lines.Append('');

  memo1.Lines.Append('多倍長演算 js 18桁表示');
  if (xb = '1.0') or (xb = '-1.0') then begin
    if xb >= '0.0' then memo1.Lines.Append(' erfinv=  INFINITY')
                   else memo1.Lines.Append(' erfinv= -INFINITY')
  end
  else begin
    ansb := erfinvjs_function_big(xb);
    ansb := ansb.RoundToPrecision(18);
    if ansb = '0.0' then
      memo1.Lines.Append(' erfinv= 0.0')
    else
      memo1.Lines.Append(' erfinv= ' + ansb.ToString);
  end;
end;

end.


download erfinv_function_Js.zip

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