十進BASICをお試しする

十進BASICというプログラミング言語がある。JISのFull BASICに独自の機能を追加した言語仕様となっている。いまさらBASICなんてと思われるかもしれないが、いまだに開発が続けられている言語である。対応するOSはWindows、Mac、Linuxである。

この言語の特徴のひとつとして数値計算にあり、なおかつ計算が十進法で行われる点である。基本は十進15桁だが、オプションによって20桁、1000桁まで扱える。ほかには有理演算モードや複素数モード、行列演算などがあり、数値計算においてはなかなかの機能を備えたBASICといえる。もちろんグラフィック機能もあるので、計算結果をグラフとして出力することができる。また、Fortranで書かれたコードを十進BASICへ、少しの変更で簡単に移植できるのも優れた点のひとつだろう。

ダウンロード

配布形式はアーカイブパッケージである。解凍して実行ファイルをダブルクリックすれば実行することができる。ダウンロードは、Vectorのサイトからできるようになっている。

十進BASICダウンロードサイト
ダウンロードサイト

ここから目的のOSに合わせたアーカイブパッケージを選択すればいい。筆者が使用しているOSはLinux Mintなので、Linux版を選択した。Gtk版とQt5版があるが、Gtk+の64ビット版をダウンロードした。ファイル名はBASIC8116Ja_Linux64.tar.xzである。ダウンロードしたら解凍してBASICJaフォルダにある実行ファイルbasicをメニューに登録しておくとよい。いちいちターミナルからの実行をしなくともよくなる。ちなみにアイコンは、同じフォルダ内のsourceにある。併せて登録しておく。

起動

起動すると以下のエディタ画面が表示される。非常にシンプルな画面といえよう。

エディタでコードを入力していくわけだが、BASICの仕様として代入にはLETをつける必要がある。実際は別につけて入力しなくとも自動で補完されるので気にしなくともよい。構文は自動的に大文字に変換されるので、コード的にはわかりやすいようになっている。ただ、色分けができないのでコードが大きくなるとわかりにくくなる。実際には大きなコードを書くことはないので、それほど問題にはならないだろう。なお、行番号の入力は不要である。

コードを書く

コードを書いてみよう。言うまでもないが、Hello Worldとやってもしかたがないので、PI(円周率)を表示させてみることにする。

PRINT PI

1行入力してツールメニューにある▶をクリックして実行してみよう。小文字で入力してもよい。

実行結果その1
実行結果その1

当たり前だが、15桁まで正しく表示されていることがわかる。少し桁数を増やしてみよう。「オプション(O)」から「数値」を選択すると、以下のダイアログが表示される。

オプションの変更
オプションの変更

「表示桁数を増やす」にチェックを入れて[OK]をクリックして再度実行してみると、

実行結果その2
実行結果その2

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