- 全て g77 で確認。
このページがとても参考になる。
コンパイルオプションに -fbounds-check を入れる。
1) プログラム trapfpe.c を作る
/******************* trapfpe.c **********************/
#define _GNU_SOURCE 1
#include <fenv.h>
static void __attribute__ ((constructor))
trapfpe ()
{
feenableexcept (FE_INVALID|FE_DIVBYZERO|FE_OVERFLOW);
}
/**********************************************************/
2) trapfpe.c をコンパイル
gcc -c -o libtrapfpe.a trapfpe.c
3) 対象となるプログラムを libtrapfpe をリンクしてコンパイル。
オプションの -g を忘れないこと
g77 -o tmp -g tmp.f -L/ディレクトリ -ltrapfpe
4-1) 実行ファイルを gdb 上で実行
4-2) gdb ./tmp
4-3) gdb 上で run を実行。問題箇所が表示される。
例 1: 不正な計算を行った場合
------------------- サンプルプログラム --------------------
program samp1
real a,b
a=0.0
b=1.0
write(*,*) b/a
end
-------------------------------------------------------------
b を 0 で割っているので INF となる。上記の方法でデバッグすると gdb に
0x0804873e in MAIN__ () at samp1.f:5
5 write(*,*) b/a
と表示される。
例 2: 共有ライブラリの関数に不正な値を渡した場合
------------------- サンプルプログラム --------------------
program samp2
write(*,*) acos(2.0)
end
-------------------------------------------------------------
gdb 上で実行すると
(gdb) run
:
Program received signal SIGFPE, Arithmetic exception.
0x40051a9e in fegetexcept () from /lib/i686/libm.so.6
となる。これだけだとよく分からない(この場合は明らかだが)。
そこで backtrace コマンドで問題にたどり着く前の情報をみる。
(gdb) backtrace
#0 0x40051a9e in fegetexcept () from /lib/i686/libm.so.6
#1 0x0804877c in MAIN__ () at samp2.f:2
#2 0x08048806 in main ()
#3 0x4008bc1f in __libc_start_main () from /lib/i686/libc.so.6
これなら samp2.f の 2 行目が怪しいことが分かる。この方法はCでも使える。
iargc,getarg を使う。
------------------- サンプルプログラム --------------------
program sample
integer iargc,nargc
external iargc
character*16 argv
integer itmp
real rtmp
nargc=iargc() ! iargcは引数の数を返す
write(*,*) '引数の数は',nargc,'個'
do i=0,nargc
call getarg(i,argv) ! argv に i番目の引数を代入
write(*,*) argv
enddo
call getarg(1,argv)
read(argv,*) itmp !argv を整数:itmpに代入
call getarg(2,argv)
read(argv,*) rtmp !argv を実数:rtmpに代入
write(*,*) itmp,rtmp,itmp+rtmp
end
-------------------------------------------------------------
注:gfortran では iargc が無くなったので COMMAND_ARGUMENT_COUNT() を使う。
Ref.: COMMAND_ARGUMENT_COUNT
Fortan で使える乱数は g77 標準の rand や
Mersenne
Twister , CERNLIB の rndm, grndm 等がある。
Mersenne Twisswter のFortran77 用コード(バックアップ)。
コンパイラ、オプションによる速度の違い(Pen4 3.0 GHz でテスト)。
Mersenne Twister:grnd() を 2^27 回計算した場合
|
time ./a.out |
ifort |
3.3 sec |
g77 -O3 |
3.8 sec |
g77 |
13.3 sec |
関数による速度の違い
rand, rndm, grndm を 2^27 回計算した場合(最適化なし)
|
time ./a.out |
rand |
5.6 sec |
rndm |
2.9 sec |
grndm |
10.2 sec |
どの関数も周期性を持っているのでどの関数を使うかは自分で判断。 rndm の周期性は n 回目と n+1 回目
を2次元プロットすることで簡単に確認できる。
乱数に周期構造が現れる様子。 横軸は n 回目、縦軸は n+1 回目に call されたときの値。rndm
は周期的な構造が見える。
(
cernlib
と
GEANT3
のドキュメント)
読みたいファイルの行数が可変の場合は read の引数に end を使うと便利。
配列はファイルの十分な大きさを確保しておく(配列も可変にしても良いけど)。
------------------- サンプルプログラム --------------------
open(10,FILE='samp.dat')
do i=1,200
read(10,*,end=99) a(i),b(i),c(i) ! end of file で 99 番に飛ぶ
enddo
99 continue
close(10)
---------------------------------------------------------
文字列操作で知っておくと便利なこと
- 二つの文字列 str1, str2 を結合したい場合は str1//str2 とする
- 関数 len は文字列の長さを返す
- 関数 len_trim は空白を除いた文字列の長さを返す
- 整数を文字列に変換したい場合は write 文を使う
:
character*2 ci
i=5
write(ci,'(I2.2)') i ! 文字列 ci に 05 が代入される
:
- 文字列を整数 or 実数に変換したい場合は read 文を使う
:
character*4 ci
integer num
ci='2007'
read(ci,*) num ! 整数 num に 2007 が代入される
:
Fortran90 以降では他にも便利な関数があるけど環境が限られるので省略。
------------------- サンプルプログラム --------------------
program sample
implicit none
character*4 ci
character*6 cnum
character*16 str1,str2
integer num
str1='Othello'
str2='world?'
write(*,*) str1//str2
write(*,*) str1(3:8)//str2(1:5)//'!'
* integer to character
num=9801
write(cnum,'(I4.4)') num
write(*,*) 'PC'//cnum
write(*,*) 'len=',len(cnum)
write(*,*) 'len_trim=',len_trim(cnum)
* character to integer
ci='9'
read(ci,*) num
write(*,*) num**2
end
-------------------------------------------------------------
実行結果
Othello world?
hello world!
PC9801
len= 6
len_trim= 4
81
追記: "/"(スラッシュ)は制御編集記述子なのでそのままでは文字列変数に代入できない。
"/"を文字として使いたい場合は write(cs,'(A)') '/' の様に一度変数に代入すると
大丈夫。もっとスマートな方法は無いのか?
CERN Program Library
に含まれる乱数やヒストグラム作成機能を使ったサンプルプログラムを置いておきます。詳しいことは
cernlib のマニュアルを見て下さい。
上のファイルをどこか適当なところにコピーして make でコンパイル。 (cernlib
が無ければ当然コンパイルできない。Makefile 中のライブラリパス は各自の環境に合わせる)
[戻る]