神戸大学 大学院システム情報学研究科 計算科学専攻 陰山 聡



倍精度浮動小数点数の指定方法

kindパラメータ

計算科学では実数の物理量を数値的に表現するのに単精度ではなく、 倍精度の浮動小数点数を使うのが普通である。 (単精度実数では精度が不十分なため。)

まは、スーパーコンピュータは単精度浮動小数点ではなく、 倍精度浮動小数点数の演算(四則演算)を高速に処理できるよう設計されている。 従ってFortran90/95言語での倍精度浮動小数点数の表現方法に早めに慣れておくことは重要である。

Fortran90/95で倍精度浮動小数点数を表す方法は幾つかある。 最も簡単でportabilityの高い方法は以下の方法であろう。 まず、単精度浮動小数点数の精度に関係したある整数定数を定義する。 ここではその整数をSPという名前にする。 SPとはSingle Precision(単精度)の頭文字をとったものである。

integer, parameter ::  SP = kind(1.0)

次に、このSPを使って単精度として指定した浮動小数点数の2倍の精度を持つ浮動小数点数、 つまり倍精度浮動小数点数に関係したある整数定数(ここではDPという名前)を以下のようにして定義する。

integer, parameter ::  DP = selected_real_kind(2*precision(1.0_SP))

この整数定数DPはDouble Precisionを意味する。

こうして定義した二つの整数SPとDPを使って単精度浮動小数点や倍精度浮動小数点数を宣言する。

real(kind=SP) :: a
real(kind=DP) :: b

とすると、aは単精度浮動小数点、bは倍精度浮動小数点数である。

上のプログラムのkind=の部分は省略可能である。 つまり

real(SP) :: a
real(DP) :: b

としてもよい。

数値データの精度指定にもこのSPやDPを使う。

a = 1.0_SP    ! 単精度の1.0
b = 1.0_DP    ! 倍精度の1.0

サンプルプログラムを見てみよう。

program double_precision_real
  implicit none
  integer, parameter ::  SP = kind(1.0)
  integer, parameter ::  DP = selected_real_kind(2*precision(1.0_SP))
  real(SP) :: a
  real(DP) :: b
  a = 3.1415926535897323846264338327950288_SP
  b = 3.1415926535897323846264338327950288_DP
  print *,' a, b = ', a, b
end program double_precision_real

【演習】

上のプログラムをdouble_precision_real.f95という名前のファイルに保存し、 コンパイル&実行せよ。

【演習】

double_precision_real.f95に現れる二つのkindパラメータSPとDPには実際にはどんな値が入っているか調べよ。

数字表現

  • 浮動小数点数
    • 1.       ! デフォルト精度
    • 0.1_SP    ! kindパラメタSPの浮動小数点数
    • 3.141593_DP   ! kindパラメタDPの浮動小数点数
    • 0.31415926535897932e-1_DP ! 上に同じ

型名

C ( C99 )Fortran90/95補註
文字charcharacter
文字列char[n+1]character(len=n)nは文字長
整数intintegerinteger(kind=4)でも可
実数floatrealreal(kind=SP)でも可
倍精度実数doublereal(kind=DP)
「長い」整数longinteger(kind=8でも可)kindの整数はシステム依存
bool( _Bool )logical値は .true. または .false.
複素数( _Complex )complex
構造体structtype詳しくは後述

構造体 (derived type)

C言語の構造体とほとんど同じである。下の例をみよ。

program type
  implicit none

  type student
     character(len=20) :: first_name, last_name
     integer :: age
  end type student

  type(student) :: st

  st = student("Albert", "Einsten", 19)

  print *,st%first_name, st%last_name, st%age
end program type

構造体のメンバにアクセスするのに%を使う。

構造体の入れ子も可能である。

構造体の配列も可能である。

構造体を構造体に代入(コピー)することも可能である。

【演習】

上のプログラムをtype.f90に保存し、コンパイル&実行せよ。

【演習】

student型の構造体変数をもう一つ(例えばst2という名前)を作り、 stのデータをst2にコピーした上で、要素の一部(例えばage)を変更し、st2を出力せよ。

