BigDecimalによるSin(x)の計算
BigDecimalによるπの計算プログラムを作成したので、Sin(x)の計算についても検討してみました。
sin(x)の中で使用されるπの値は、BigDecimalによるπの計算にあるBigDecimalのπ計算ルーチンを利用します。
Double程度の有効桁数であれば、固定値で良いのですが、有効桁数が非常に大きい場合を考慮して、計算により求めます。
問題無く計算出来れば、BigDecimal用の各種関数サブルーチン作成に使用します。
BigDecimalの組み込みについては、ベルヌーイ数 その4を参照してください。
マクローリン展開によるsin(x)の計算です。
角度の範囲は、±90°になっているので、360°の角度で計算する場合は、別途角度変換が必要です。
計算は、Double、多倍長、BigDecimal、多倍長sin->BigDecimal変換でプログラムを作成しました。
計算の精度指定桁数はDoubleを除き10000桁までです。
通常の計算では、そこまで必要はないかもしれません。
プログラム
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, Vcl.Buttons; type TForm1 = class(TForm) BitBtn1: TBitBtn; LabeledEdit1: TLabeledEdit; Memo1: TMemo; BitBtn2: TBitBtn; BitBtn3: TBitBtn; BitBtn4: TBitBtn; BitBtn5: TBitBtn; LabeledEdit2: TLabeledEdit; CheckBox1: TCheckBox; procedure BitBtn1Click(Sender: TObject); procedure BitBtn2Click(Sender: TObject); procedure BitBtn3Click(Sender: TObject); procedure BitBtn4Click(Sender: TObject); procedure BitBtn5Click(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private 宣言 } function inputcheck(DF: Boolean): boolean; public { Public 宣言 } end; var Form1: TForm1; implementation {$R *.dfm} uses mp_real, mp_types, mp_base, Velthuis.BigDecimals; var DRD : integer; // 有効桁数 dsr : string; // πの値string const DEG = '180'; // 180° // πの計算 ガウス=ルジャンドルのアルゴリズム // pcs πの精度 // np true ループ数 表示 function pi_big(pcs: integer; np: boolean): Bigdecimal; var a, b, t, x, an, two, one, four, EPSILON : BigDecimal; n, dpcs, pcsBack : integer; rmback : BigDecimal.RoundingMode; begin pcsBack := BigDecimal.DefaultPrecision; // 除算演算精度バックアップ rmBack := BigDecimal.DefaultRoundingMode; // 除算丸めモードバックアップ dpcs := pcs + 10; // 指定精度 + α BigDecimal.DefaultPrecision := dpcs; BigDecimal.DefaultRoundingMode := rmNearesteven; one := BigDecimal.One; two := BigDecimal.Two; EPSILON := one / BigDecimal.IntPower(two, pcs shl 1); // 収束判定値 one := one.RoundToPrecision(dpcs); // oneの有効桁数をdpcsに設定 two := two.RoundToPrecision(dpcs); // twoの有効桁数をdpcsに設定 four := two + two; a := one; b := one / BigDecimal.Sqrt(two); t := one / four; // 除算精度 x := '1'; n := 0; repeat an := (a + b) / two; // 平均値 除算有効桁数での丸め b := a * b; b := b.RoundToPrecision(dpcs); // pcs + α 丸め b := BigDecimal.Sqrt(b); t := t - x * (an - a) * (an - a); t := t.RoundToPrecision(dpcs); // pcs + α 丸め x := x + x; a := an; inc(n); until (BigDecimal.abs(a - b) < EPSILON) or (n > 100); // n>100はデバッグ用 a := a + b; result := (a * a) / (four * t); // 分子と分母の有効桁数が同じ // result := result.RoundToPrecision(pcs); // 指定の精度に丸め BigDecimal.DefaultPrecision := pcsBack; // 除算演算精度復帰 BigDecimal.DefaultRoundingMode := rmBack; // 除算丸めモード復帰 if np then Form1.Memo1.Lines.Append('πの計算 ガウス=ルジャンドル Big n = ' + intTostr(n)); end; // 入力チェック // 有効桁数の読み込みセット DRD // πの値をmp_float又はBigDecimalからString変換 dsr。 // DF double フラグ function TForm1.inputcheck(DF: Boolean): boolean; var x : double; c : integer; paif : mp_float; paib : BigDecimal; begin result := false; val(labelededit1.Text, x, c); if c <> 0 then begin memo1.Text := 'xの値に間違いがあります。'; exit; end; if abs(x) > 90 then begin memo1.Text := 'xの値は90以下にして下さい。'; exit; end; val(labelededit2.Text, DRD, c); if c <> 0 then begin memo1.Text := 'xの値に間違いがあります。'; exit; end; if (DRD < 30) or (DRD > 1000) then begin memo1.Text := '精度桁数の値は30以上 又は 1000迄にして下さい。'; exit; end; if not DF then if checkbox1.Checked = False then begin // πの値計算選択 paib := pi_big(DRD, true); // 有効桁数 (DRD + 10) dsr := paib.ToPlainString; end else begin mpf_set_default_decprec(DRD + 10); // 有効桁数 DRD + 10に設定 mpf_init(paif); mpf_set_pi(paif); // πの値 dsr := string(mpf_adecimal_alt(paif, DRD + 10)); // 有効桁数 DRD + 10 String変換 mpf_clear(paif); Form1.Memo1.Lines.Append('πの値 多倍長'); end; result := true; end; // sin(x)計算 double // マクローリン展開 // delphiには標準でsin(x)が組み込まれています参考用です。 function sindouble(xi: double): double; var x, v, d, sq, u : double; k : integer; begin x := abs(xi); d := x; sq := x * x; u := d; k := 0; repeat k := k + 2; v := u; d := -d * sq / (k * (k + 1)); u := v + d; until (v = u) or (k > 100); if xi < 0 then v := -v; result := v; Form1.memo1.Lines.Append('Double sin 変換ループ n=' + intTostr(k)); // Loop表示 end; procedure TForm1.BitBtn1Click(Sender: TObject); var x : double; begin if not inputcheck(True) then exit;; x := strTofloat(labelededit1.Text); x := x / 180 * pi; x := sindouble(x); memo1.Lines.Append(floatTostr(x)); memo1.Lines.Append(''); end; // sin(x)計算 多倍長 // マクローリン展開 // mp_arith には、sin関数がありここのプログラムは参考用です。 procedure sinmpf(var xi, v : mp_float); var tmp0, tmp1 : mp_float; x, K: mp_float; d, sq, u, one, two, vd : mp_float; c : integer; begin mpf_init2(tmp0, tmp1); mpf_init3(x, K, d); mpf_init5(sq, u, one, two, vd); mpf_abs(xi, x); // 絶対値 mpf_set1(one); // 1 mpf_add(one, one, two); // 2 mpf_copy(x, d); // x = d mpf_sqr(x, sq); // sq = x * x mpf_copy(x, u); // x = u mpf_set0(k); // k = 0 c := 0; repeat // repeat mpf_add(k, two, k); // k = k + 2 mpf_copy(u, v); // u = v mpf_add(k, one, tmp0); // tmp0 = k + 1 mpf_mul(tmp0, k, tmp1); // tmp1 = k(k + 1) mpf_div(sq, tmp1, tmp0); // tmp0 = sq / k(k + 1) mpf_chs(d, d); // d = -d mpf_mul(d, tmp0, d); // d = d * tmp0 d = -d * sq / k(k + 1) mpf_add(v, d, u); // u := v + d inc(c); // c = c + 1 until mpf_is_eq(v, u) or (c > 500); // until (v = u) or (c > 200) if not mp_real.s_mpf_is_ge0(xi) then mpf_chs(v, v); // 符号設定 Form1.memo1.Lines.Append('Mp_float sin 変換ループ n=' + intTostr(c)); // Loop表示 mpf_clear2(tmp0, tmp1); mpf_clear3(x, K, d); mpf_clear5(sq, u, one, two, vd); end; procedure TForm1.BitBtn2Click(Sender: TObject); var x, v, pai : mp_float; begin if not inputcheck(False) then exit;; mpf_set_default_decprec(DRD + 10); // 精度DRD + 10桁に設定 mpf_init3(x, v, pai); if checkbox1.Checked = False then mpf_read_decimal(pai, pansichar(ansistring(dsr))) // π BigDecimal計算値 else mpf_set_pi(pai); // π mpf_read_decimal(x, pansichar(ansistring(Form1.LabeledEdit1.Text))); // 入力 mpf_div_int(x, 180, x); // Rad変換 x = x / 180 mpf_mul(x, pai, x); // x = x * pi sinmpf(x, v); // sin(x) memo1.Lines.Append(string(mpf_adecimal_alt(v, DRD))); // DRD桁表示 memo1.Lines.Append(''); mpf_clear3(x, v, pai); end; // sin(x)計算 BigDecimal // マクローリン展開 // BigDecimalは、 // 除算時、有効桁数がDefaultより大きくて、分子の有効桁数が分母の有効桁数より大きい場合 // 除算に先だって分子に小数部がある時はその小数点以下桁数を、RoundToで小さくするか、 // Defaultの有効桁数を大きくする必要があります、多少は余裕があるようですが、注意が必要です。 // 整数部の桁数が有効桁数より大きい場合、有効桁数以外には"0"がはいります。 // 演算での有効桁数丸めは除算でしか発生しません。 function sinBig(xi : BigDecimal): BigDecimal; var x, v, d, sq, u, k : BigDecimal; c: integer; begin x := BigDecimal.Abs(xi); // 絶対値 d := x; sq := x * x; // sqは小数点以下 (DRD + 10) * 2 桁 u := d; k := 0; c := 0; repeat k := k + 2; v := u; d := -d * sq; // 小数点以下(DRD + 10)×2 桁 d := d.RoundToPrecision(DRD + 10); // 有効桁数DRD + 10桁に丸めないと次の除算不可分母は小数点以下無し整数 d := d / (k * (k + 1)); // 除算で有効桁数DRD + 10桁 除算Default値 u := v + d; u := u.RoundToPrecision(DRD + 10); // 有効桁数DRD + 10桁に丸めないと次の除算不可分母は小数点以下無し整数 inc(c); until (v = u) or (c > 500); if xi < 0 then v := -v; // 符号設定 result := v; Form1.memo1.Lines.Append('BigDecimal sin 変換ループ n=' + intTostr(c)); // Loop表示 end; procedure TForm1.BitBtn3Click(Sender: TObject); var x , v : BigDecimal; pai, d180 : BigDecimal; begin if not inputcheck(False) then exit;; BigDecimal.DefaultPrecision := DRD + 10; // 除算でのデフォルトの有効桁数 整数部が有効桁数を超える場合有効桁数以外は"0" BigDecimal.DefaultRoundingMode := rmNearestDown; // デフォルトの丸め指定 if checkbox1.Checked = False then // πの値設定 pai := pi_big(DRD + 10, False) // πの計算 ガウス=ルジャンドルのアルゴリズム else pai := dsr; // πの値設定 pai := dsr 多倍長->big x := labelededit1.Text; // 角度入力 誤差を防ぐ為string直接変換 x := x * pai; // radに変換 xは小数点以下100 + x 小数点以下桁数入力値次第 x := x.RoundTo(-DRD - 2); // xを小数点以下DRD + 2桁に丸めないと次の除算出来ない時があります d180 := DEG; x := x / d180; // xは有効桁数DRD + 10桁 除算Default値 x < pi / 2 v := sinBig(x); // sin(x) v := v.RoundTo(-DRD + 1); // 小数点以下DRD-1桁表示DRD桁で丸め |V| <=1 v := BigDecimal(v).RemoveTrailingZeros(0); // 末尾のゼロ消去 memo1.Lines.Append(v.ToPlainString); // 値表示 memo1.Lines.Append(''); end; // 多倍長浮動小数点の利用によるsin(x)の計算 // mp_floatとBigDecimalの値はStringで変換 function sinmpg(xi : BigDecimal): BigDecimal; var x, v : mp_float; begin mpf_set_default_decprec(DRD + 10); // mp_float 精度DRD + 10桁に設定 mpf_init2(x, v); mpf_read_decimal(x, pansichar(ansistring(xi.ToPlainString))); // BigDecimal -> mp_float mpf_sin(x, v); // 多倍長sin(x) result := string(mpf_adecimal_alt(v, DRD)); // mp_float -> BigDecimal Form1.memo1.Lines.Append('多倍長->BIG sin 変換ループ無し'); // Loop表示 mpf_clear2(x, v); end; procedure TForm1.BitBtn4Click(Sender: TObject); var x , v : BigDecimal; pai, d180 : BigDecimal; begin if not inputcheck(False) then exit;; BigDecimal.DefaultPrecision := DRD + 10; // 除算でのデフォルトの有効桁数 有効桁数を超える場合は先頭からの桁数 BigDecimal.DefaultRoundingMode := rmNearesteven; // デフォルトの丸め指定 pai := dsr; // πの値設定 x := labelededit1.Text; // 角度入力 誤差を防ぐ為string直接変換 x := x * pai; // radに変換 小数点以下桁数入力値次第 x := x.RoundToPrecision(DRD + 10); // xを有効桁数DRD + 10桁に丸めないと次の除算出来ない時があります d180 := DEG; d180 := d180.RoundToPrecision(DRD + 10); x := x / d180; // xは有効桁数DRD + 10桁 除算Default値 x < pi / 2 v := sinmpg(x); // sin(x) v := v.RoundTo(-DRD + 1); // 小数点以下DRD桁で丸め v := BigDecimal(v).RemoveTrailingZeros(0); // 末尾のゼロ消去 memo1.Lines.Append(v.ToPlainString); // 値表示 memo1.Lines.Append(''); end; procedure TForm1.BitBtn5Click(Sender: TObject); begin memo1.Clear; end; procedure TForm1.FormCreate(Sender: TObject); begin memo1.Width := 820; // 一行100桁表示に設定 memo1.Font.Size := 11; // 一行100桁表示にフォントサイズ設定 end; end.
sin_x.zip
三角関数、逆三角関数、その他関数 に戻る