山中和義さんの FUNCTION ExSTR$(x) で分数を循環小数に変換するループを拝見し、これを10進15桁モードではできないかと思い立ち作ってみました。
計算過程は筆算と同じです。分母をnとすれば、n-1桁までで割り切れるか同じ余りが現れるかです。
計算結果の桁数に制限はないのです(真値が求まります)が、分母の大きさで配列sを宣言しているため、分母が大きすぎるとエラーになります。
1000桁モード,有理数モードでも実行できます。(2進モードでは小数で誤差が出てしまう)
DECLARE EXTERNAL FUNCTION Ex2STR$ ! 分数を小数,循環小数の文字列に変換する関数
DO
READ IF MISSING THEN EXIT DO : x$
PRINT x$ ; " 入力数値"
CALL fraction(x$,a,b) ! 整数,有限小数,循環小数,分数の文字列(x$)を既約分数(a/b)に変換
IF a>=0 THEN LET m$=" " ELSE LET m$=""
IF b=1 THEN LET f$=STR$(a) ELSE LET f$=STR$(a)&"/"&STR$(b)
PRINT m$&f$ ; " 分数表示"
PRINT a/b ; " 小数15桁表示"
PRINT m$&Ex2STR$(a,b) ; " 小数真値[循環節]表示" ! 分数(a/b)を小数,循環小数の文字列に変換
PRINT
LOOP
DATA "-18" , "47." , "-12.34" , "-.67" , "+972.[51]" , "-73.482[3058]"
DATA "0.000[217]" , "+6.0054[83]" , ".031[040]" , "-78/13" , "0/26" , "740.52/29.84"
DATA "634517/3637" , "24/56" , "-5/12" , "91/35" , "886240513930735/10485760"
END
EXTERNAL SUB fraction(x$,numer,denom) ! x$を分数numer/denomに変換
LET dec$=LTRIM$(RTRIM$(x$))
IF dec$(1:1)="-" THEN LET s=-1 ELSE LET s=1
IF dec$(1:1)="+" OR dec$(1:1)="-" THEN LET dec$=dec$(2:LEN(dec$))
LET sp=POS(dec$,"/")
IF sp>1 THEN ! 分数
LET numer=VAL(dec$(1:sp-1))
LET denom=VAL(dec$(sp+1:LEN(dec$)))
CALL reduce(numer,denom) ! 約分
LET numer=s*numer
EXIT SUB
END IF
LET dp=POS(dec$,".")
IF dp=0 OR dp=LEN(dec$) THEN ! 整数
LET numer=s*VAL(dec$)
LET denom=1
EXIT SUB
END IF
IF dp=1 THEN LET intp=0 ELSE LET intp=VAL(dec$(1:dp-1)) ! 整数部
LET rp=POS(dec$,"[")
IF rp=0 THEN ! 有限小数
LET dl=LEN(dec$)-dp
LET denom=10^dl
LET numer=intp*denom+VAL(dec$(dp+1:LEN(dec$)))
CALL reduce(numer,denom)
LET numer=s*numer
EXIT SUB
END IF
IF rp=dp+1 THEN
LET dconst=0 ! 例"37.[61]"
LET dconst_denom=1
LET dl=0
ELSE
LET dconst=VAL(dec$(dp+1:rp-1)) ! 小数定数部
LET dl=rp-dp-1 ! 小数定数部桁数
LET dconst_denom=10^dl
CALL reduce(dconst,dconst_denom)
END IF
LET rl=LEN(dec$)-rp-1 ! 循環節桁数
LET recur=VAL(dec$(rp+1:LEN(dec$)-1)) ! 循環節
LET recur_denom=10^(LEN(dec$)-dp-2)*(1-1/10^rl)
CALL reduce(recur,recur_denom)
!PRINT intp;dconst;dconst_denom;intp+dconst/dconst_denom
!PRINT recur;recur_denom;recur/recur_denom
!! intp/1 + dconst/dconst_denom + recur/recur_denom
LET numer=intp*dconst_denom+dconst
CALL reduce(numer,dconst_denom)
LET numer=numer*recur_denom+recur*dconst_denom
LET denom=dconst_denom*recur_denom
CALL reduce(numer,denom)
LET numer=s*numer
END SUB
EXTERNAL FUNCTION Ex2STR$(numer,denom) !分数を小数,循環小数の文字列に変換
!「No.422 循環小数の計算(山中和義氏)」FUNCTION ExSTR$(x) 参照
IF SGN(numer/denom)=-1 THEN LET u$="-" ELSE LET u$=""
LET numer=ABS(numer)
LET denom=ABS(denom)
CALL reduce(numer,denom) ! 約分
!整数部
IF denom=1 THEN
LET Ex2STR$=u$&STR$(numer)
EXIT FUNCTION
END IF
DIM s(denom-1) ! 剰余を格納する配列
LET aa=INT(numer/denom) !小数部を削除する
IF aa=0 THEN LET b$="." ELSE LET b$=STR$(aa)&"."
!小数部
LET p=POS(b$,".")
LET numer=MOD(numer,denom) ! 剰余(商=aa)
LET k=1 !小数桁
DO UNTIL numer=0
FOR i=1 TO k-1 !循環したか確認する
IF s(i)=numer THEN
LET b$(i+p:i+p)="["&b$(i+p:i+p) !開始記号を挿入
LET b$=b$&"]" !終了記号
EXIT DO
END IF
NEXT i
LET s(k)=numer
LET b$=b$&STR$(INT(10*numer/denom))
LET numer=MOD(10*numer,denom)
LET k=k+1
IF k>denom THEN ! 配列sの添字オーバーを回避する
PRINT "変換を打ち切りました。"
EXIT DO
END IF
LOOP
LET Ex2STR$=u$&b$
END FUNCTION
EXTERNAL SUB reduce(p,q) ! 約分
!十進BASIC添付 "\BASICw32\Math\GCDLOOP.BAS" 参照
REM 互除法により,入力された2数の最大公約数を求める → その後,約分
LET a=p
LET b=q
DO
LET r=MOD(a,b)
IF r=0 THEN EXIT DO
LET a=b
LET b=r
LOOP
LET p=p/b
LET q=q/b
END SUB