宣言

C言語では変数の宣言はブロックの先頭にまとめて置く。(C99やC++はもっと自由だが。) Fortran90/95でも同じ。

暗黙の型宣言

Fortran90/95ではimplicit noneを省略すると「暗黙の型宣言」をしたことになる。 暗黙の型宣言とは、次の6つのアルファベット i,j,k,l,m,n で始まる変数は整数である等のルールである。 使わない方がいい機能なので詳細は知らなくて良いだろう。 バグが入りやすいので、暗黙の型宣言は使わない方が良い。 Fortran90/95プログラムでは常にimplicit none宣言をすること。

暗黙の型宣言はなぜバグが入りやすいか

宣言したつもりのない変数を間違って使っていても気がつかない可能性がある。 例えば、次のプログラムにはバグがある。すぐに見つけられるか?

program use_implicit_none
  integer :: fresh_meat
  flesh_meat = 100 ! yen
  print *, "today's price = ", fresh_meat
end program use_implicit_none

【演習】

上のプログラムをuse_implicit_nene.f95というファイルに書き込み、コンパイル&実行せよ。

  • バグがあるのにコンパイラは教えてくれないだけでなく、 何事もないかのように正常終了する。しかも、それらしい答えを返す。 つまりバグがあることにすら気がつかない!

【演習】

今作ったuse_implicit_nene.f95の2行目(program ...の次の行)に implicit noneと書いてから、コンパイルせよ。

行の継続

行末に&をつけると継続行

a = b + c + &
       d  + f

セミコロン

セミコロンは改行と同じ

tmp = right
right = left
left = tmp

tmp = right; right = left; left = tmp

は同じ。

1次元配列

C言語では

array01[NX];

が大きさNXの1次元配列である。 メモリ上に、array01[0], array01[1], array01[2], ..., array01[NX-1]と並んでいる。

Fortran90/95では

integer, dimension(NX) :: array01

と宣言するとメモリ上に、array01(1), array01(2), array01(3), ..., array01(NX)と並ぶ。 配列のインデックスは1から始まる。

【演習】

大きさNXの1次元整数配列array_01を作り、 各要素に1,2,3,...,NXを代入した上で、 全要素の和をとるFortran90/95プログラムを書け。

2次元配列

厳密に言えばC言語には2次元配列はない。 あるのは「配列の配列」である。 つまり二つの整数インデックスiとjを使って

array02[j][i]

という形でアクセスできるものである。

このarray02を2次元配列とは呼べないということは、 array02のサイズ(インデックスiとjの範囲)は実行時に不定として、 array02をまるごと引数で受け取る関数がC言語では作れないことを考えれば納得出来るであろう。

メモリ空間中での2次元配列の各要素の位置

上記のarray02は、

array02[0][0], array02[0][1], array02[0][2], ..., array02[0][NX-1]

と並び、そして (それに続く場合もあるし、 まったく別の場所にあるかもしれない)ある位置から、

array02[1][0], array02[1][1], array02[1][2], ..., array02[1][NX-1]

という順番で並ぶ。

一方、 Fortran90/95ではarray02(i,j)という形でアクセスできる量は本物の2次元配列である。 サイズがわかっている場合には

integer, dimension(NX,NY) :: array02

と宣言する。 不明の場合には、

integer, dimension(:,:) :: array02

と宣言し、後ほど(実行時に)メモリをallocateする。 メモリ上では、array02の要素は

array02(1,1), array02(2,1), array02(3,1), ..., array02(NX,1), array02(1,2),...

に全ての要素(NX*NY個)が連続して並ぶ。

配列の要素がメモリ空間中に連続してあることは、 メモリへのアクセス速度が極めて重要となるスーパーコンピューティングでは極めて重要である。

3次元配列

C言語では

array03[k][j][i]

が上の意味での「擬似」3次元配列である。

Fortran90/95では

array03[i][j][k]

が3次元配列で、 宣言は

integer, dimension(NX,NY,NZ) :: array03

または

 integer, dimension(:,:,:) :: array03

とする。

