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