このように,長い歴史を持つ言語だけに, FORTRANで書かれたライブラリーは膨大な量にのぼる. 反面,C言語など新しい言語が数多く存在する現在では, 古い歴史を引きずった堅い(融通の効かない)言語という印象も免れない. しかしながら, FORTRANには科学計算用の言語として, 他の言語にはない使いやすさがある. 単に「蓄積が大きい」というだけでなく, この「使いやすさ」こそ, FORTRANが30年以上も使い続けられてきた真の理由である.
FORTRANに限らず,どんな言語でもそれを使いこなすのは, なかなか大変である. 幸にも,FORTRAN の仕様は比較的小さい(文法書が薄い)ので 学習は容易なほうであるが, それでもFORTRANを``極める''のは容易なことではない. MATH1を含む地球流体電脳ライブラリでも, 入門書に書いてない「奥義」をかなり使っている. もちろん, これらの知識がなくても電脳ライブラリを使うことは可能である. しかし,電脳ライブラリをよりよく理解し上手に使いこなすためには 是非とも知っておいていただきたい. また, これらの知識は応用範囲が広いので, 一般のFORTRANプログラムを書く際にも, きっと役にたつはずである.
言語規格というのは「日本語の文法」の様なもので, 計算機に自分が意図した動作をさせるためには これをよく知っていなければならない. これは,当り前のようで,意外に.く.せ.も.のである. なぜならば,普通,多くの人々は FORTRAN を勉強する時, 英語の勉強のように,文法を厳しく教わりながら勉強 するわけではないからである. どちらかというと,日本語を覚えたときと同じ様に, 「見よう見まね」または「試行錯誤」により FORTRANを勉強するのが普通である. そうなると,多くの人が生まれた土地の方言を, 方言と気づかずに覚えてしまうのと同じ様に, 特定の計算機の特定のコンパイラだけが解釈できるプログラムを 平気で書いてしまうことになる.
FORTRAN コンパイラは通常, 標準語としてのFORTRAN77規格 + α の機能を持っている. この + α の部分は,拡張機能と呼ばれ, このような拡張機能を持つコンパイラは FORTRAN77 上位互換(アッパーコンパチ)コンパイラと呼ばれる. これは,標準語だけで書かれたプログラムでも, + α の機能を使ったプログラムも, どちらもコンパイルできるという意味では コンパイラの「機能が高い」のであるが, 逆に+αの機能を使ったプログラムは, そのコンパイラでないとコンパイルできないことになる. したがって,我々は+αの部分を「方言」と呼ぶ.
日常会話の中では,方言は特に問題にならず, むしろ標準語では表現できないニュアンスまで 伝えることができて便利である場合が多い. これは,方言が標準語の拡張機能として働いているためであるが, ビジネス上の会話で方言を使うと,とんでもない結果をもたらす. 多くの人は「明日, 東京にいかずー」という約束をしたら, 明日, 東京にいくべきか否か迷うに違いない. (これは中部地方の方言で,東京にいこう,という意味である)
これと同じ様に,FORTRANの「方言」は特定の計算機を使いこなすには 便利な場合も多いが, むやみに使うと非常に危険である. しかし,生まれた土地を離れたことがないと, 自分の言葉が方言かどうかわからないのと同じ様に, 特定の計算機しか使ったことがないと, 正確なFORTRANの文法を身につけるのは難しい. なぜなら, いつも使っているFORTRAN77のコンパイラがちゃんとコンパイルしてくれれば, そのプログラムはFORTRAN77規格に準拠したプログラムであると 思ってしまうからである. しかし,これは明かな間違いである. どんなコンパイラでも大なり小なり「方言」を持っており, その「方言」を使ったプログラムは, たとえ,FORTRAN77規格のコンパイラでコンパイルできたとしても, FORTRAN77規格準拠のプログラムではない.
そこで,FORTRANで何とかプログラムが書けるようになった初心者も, この道10年のベテランも, 市販の文法書以外に「岩波FORTRAN辞典」を 座右におかれることを強くお勧めする. これは単なる文法書ではなく, 規格が意味するところや,歴史的背景などが解説してあって, 辞典としてだけでなく,読物としても面白い書物である.
電脳ライブラリはいくつかの例外を除いて, 極力この「方言」を避けて書いてある. どうしても「方言」使わなければならないようなところは, 直接「方言」を使わず「電脳標準語」を定義して, 各プログラムの中では「電脳標準語」を使うようにしている.
例えば,SYSLIB の中の MSGDMP はエラーを検出したとき, システムに依存したエラー処理をおこなって (たとえば,エラーのトレースバック情報を出力して) プログラムを強制終了する OSABRT というサブルーチンを呼ぶ. 各コンパイラは必ずといってよいほど, この種のサブルーチンを「方言」として持っており, OSABRT は単にそのコンパイラ固有のサブルーチン(方言)を 呼んでいるだけである. しかし,その「方言」を使わず, 「電脳標準語」である OSABRT を使って他のサブルーチンを書いておけば, OSABRT を各コンパイラ用に書きなおすだけで, そのようなプログラム全てを移植できることになる.
この移植のしやすさ(可搬性)こそ電脳ライブラリの最大の特徴であり, ライブラリ開発者が最も苦労した部分でもある. この特徴を最大限に生かすためにも, FORTRANの文法をよく理解して, 不必要な「方言」を使わないよう心がけて欲しい.
以下の節では不必要な方言を避けるために知っておくべき知識,知っていると 便利なことがら,そして,プログラミング上の盲点となるような落し穴につい て多少の解説をしておく.
○○の時, 変数××は不定となる.という記述を見て, 首をかしげた経験が1度はあると思う. 最初にこれを読むと,「××に乱数が代入される」とか 「数値がメモリ上から消滅する」ということを想像してしまうが, 決してそういう意味ではない. 大抵の場合,○○の時, ××がどうなるか, コンパイラはちゃんと知っている.
この奇怪な文章を理解するには, FORTRANの規格の文章が誰に対する文章であるかということを, よく理解しておく必要がある. FORTRAN規格というのは,我々プログラマに対する文章であると同時に, FORTRANコンパイラをつくる人に対する文章でもある. したがって,この文章は2通りに翻訳できるように書いてあるのである. 上記の文章を, コンパイラをつくる人に対する文章に翻訳すれば,
○○の時, 変数××が占めていた記憶領域をどのように使ってもよい.ということになり, 我々プログラマに対しては,
コンパイラをつくる人に,上のように言ってしまったので, ××がどうなるかわからない.ということになる.
もし,この文章をコンパイラを「作った人」から「使う人」へのメッセージだと 解釈してしまうと, 「自分で作っておきながら,わからない,どういうことだ」 と言いたくなってしまう. 特に,コンパイラに付属の文法書を読んでいると, そのような錯覚に落ちいりやすいが, FORTRANの文法書とはあくまでも,
コンパイラを作る人と,それを使う人の .両.者.の.責.任.関.係を明確にするための法律なのである.
「不定」とは逆に, 規格によって値が保証されている状態を「確定」という. 規格上,プログラムの中で引用できる変数は「確定」された変数だけである. 以下,変数が「不定」となる例である.
これは,最もわかり易い「不定」の場合である. 変数に値が代入なければ, 当然その変数にどのような値が入っているかわからない.
コンパイラによっては全ての変数を初期化(通常は0を代入) する ものもある. FORTRAN規格はあくまで「不定」である(どうしてもよい)ので, そのようなコンパイラでも規格に違反しているわけではない. しかし,これは規格によって保証されているものではなく, 一種の「方言」であるので, これに頼ってプログラムを書くと後で必ず後悔する.
最初から変数に値を入れておきたい時にはDATA文を使う. 次の例を参照のこと.
サブルーチンの中で使われる変数は, いくつかの例外を除いて, サブルーチンの実行が終了した段階で「不定」となる.
これは少々わかりにくいが, 要するに,同じサブルーチンを2回呼んだとき, 1回目で値が代入された変数を, 2回目に参照しようとしても, 1回目の値が保存されている保証はない, ということである.
例えば,次のプログラムを見て欲しい.
*−−−−−− main program −−−−−- DO 100 I=1, 100 CALL BEAT 100 CONTINUE END *−−−−−−−−−−−−−−−− SUBROUTINE BEAT DATA N /0/ SAVEN=N+1 IF(N.EQ.10) THEN WRITE(6,*) ' I hate you !' N=0 ENDIF END
このサブルーチンBEAT は呼ばれた回数(N)を覚えていて, 10回呼ばれるごとにメッセージを出力するものである.
変数NはBEATが呼ばれる前に, DATA文によって 0 という値で確定している. BEATが1回呼ばれると, この変数Nに1が加えられるが, もし,9行目のSAVE文がなければ, 16行目のEND文を実行した段階で,FORTRANの規定により Nは不定となる.
SAVE文は, サブルーチン内の変数の値を サブルーチンの実行が終っても保持するように 指示する宣言文である. このプログラムのように変数Nの値が 次回の呼び出しまで保持されないと 正常に動かないようなプログラムでは必ず書かなければならない.
コンパイラによっては(というより多くのコンパイラでは) サブルーチンの中で使われる局所変数を保存するが, これも「方言」である.
ただし,DATA文で指定された変数は, .そ.の.値.が.書.き.換.え.ら.れ.な.い.限.り, RETURN文またはEND文を実行しても「不定」にはならない.
因みに,地球流体電脳ライブラリの GLpGET/GLpSETが, 掲示板の役目を果たせるのは, このDATA文とSAVE文のおかげである. (節参照.)
なお,SAVE文を指定すると,通常,実行ファイルのサイズは 大きくなるが,実行速度は若干速くなることが多い.
異なる型の変数がEQUIVALENCE文などにより結合しており, その一方の変数が確定したとき, もう一方の変数は不定となる. 例えば,
INTEGER IX REAL RX EQUIVALENCE (IX, RX) RX = 1. WRITE(6,*) IX ENDというプログラムで, 実変数 RX が代入されると,整変数IXの値は不定となる. したがって,不定の変数を引用しているWRITE文は, 文法違反である.
しかし,この時IX は,ちゃんと「ある値」を持っている. ただし,整数や実数の表現形式がコンパイラによって異なるため, この「ある値」が, どのような値になるかFORTRAN規格からは予測できないという意味で 「不定」なのである.
したがって, 文法上このプログラムの結果はわからないが, 通常は正常に動作して「ある値」を書き出すことになる. (節参照.)
FORTRANでサブルーチンを書く時, いろいろな場合を想定して, なるべく汎用的なサブルーチンにしようとすると, 引数がどんどん増えて困ることがある. 大事な引数は2-3個なのに, どうでもよいような引数がずらずらと並んでいると, それだけで CALL .... と書く手が重くなってくる.
そのような時にENTRY文を使うと解決できる場合がある. ENTRY文とは,サブルーチンの途中から実行を始めるための文である.
*−−−−−- main program −−−−−- REAL X(10) ....... CALL TABLE(X, 10) ....... END *−−−−−−−−−−−−−−−- SUBROUTINE TABLE(X,N) REAL X(N) DATA IOU /6/ SAVEWRITE(IOU,'(10F12.4)') X RETURN
ENTRY IOUSET(IOU0) IOU = IOU0 RETURN END
このサブルーチンTABLEは 1次元配列Xを出力するものであるが, TABLEだけを呼べば, DATA文で指定されたIOUの機番(6)に出力される. ファイルなど別の機番に出力させたい場合は, TABLE を呼ぶ前にIOUSET を呼んで, IOUを書き換えておけばよい. つまり,1つのサブルーチンに, 印刷すべき変数だけを与える入口と, その他のパラメータを設定する入口の,2つの入口を作るわけである.
ここで,注意すべきことはSUBROUTINE文またはENTRY文で指定された引数は, そのSUBROUTINE文またはENTRY文から入った時でないと使えないことである. 例えば,このプログラムのENTRY文から入った時(IOUSETが呼ばれた時), 変数N を引用することはできない. 同じように, TABLEが呼ばれた時にIOU0を引用することはできないので, IOUSETが呼ばれたときにIOUに代入しておかなければならない. また,このようなプログラムでもSAVE文とDATA文は不可欠である.
電脳ライブラリの「パラメータ掲示版」である SYSLIB の GLpGET/GLpSETは, 毎回指定する必要のないパラメータをうまく隠蔽して見えないようにしておく という前述のテクニックを極限にまで利用したライブラリなのである. GLpGET/GLpSETは他のプログラムにパラメータを渡すだけで, あとは何もしない. その構造は, 以下のようになっている(実際のソースより簡略化されている).
(注: Ver.5 の電脳ライブラリでは,実行時パラメータによって 内部変数への介入が可能になったため GLpGET/GLpSETは 内部でさらに下請けルーチンを呼ぶようになった. 以下では,Ver.4 の電脳ライブラリにおける GLpGET/GLpSETを 用いて説明をおこなう. Ver.5 の下請けルーチンの構造は,基本的に Ver.4 の GLpGET/GLpSETと同じである.)
*−−−−−−−−−−−−−−−−−−−−−−−- * GLPGET / GLPSET *−−−−−−−−−−−−−−−−−−−−−−−- SUBROUTINE GLPGET(CP,IPARA) CHARACTER CP*(*) PARAMETER (NPARA=17) INTEGER IX(NPARA) REAL RX(NPARA) LOGICAL LX(NPARA) CHARACTER CPARA(NPARA)*8 EQUIVALENCE (IX,RX,LX) SAVE DATA CPARA( 1)/'NBITSPW '/, IX( 1)/32/ ........ DATA CPARA(12)/'LMISS '/, LX(12)/.FALSE./ DATA CPARA(13)/'IMISS '/, IX(13)/999/ DATA CPARA(14)/'RMISS '/, RX(14)/999.0/ ........ DO 10 N=1,NPARA IF (CP.EQ.CPARA(N)) THEN IPARA=IX(N) RETURN END IF 10 CONTINUE エラー処理 STOP *−−−−−−−−−−−−−−−−−−−−−−−- ENTRY GLPSET(CP,IPARA) DO 15 N=1,NPARA IF (CP.EQ.CPARA(N)) THEN IX(N)=IPARA RETURN END IF 15 CONTINUE エラー処理 STOP END
ここで,CPARAという文字変数にパラメータ名が登録されており, その名前に対応した値が IX, RX, LX という変数に入っている. (これら3つの変数はEQUIVALENCE文により結合されているので, 実際には1つの記憶領域しか持たない)
さて,IFALIBの関数IMAXは,欠損値以外の最大値を求めるものであるが, IMAXは欠損値を示す整数値を引数から得る代りに,
CALL GLPGET('IMISS', IMISS)
という文を実行する. この時, GLPGET では,CPARAの中から'IMISS'という名前を探して (24-29行目)その名前に対応する値IMISSとして返す. この時返される値は,DATA文によって指定されているが, 必要であれば,あらかじめこれらの値をGLISETで変更しておくこともできる.
この方法が前の節の方法 (各プログラムがIMISS 設定用のENTRYを持つ方法) より優れている点は, GLPGETが持つ情報を複数のサブルーチンから参照できることである. 例えば,欠損値を示す値は何も初期値としては999が与えられているが, 999がデータ範囲に入っているような場合には, これを変更しておかなければならない. そのような時にでも GLPSET を一度呼ぶだけで, 欠損値処理をする全てのサブルーチンの動作を制御することが可能になる.
さらに,これは規格上の話だけではなく, 上記のような1ワードが4バイトに相当する標準的な処理系で, ユーザーが「数値記憶単位」と「文字記憶単位」の対応をよく考えた上で 結合したとしても実害が生じる場合がある. これは, 文字型変数が文字単位で任意の長さがとれることから, 文字型変数の扱い方が,それ以外の変数(実数,整数,論理変数)と 大きく異なる場合があるからである. 特にサブルーチンの引数の渡し方が異なる場合が多い.
もともとFORTRAN66の規格には文字型変数はなく, 文字を扱う場合にはキャラクターコード列(整数)として扱われていた (4文字を1つの整数として扱っていた). そのため4文字単位の長さの文字列しか扱えず不便であったが, FORTRAN77から文字型変数として正式に認知されるようになった. これに伴って,任意の長さの文字列が扱えるようになるなど, 文字の扱いが非常に楽になった反面, 上記のような制約も増えた. もっとも,かなり多くのコンパイラでは文字型とそれ以外の変数の 結合が可能であるが, これも「方言」の一つである.
実際の観測データなどを扱う分野では, 文字と数字が混在したデータを扱うことも少なくないので, 伝統的にこのようなEQUIVALENCE文を使っている場合もあるが, 実際にこのような結合ができないコンパイラもあるので, 注意が必要である.
さらに,文字型変数は他の型の変数と違って,以下の例のように, 同じ文字要素を式の右辺と左辺に書く(3行目)ことが禁じられている.
CHARACTER*12 CX CX = 'DENNOU' CX = CX(1:6)//'DENNOU'
文字以外の変数では,同様のことが許されているので, つい,このような代入文を書いてしまいそうになるが, このような場合には,
CHARACTER*12 CX CX = 'DENNOU' CX(7:12) = 'DENNOU'
とすれば,問題はない.
FORTRAN77では WRITE文の書式識別子にFORMAT文の文番号ではなく, 以下のように,文字.変.数を指定することができる. この機能を使えば,書式の動的な変更が可能になる.
CHARACTER CFMT*12 ... CFMT = '(T12, F5.2)' WRITE(6,CFMT) X ...
この例では,文字変数CFMTに定数を代入しているだけなので, 動的に書式を変更するようにはなっていないが, CFMTは.変.数であるから, プログラムの中で変更するのは容易である.
桁数などの数値変数を文字変数に書き込むには, やはりWRITE文を使う. WRITE文の出力機番を書くところに文字変数を書くと, その文字変数を``内部ファイル''として, 普通のWRITE文と同じ様に編集,出力ができる.
CHARACTER CFMT*12 ... CFMT = '(T12, Fx.2)' WRITE(CFMT(8:8),'(I1)') N WRITE(6,CFMT) X ...
この例では,整変数Nを文字変数CFMTの8桁目に書き込み, Xを出力する際の桁数としている. このあたりのテクニックはFORTRAN66の時代には使えなかったので 知らない人も多いが, 知っていると非常に便利である.
REAL X(6), Y(2,3) COMPLEX Z(3) EQUIVALENCE (X,Y,Z)
という場合, 変数X, Y, Zはどれも長さが6語(48バイト)の配列で, EQUIVALENCE文により同じ記憶領域を占める. この時, それぞれの変数の並び方は
+----------+----------+----------+----------+----------+----------+ | X(1) | X(2) | X(3) | X(4) | X(5) | X(6) | |----------|----------|----------|----------|----------|----------| | Y(1,1) | Y(2,1) | Y(1,2) | Y(2,2) | Y(1,3) | Y(2,3) | |----------|----------|----------|----------|----------|----------| | Re(Z(1)) | Im(Z(1)) | Re(Z(2)) | Im(Z(2)) | Re(Z(3)) | Im(Z(3)) | +----------+----------+----------+----------+----------+----------+
という様に,2次元配列Yは添字の左側の数字から変るように 1次元的に展開される. また,複素数データZは2つの実数が並んだ形で展開される. したがって,X(3) の数値はY(1,2), Re(Z(2)) と 全く同じになる.
この規則は多くのプログラムで積極的に使われており, 多次元配列を1次元配列として扱ったり, 複素数データを実数データを見なして処理したり することが行われている. この規則は,計算機のハードウェアに近い部分の規則なので, 計算機によって異なるように思えるかもしれないが, FORTRAN77規格で定められた「標準語」である.
REAL X(100) ...... CALL SUM(X, 100, 1, XSUM) ...... ENDSUBROUTINE SUM(X, N, JX, XSUM) REAL X(*)
XSUM = 0. DO 100 I=1, JX*(N-1)+1, JX XSUM = XSUM + X(I) 100 CONTINUE END
このサブルーチンは1次元配列Xの和を求めて, XSUMとして返すものである. この同じサブルーチンを2次元配列Y に対して
REAL Y(10,10) ........ CALL SUM(Y(2,1), 10, 10, YSUM) ...... END
という様に使うと サブルーチンSUMは,以下のように2次元配列要素Y(2,1)を 先頭とする記憶領域を1次元配列Xに割り当てる.
+------+-------+-----+------+-------+-------+-----+------+-------+-------+-----+ | Y | Y | ... | Y | Y | Y | ... | Y | Y | Y | ... | | (2,1 | (3,1) | | (10, | (1,2) | (2,2) | | (10, | (1,3) | (2,3) | | | ) | | | 1) | | | | 2) | | | | |------|-------|-----|------|-------|-------|-----|------|-------|-------|-----| | X(1) | X(2) | ... | X(9) | X(10) | X(11) | ... | X | X(20) | X(21) | ... | | | | | | | | | (19) | | | | +------+-------+-----+------+-------+-------+-----+------+-------+-------+-----+
その1次元配列Xの要素を10 (JX) 個おきに10個の和をとると, Y(2,1), Y(2,2), ..., Y(2,10) という具合に,二次元配列 Yの第2添字についての和をとることになる.
ここで,サブルーチンの中では配列Xの長さはわからず, 単に,X(1)を先頭とする1つながりのデータとして扱われていることに 注意して欲しい. 配列がサブルーチンに渡されるとき, メインプログラムの配列の中身がサブルーチンの 配列の中身にコピーされるのではなく, メインプログラムの中の配列の番地だけが渡され, サブルーチンの方はその番地を基準に処理を行うのである. このようなことを意識してプログラムを書くと, 少ない引数で柔軟な処理が可能になる.
もう一つの応用例を紹介しよう.
SUBROUTINE SUM2(X, IMAX, IX, JX, XSUM) REAL X(IMAX,JX)XSUM = 0. DO 100 I=1,IX DO 100 J=1,JY XSUM = XSUM + X(I,J) 100 CONTINUE END
これは2次元配列X(I,J)の一部の和をとるものであるが, これを
REAL X(102,102) ........ CALL SUM2(X(2,2), 102, 100, 100, XSUM) ........
という具合に使うと,102 102の2次元配列のデータのうち, 周辺を残して真中の100 100 の部分, すなわち,(2,2)-(101,101)を対角線とする部分の和をとることができる. MATH1のルーチンで,このような使い方を想定しているものはないが, GRPH2のコンターを描くルーチンなどで応用すると便利である.
なお,サブルーチンに渡すことのできる引数のなかに, 「手続き名」がある. これは,他の引数と異なり「変数」ではないので, 動的な変更はできないが, これが指定できるおかげで,MATH1のVRFNA/VIFNAの様に, サブルーチンを呼ぶ際に使うべき関数を指定することが可能になる. この場合,実引数に指定する手続き名は,サブルーチンを呼ぶ プログラムの中で EXTERNAL文またはINTRINSIC文により 手続き名であることを宣言しておかなければならない.
SINやCOSのような組み込み関数は, FORTRAN規格によって処理系が用意しなければならないが, FORTRAN規格はさらに処理系が規格外の組み込み関数を用意することを許している. したがって,各処理系はFORTRAN規格に定められた組み込み関数よりも多くの 組み込み関数を持っているのが普通である. そして当然のことながら,それら拡張された組み込み関数の名前に関しては, 処理系によって全く異なる. ということは, FORTRAN規格にない関数名で, EXTERNAL文やINTRINSIC文によって宣言されていない関数名は, 処理系によって組み込み関数として解釈されたり外部関数として解釈されたり することがある.
例えば,自分で外部関数を定義して, それを引用しているつもりでも, たまたまその処理系が同名の組み込み関数を持っていた場合には, ユーザー定義の関数ではなく, 処理系が用意した関数が呼ばれてしまう可能性がある. 関数名の長さは6文字以内と制限されている上, 人間の発想も多様性に限りがあるので, この手の衝突は意外に多い. しかも,この種の原因でプログラムが暴走したりすると, その原因を突き止めるのはかなり難しい. (自分のプログラムそのものには全く誤りがないのだから)
そこで,処理系に依存しないプログラムを書くためには, そのプログラム単位で引用している外部関数名は全て EXTERNAL文に指定することが望ましい. また,FORTRAN規格にない処理系固有の関数を引用している場合には, INTRINSIC文に指定すべきである.
なお,当然のことながら, 電脳ライブラリが用意する関数は全て「外部関数」である. 処理系から見れば電脳ライブラリを含めて全て「ユーザープログラム」 であるから, 自分が書かなかったからといって組み込み関数と混同することのないように 注意されたい.
初期のFORTRANでは引数によるデータの引渡しに時間がかかったため, COMMON文によりメインプログラムから サブルーチンへデータを引き渡す方法が多用された歴史がある. しかし,現在ではデータの引渡しに要する時間は, どちらの方法でもほとんど変らない. それより,COMMON文を使うと, どのサブルーチンで値が代入されているのかわかりにくくなってしまうので, 可読性を高めるという見地から, COMMON文によるサブルーチンへのデータ引渡はお勧めできない.
電脳ライブラリでも余程特殊な理由(内部の作業領域確保など)がない限り, COMMON文は使用していない. 各サブルーチンへのデータの引渡は全て引数を使っている.
ほとんどのシステムでは, 整変数がオーバーフローしても一切の警告を発する事なく, 上位の桁を切捨てて何事もなかったかのように計算を続ける.
これは非常に危険な事であるが, 乱数を発生させるプログラムなどでは意識的にオーバーフローさせて いる場合もある.
とにかく,大きな整数を扱う場合には, 注意が必要である.
実数値を整変数に代入すると,当然小数点以下は切捨てられるが, その切捨ては常に「0に近くなるように」切捨てられる. これは,正の範囲では数学的な切捨てと同じであるが, 負の範囲では異なる.(-1.5が切捨てられると-1になってしまう) また同様に, 組み込み関数のMODも負の範囲で数学的な剰余の定義と異なる.
数学的な切捨て及び剰余を求めたい場合には, FNCLIB または INTLIB のルーチンを使う.
べき乗計算X = B**Nでは, 基数Bの丸め誤差がN倍になって現れるので, 丸め誤差だけでもNが大きいとかなりの誤差を生じる. さらに,計算の過程で生じる誤差が加わると, 思わぬ誤差を生じてしまう. しかも,処理系によってこの誤差の生じかたが 大きく異なるのでたちが悪い.
べき乗の精度が気になるところでは, その部分だけ倍精度演算をするなどの安全対策を心がけた方がよい. 特に,基数Bの精度に気を付けること. たとえ,1つの計算機で所定の精度が得られても, 他の計算機で同じ結果が得られる保証はない.
FORTRAN 規格では 「定数は,それを表す文字列そのものが.型と値を決める」 とされている. したがって,定数の書き方によって値だけでなく, その精度も決められることになる. 例えば
DOUBLE PRECISION X .... X = 3.8*X*(1.-X) ....と書くと,せっかく倍精度計算をしているのにも関わらず, 定数 3.8 や 1. が単精度の定数なので, 全体として倍精度計算の精度が出ない. 正確にいうと 3.8 という定数はXとの乗算が行われる前に, 倍精度実数に変換されるのであるが, その倍精度実数の値はあくまで単精度の 3.8 であり, 倍精度の 3.8D0 とは値が異なる. このようなところでは,必ず
X = 3.8D0*X*(1.D0-X)と書かなければならない. これは,DATA文中の定数も同じである.
NUMAGUTI Atusi <a1n@gfdl.gov> Last Modified: Thu Aug 31 13:00:41 EDT 1995