|
GMP互換のmpirライブラリー、mpfrライブラリーを使用して多倍長計算を行います。
https://gmplib.org
http://mpir.org
https://www.mpfr.org
mpirには整数型、浮動小数型、有理数型がありますが四則計算しかサポートされていません。
そこでmpirを使って関数を使えるようにしたのがmpfrです。
初等関数等がサポートされています。
mpfrを使って複素数を使えるようにしたのがmpcです。
自前で定義するより高速に計算できます。
mpfr及びmpcはC++インターフェイスがありません(Cインターフェイスのみ)のでラッパーにboostライブラリーを使用しています。
(※C++インターフェイスは関数定義でCインターフェイスは副プログラム定義のようなもの)
なお、これらのDLLは自前でライブラリーをビルドしたものではなく、ネットよりダウンロードして入手したものです。
予めご了承ください。
https://www.dll4free.com
1000桁モードで使用できるように関数として定義する場合と
2進モードや10進モードで使用する副プログラムとして定義する2通りがあります。
1000桁モードで使用する場合は下記のように関数として定義します。
OPTION ARITHMETIC DECIMAL_HIGH
INPUT X
PRINT LEXP(X)
END
EXTERNAL FUNCTION LEXP(X)
OPTION ARITHMETIC DECIMAL_HIGH
OPTION CHARACTER BYTE
LET KETA=1000
LET X$=STR$(X)
LET B$=REPEAT$(CHR$(32),KETA+100)
CALL EXP1000(KETA,X$,B$)
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET LEXP=VAL(B$(1:I))
SUB EXP1000(KETA,X$,Y$)
IF POS(X$,"/")>0 OR POS(X$,"(")>0 OR X$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\exp1000.dll","exp1000"
END SUB
END FUNCTION
1000桁モードではほぼ数式通りの記述ができます。
LET N=LEXP(-X)*X
副プログラムとして定義する場合は
OPTION CHARACTER BYTE
LET X$="1.04719755119659774615421446109316762806572313312503527365831486410260546876206966620934494178070568932738269550442743554903128153651686074390845313604282703915009470090064617370185321487431631831012732147627032522197781537615854941126226105509040063638188285564115344953681810888273779786908674971375790819566886877186272496050697365427641803057178812263086345333711017684960682217379471565064717053647768575678858653065103072870579397753726436837284935815412665424985578396191757496374264606100398304327789112081355221436200713164879840824573023405995364790092351307239209772558412822493948922313504400018937571508785360926192378091926320305787905957382281363374165114338218319512368359742656308630784733998537070967398695467813938660454325825710332017290240378333333279099268331701991057760536543953167481981844896943421417410275111489501175397706272367000104594625096219584440279380687239255638243453275116347625182291038652095462745126253125065259395259351072374226887100064262553706530307214006633333333333333"
CALL LSIN(1000,X$,RESULT$)
PRINT RESULT$
END
EXTERNAL SUB LSIN(KETA,X$,RESULT$)
OPTION CHARACTER BYTE
LET B$=REPEAT$(CHR$(32),KETA+100)
CALL SIN1000(KETA,X$,B$)
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET RESULT$=B$(1:I)
SUB SIN1000(KETA,X$,Y$)
IF POS(X$,"/")>0 OR POS(X$,"(")>0 OR X$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\sin1000.dll","sin1000"
END SUB
END SUB
副プログラムとして定義する場合は数式通りには記述できません。
LET N=LEXP(-X)*X
PRINT N
これを記述し直すと
LET KETA=4000
!!! CALL LMUL(KETA,X$,STR$(-1),XX$)
LET XX$="-" & X$
CALL LEXP(KETA,XX$,Y$) ! Y=EXP(-X)
CALL LMUL(KETA,Y$,X$,N$) ! N=Y*X
PRINT N$
! CALL DISPLAY(N$)
記述通りに書けない代わりに1000桁である必要もないので
例では4000桁を計算します。
あまり意味はありませんが、桁数を16桁とすると2進モード、10進モードでも
使用できます。
PRINT LSIN(PI/6)
END
EXTERNAL FUNCTION LCOS(X)
OPTION CHARACTER BYTE
LET B$=REPEAT$(CHR$(32),32)
CALL COS1000(16,STR$(X),B$)
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET LCOS=VAL(B$(1:I))
SUB COS1000(KETA,X$,Y$)
IF POS(X$,"/")>0 OR POS(X$,"(")>0 OR X$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\cos1000.dll","cos1000"
END SUB
END FUNCTION
複素数モードで使用できる関数も定義してみました。
OPTION ARITHMETIC COMPLEX
PRINT CSIN(COMPLEX(PI/4,0))
END
EXTERNAL FUNCTION CSIN(X)
OPTION CHARACTER BYTE
OPTION ARITHMETIC COMPLEX
LET X$=REPEAT$(CHR$(0),32)
LET Y$=REPEAT$(CHR$(0),32)
CALL CSIN1000(16,STR$(RE(X)),STR$(IM(X)),X$,Y$)
FOR I=LEN(X$) TO 1 STEP -1
IF X$(I:I)<="9" AND X$(I:I)>="0" THEN EXIT FOR
NEXT I
FOR I=LEN(Y$) TO 1 STEP -1
IF Y$(I:I)<="9" AND Y$(I:I)>="0" THEN EXIT FOR
NEXT I
LET CSIN=COMPLEX(RE(VAL(X$(1:I))),IM(VAL(Y$(1:I))))
SUB CSIN1000(KETA,A$,B$,X$,Y$)
IF POS(A$,"/")>0 OR POS(B$,"/")>0 OR POS(A$,"(")>0 OR POS(B$,"(")>0 OR A$="" OR B$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\complex1000.dll","csin1000"
END SUB
END FUNCTION
副プログラムとして定義すれば、複素数多倍長で計算できます。
OPTION CHARACTER BYTE
LET A=1
LET B=2
CALL CEXP(1000,STR$(A),STR$(B),RE$,IM$)
PRINT "(";RE$;" , ";IM$;")"
END
EXTERNAL SUB CEXP(KETA,A$,B$,RE$,IM$)
OPTION CHARACTER BYTE
LET X$=REPEAT$(CHR$(32),KETA+100)
LET Y$=REPEAT$(CHR$(32),KETA+100)
CALL CEXP1000(KETA,A$,B$,X$,Y$)
FOR I=LEN(X$) TO 1 STEP -1
IF X$(I:I)<="9" AND X$(I:I)>="0" THEN EXIT FOR
NEXT I
LET RE$=X$(1:I)
FOR I=LEN(Y$) TO 1 STEP -1
IF Y$(I:I)<="9" AND Y$(I:I)>="0" THEN EXIT FOR
NEXT I
LET IM$=Y$(1:I)
SUB CEXP1000(KETA,A$,B$,X$,Y$)
IF POS(A$,"/")>0 OR POS(B$,"/")>0 OR POS(A$,"(")>0 OR POS(B$,"(")>0 OR A$="" OR B$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\cexp1000.dll","cexp1000"
END SUB
END SUB
三角関数(SIN,COS等)、逆三角関数(ASIN,ACOS等)、双曲線関数(COSH,TANH等)、逆双曲線関数(ACOSH,ASINH等)、指数関数(EXP)、対数関数(LOG)等
定数計算として副プログラムで円周率(π)、ネイピア数(e)、log(2)等を定義しています。
また、ガウス・ルジャンドル則(有限区間:-1~1)、ガウス・ラゲール則(半無限区間:0~∞)、ガウス・エルミート則(無限区間:-∞~∞)等を使用し
高精度数値積分をサポートしました。
#214
#215
#216
これは、1000桁モード等で使用するためにその零点及び重み係数を1000桁等の精度で算出し次数も1000次(1000点)等という超高精度で数値積分を行おうというものです。
但し、DATA文として記述するにはサイズが大きすぎるため、データはファイルからの読み出しになります。
(1000桁で零点と重み係数がそれぞれ1000点では2MB近いファイルサイズになります)
ガウス・ルジャンドル則で被積分関数 f(x)=1/(1+x*x) を積分区間A~Bで数値積分します。
OPTION ARITHMETIC DECIMAL_HIGH
LET N=1000 !次数
LET A=0 !下限
LET B=1 !上限
!'INPUT B
LET U=(B+A)/2
LET V=(B-A)/2
OPEN #1:NAME "..\data\legendre1000_"&STR$(N)&".txt"
FOR I=1 TO N
LINE INPUT #1:X$
LINE INPUT #1:W$
LET X=VAL(X$)
LET WEIGHT=VAL(W$)
LET S=S+F(U+V*X)*V*WEIGHT
NEXT I
PRINT S*4 !ATN(B)
PRINT PI
END
EXTERNAL FUNCTION F(X)
OPTION ARITHMETIC DECIMAL_HIGH
LET F=1/(1+X*X)
END FUNCTION
実行結果
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420198938095324
3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086513282306647093844609550582231725359408128481117450284102701938521105559644622948954930381964428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273724587006606315588174881520920962829254091715364367892590360011330530548820466521384146951941511609433057270365759591953092186117381932611793105118548074462379962749567351885752724891227938183011949129833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132000568127145263560827785771342757789609173637178721468440901224953430146549585371050792279689258923542019956112129021960864034418159813629774771309960518707211349999998372978049951059731732816096318595024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303598253490428755468731159562863882353787593751957781857780532171226806613001927876611195909216420199
注意点として1000桁精度で数値積分を行っても、計算結果も1000桁の精度があるわけではありません。
計算結果が既知である場合は、その値と比較して計算精度を確認してください。
計算結果が未知である場合は、数値積分を精度を変えて2度(800点と1000点等)行い、どこまで一致しているかで
計算精度を確認してください。
下記は副プログラムで定義して計算しています。
被積分関数 f(x)=exp(-x*x)として無限区間積分をガウス・エルミート則で行っています。
例えばファイルに2000桁で係数を求めていれば2000桁までの数値積分ができます。
OPTION CHARACTER BYTE
LET KETA=1000
LET N=1000 !次数
OPEN #1:NAME "..\data\hermite1000_"&STR$(N)&".txt"
FOR I=1 TO N
LINE INPUT #1:X$
LINE INPUT #1:WEIGHT$
CALL LMUL(KETA,X$,X$,W$) !'W=X*X
CALL LEXP(KETA,"-"&W$,F$) !'F=EXP(-W)
CALL LMUL(KETA,F$,WEIGHT$,S$) !' S=F*WEIGHT
CALL LADD(KETA,SS$,S$,TOTAL$) !'TOTAL=SS+S
LET SS$=TOTAL$
NEXT I
CALL DISPLAY(SS$) ! SQR(PI)
END
EXTERNAL SUB LEXP(KETA,X$,RESULT$)
OPTION CHARACTER BYTE
LET B$=REPEAT$(CHR$(32),KETA+100)
CALL EXP1000(KETA,X$,B$)
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET RESULT$=B$(1:I)
SUB EXP1000(KETA,X$,Y$)
IF POS(X$,"/")>0 OR POS(X$,"(")>0 OR X$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\exp1000.dll","exp1000"
END SUB
END SUB
EXTERNAL SUB LADD(KETA,X$,Y$,RESULT$)
OPTION CHARACTER BYTE
LET B$=REPEAT$(CHR$(32),KETA+100)
IF X$="" THEN LET X$="0"
IF Y$="" THEN LET Y$="0"
CALL LADD1000(KETA,X$,Y$,B$)
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET RESULT$=B$(1:I)
SUB LADD1000(KETA,X$,Y$,RESULT$)
IF POS(X$,"/")>0 OR POS(Y$,"/")>0 OR POS(X$,"(")>0 OR POS(Y$,"(")>0 OR X$="" OR Y$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\calc1000.dll","add1000"
END SUB
END SUB
EXTERNAL SUB LSUB(KETA,X$,Y$,RESULT$)
OPTION CHARACTER BYTE
LET B$=REPEAT$(CHR$(32),KETA+100)
IF X$="" THEN LET X$="0"
IF Y$="" THEN LET Y$="0"
CALL LSUB1000(KETA,X$,Y$,B$)
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET RESULT$=B$(1:I)
SUB LSUB1000(KETA,X$,Y$,RESULT$)
IF POS(X$,"/")>0 OR POS(Y$,"/")>0 OR POS(X$,"(")>0 OR POS(Y$,"(")>0 OR X$="" OR Y$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\calc1000.dll","sub1000"
END SUB
END SUB
EXTERNAL SUB LMUL(KETA,X$,Y$,RESULT$)
OPTION CHARACTER BYTE
LET B$=REPEAT$(CHR$(32),KETA+100)
IF X$="" THEN LET X$="1"
IF Y$="" THEN LET Y$="1"
CALL LMUL1000(KETA,X$,Y$,B$)
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET RESULT$=B$(1:I)
SUB LMUL1000(KETA,X$,Y$,RESULT$)
IF POS(X$,"/")>0 OR POS(Y$,"/")>0 OR POS(X$,"(")>0 OR POS(Y$,"(")>0 OR X$="" OR Y$="" THEN
PRINT "ERROR"
STOP
END IF
ASSIGN ".\DLL\calc1000.dll","mul1000"
END SUB
END SUB
EXTERNAL SUB DISPLAY(X$)
OPTION CHARACTER BYTE
LET N=POS(X$,".")
IF N>0 THEN
FOR I=1 TO N
PRINT X$(I:I);
IF MOD(I,5)=0 THEN PRINT " ";
IF MOD(I,50)=0 THEN PRINT " ";
IF MOD(I,100)=0 THEN PRINT
NEXT I
PRINT
END IF
FOR I=0 TO LEN(X$)-N STEP 5
PRINT X$(I+N+1:I+N+5);" ";
IF MOD(I+5,100)<>0 AND MOD(I+5,50)=0 THEN PRINT " ";
IF MOD(I+5,100)=0 THEN PRINT ":";I+5
IF MOD(I+5,1000)=0 THEN PRINT
IF MOD(I+5,10000)=0 THEN PRINT
NEXT I
END SUB
パーサーも作ってみました。sin,cos,tan,sqrt,%,^,abs,exp,log,max,acos,sinhなどの関数が使えます。
EXPRESSION1$にはf(x) (1変数)
EXPRESSION2$にはg(x,y) (2変数)を
EXPRESSION3$にはh(x,y,z) (3変数)の関数定義します。
FUNC$に呼び出す関数を入れます。
パラメーターにu,v,w,x,y,zが使用できます。
EXPRESSION1$に被積分関数 1/(1+x*x)を定義し
EXPRESSION2$で和を求めています。
OPTION CHARACTER BYTE
LET KETA=1000
LET A=0 !下限
LET B=1 !上限
LET U=(B+A)/2
LET V=(B-A)/2
LET EXPRESSION1$="1/(1+x*x)"
LET EXPRESSION2$="y+x"
LET N=800
OPEN #1:NAME "..\data\legendre1000_"+STRT$(N)+".txt"
FOR I=1 TO N
LINE INPUT #1:X$
LINE INPUT #1:WEIGHT$
LET FUNC$="f(u+v*x)*v*w"
CALL PARSER(KETA,FUNC$,STR$(U),STR$(V),WEIGHT$,X$,"","",EXPRESSION1$,EXPRESSION2$,EXPRESSION3$,OUTPUT$)
LET FUNC$="g(x,y)"
CALL PARSER(KETA,FUNC$,"","","",OUTPUT$,Y$,"",EXPRESSION1$,EXPRESSION2$,EXPRESSION3$,S$)
LET Y$=S$
NEXT I
CLOSE #1
CALL PARSER(KETA,"f(x)",U$,V$,W$,S$,Y$,Z$,"4*x",EXP2$,EXP3$,S$)
PRINT S$
END
EXTERNAL SUB PARSER(KETA,INPUT$,U$,V$,W$,X$,Y$,Z$,EXPRESSION1$,EXPRESSION2$,EXPRESSION3$,OUTPUT$)
OPTION CHARACTER BYTE
LET B$=REPEAT$(CHR$(32),KETA+100)
IF EXPRESSION1$="" THEN LET EXPRESSION1$="x" !' F(X)
IF EXPRESSION2$="" THEN LET EXPRESSION2$="x" !' G(X,Y)
IF EXPRESSION3$="" THEN LET EXPRESSION3$="x" !' H(X,Y,Z)
IF INPUT$="" THEN
PRINT "ERROR"
STOP
END IF
IF U$="" THEN LET U$="0"
IF V$="" THEN LET V$="0"
IF W$="" THEN LET W$="0"
IF X$="" THEN LET X$="0"
IF Y$="" THEN LET Y$="0"
IF Z$="" THEN LET Z$="0"
CALL PARSER1000(KETA,LCASE$(INPUT$),U$,V$,W$,X$,Y$,Z$,LCASE$(EXPRESSION1$),LCASE$(EXPRESSION2$),LCASE$(EXPRESSION3$),B$)
IF B$(1:5)="error" THEN
PRINT "ERROR!!"
STOP
ELSE
FOR I=LEN(B$) TO 1 STEP -1
IF B$(I:I)<="9" AND B$(I:I)>="0" THEN
EXIT FOR
END IF
NEXT I
LET OUTPUT$=B$(1:I)
END IF
SUB PARSER1000(KETA,INPUT$,U$,V$,W$,X$,Y$,Z$,EXP1$,EXP2$,EXP3$,OUTPUT$)
ASSIGN ".\DLL\parser1000_3.dll","parser1000"
END SUB
END SUB
|
|