if構文

if文はC言語とほとんど同じ。下の例を見れば一目瞭然であろう。

if (criterion) then
  action
end if

actionが1行で書けるなら

if ( criteron ) action

ともできる。

else-if構文も簡単である。

if (criterion_1) then
  action_1
else if (criterion_2 ) then
  action_2
else if (criterion_3 ) then
  action_3
end if

関係演算子

これも下の例を見ればわかるであろう。

if (a==b) then   ! aとbが等しい
if (a>b) then
if (a>=b) then
if (a<b) then
if (a<=b) then
if (a/=b) then    ! aとbが等しくない

関係演算子(古い書き方)

上の関係演算子はそれぞれ下のようにも書ける。

if (a.eq.b) then   ! aとbが等しい
if (a.gt.b) then
if (a.ge.b) then
if (a.lt.b) then
if (a.le.b) then
if (a.ne.b) then   ! aとbが等しくない

論理値

bool変数はlogicalという型名を持ち、

.true.

.false.

の二つをとる。

logical :: a, b, c
a = .true.
b = .false.
c = a .and. b
c = a .or. b
c = .not.a

繰り返し構文(doループ)

C言語でいうfor文である。

incrementが1の場合のdo-loop

program do_loop
  implicit none
  integer :: i
  do i = 1 , 100
     print *, ' i = ', i
  end do
end program do_loop

incrementが2の場合のdo-loop

program do_loop
  implicit none
  integer :: i
  do i = 1 , 100 , 2
     print *, ' i = ', i
  end do
end program do_loop

【演習】

  • 上のプログラムをdo_loop.f95に保存し、increment値(上の場合2)を自由に変えて実行せよ。
  • do_loop.f95のdo文を
    do i = 100 , -100 , -2 
    として実行せよ。

exitとcycle

整数のカウンターのないdo-loopも可能である。

do 
   ...
end do 

このままだと無限ループになるので、通常はある条件を満たせばループから抜け出すようにする。

C言語だとbreak文でループから抜け出す。(正確には最も内側のループから抜け出す。) Fortran90/95でbreakに相当するのはexitである。

do 
  ...
  if (condition) exit !---+
end do                !   | ループから抜け出す。
                      !<--+

C言語ではループの先頭に戻る時にはcontinue文を使うが、 それに相当するのはFortran90/95ではcycleである。

do                     !<--+
  ...                  !   | 戻る
  if (condition) cycle !---+
  ...
end do                      

関数

C言語と同様に関数(function)という手続き(procedure)は、 Fortran90/95プログラムの重要な構成部品である。 関数の使い方は以下の例を見ればわかるであろう。

【演習】

  • 次のプログラムをfunction01.f95として保存し、実行せよ。
    integer function next_int(i)
      implicit none
      integer, intent(in) :: i
    
      next_int = i+1
    end function next_int
    
    program function01
      implicit none
      integer, external :: next_int
    
      print *, 'ans = ', next_int(-100)
    
    end program function01
    intent(in)は引数iが入力引数であり、関数next_intの内部で変更されることがないことを保証するものである。
  • 関数を以下のように内部副プログラムとしてメインの中に含むこともできる。 次のプログラムをfunction02.f95として保存し、実行せよ。
    program function02
      implicit none
    
      print *, 'ans = ', next_int(-100)
    
    contains
    
      integer function next_int(i)
        integer, intent(in) :: i
    
        next_int = i+1
      end function next_int
    
    end program function02

サブルーチン

関数の本来の機能は入力に応じて出力を返すものであるが、 出力(返り値)が不要な場合も多い。 C言語ではこのような場合void関数とするが、 Fortran90/95ではvoid関数をsubroutineと呼ぶ。

サブルーチンを呼び出すにはcall subroutine名とする。 以下の例をみよ。

subroutine next_int(input, output)
  implicit none
  integer, intent(in)  :: input
  integer, intent(out) :: output

  output = input + 1
end subroutine next_int

program subroutine01
  implicit none

  integer :: i, j
  i = 10
  call next_int(i,j)
  print *, 'ans = ', j

