FreeBASIC マニュアルのトップに戻る

FreeBASIC MPFR ライブラリと使用例(対数・平方根・除算・ガンマ関数(ランチョス近似))

目次→フォーラム→FreeBASIC→補足gmp 6.2.0 and mpfr 4.0.2←オリジナル・フォーラム

MPFR ライブラリと使用例(対数・平方根・除算・ガンマ関数(ランチョス近似)) 左にメニュー・フレームが表示されていない場合は、ここをクリックして下さい

←リンク元に戻る プログラム開発関連に戻る

このページは、srvaldez さんが、フォーラム Programming→Generalgmp 6.2.0 and mpfr 4.0.2 gmp 6.1.2 and mpfr 3.1.5 で公開している MPFR ヘッダー・ファイルに同梱されいてるサンプル・プログラムを、日本語化したものです。

MPFR (GNU Multiple Precision Floating-Point Reliably:高精度浮動小数点) は、GMP に基いた、任意精度浮動小数点(多倍長浮動小数点)演算ライブラリです。
C99 に含まれるすべての数学関数を多倍長でサポートしています。
(底が $ e$ , $ 2$ , $ 10$ の対数関数と指数関数と log1p() ( $ =\log(1+x)$ ) と expm1() ( $ =\exp(x)-1$ ), 三角関数、逆三角函数、双曲線函数、逆双曲線関数、 ガンマ関数、ゼータ関数、誤差関数 (?erfc())、 算術幾何平均、冪乗 pow() ($ =x^y$ )。

【各種計算のための関数】 (一部の代表的なもの)
http://www.k-techlabo.org/blog2/?p=401

関数の詳細は下記PDFを参照下さい。
http://kako.ics.nara-wu.ac.jp/~kako/student/2010/Ushimi.pdf

GMP(任意精度算術演算ライブラリ)と MPFR の関係は、下記サイトを参照下さい。
http://etc2day-linux.blogspot.jp/2014/06/mpfr.html
GMP←MPFR という依存関係があります。

mpfr は、次の 7(6)つの「まるめモード」をサポートしています。
1. MPFR_RNDN: 最近接偶数へのまるめ(RN, round to nearest / roundTiesToEven in IEEE 754-2008)
最近接偶数へのまるめは、端数が0.5 より小さいなら切り捨て、端数が0.5 より大きいならは切り上げ、端数がちょうど0.5 なら切り捨てと切り上げのうち結果が偶数になる方へ丸める方法。
2. MPFR_RNDZ: ゼロ方向へのまるめ(RZ, round towards zero / roundTowardZero in IEEE 754-2008)
3. MPFR_RNDU: 正の無限大方向へのまるめ(RP, round towards plus in ̄nity / roundTowardPositive in IEEE 754-2008)
4. MPFR_RNDD: 負の無限大方向へのまるめ(RM, round towards minus in ̄nity / roundTowardNegative in IEEE 754-2008)
5. MPFR_RNDA: 小数点以下切り上げ(round away from zero)
6. MPFR_RNDF: 忠実な丸め(まだ実装されていない)faithful rounding (not implemented yet)
忠実な丸めとは、誤差を含まない正確な計算結果を隣り合うどちらかの浮動小数点数に丸めたもの。
7. MPFR_RNDNA 最も近い値への丸め(0から遠いほうへ) round to nearest, with ties away from zero (mpfr_round)
2つの値の中間の値の場合は、正の値ならより大きいほう、負の値ならより小さいほうの値を採用。


環境準備:
mpfr-4.0.2.7z mpfr-3.1.5.zip ファイルをダウンロードして、共有ライブラリ libmpfr.a を該当フォルダに保存します。
私の場合は、C:\Tool\FreeBASIC\lib\win32\libmpfr.a

対数・平方根・除算

以下を、例えば mpfr-fb testJP.bas という名前で保存します。
同じフォルダに、mpfr.bi と mpfr-fb.bi を保存したうえで、コンパイル実行します。
'srvaldez ≫ 2015-11-08 22:32
''=============================================================================
#include once "gmp.bi"
#include once "mpfr.bi"

'多重定義する演算子を含める前に mpfr_digits を設定する必要があります
dim shared as long mpfr_digits_precision = 50 '<- mpfr digits-precision を設定
dim shared as mpfr_rnd_t mpfr_rounding_mode = MPFR_RNDN '<- まるめモードを設定
' まるめモードの選択肢は
'  MPFR_RNDN    最近接偶数へのまるめ
'  MPFR_RNDZ    ゼロ方向へのまるめ
'  MPFR_RNDU    正の無限大方向へのまるめ
'  MPFR_RNDD    負の無限大方向へのまるめ
'  MPFR_RNDA    小数点以下切り上げ
'  MPFR_RNDF    忠実な丸め(まだ実装されていない)
'  誤差を含まない正確な計算結果を隣り合うどちらかの浮動小数点数に丸めたもの)
'  MPFR_RNDNA   最も近い値への丸め(0から遠いほうへ)(mpfr_round)
'  2つの値の中間の値の場合は、正の値ならより大きいほう、負の値ならより小さいほうの値を採用。

'#include once "C:\dev\FreeBASIC-1.04.0-win64\examples\mpfr-fb2.bas" 'mpfr overloaded operators
#include once "mpfr-fb.bi" 'mpfr 多重定義演算子
''=============================================================================

Dim As mpfr x, y, z
Dim As Long i

For x=2 To 10
    y=Log10(x)
    Print Exp10(y), y.toString("%25.20f")  '書式指定 <全体の幅 25>.<小数点以下の幅 20> 実数を出力
Next

Print "============================================="
x=1
For i=1 To 10
	y=Sqr(mpfr(i))
	z=y*y
    Print z.toLong, y.toString("%25.20f")
Next

Print
Print mpfr(7)/9
Print

Print "何かキーを押すと終了 ";
Sleep

参考:printf の書式指定(詳細)
http://seiai.ed.jp/sys/text/csb/chs05/c05b050.html

ガンマ関数(ランチョス近似)

これは ニューメリカル・レシピ に掲載されている、ランチョス ガンンマ関数近似のサンプルです。
以下を、例えば make-lanczosJP.bas という名前で保存します。
そして、同じフォルダに、mpfr.bi と mpfr-fb.bi を保存したうえで、コンパイル実行します。

 参考:複素ガンマ関数の数値計算: http://slpr.sakura.ne.jp/qp/gamma-function/

''=============================================================================
#Include Once "gmp.bi"
#Include Once "mpfr.bi"

'多重定義の演算子を含める前に mpfr_digits を設定する必要があります
Dim Shared As Long mpfr_digits_precision = 50 '<- mpfr digits-precision を設定
Dim Shared As mpfr_rnd_t mpfr_rounding_mode = MPFR_RNDN '<- まるめモードを設定
' まるめモードの選択肢は
'  MPFR_RNDN    最近接偶数へのまるめ
'  MPFR_RNDZ    ゼロ方向へのまるめ
'  MPFR_RNDU    正の無限大方向へのまるめ
'  MPFR_RNDD    負の無限大方向へのまるめ
'  MPFR_RNDA    小数点以下切り上げ
'  MPFR_RNDF    忠実な丸め(まだ実装されていない)
'  誤差を含まない正確な計算結果を隣り合うどちらかの浮動小数点数に丸めたもの)
'  MPFR_RNDNA   最も近い値への丸め(0から遠いほうへ)(mpfr_round)
'  2つの値の中間の値の場合は、正の値ならより大きいほう、負の値ならより小さいほうの値を採用。

'#include once "C:\dev\FreeBASIC-1.04.0-win64\examples\mpfr-fb2.bas" 'mpfr overloaded operators
#Include Once "mpfr-fb.bi" 'mpfr 多重定義演算子
''=============================================================================


Declare Sub gauss_jordan ( a() As mpfr, y() As mpfr, coef() As mpfr, ByVal ncol As Long, ByRef error_code As Long)
Declare Sub make_lanczos(x() As mpfr, ByVal g As String, ByRef ercode As Long)
Declare Sub gamma_lanczos( ByVal n As Integer = 6, ByRef g As String = "5", ByVal digits_prec As Long=20)
Dim Shared As mpfr e, pi, zero, one, half

Print "これは ニューメリカル・レシピ に掲載されている"
Print "ランチョス ガンンマ関数近似のサンプルです"
Print

'ガンマ関数(ランチョス近似)
gamma_lanczos(6, "5", 20)
'print "=================================="
'gamma_lanczos(14, "4.7421875",20)
gamma_lanczos(13, "6.024680040776729583740234375", 20)

Print "何かキーを押すと終了 ";
Sleep
End

'===============================================================================
Sub gamma_lanczos( ByVal n As Integer = 6, ByRef g As String = "5", ByVal digits_prec As Long=20)

Dim As Integer i, plus_prec=4*n+digits_prec
Dim As Long error_code
Dim As mpfr x(n)
Dim As String frmt, str_out

zero=0
one=1
half=".5"
e=Exp(one)
pi=pi_const()

make_lanczos(x(), g, error_code)

If Not error_code Then
	'open "lanczos_gamma_"+str(n)+".txt" for output as 1
	For i=0 To n
		frmt="%"+Str(digits_prec+5)+"."+Str(digits_prec)+"g"
		str_out=x(i).toString(frmt)
		frmt=Str(i)
		If i<10 Then frmt=" "+frmt
		str_out="C("+frmt+") = "+str_out
		Print str_out
		'Print #1, str_out
	Next
	'close 1
	Print
End If

End Sub

Sub make_lanczos(x() As mpfr, ByVal g_ As String, ByRef error_code As Long)
	
	Dim As Long i, j
	Dim As Integer sp
	Dim As Integer n = UBound(x)
	Dim a(n, n) As mpfr 
	Dim b(n) As mpfr
	Dim As mpfr g, sqr2pi, pi2, fac
	
	pi2=pi*2
	sqr2pi=Sqr( pi2)
	g=g_
	
	'行列を設定します。n = 6 の場合、行列は次のようになります
	' [[ 1,	1,	  1/2, 1/3, 1/4,  1/5,  1/6  ] 
	' [  1,	1/2, 1/3, 1/4, 1/5,  1/6,  1/7  ] 
	' [  1,	1/3, 1/4, 1/5, 1/6,  1/7,  1/8  ] 
	' [  1,	1/4, 1/5, 1/6, 1/7,  1/8,  1/9  ] 
	' [  1.	1/5, 1/6, 1/7, 1/8,  1/9,  1/10 ] 
	' [  1,	1/6, 1/7, 1/8, 1/9,  1/10, 1/11 ]
	' [  1,	1/7, 1/8, 1/9, 1/10, 1/11, 1/12 ]] 

	For i=0 To n
	  a(i,0)=1
	  For j=1 To n
		a(i,j)=one/(i+j)
	  Next
	Next

	'定数項を設定します。n = 6、g = 5 の場合、
	'b( 0) = 41.62443691643906820752
	'b( 1) = 16.01231640525168135809
	'b( 2) =  9.36473553710404851170
	'b( 3) =  6.57049625931606149987
	'b( 4) =  5.09521672929646781312
	'b( 5) =  4.20380175313001102196
	'b( 6) =  3.61487381446333490266
	
	fac=1
	
	For i=0 To n
		If i>1 Then
			fac=fac*i
		End If
		b(i)=Exp(i+half+g)/(i+half+g)^(i+half)*fac/sqr2pi
		'print b(i).toString("%25.20f")   '書式指定 <全体の幅 25>.<小数点以下の幅 20> 実数を出力
	Next

	'ここで線形方程式の系を解きます。n = 6、g = 5 の場合
	' [[ 1,	1,	  1/2, 1/3, 1/4,  1/5,  1/6  ]	= 41.62443691643906820752
	' [  1,	1/2, 1/3, 1/4, 1/5,  1/6,  1/7  ]	= 16.01231640525168135809
	' [  1,	1/3, 1/4, 1/5, 1/6,  1/7,  1/8  ]	=  9.36473553710404851170
	' [  1,	1/4, 1/5, 1/6, 1/7,  1/8,  1/9  ]	=  6.57049625931606149987
	' [  1.	1/5, 1/6, 1/7, 1/8,  1/9,  1/10 ]	=  5.09521672929646781312 
	' [  1,	1/6, 1/7, 1/8, 1/9,  1/10, 1/11 ]	=  4.20380175313001102196
	' [  1,	1/7, 1/8, 1/9, 1/10, 1/11, 1/12 ]]	=  3.61487381446333490266
	
	'上記の例の解は
	'C(0) =	  1.00000000019001482399
	'C(1) =	 76.18009172947146348309
	'C(2) =	-86.50532032941676765250
	'C(3) =	 24.01409824083091049018
	'C(4) =	 -1.23173957245015538752
	'C(5) =	  0.00120865097386617851
	'C(6) =	 -0.00000539523938495313
	
	'これは ニューメリカル・レシピ に掲載されている
	'ランチョス ガンンマ関数近似です

	gauss_jordan(a(), b(), x(), n, error_code)
End Sub

Sub gauss_jordan ( a() As mpfr, y() As mpfr, coef() As mpfr, ByVal ncol As Long, ByRef error_code As Long)

	' ガウスの消去法(Gaussian elimination)による行列解

	Dim As mpfr b(ncol, ncol), w(ncol)				' work array, ncol long

	Dim As Long i,j,i1,k,l,n
	Dim As mpfr hold, sm, t, ab, big
'	Const TRUE = -1
'	Const FALSE = Not TRUE

	error_code=FALSE
	n=ncol
	
	For i=0 To n
		' copy to work arrays
		For j=0 To n
			b(i,j)=a(i,j)
		Next j
		w(i)=y(i)
		coef(i)=zero
	Next
	For i=0 To n-1
		big=Abs(b(i,i))
		l=i
		i1=i+1
		For j=i1 To n
			' search for largest element	
			ab=Abs(b(j,i))
			If ab>big Then
				big=ab
				l=j
			End If
		Next
		If big=zero Then
			error_code= TRUE
		Else
			If l<>i Then
				' 対角線上で最大の要素を入れる行を交換する
				For j=0 To n
					hold=b(l,j)
					b(l,j)=b(i,j)
					b(i,j)=hold
				Next
				hold=w(l)
				w(l)=w(i)
				w(i)=hold
			End If
			For j=i1 To n
				t=b(j,i)/b(i,i)
				For k=i1 To n
					b(j,k)=b(j,k)-t*b(i,k)
				Next
				w(j)=w(j)-t*w(i)
			Next	' j-loop
		End If	' if big - else
	Next			' i-loop
	If b(n,n)=zero Then
		error_code=TRUE
	Else
		coef(n)=w(n)/b(n,n)
		i=n-1
		' back substitution
		Do
			Sm=zero
			For j=i To n
				sm=sm+b(i,j)*coef(j)
			Next
			coef(i)=(w(i)-sm)/b(i,i)
			i=i-1
		Loop Until i<0
	End If
	If error_code Then Print "ERROR: Matrix is singular" 
End Sub
 
補足 に戻る
←リンク元に戻る プログラム開発関連に戻る
ページ歴史:2017-05-08
日本語翻訳:WATANABE Makoto、原文著作者:srvaldez , 2017-05-07 09:44

ホームページのトップに戻る

表示-非営利-継承