FORTRANのおぼえがき。主にg77(GNUのFORTRAN77コンパイラ)について書く。本おぼえがきによって、同じことを何度も調べるという愚行が排除されるでしょう。
(制作中。わからないところ多し。間違いが有る可能性大。実行してないコードが多々あり。)
FORTRANでは、大文字と小文字の区別はない。
コメントは、行の最初に c, !, * を書くか、行末に ! を書く。
c ここはコメント ! ここはコメント para = 1 ! ここもコメント
行頭は6文字分のスペースを開ける。
para = 1.0
行頭6文字以内に、整数を書くこともできる。プログラムの行数を書いたり、goto文用に使う。
101 para1 = 1.1 102 para2 = 1.2 103 para3 = 1.3 104 goto 105 c ここは飛ばし 105 continue
1行は72文字以内に収める。72文字以上書いても無視される。
x8901 = 5+7+9+1+3+5+7+9+1+3+5+7+9+1+3+5+7+9+1+3+5+7+9+1+3+5+7+9012
長い式などを書きたい場合は、複数行に分けて書く。2行目以降を継続行と呼び、行頭から6文字目に空白でもゼロでもない文字を書くと、継続行となる。
x8901 = 5+7+9+1+3+5+7+9+ ! 最大72文字 &789 + ! 継続行(最大72文字) & +901 ! 継続行(最大72文字) + +901 ! 継続行(最大72文字)
行頭6文字以内にタブ文字があると、その行はタブフォーマットと呼ばれる行になり、1行に255文字目までかける。
para1=1 ! 最大255桁 101 para2=2 ! 最大255桁
変数名の頭文字はa〜zのアルファベットでなければならない。頭文字以外は、数字やアンダーバー(_)が使える。
season ! OK spring_8 ! OK autumn2winter ! OK _summer ! NG 4seasons ! NG
変数名は割りと長くても大丈夫かも。100文字の変数名が使えた(タブフォーマットの行)。
2バイト整数、4バイト整数を宣言する。
integer para1 ! 4バイト整数を格納する変数para1の宣言 integer*2 para2 ! 2バイト整数を格納する変数para2の宣言 integer*4 para3 ! 4バイト整数を格納する変数para3の宣言
メモ) 2バイト整数は、-32768 〜 32767 (-215 〜 215-1) の65536個 (216個=16bits=2bytes) の整数が扱える。4バイト整数は、-2147483648〜2147483647 (-231〜231-1) の232個 (=32bits=4bytes) の整数が扱える。
4バイト実数、8バイト実数を宣言する。
real para1 ! 4バイト実数を格納する変数para1の宣言 real*4 para2 ! 上と同義 real*8 para3 ! 8バイト実数を格納する変数para3の宣言
メモ) 4バイト実数は、単精度実数、8バイト実数は倍精度実数とも言う。倍精度実数の方が、単精度実数よりも精度が倍?より正確な計算をしたいときは、倍精度を使う。
配列の添字は1から始まる。
real*8 a(3) ! 8バイト実数型の配列 a(1),a(2),a(3)の宣言
添え字を0からにとか、-3からとかにしたい場合は、以下のように書く。
real*8 b(0:3) ! 配列 a(0),a(1),a(2),a(3)の宣言 real*8 c(-3:1) ! 配列 a(-3),a(-2),a(-1),a(0),a(1)の宣言
配列の数の省略はできない。
real*8 a(:) ! コンパイルエラー real*8 a(3:) ! コンパイルエラー
多次元配列の宣言
real*8 a(2,3) c 以下の6個の配列宣言 c a(1,1) a(1,2) a(1,3) c a(2,1) a(2,2) a(2,3) real*8 b(0:2, 1:3, -1:1) c 以下の27個の配列宣言 c b(0,1,-1) b(0,1,0) b(0,1,1) c b(0,2,-1) b(0,2,0) b(0,2,1) c b(0,3,-1) b(0,3,0) b(0,3,1) c c b(1,1,-1) b(1,1,0) b(1,1,1) c b(1,2,-1) b(1,2,0) b(1,2,1) c b(1,3,-1) b(1,3,0) b(1,3,1) c c b(2,1,-1) b(2,1,0) b(2,1,1) c b(2,2,-1) b(2,2,0) b(2,2,1) c b(2,3,-1) b(2,3,0) b(2,3,1)
配列を宣言するとき、配列の長さは定数でなければならず、変数にすることはできない。
implicit none integer m m=2 ! ここでエラー。宣言文の前に実行文が来てはいけない。 real a(m) ! これは、無理。parameter文を用いて同じようなことが出来る(下記参照)。 a(1)=1. a(2)=2. write(*,*) a end
定数式を用いた配列の宣言。FORTRANのparameter文は、C言語のプリプロセッサ命令 #define に似た役割をする?
implicit none integer m, n parameter(m=3, n=2) ! 定数3, 2 にm, nという名前をつける。 real a(m,n) ! 配列a(3,2)の宣言 a(1,1) = 1. a(2,1) = 2. a(3,1) = 3. a(1,2) = 4. a(2,2) = 5. a(3,2) = 6. write(*,*) a end
実行結果 |
---|
1. 2. 3. 4. 5. 6. |
character*6 str1, str2 ! 6つの文字(1バイト文字が6個)を格納できる変数str1とstr2の宣言 character str3*8, str4*10 ! 8文字を格納できる変数str3と10文字を格納できる変数str4の宣言
関数を使うときは、宣言しなければならない(ただし、implicit noneを書いたとき)。
implicit none real f,x x=1.0 write(*,*) f(x) end real function f(x) implicit none real x f = x+1 return end
関数の引数の型は関数内での型宣言と同じでなければならない。よって、以下のコードはエラーとなる。
implicit none real*8 f,x,y x=2.0d0 ! 倍精度実数の2.0d0がxに代入される y=2.0 ! 単精度実数の2.0が倍精度実数に型変換されてyに代入 write(*,*) f(x) ! ここは正しい write(*,*) f(y) ! ここも正しい write(*,*) f(2.0d0) ! ここも正しい write(*,*) f(2) ! 整数型を引数としているため、ここでコンパイルエラーとなる write(*,*) f(2.0) ! 単精度実数を引数としているため、ここもコンパイルエラーとなる write(*,*) f(float(2)) ! float()の戻り値は単精度実数であるため、ここもコンパイルエラーとなる end real*8 function f(x) implicit none real*8 x f = x+1 return end
宣言文を省略した場合、変数名の頭文字によって、その型が暗黙のうちに宣言される。これを暗黙の型宣言という。宣言のされ方は以下のとおり。
以下の2つのコードは同じ意味。
integer*4 i real*4 para i = 3 para = 4.0 write(*,*) i, para end | ⇔ |
i = 3 para = 4.0 write(*,*) i, para end |
コードの頭にimplicit noneと書く。
implicit none integer*4 i real*4 para i = 3 para = 4.0 write(*,*) i, para end
変数名の頭文字によって型を決定したいときは、implicit文を使う。
implicit integer(a-c,i,j) ! 頭文字がa, b, c, i, jで始まる変数は整数型になる。 implicit real*8(d-g,k) ! 頭文字がd, e, f, g, kで始まる変数は実数型になる。 aa = 1 ! 整数型変数 aa ii = 2 ! 整数型変数 ii dd = 1.0 ! 実数型変数 dd kk = 2.0 ! 実数型変数 kk mm = 2 ! 暗黙の型宣言に従い、mmは整数型変数 end
メモ) "inplicit none"と"implicit integer(a-c)"などは一緒に書けない。
read(*,*), write(*,*)については後述
倍精度で数値を代入するときは、最後にd0をつける。ただし、ファイルやキーボードからread(*,*)で読み込むときは、d0と書かなくても倍精度で読み込まれるらしい。
real*8 para1, para2 para1 = 1.1 ! 単精度の数値を代入 para2 = 1.1d0 ! 倍精度の数値を代入 c 1.1d0 は 1.1*10^0という意味。 c 1.1d-1 とすれば、 1.1*10^-1という意味になり、1.1d-1≒0.11。 write(*,*) "para1=", para1 write(*,*) "para2=", para2 read(*,*) para1 ! キーボードで1.1と入力 read(*,*) para2 ! キーボードで1.1d0と入力 write(*,*) "para1=", para1 write(*,*) "para2=", para2
実行結果 |
---|
para1= 1.10000002 para2= 1.1 para1= 1.1 para2= 1.1 |
文字列は"〜"または、'〜'で表す。" 自身を表したいときは、'〜'の中で用いるか、\"とする。
character*6 str1, str2 str1 = "abcdef" str2 = 'ghi' write(*,*) "str1=" , str1, ' str2=', str2 str1 = 'tes"t"' str2 = "tes\'t\"" write(*,*) "str1=" , str1, ' str2=', str2 end
実行結果 |
---|
str1=abcdef str2=ghi str1=tes"t" str2=tes't" |
write(*,*)の後ろにパラメータをカンマで区切って書く。
para1 = 2.0d0 para2 = 3.0d0 write(*,*) "paras:", para1, para2
実行結果 |
---|
paras: 2. 3. |
以下のように書くと、read(*,*)でデータが読み込まれなくなったとき(?)、文番号100にすすむ。
do read(*,*,end=100) a, b, c enddo 100 continue
以下のファイルを読む。
ファイル名: input |
---|
99911.0 99912.0 99913.0 99921.0 99922.0 99924.0 99931.0 99932.0 99933.0 |
入力/出力プロラムは以下のとおり。
real*8 a(3,3) open(10, file = "input") ! ファイルをオープンし、ファイルハンドルっぽいもの10を取得 read(10,*) a(1,1) read(10,*) a(2,1),a(2,2),a(2,3) read(10,*) (a(3,i), i=1,3) ! a(2,1),a(2,2),a(2,3) ⇔ (a(3,i), i=1,3) ? c ファイルはクローズしなくても良いかも? open(20, file = "output") write(20,*) a(1,1), a(1,2), a(1,3) write(20,*) a(2,1), a(2,2), a(2,3) write(20,*) (a(3,i), i=1,3) end
実行後、以下の内容のファイルができる(*にはなんか変な値が入る)。
ファイル名: output |
---|
99911. * * 99921. 99922. 99924. 99931. 99932. 99933. |
上記プログラムは、シェルのリダイレクションを用いても実現できる。プラグラムを以下のように変更。
real*8 a(3,3) read(*,*) a(1,1) read(*,*) a(2,1),a(2,2),a(2,3) read(*,*) (a(3,i), i=1,3) write(*,*) a(1,1), a(1,2), a(1,3) write(*,*) a(2,1), a(2,2), a(2,3) write(*,*) (a(3,i), i=1,3) end
したらば、シェルで以下を実行。
$ ./a.out < input > output
g77において、関係演算子に<、>、<=、>=が使えない。gfortranとかは使えるかも?とりあえず、関係演算子は以下のとおり。
関係演算子 | |
---|---|
演算子 | 意味 |
.gt. | greater than ( > ) |
.ge. | greater than / equal ( >= ) |
.lt. | less than ( < ) |
.le. | less than / equal ( <= ) |
.eq. | equal ( == ) |
.ne. | not equal ( != ) |
論理演算子は以下のとおり。
論理演算子 | |
---|---|
演算子 | 意味 |
.and. | かつ (&) |
.or. | または (|) |
使用法は以下の様。
if (i .eq. 10 .and. j .ne. 10) then write(*,*) "i is eqaul to 10, and j is not equal to 10." endif
ループ処理はC言語のwhile,for,do文のようにバリエーションがなく、do文ひとつしかない(ただし、書式によって動作が変わる)。
iを1から10まで1ずつ変化させる。
do i = 1, 10 write(*,*) "i=", i enddo
iを1から10まで2ずつ変化させる。
do i = 1, 11, 2 write(*,*) "i=", i enddo
ループを抜けるときは、goto文しかつかえないかも?
i = 1 do if (i .gt. 10) then goto 100 endif write(*,*) "i=", i i = i + 1 enddo 100 continue