end program subroutine01

値渡しと参照渡し

C言語は値渡し、Fortran90/95は参照渡しである。 C言語が値渡しであることは、以下の例でkの値が変わっていないことから確認できる。

void pass(int i)
{
  i = 0;
  printf("i=%d\n", i);
}

main()
{
  int k = 2010;
  printf("before: k = %d\n", k);
  pass(k);
  printf("after:  k = %d\n", k);
}

これと似たプログラム(同じではない)をFortran90/95で書くと以下のようになる。

subroutine pass(i)
  implicit none
  integer :: i
  i = 0;
  print *, "i=", i
end subroutine pass

program call_by_reference
  implicit none
  integer :: k = 2010

  print *, "before: k = ", k
  call pass(k)
  print *, "after:  k = ", k
end program call_by_reference

このプログラムではkの値はサブルーチンpassを読んだ後に変更されている。

【演習】

上の二つのプログラムをそれぞれcall_by_value.c、call_by_reference.f95という名前で保存し、実行せよ。

入出力属性

値渡しに比べて参照渡しは値をコピーする必要がないので実行が速い。 だが、上のcall_by_reference.f95プログラムで見たように、 引数として渡した変数がそのサブルーチン内部で勝手に(誤って)変更されると見つけにくいバグになり、危険である。 このような事故を防ぐためにFortran90/95には引数に入出力属性をつけることができる。 サブルーチンincrementの引数iは入力属性をもつと指定すると(下のプログラムの2行目)、 コンパイラがエラーを出してくれる。

subroutine increment(i)    ! コンパイルエラーを出してくれる
  integer, intent(in) :: i
  i = 0;
  print *, "i=", i
end subroutine increment

program call_by_reference
  implicit none
  integer :: k = 2010

  print *, "before: k = ", k
  call increment(k)
  print *, "after:  k = ", k
end program call_by_reference

【演習】

call_by_reference.f95を上のように変更してコンパイルせよ。

入出力属性の種類

入出力属性には以下の3種類がある。

intent(in)入力その手続き内で値は変更されない変数
intent(out)出力その手続き内で値が設定される変数
intent(inout)入出力両者の混合。デフォルト。

バグの混入を防ぐために、Fortran90/95プログラムではすべての引数に入出力属性をつけることを強く勧める。

case 構文

C言語でいうswitch文である。

select  case (case expression)
case (case selector 1)
  action 1
case (case selector 2)
  action 2
case (case selector 3)
  action 3
  .
  .
case default   ! なくても良い。
  default action
end select

C言語では普通breakが必要だがFortran90/95のcase文には不要である。

【演習】

次のプログラムをcase.f95に保存し、実行せよ。

program case
  implicit none
  integer :: month

  month = 6

  select case (month)
  case (1)
    print *,'January.'
  case (2)
    print *,'February'
  case (3:5)
    print *,'Spring'
  case default
    print *,'Other season'
 end select
end program case

文字列の比較

C言語では二つの文字列が等しいかどうかを判断するためにわざわざstrcmpを呼ぶ。 Fortran90/95では

if (string1==string2) 

if ( response == "what do you mean?" ) 

などと書ける。

従って長さの違う文字列もselect case構文で以下のように簡単に場合分けできる。

program case_string
  implicit none
  character(len=*), parameter :: color = 'white'

  select case (color)
  case ('aka')
    print *, 'red'
  case ('ao')
    print *, 'blue'
  case ('midori')
    print *, 'green'
  case default
    print *, "I don't know the color."
 end select
end program case_string

【演習】

上のプログラムをcase_string.f95というファイルに保存し、実行せよ。

様々な文字列操作

ここでFortran90/95における文字列操作機能を紹介しておく。 参考までにCでの同様な操作をコメントに書いておいた。

