十進BASICをお試しする
十進BASICというプログラミング言語がある。JISのFull BASICに独自の機能を追加した言語仕様となっている。いまさらBASICなんてと思われるかもしれないが、いまだに開発が続けられている言語である。対応するOSはWindows、Mac、Linuxである。
この言語の特徴のひとつとして数値計算にあり、なおかつ計算が十進法で行われる点である。基本は十進15桁だが、オプションによって20桁、1000桁まで扱える。ほかには有理演算モードや複素数モード、行列演算などがあり、数値計算においてはなかなかの機能を備えたBASICといえる。もちろんグラフィック機能もあるので、計算結果をグラフとして出力することができる。また、Fortranで書かれたコードを十進BASICへ、少しの変更で簡単に移植できるのも優れた点のひとつだろう。
ダウンロード
配布形式はアーカイブパッケージである。解凍して実行ファイルをダブルクリックすれば実行することができる。ダウンロードは、Vectorのサイトからできるようになっている。
ここから目的のOSに合わせたアーカイブパッケージを選択すればいい。筆者が使用しているOSはLinux Mintなので、Linux版を選択した。Gtk版とQt5版があるが、Gtk+の64ビット版をダウンロードした。ファイル名はBASIC8116Ja_Linux64.tar.xzである。ダウンロードしたら解凍してBASICJaフォルダにある実行ファイルbasicをメニューに登録しておくとよい。いちいちターミナルからの実行をしなくともよくなる。ちなみにアイコンは、同じフォルダ内のsourceにある。併せて登録しておく。
起動
起動すると以下のエディタ画面が表示される。非常にシンプルな画面といえよう。
エディタでコードを入力していくわけだが、BASICの仕様として代入にはLETをつける必要がある。実際は別につけて入力しなくとも自動で補完されるので気にしなくともよい。構文は自動的に大文字に変換されるので、コード的にはわかりやすいようになっている。ただ、色分けができないのでコードが大きくなるとわかりにくくなる。実際には大きなコードを書くことはないので、それほど問題にはならないだろう。なお、行番号の入力は不要である。
コードを書く
コードを書いてみよう。言うまでもないが、Hello Worldとやってもしかたがないので、PI(円周率)を表示させてみることにする。
PRINT PI
1行入力してツールメニューにある▶をクリックして実行してみよう。小文字で入力してもよい。
当たり前だが、15桁まで正しく表示されていることがわかる。少し桁数を増やしてみよう。「オプション(O)」から「数値」を選択すると、以下のダイアログが表示される。
「表示桁数を増やす」にチェックを入れて[OK]をクリックして再度実行してみると、
20桁まで正しく表示される。さらにオプションを変更すれば1000桁まで表示できるが、あまり意味のないことなのでやめておく。ちなみに1000桁を指定しておくと計算も1000桁で計算される。
例のやつを試す
(0.1 + 0.7) * 10
十進BASICでは正しく8が計算される。いいね。
さらにコードを書く
これだけでは面白くともないので、もう少し実用的なコードを書いてみよう。天文計算の日数計算に必要なユリウス日を求めるコードである。修正ユリウス日を求めるModifiedJulian.bas、ユリウス日を求めるJulianDay.bas、ユリウス日から年月日時刻を求めるJulian2Date.bas、メインルーチンのm21.basから構成されるコードである。それぞれファイルを分けているのは、メインルーチン以外は他のプログラムで使用するために分けている。
メインルーチンの中に記述していると、コピー&ペーストしなければならず面倒なのである。エディタには末尾に読み込む機能があるのでこれを利用する。ただし、メインルーチンを先に読み込むと、サブルーチンの扱いが異なるために注意が必要である。メインルーチンの後ろにサブルーチンを配置すると、外部副サブルーチン扱いになる。先にサブールチンを読み込んで、最後にメインルーチンを読み込むと内部副サブルーチン扱いとなる。サブルーチンは内部副サブルーチンとして記述しているので先に読み込むことにする。詳しくはヘルプを読むとよい。
mjd.bas
REM 修正ユリウス日を求める
SUB ModifiedJulianDay(dy, dt, julian, year, month, day, hour, minute, second)
LET year = SGN(dy) * INT(ABS(dy) / 10000.0)
LET month = MOD(INT(ABS(dy) / 100.0), 100.0)
LET day = MOD(ABS(dy), 100.0)
LET hour = INT(dt / 10000.0)
LET minute = MOD(INT(dt / 100.0), 100.0)
LET second = MOD(dt, 100.0)
LET julian = JulianDay(year, month, day)
LET julian = julian + (hour / 24.0 + minute / 1440.0 + second / 86400.0)
LET julian = julian - 0.375
LET julian = julian - 2400000.5
END SUB
jdate.bas
REM ユリウス日から日付を求める
SUB Julian2Date(julian, T(), year, month, day)
LET jj = julian
LET year = INT(2.7379093E-3 * julian + 1858.877)
LET month = 1.0
LET day = 0.0
LET julian = JulianDay(year, month, day)
LET r2 = jj - (julian - 2400000.5)
IF (MOD(year, 4.0) = 0 AND MOD(year, 100.0) <> 0 OR MOD(year, 400.0) = 0) THEN
LET T(2) = 29
END IF
LET r1 = 0.0
LET m = 1
DO WHILE m < 13
IF (INT(r2) - r1 - T(m) <= 0) THEN
EXIT DO
END IF
LET r1 = r1 + T(m)
LET m = m + 1
LOOP
LET month = m
LET day = r2 - r1
LET T(2) = 28
LET julian = jj
IF (month = 13) THEN
LET year = year + 1.0
LET month = month - 12.0
END IF
END SUB
julian.bas
REM ユリウス日を求める
FUNCTION JulianDay(year, month, day)
LET branch = year + (month - 1.0) / 12.0 + day / 365.25
IF (INT(month) < 3) THEN
LET month = month + 12.0
LET year = year - 1.0
END IF
IF (branch >= 1582.78) THEN
LET julian = INT(year * 365.25) + INT(year / 400.0) - INT(year / 100.0) + INT(30.59 * (month - 2.0)) + day + 1721088.5
ELSE
IF (branch >= 0) THEN
LET julian = INT(year * 365.25) + INT(30.59 * (month - 2.0)) + day + 1721086.5
ELSE
IF (year < 0) THEN
LET julian = SGN(year) * INT(ABS(year) * 365.25) + INT(30.59 * (month - 2.0)) + day + 1721085.5
END IF
END IF
END IF
LET JulianDay = julian
END FUNCTION
m21.bas
REM メイン
OPTION BASE 1
DIM T(12)
READ T(1), T(2), T(3), T(4), T(5), T(6), T(7), T(8), T(9), T(10), T(11), T(12)
DATA 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
INPUT PROMPT "DATE AND TIME(JST) ? ":dy,dt
CALL ModifiedJulianDay(dy, dt, julian, year, month, day, hour, minute, second)
LET dtime = hour / 24.0 + minute / 1440.0 + second / 86400.0
PRINT
PRINT USING " INPUT DATE = ##### %% %%.# UT":year, month, day + dtime - 0.375
PRINT USING " MJD = ########.# UT":julian
PRINT
CALL Julian2Date(julian, T, year, month, day)
PRINT USING " RETURN DATE = ##### %% %%.# UT":year, month, day
LET julian = JulianDay(year, month, day)
PRINT USING " MJD = ########.# UT":julian - 2400000.5
END
コードの中で扱っている数値に.0とつけているが、あくまでも表記なのでつけなくとも結果は変わらない。
実行
コードを実行すると、日付と時刻を入力するよう求められるので入力する。
入力は日付と時刻を“YYYYMMDD, HHMMSS”形式で入力する。入力したデータの正当性はチェックしていないので了承していただきたい。
簡単なコードで、おおざっぱに説明してきたが、理解しなくともよい。どうせ使わないだろうからだ。ただ、こういった言語もあるということを知ってもらいたいだけである。十進BASICで書かれたコードはFortranへ少しの変更で書き換えることができる。一番の変更はPRINT文だろう。また、Fortranで出力したデータを十進BASICで読み込んでグラフ化することも可能である。GNUPLOTいらずかもである。
十進BASICは数学の教育プログラミング言語としていいのだが、残念ながら採用はされない。Pythonよりいいと思うのだが。BASICでプログラミングを学んだからといって無駄になることはなく、セカンド的にほかの様々なプログラミング言語を学ぶとしても学習コストが低いのである。
参考 Fortranへコンバート
コピー&ペーストして修正してみた。実際はこんな書き方はしないがBASICに合わせている。Fortranは大文字小文字を区別しないので、こういった書き方もできるのである。
! メイン
PROGRAM main
IMPLICIT none
DOUBLE PRECISION :: year, month, day
year = 2022.0d0
month = 6.0d0
day = 1.0d0
WRITE(*, *) JulianDay(year, month, day)
CONTAINS
! ユリウス日を求める
FUNCTION JulianDay(year, month, day)
IMPLICIT none
DOUBLE PRECISION, INTENT(inout) :: year, month, day
DOUBLE PRECISION :: JulianDay
DOUBLE PRECISION :: julian
DOUBLE PRECISION :: branch
branch = year + (month - 1.0) / 12.0 + day / 365.25
IF (INT(month) < 3) THEN
month = month + 12.0
year = year - 1.0
END IF
IF (branch >= 1582.78) THEN
julian = INT(year * 365.25) + INT(year / 400.0) - INT(year / 100.0) + INT(30.59 * (month - 2.0)) + day + 1721088.5
ELSE
IF (branch >= 0) THEN
julian = INT(year * 365.25) + INT(30.59 * (month - 2.0)) + day + 1721086.5
ELSE
IF (year < 0) THEN
julian = sgn(year) * INT(ABS(year) * 365.25) + INT(30.59 * (month - 2.0)) + day + 1721085.5
END IF
END IF
END IF
JulianDay = julian
RETURN
END FUNCTION JulianDay
! FortranにはSGN関数がないため
FUNCTION sgn(x)
IMPLICIT none
DOUBLE PRECISION :: x
INTEGER :: y
INTEGER :: sgn
IF (x < 0) THEN
y = -1
ELSE
y = 1
END IF
sgn = y
RETURN
END FUNCTION sgn
END PROGRAM main