program strings
  implicit none

  character(len=*), parameter :: string01 = 'Computational'
  character(len=*), parameter :: string02 = 'Science'

  character(len=30) :: message30


  print *, ' length of string01 = ', len(string01)       ! strlen(string01)
  print *, ' string01==string02 = ', string01==string02  ! strcmp(string01,string02)
  print *, ' string01//string02 = ', string01//string02  ! strcat(string01,string02)
  print *, ' string01//string02 = ', string01//string02  ! strcat(string01,string02)
  print *, ' string01//string02//string02 = ', string01//string02//string02  ! ?

  message30 = string01   ! strcpy(message,string01)
  print *, ' message in 30 characters = ', message30
  print *, ' length of message30 is = ', len(message30)
  print *, ' length of message30 is = ', len_trim(message30)

  print *, ' message30(10:13) = ', message30(10:13)
  print *, ' message30//string02 = ', message30//string02
  print *, ' trim(message30)//string02 = ', trim(message30)//string02

end program strings

配列演算機能: スカラーの代入

まずはC言語版を見る。

/* array01.c */
#define NX 10
#define NY 12
#define NZ 14
main()
{
  int i, j, k;
  double A[NZ][NY][NX];

  for (k=0; k<NZ; k++) {
    for (j=0; j<NY; j++) {
      for (i=0; i<NX; i++) {
       A[k][j][i] = 0.0_DP;
      }
    }
  }
}

同じことをFortran90/95で「丁寧に」書けばこうなる。

!
! array01.f95
!
module constants
  implicit none
  integer, parameter :: SP = kind(1.0)
  integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))

  integer, parameter :: NX = 10
  integer, parameter :: NY = 12
  integer, parameter :: NZ = 14
end module constants

program array01
  use constants
  implicit none
  integer :: i, j, k
  real(DP), dimension(NX,NY,NZ) ::  A

  do k =1 , NZ
    do j = 1 , NY
       do i = 1 , NX
          A(i,j,k) = 0.0_DP
       end do
    end do
  end do
end program array01

普通はこう書く。module constantsは同じなのでmainプログラムだけ示す。

program array01
  use constants
  implicit none
  integer :: i, j, k
  real(DP), dimension(NX,NY,NZ) ::  A
  A = 0.0
end program array01

配列演算機能: 配列の要素毎の計算(その1)

C言語で

/*     array02.c	  */
#define NX 10
#define NY 12
#define NZ 14
main()
{
  int i, j, k;
  double A[NZ][NY][NX];
  double B[NZ][NY][NX];
  double C[NZ][NY][NX];
  for (k=0; k<NZ; k++) {
    for (j=0; j<NY; j++) {
      for (i=0; i<NX; i++) {
        A[k][j][i] = 3*B[k][j][i]-C[k][j][i];
      }
    }
  }
}

Fortran90/95ではこうなる。「丁寧に」書いた方法をコメントで示した。 (module constantsは同じ)

program array02
  use constants
  implicit none
  integer :: i, j, k
  real(DP), dimension(NX,NY,NZ) ::  A, B, C
!------------------------------------------
!   do k =1 , NZ
!     do j = 1 , NY
!        do i = 1 , NX
!           A(i,j,k) = 3*B(i,j,k)-C(i,j,k)
!        end do
!     end do
!  end do
!------------------------------------------
  A = 3*B-C
end program array02

配列演算機能: 配列の要素毎の計算(その2)

割り算でも同じである。

!
!  array03.f95
!
module constants
  implicit none
  integer, parameter :: SP = kind(1.0)
  integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
  integer, parameter :: NX = 10
  integer, parameter :: NY = 12
  integer, parameter :: NZ = 14
end module constants

program array03
  use constants
  implicit none
  integer :: i, j, k
  real(DP), dimension(NX,NY,NZ) ::  A, B, C
!------------------------------------------
!   do k =1 , NZ
!     do j = 1 , NY
!        do i = 1 , NX
!           A(i,j,k) = B(i,j,k)/C(i,j,k)
!        end do
!     end do
!  end do
!------------------------------------------
  A = B/C
end program array03

配列演算機能: 組み込み関数の引数

Fortran90/95には数学関係の豊富な組み込み関数が用意されている。 それらの引数に配列を渡すと配列の全要素についてその関数を適用してくれる。 たとえば、C言語で

/*
 *   array04.c
 *                   cc -lm array04.c
 */
#include <math.h>
#define NX 10
#define NY 12
#define NZ 14
main()
{
  int i, j, k;
  double A[NZ][NY][NX];
  double B[NZ][NY][NX];
  for (k=0; k<NZ; k++) {
    for (j=0; j<NY; j++) {
      for (i=0; i<NX; i++) {
        A[k][j][i] = sin(B[k][j][i]);
      }
    }
  }
}

というプログラムは、Fortran90/95では下のようになる。

!
!  array04.f95
!
module constants
  implicit none
  integer, parameter :: SP = kind(1.0)
  integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
  integer, parameter :: NX = 10
  integer, parameter :: NY = 12
  integer, parameter :: NZ = 14
end module constants

program array04
  use constants
  implicit none
  integer :: i, j, k
  real(DP), dimension(NX,NY,NZ) ::  A, B
!------------------------------------------
!   do k =1 , NZ
!     do j = 1 , NY
!        do i = 1 , NX
!           A(i,j,k) = sin(B(i,j,k))
!        end do
!     end do
!  end do
!------------------------------------------
  A = sin(B)
end program array04

便利な配列演算(その01)

そろそろFortran90/95にも慣れてきたと思うので、 C言語との比較ではなく、Fortran90/95言語で説明する *1

real(DP), dimension(NX,NY)    :: array02d
real(DP), dimension(NX,NY,NZ) :: array03d
do j = 1 , NY
  do i = 1 , NX
    array03d(i,j,1) = arrya02d(i,j)
  end do
end do 

というプログラムは、コロンを使えば次のように簡潔に記述できる。

real(DP), dimension(NX,NY)    :: array02d
real(DP), dimension(NX,NY,NZ) :: array03d
array03d(:,:,1) = arrya02d(:,:)

と書ける。

便利な配列演算(その02)

real(DP), dimension(10) :: A
real(DP), dimension(15) :: B
do i = 1 , 10
  A(i) = B(i+5)
end do 

は、

real(DP), dimension(10) :: A
real(DP), dimension(15) :: B
A(:) = B(6:15)

と書ける。多次元でも同様で、

A(11:15,21:30) = B(111:115,121:130),

などと書ける。両辺の配列の各次元のサイズが一致している必要がある。

配列を返す関数

組み込み関数だけでなく、 「配列を受け取って配列を返す」関数を自分で定義することができる。 下に示すのは結構本格的なプログラムである。 乱数を生成する組み込み関数(サブルーチン)random_numberをcallしている。 意味は明確であろう。

!
!  array_func.f95
!
module constants
  implicit none
  integer, parameter :: SP = kind(1.0)
  integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
  integer, parameter :: NX = 10
  integer, parameter :: NY = 12
end module constants

module array_sample
  use constants
  implicit none
  private
  public :: shift

contains  

  function average(a)
    real(DP), dimension(NX,NY), intent(in) :: a
    real(DP) :: average
    average = sum(a) / (NX*NY)
  end function average

  function shift(a)
    real(DP), dimension(NX,NY), intent(in) :: a
    real(DP), dimension(NX,NY) :: shift
    real(DP) :: mean
    mean = average(a)
    shift(:,:) = a(:,:) - mean
  end function shift
end module array_sample

program array_func
  use constants
  use array_sample
  implicit none
  integer :: i, j
  real(DP) :: random
  real(DP), dimension(NX,NY) :: array01, array02
  do j = 1 , NY
     do i = 1 , NX
        call random_number(random)
        array01(i,j) = random
     end do
  end do
  array02 = shift(array01)
end program array_func

便利な組み込み関数

ベクトルの内積

v1とv2を1次元配列(ベクトル)として、内積は

value = dot_product(v1,v2)

と書ける。

sum = 0.0_DP
do i = 1 , NX
  sum = sum + v1(i)*v2(i)
end do

といった計算を一度にやってくれる便利な関数である。

行列・ベクトルのかけ算

a, bを行列(2次元配列)、 v1, v2をベクトル(1次元配列)として、

c = matmul(a,b)   ! 行列aとbのかけ算
v2 = matmul(c,v1)  ! 行列かけるベクトル

などと書ける。

行列の転置

transpose(a)

配列の全要素中の最大値と最小値

maxval(a)
minval(a)

配列の全要素の和

sum(a)

配列の全要素の積

product(a)

配列の上下限の大きさ

a(3:10,4:20)

に対して

lbound(a,dim=1)

は3を

ubound(a,dim=2)

は20を返す。

size(a,dim=1)

は8である。 意味は明白であろう。

不定サイズの配列とメモリ割当

サイズが不定の配列aは以下のようにallocatableをつけて宣言する。

real(DP), dimension(:,:), allocatable :: a

実際にaを使う時には

allocate(a(10,20))

などとallocate文を使ってメモリに確保する。メモリを開放するには

deallocate(a)

とする。

サイズが不定の配列の渡し方

実行時に配列サイズが決まる3次元配列をサブルーチンと関数に渡すサンプルプログラムを下に示す。

!
!  array_subroutine.f95
!
module constants
  implicit none
  integer, parameter :: SP = kind(1.0)
  integer, parameter :: DP = selected_real_kind(2*precision(1.0_SP))
end module constants

module array
  use constants
  implicit none

contains  

  function average(a)
    real(DP), dimension(:,:), intent(in) :: a
    real(DP) :: average
    average = sum(a) / (size(a,1)*size(a,2))
  end function average

  subroutine shift(a)
    real(DP), dimension(:,:), intent(inout) :: a
    real(DP) :: mean
    mean = average(a)
    a = a - mean    ! or you can write a(:,:) = a(:,:) - mean
  end subroutine shift
end module array

program array_subroutine
  use constants
  use array
  implicit none
  integer :: nx, ny
  integer :: i, j
  real(DP) :: random
  real(DP), dimension(:,:), allocatable :: array2d

  call random_number(random)
  nx = 10*random; ny = nx
  print *,'  nx = ', nx
  allocate(array2d(nx,ny))
  do j = 1 , ny
     do i = 1 , nx
        call random_number(random)
        array2d(i,j) = random
     end do
  end do
  print *,'  before: ', average(array2d)
  call shift(array2d)
  print *,'   after: ', average(array2d)
  ! call report
end program array_subroutine

【課題1】

(1) 上のarray_subroutine.f95プログラムの最後から2行目のコメントをはずして reportというサブルーチンをcallするようにした上で、

(2) 次のサブルーチンを module arrayの中に入れ、

  subroutine report
    character(len=8)  :: date
    character(len=10) :: time
    call date_and_time(date,time)
    print *,date(3:8)//'/'//time(1:2)//':'//time(3:4)//':'//time(5:6)
  end subroutine report

(3) コンパイル&実行してその実行結果を確認し、

(4) 問題がなさそうであれば(出力が4行であれば)

./a.out | mail kage

としてその実行結果を送信せよ。("scalar"でのみ有効。)

【課題2】

上のarray_subroutine.f95プログラム中のサブルーチンshiftは 「配列aの全要素の平均値がゼロとなるように配列aを変更する」 という機能を持っている。

この機能を自分の好きなように変えて新しいサブルーチンにした上で、

  1. どのような機能を持ったサブルーチンに変更したという説明
  2. 変更したshiftルーチンのソースコード(他の部分は不要)
  3. 実行結果

の3つをレポートにまとめて次回の演習時に提出せよ。

ただし、reportルーチンの出力は外さないこと。 averageルーチンの出力は外しても良い。

授業アンケート

これで全2回の「Fortran90/95入門」は終わりです。 全体的に難易度はどうでしたか?

選択肢 投票
簡単すぎた 0  
難しすぎた 0  
ちょうどよかった 8  

C言語との比較による説明の仕方について

選択肢 投票
Cとの比較で良い 5  
CよりもJavaと比較して欲しい 1  
他の言語との比較はしない方がわかりやすい 1  

as of 2019-10-22 (火) 15:27:03 (19290)

注釈


*1 ブートストラップ解説とでも言うべきか。