Edited at
FortranDay 1

Fortranにおける整数型・実数型・複素数型変数の宣言方法


概要

Fortranの整数,実数,複素数型の型宣言についてまとめました.本当は文字型や配列なども含めた包括的な記事にしたかったのですが,型の指定の話だけでとても長くなったので,文字と配列については別の記事で言及することにしました.

結局どうしよう?にまとめがあるので,時間のない方はそこまで飛ばしてください.


使用環境

コンパイラ
バージョン

intel Parallel Studio XE Composer Edition for Fortran
17.0.4.210

PGI Visual Fortran for Windows
18.7

gfortran
4.10.0 (for mingw32)
7.3.0 (for Ubuntu on WSL)


謝辞

光のインターネットの闇@no_maddo 氏に貴重なご意見を頂いた.ここに記して謝意を表する.


Fortranの型宣言の基本

([kind=]種別)[,属性,属性,...] :: 変数名[=初期値]

[]は省略可能であることを意味しています.

で整数か実数か複素数か(あるいは他の型)を指定し,種別で精度(表現できる数値の範囲)を指定します.定数や動的割付などの情報を,属性という形で型の後ろに追加していきます.

種別は,ほとんどの場合変数のサイズ(バイト数)です.それ以外の状況を見たことはありませんが,コンパイラによってはそうでないという情報があります1

kind==初期値は省略可能ですが,定数を宣言する場合は初期値の代入が必須です.

宣言時の初期値代入ができない場面もあります.

また,宣言時に初期値を代入すると変数が静的変数になるので,宣言時の初期値代入は原則としてしないよう習慣づけた方が無難です.宣言時に値を代入するのは以下の場合くらいでしょう.

1. 定数を宣言する場合

2. 静的変数を宣言する場合

古い書き方では型と変数名はスペースで区切りましたが,現在はコロン二つ::で区切ります.


属性

属性はこの記事の主眼ではないので,一覧表だけ置いておきます.この記事では,parameter属性を使用しています.


変数の基本的な属性

属性
機能

parameter
定数であることを示す

save
静的変数であることを示す

dimension(:[,:,...])
(多次元)配列であることを示す

allocatable
動的割付けする変数・配列であることを示す

pointer
ポインタ変数であることを示す

target
ポインタ変数によって参照可能な変数(pointee)であることを示す

volatile
プログラム以外(例えばハードウェア)から変更される可能性のあることを示す2


仮引数の属性

属性
機能

intent({in,out,inout})
関数・サブルーチンの仮引数が{読取り用,書込み用,読み書き可能}であることを示す

value
関数・サブルーチンの仮引数が値渡しであることを示す

optional
関数・サブルーチンの仮引数が省略可能であることを示す


アクセス制御

属性
機能

public
変数を派生型外部あるいはモジュール外部に公開されることを示す

private
変数を派生型外部あるいはモジュール外部に公開しないことを示す

protected
変数をモジュール外部に読取り専用で公開することを示す


C言語との相互利用

属性
機能

bind(c)
C言語と相互利用可能であることを示す(列挙型もしくは派生型)


整数型

整数型を例に,Fortranにおける型宣言を見てみましょう.

範囲
表記
Intel
PGI
gfortran
備考

integer



コンパイルオプションでサイズを変更可能
Intel /integer_size:{16,32,64}3
PGI -i{2,4,8}
gfortran -fdefault-integer-84

$-2^7$~$2^7-1$
integer(1)



kind=を省略

integer*1



古い書き方

$-2^{15}$~$2^{15}-1$
integer(2)


integer*2


$-2^{31}$~$2^{31}-1$
integer(4)


integer*4


$-2^{63}$~$2^{63}-1$
integer(8)


integer*8


ところが,これらはいずれも推奨されない書き方のようです1.kindで指定する種別はバイト数と同じと思われますが,コンパイラによってはバイト数を意味しないことがあるようで,互換性の高いプログラムを書く上では推奨されません.

コンパイラ間の可搬性もないのに互換性の高いプログラムとは・・・

selected_int_kind()kind()で種別を指定するのが正しい方法とされています.また,Fortran 2008からは,型とビット数を名前に持つ定数が,種別を表すために追加されました.

表記
Intel
PGI
gfortran
備考

integer(selected_int_kind(r=桁数))



桁数rに応じた種別が用いられる

integer(kind(x=整数))



引数として渡した整数xを取り扱える種別が用いられる
標準の種別が優先される

integer(int8)


iso_fortran_envモジュールをuseする

integer(int16)



integer(int32)



integer(int64)



selected_int_kind()kind()の実行例を以下に示します.コメントは実行結果で,IはIntel Fortran,PはPGI Fortran,gはgfortranを表しています.

program main

implicit none
print *,selected_int_kind(2) !I 1 P 1 g 1
print *,selected_int_kind(4) ! 2 2 2
print *,selected_int_kind(8) ! 4 4 4
print *,selected_int_kind(16) ! 8 8 8
print *,kind(31) ! 4 4 4
print *,kind(127) ! 4 4 4
print *,kind(32767) ! 4 4 4
print *,kind(2147483647) ! 4 4 4
print *,kind(9223372036854775807) ! 8 8 4
end program main

selected_int_kind()は引数で指定した桁数を正しく表現できる種別を返します.kind()は,引数で渡した数が標準となるintegerの種別で表現できるなら標準の種別を,表現できないのであれば,正しく表現できる種別を返してくるようです.

gfortranではコンパイルエラーが出ます.

   print *,kind(9223372036854775807) !  8   8   4

1
Error: Integer too big for its kind at (1). This check can be disabled with the option -fno-range-check

そのエラーメッセージに沿って-fno-range-checkオプションを付与してコンパイル・実行した結果です.正確に記述するには,数値リテラルの種別を指定して,9223372036854775807_int64などと書く必要があります.数値リテラルの記述方法は後ほど説明します.

*?でバイト数を指定する古い書き方では,上記のように数字をselected_int_kind()などに置き換えて種別を指定することはできません.

表記
Intel
PGI
gfortran
備考

integer*selected_int_kind(r=桁数)
×
×
×
integer*?との類似性に基づく

integer*kind(x=整数)
×
×
×
integer*?との類似性に基づく


2進数,8進数,16進数

変数を2,8,16進数で表示することも,2,8,16進数で表現した数値リテラルを変数に代入することもできます.

数値リテラルとして取り扱う場合は,基数を表す記号の後ろに' 'あるいは" "で囲んだ数値を置きます.

16進数の場合,HEXに含まれるいずれの文字でもなくZを使っています.書式指定において,H, E, Xはそれぞれホレリス定数,実数の指数表示,空白の制御が割り当てられているためです.

基数
基数を表す記号
表記例

2
B
B'1111100111'

8
O
O'1747'

16
Z
Z'3E7'

表示する場合には,print文もしくはwrite文の書式で指定します.

基数
書式例

2
'(B8)'

8
'(O4)'

16
'(Z4)'

program main

implicit none
integer :: e
e = 127
print '(B8)',e ! 1111111
print '(O4)',e ! 177
print '(Z4)',e ! 7F

print *,B'11',O'77',Z'FF' ! 3 63 255
end program main


実数型

整数型に続いて,実数(正確には浮動小数点数)型の型宣言を確認します.Fortranの実数型には,単精度,倍精度,4倍精度実数があります.

実数型は実数型でまたややこしい状況です.とりあえずは,正しいとされている,selected_real_kind()およびkind()を用いる方法を示します.

表記
Intel
PGI
gfortran
備考

real(selected_real_kind([p=桁数,r=指数範囲,radix=基数]))



桁数pあるいは指数範囲rに応じた種別が用いられる
PGIはradixをサポートしていない

real(kind(x=実数))



引数の実数xを取り扱える種別が用いられる

selected_real_kind()の引数は,10進数での有効桁数を指定するp, 指数の範囲を指定するrのどちらか,あるいは両方を指定します.prで要求される精度が異なる場合は,精度の高い方が採用されます.radixは計算機が有している実数体系(数の内部表現)の基数のようですが,著者が扱える計算機を調べた範囲では,2しかありません5

実数の型
桁数p

指数の範囲r

単精度
6
37

倍精度
15
307

4倍精度
33
4931

kind()の引数に実数を渡しますが,ここでは0を渡すことが一般的です.このとき,単純に0を渡すと整数に対する標準の種別が返されてしまいます.一方で,0.0とすると単精度実数扱いになります.そこで,指数表記を使って精度を指定します.

実数の型
指数記号
指数表記

単精度
e
0e0

倍精度
d
0d0

4倍精度
q
0q0

数値リテラルの記述方法は後ほど説明します.

selected_real_kind()kind()の実行例を以下に示します.

program main

implicit none
print *,selected_real_kind(p=6,r=37) !I 4 P 4 g 4
print *,selected_real_kind(p=15,r=307) ! 8 8 8
print *,selected_real_kind(p=33,r=4931) ! 16 -3 16
print *,selected_real_kind(p=5,r=300) ! 8 8 8
print *,kind(0e0) ! 4 4 4
print *,kind(0d0) ! 8 8 8
print *,kind(0q0) ! 16 16
end program main

selected_real_kind(p=5,r=300)では,有効桁数は単精度の範囲内ですが,指数範囲は倍精度でなければ表現できないので,戻り値として倍精度を表す8が得られています.

PGIコンパイラは4倍精度実数をサポートしないので,0q0を用いるとコンパイルエラーになります.同様の理由で,selected_real_kind(p=33,r=4931)の戻り値が-3になっています.radixは正しい6prの要件を満たすことができないときに出るエラーのようです.

このように種別を指定することで,取り扱いたい実数に適した精度を持つ変数を宣言できますが,個別の実数の宣言方法も確認しておきましょう.


単精度

単精度実数の宣言には以下の方法があります.

表記
Intel
PGI
gfortran
備考

real



コンパイルオプションで精度を変更可能
/real_size:{32,64,128}
-r8
-fdefault-real-84

real(4)


real*4


single precision
×
×
×
倍精度との比較のため

real(real32)


iso_fortran_envモジュールをuseする

仕様の観点から種別を指定しないrealの使用が推奨されています1が,コンパイルオプションで精度を変更できてしまいます.これは移植性や互換性という観点からするとどうなんでしょう?計算機やコンパイラが変わっても精度を固定したいのであれば,種別を指定した方がよいでしょう.

gfortranの場合はさらに厄介で,コンパイルオプション次第でいくらでも精度を変更できます.

program main

use,intrinsic :: iso_fortran_env
implicit none
real :: a
real(4) :: b
real*4 :: c
real(real32) :: d
print *, sizeof(a), sizeof(b),sizeof(c),sizeof(d)
!4 4 4 4
!8 4 4 4 /real_size:64, -r8, -fdefault-real-8を付与
!16 4 4 4 /real_size:128を付与
!8 8 8 8 -freal-4-real-8を付与
!12 12 12 12 -freal-4-real-10を付与
!16 16 16 16 -freal-4-real-16を付与
end program main

single precisionという型は利用できませんが,次に紹介する倍精度にはdouble precisionという型宣言が存在するので,統一的な表記のために利用できないかを確認する目的で掲載しています.


倍精度

表記
Intel
PGI
gfortran
備考

real(8)


real*8


double precision



コンパイルオプションで精度を変更可能
/double_size:{64,128}
-r47
-fdefault-real-88

real(real64)


iso_fortran_envモジュールをuseする

仕様の観点から,double precisionの使用が推奨されています1.単精度と同様にコンパイルオプション次第で精度が変わります.精度を固定するには,種別を指定する必要があると思われます.

program main

use,intrinsic :: iso_fortran_env
implicit none
double precision :: a
real(8) :: b
real*8 :: c
real(real64) :: d
print *, sizeof(a), sizeof(b),sizeof(c),sizeof(d)
!8 8 8 8
!16 8 8 8 /real_size:128, -fdefault-real-8を付与
!4 4 4 4 -freal-8-real-4を付与
!12 12 12 12 -freal-8-real-10を付与
!16 16 16 16 -freal-8-real-16を付与
end program main


4倍精度

Fortranの一部のコンパイラでは,倍精度より高い精度の実数を利用できます.PGIコンパイラは4倍精度実数を利用できません.

表記
Intel
PGI
gfortran
備考

real(16)

×

real*16

×

quad precision
×
×
×
倍精度との比較のため

real(real128)

×

iso_fortran_envモジュールをuseする

single precisionと同様,quad precisionという型は利用できません.

4倍精度実数の場合,仕様的にどうするのが正しいのかはわかりませんが,種別を指定すれば問題ないでしょう.


モジュールを用いた種別の指定

selected_real_kind()あるいはkind()を変数宣言ごとに記述するのは煩わしいので,モジュールに種別を表す定数を定義することが行われているようです.

module floating_point_kinds

implicit none
integer,parameter :: sp = selected_real_kind(6) !single precision
integer,parameter :: dp = selected_real_kind(15) !double precision
end module floating_point_kinds

program main
use floating_point_kinds, only : sp, dp
implicit none
real(sp) :: a
real(dp) :: b
a = 0 !暗黙の型変換を利用
b = 0 !
print *,a,b !0.0000000E+00 0.000000000000000E+000
end program main

このようなモジュールを使うことで,selected_real_kind()等で種別を定義するのが一度だけで済みます.

また,かなり小賢しい方法で精度を切り替えることができます.(このような方法が許されるのかはわかりませんが)

program main

use floating_point_kinds, only : sp, dp=>sp !rename参照
implicit none
real(sp) :: a
real(dp) :: b
a = 0 !暗黙の型変換を利用
b = 0 !
print *,a,b !0.0000000E+00 0.0000000E+00
end program main

dp=>spは,モジュールの変数spをローカル変数dpとして参照することを意味しています.つまり,上のプログラムでは,dpfloating_point_kindsで定義された,倍精度実数の種別を表す定数ではなく,単精度実数の種別を指すローカル変数という扱いになります.そのため,わずかな変更でreal(dp)を倍精度実数から単精度実数に,同様の方法でreal(sp)を単精度から倍精度に切り替えることができます.

それ以外には,プリプロセッサディレクティブを用いて精度を切り替える方法もあります.

module floating_point_kinds

implicit none
integer,parameter :: sp = selected_real_kind(6) !single precision
integer,parameter :: dp = selected_real_kind(15) !double precision

#ifdef DOUBLE
integer,parameter :: fp = dp
#else
integer,parameter :: fp = sp
#endif
end module floating_point_kinds

program main
use floating_point_kinds, only : fp
implicit none
real(fp) :: a
a = 0
print *,a
end program main

このような精度を切り替えは,アクセラレータで動作するプログラムを作成する際に利用されます.安価なGPUでは,倍精度実数の実効性能が単精度実数に対するそれと比較して非常に遅いことがあります.精度を切り替えることで,プログラムの実効性能を改善することができます.

プリプロセッサディレクティブを処理するためのコンパイルオプションは,以下の通りです.

コンパイラ
オプション

intel
/fpp /D識別子
/fpp /DDOUBLE

PGI
-Mpreprocess -D識別子
-Mpreprocess -DDOUBLE

gfortran
-cpp -D識別子
-cpp -DDOUBLE


複素数型

Fortranでは複素数を取り扱う型があります.宣言の方法は実数型と同じですが,*?でバイト数を指定する方法は倍精度と差異があり,統一的ではありません.

表記
Intel
PGI
gfortran
備考

complex(selected_real_kind([p=桁数,r=指数範囲,radix=基数])



桁数あるいは指数範囲に応じた種別が用いられる

complex(kind(x=実数))



引数の実数を取り扱える種別が用いられる

実数型変数2個をそれぞれ実部と虚部に充てているので,実数型のサイズを変化させるコンパイルオプションを付与するとそれに併せてサイズが変化します.


単精度

表記
Intel
PGI
gfortran
備考

complex



コンパイルオプションで精度を変更可能
/real_size:{32,64,128}
-r8
-fdefault-real-8

complex(4)


complex*4
×
×
×
単精度実数の型宣言との比較のため

complex*8


single complex
×
×
×
倍精度との比較のため

種別を指定すると,種別を表す数字と精度が実数型変数と同じになりますが,*?でバイト数を指定する場合は ?に入れる数字は精度を指定する種別の2倍 になります.実数型変数が2個あるためです.

complexの使用が推奨されています1.コンパイルオプションによってサイズが変わるため,精度を固定する場合には種別を指定する必要があります.

program main

use,intrinsic :: iso_fortran_env
implicit none
complex :: a
complex(4) :: b
complex*8 :: c
complex(real32) :: d
print *, sizeof(a), sizeof(b),sizeof(c),sizeof(d)
!8 8 8 8
!16 8 8 8 /real_size:64, -r8, -fdefault-real-8を付与
!16 16 16 16 -freal-4-real-8を付与
!24 24 24 24 -freal-4-real-10を付与
!32 32 32 32 -freal-4-real-16を付与
end program main


倍精度

表記
Intel
PGI
gfortran
備考

complex(8)


complex*16


double complex



コンパイルオプションで精度を変更可能
/double_size:{64,128}
-r4
-fdefault-real-8

complex(real64)


iso_fortran_envモジュールをuseする

double complexはコンパイルオプションで倍精度実数のサイズを変えると,それに伴って変化します.

倍精度複素数については,double complexではなく,complex(kind(0d0))で種別を取得することが推奨されています1.この統一感のなさは何でしょうか.

program main

use,intrinsic :: iso_fortran_env
implicit none
double complex :: a
complex(8) :: b
complex*16 :: c
complex(real64) :: d
print *, sizeof(a), sizeof(b),sizeof(c),sizeof(d)
!16 16 16 16
!32 16 16 16 /double_size:128, -fdefault-real-8を付与
!8 8 8 8 -freal-8-real-4を付与
!24 24 24 24 -freal-8-real-10を付与
!32 32 32 32 -freal-8-real-16を付与
end program main


4倍精度

PGIコンパイラは4倍精度実数を利用できないので,同様に4倍精度複素数は利用できません.

表記法
Intel
PGI
gfortran
備考

complex(16)

×

complex*32

×

quad complex
×
×
×
倍精度との比較のため

complex(real128)

×

iso_fortran_envモジュールをuseする


数値リテラル

kind()で種別を指定する場合,渡す数値を正しく指定しないと想定外の種別を得てしまうことになります.また,プログラム中に数値リテラルを記述するにしても,整数型のところで見たように,種別を指定しないと正しく表現できません.あるいは暗黙の型変換によって実行時間が長くなる可能性もあります.わずかでも実行時間が遅くなるなら余計なものはいらない!とおっしゃるFORTRAN使いの皆さんはもちろん気をつけていますよね?


整数

数値リテラルは,数値[_kind]として記述します._kindはなくても構いません(というか基本的につけません)が,種別を明記したい場合には,_kindで種別を指定します._kindは数値か定数です.

program main

use,intrinsic :: iso_fortran_env
implicit none

integer(int8),parameter :: k=4
print *,kind(0) !4
print *,kind(0_1),kind(0_int8) !1 1
print *,kind(0_2),kind(0_int16) !2 2
print *,kind(0_4),kind(0_int32) !4 4
print *,kind(0_8),kind(0_int64) !8 8
print *,0_4 !数値で種別を指定
print *,0_int32 !定数で種別を指定
print *,0_k !ユーザ定義の定数でも種別を指定可能
end program main

整数の型宣言のところでも説明しましたが,整数型は標準の種別を返すので,1バイトの整数型が欲しいのにinteger(kind(0)) ::としてしまうと,1バイトより大きな整数型になってしまいます.


実数

実数も整数と同様に数値[_kind]で指定します.このとき,数値は実数で書く必要があります._kindの指定にint32real32などの定数を使ったとしても,その実体はただの数字でしかなく,型の情報を持たないためです.

program main

use,intrinsic :: iso_fortran_env
implicit none

integer(int8),parameter :: k=4
print *,0_real32,0.0_real32 !0 0.0000000E+00
print *,0.0_real64,0.0_real128 !0.000000000000000E+000 0.000000000000000000000000000000000E+0000
print *,0.0_k !0.0000000E+00
end program main

他には,指数表記する際に指数記号を型に応じて変化させる書き方があります.ただし,これらはコンパイルオプションによって精度が変化するので注意が必要です.

実数の型
指数記号

単精度
e

倍精度
d

4倍精度
q

指数を指定しますが,仮数部は整数である必要はなく,$0.1\times 10^{2}$のような記述も可能です.

program main

use,intrinsic :: iso_fortran_env
implicit none

print *,0e0,0d0,0q0 !0.0000000E+00 0.000000000000000E+000 0.000000000000000000000000000000000E+0000
print *,1e-14,1d2,0.314q-33 ! 9.9999998E-15 100.000000000000 3.140000000000000000000000000000000E-0034

!/real_size:64 /double_size:128, -fdefault-real-8を付与して実行
print *,0e0,0d0 !0.000000000000000E+000 0.000000000000000000000000000000000E+0000
end program main


複素数

複素数の数値リテラルを記述する場合は,実部と虚部に相当する実数をカンマで区切って書き,それらを丸括弧で囲みます.実数を代入すると,虚部を0と扱うようです.

program main

use,intrinsic :: iso_fortran_env
implicit none

complex(real64) :: c
c = (1d0,0.5d0)
print *,c ! (1.00000000000000,0.500000000000000)
c = 0.5d0
print *,c ! (0.500000000000000,0.000000000000000E+000)
end program main

実行結果にある( , )は自動で表示されます.


整数,実数の型変換

型変換に使用する関数を示しておきます.

関数
機能
備考

int(a=実数[,kind=整数の種別])
整数に変換
小数点以下は切り捨て

nint(a=実数[,kind=整数の種別])
整数に変換
同符号の最も近い整数へ丸める

real(a=整数もしくは実数もしくは複素数[,kind=実数の種別])
実数に変換
kindで精度を指定

sngl(a=倍精度実数)
単精度実数へ変換

dble(a=整数,実数もしくは複素数)
倍精度実数へ変換

cmplx(x=実部,y=虚部[,kind=実数の種別])
複素数に変換
実部と虚部は実数

real(z=複素数)
複素数から実部を取り出す

aimag(z=複素数)
複素数から虚部を取り出す

例を示しておきます.

program main

use,intrinsic :: iso_fortran_env
implicit none

real(real64) :: one,r,i
complex(real64) :: c

one = 1d0-epsilon(0d0) ! 1より小さい1に最も近い倍精度実数
print *,one !I 1.00000000000000 (print時の四捨五入により1と表示)
!P 0.9999999999999998
!g 0.99999999999999978
print *,int(one) ! 0 小数点以下を切り捨て
print *,nint(one) ! 1 最も近い整数(1)へ丸める

print *,real(0,real128) !0.000000000000000000000000000000000E+0000
print *,sngl(0d0) !0.0000000E+00
print *,dble(0e0) !0.000000000000000E+000

r = 1d0; i = 0.5d0
c = cmplx(r,i) !変数の場合は,(r,i)では複素数へ変換できない
print *,c,real(c),aimag(c) !(1.00000000000000,0.500000000000000) 1.00000000000000 0.500000000000000
end program main


論理型

数値を取り扱う型以外に,真または偽の2値のみを扱う論理型が存在します.

表記
Intel
PGI
gfortran
備考

logical


integerのサイズに応じて変化

logical(1)


logical*1


logical(2)


logical*2


logical(4)


logical*4


logical(8)


logical*8


Intelコンパイラおよびgfortranでは,論理型の種別を表す定数が定義されていません.PGIコンパイラでは,論理型の種別を表す定数として,logical{8,16,32,64}が定義されています.いずれのコンパイラにおいても,どのような種別があるかは,logical_kindsという配列を表示することで調べることができます.

標準では,最下位ビットが1なら真,0なら偽と扱います9.真偽の2値しか取らないので,logical(1)でよさそうですが,Intel Fortranの古いマニュアル10には,


ia64 システムでは,性能を向上させるために,LOGICAL(2) や LOGICAL(1) ではなく,LOGICAL(4) (または LOGICAL(8)) を使用するようにしてください。


とあるので,Intelコンパイラを利用している場合は,他の計算機環境でもlogicalを使っておけば間違いはないと推察されます.


リテラル

リテラルは,真を表す.true.と偽を表す.false.の二つのみです..not.を前に置くことで否定を表現できます.

program main

implicit none

logical :: a
logical(1) :: b
logical(2) :: c
logical(4) :: d
logical(8) :: e
print *,sizeof(a),sizeof(b),sizeof(c),sizeof(d),sizeof(e)
!4 1 2 4 8
!8 1 2 4 8 /integer_size:64を指定したとき

a = .true.
print *, a, .false. !T F
print *, .not.a, .not..false. !F T
end program main

print文で論理型の変数やリテラルを表示すると,T, Fのように省略形で表されます.


型変換

IntelおよびPGIコンパイラでは,.true.は整数の-1.false.0に相当します.gfortranでは,.false.0であることは共通ですが,.true.1のようです.

論理型から整数型への変換は,関数int()ではできません.ビット列を異なる型に変換する関数transfer(source=値,mold=見本[,size=配列要素数])を使って変換します.

program main

use,intrinsic :: iso_fortran_env
implicit none

logical(int32) :: a

a = (*1)
print *,a !出力1
print *,transfer(a,0_int32 ) !出力2
print *,transfer(a,0.0_real32) !出力3
print '(B32.32)', a !出力4
end program main

(*1)の値
コンパイラ
出力1
出力2
出力3
出力4

.ture.
Intel
T
-1
NaN
11111111111111111111111111111111

PGI
T
-1
NaN
11111111111111111111111111111111

gfortra
T
1
1.40129846E-45
00000000000000000000000000000001

.false.
Intel
F
0
0.0000000E+00
00000000000000000000000000000000

PGI
F
0
0.000000
00000000000000000000000000000000

gfortra
F
0
0.00000000
00000000000000000000000000000000

IntelおよびPGIコンパイラでは,a.true.を代入した後,ビット列を整数型に変換すると-1となっています.このとき,見本として渡した0integer型であり,logical型のaと同じサイズであるため,ビット列がinteger型に変換されます.単精度実数0.0を見本として単精度実数型に変換すると,最上位ビットから9ビットが1のため,NaNと判断されました..false.を代入するとすべてのビットが0になっていることが確認できます.

IntelおよびPGIコンパイラでは,論理型変数には整数を代入することができます.どのように論理値として取り扱われるかを見てみましょう.

(*1)の値
コンパイラ
出力1
出力2
出力3
出力4

0
Intel
F
0
0.0000000E+00
00000000000000000000000000000000

PGI
F
0
0.000000
00000000000000000000000000000000

1
Intel
T
-1
NaN
11111111111111111111111111111111

PGI
T
1
0.000000
00000000000000000000000000000001

2
Intel
F
0
0.0000000E+00
00000000000000000000000000000000

PGI
F
2
0.000000
00000000000000000000000000000010

-1
Intel
T
-1
NaN
11111111111111111111111111111111

PGI
T
-1
NaN
11111111111111111111111111111111

0が偽,最下位ビットが1の数が真9であることは共通ですが,Intelコンパイラでは,偽あるいは真となる整数を0あるいは-1に変更しているようです.


コンパイルオプションによる論理値表現の変更

論理型変数に整数を代入できるコンパイラは,コンパイルオプションで表現を切り替えることができます.その場合は値が0以外なら真,0なら偽と解釈されます.

コンパイラ
最下位ビットの0,1で判別
数値(非ゼロ,ゼロ)で判別

Intel
オプションなし(標準)
/fpscomp:logicals

PGI
オプションなし(標準)
-Munixlogical7

上記オプションをつけて実行したところ,Intelコンパイラしか効果がなく,PGIコンパイラでは相変わらず最下位ビットで判別されていました.

(*1)の値
コンパイラ
出力1
出力2
出力3
出力4

2
Intel
T
1
1.4012985E-45
00000000000000000000000000000001

PGI
F
2
0.000000
00000000000000000000000000000010

-1
Intel
T
1
1.4012985E-45
00000000000000000000000000000001

PGI
T
-1
NaN
11111111111111111111111111111111

IntelコンパイラはIntelコンパイラで,/fpscomp:logicalsオプションを付与すると,真を1に変更しているようです.このことから,論理型を数値に変換して使っているプログラムがあって,それをgfortranからIntelコンパイラに移植する場合は,Intelコンパイラ側で上記オプションをつけて真を1とすることで,移植性を確保できることがわかります.


暗黙の型変換

IntelおよびPGIコンパイラでは,論理型変数を演算に組み込むことで,暗黙的に型変換が行われます..true.-1.false.0であることは変わりません.マスクなどの処理に利用できますが,素直に場合分けを書いたときや,同様の処理をおこなう組み込み関数と比較して効率的かどうかは,さらなる調査が必要です.

program main

implicit none

logical :: a,b

a = .true.
b = .not.a
print *,1d0*a, 1d0*b !-1.00000000000000 0.000000000000000E+000
end program main


結局どうしよう?

iso_fortran_envuseしてint32などの種別定数を使うのがよいと考えています.logicalは種別を指定せずに宣言し,整数型と種別が同じになるようにします.

この方法は以下の利点を持っています.


  • 型の表記が統一的になる

  • 数値リテラルの種別指定とも親和性が高い

  • ユーザが自前で定数を定義しなくてもよい11

  • コンパイルオプションでも型のサイズが変わらない(logical以外)


  • selected_{int,real}_kind()を用いて指定する必要が生じた場合に置換で対応できる

selected_{int,real}_kind()を使うよりはマシとしても,real(real32)など2回もrealと書くのは冗長であることは事実です.個人で開発する場合は,この冗長な記述を省略するために,直接種別を指定してもよいのではないかと考えています.ただし,sizeof()関数,iso_fortran_envで定義されている配列integer_kindsreal_kindsを利用して,用意されている型種別とその数字,変数のバイト数の対応を確認し,かつコンパイルオプションの管理に責任を持つことが前提です.


著者が適切と考える型の表記
著者が個人開発で使う簡略表記12

1バイト整数
integer(int8)
integer(1)

2バイト整数
integer(int16)
integer(2)

4バイト整数
integer(int32)

integer 13

8バイト整数
integer(int64)
integer(8)

単精度実数
real(real32)
real(4)

倍精度実数
real(real64)
real(8)

4倍精度実数
real(real128)
real(16)

単精度複素数
complex(real32)
complex(4)

倍精度複素数
complex(real64)
complex(8)

4倍精度複素数
complex(real128)
complex(16)

論理型
logical
logical

実数の精度を切り替える必要があるときは,モジュールを用いた種別の指定にある方法で切り替えるようにしますが,そもそも精度を変更するかどうかは作るアプリケーションの用途によるので,そこは確認が必要です.

整数に関してはinteger, integer(int64),単精度・倍精度・4倍精度の種別はモジュールを用いて指定するのがグッドプラクティスであるという意見もいただきました.著者の考えもこれらの大部分を支持しています.

*?でバイト数を指定する方法は,?の部分に種別定数を使うことができませんし,モジュールを利用した精度の制御もできません.複素数型では型種別の2倍にするなど罠があるので,これといった利点がみられない以上使わない方がよいでしょう.古いコードには未だに現れるので,そういう書き方もあるということだけ知っておけばよいと思います.

double precisionも,real(real64)という表記ができる以上,統一的な表記という観点からは使わなくてもよいと思います.精度をsingle, double, quadで指定できるとよかったのですが.


まとめ

こういった基本的な型の宣言がややこしいのはよろしくないと思います.





  1. http://www.nag-j.co.jp/fortran/FI_4.html 



  2. それに伴ってその変数に対する最適化が抑制されるので,Fortranの場合はそれを目的にvolatile属性を付与します. 



  3. Linux, MacOSの場合は,-integer_size {16,32,64}とする. 



  4. gfortran 7.3.0では,iso_fortran_evnをuseしている場合にWarningが出る. 



  5. 内部的に2進数以外で数を保持している計算機を使う機会はあるのか? 



  6. PGIコンパイラではそもそも指定できない. 



  7. ドキュメントには書かれていますが,付与しても効果がありません. 



  8. -fdefault-real-8で単精度と倍精度の両方を変化させていると思われます. 



  9. コンパイルオプションで表現を切り替えることができ,その場合は値が0以外なら真,0なら偽と解釈されます. 



  10. https://jp.xlsoft.com/documents/intel/cvf/vf-html/pg/pg20_02.htm 



  11. こういう基本的なところで任意性や進んだ知識を必要とするのはよいことではありません.C言語を学び初めた直後に#include<stdio.h> //おまじないで何千万人も挫折していることを鑑みると,避けられるのであれば避けるべきでしょう. 



  12. あくまで著者の置かれた状況で検討した結果です.著者が対象とするアプリケーションでは,整数は4バイト以外をめったに使わない一方で,実数は倍精度を主に用いるので,単精度はrealとせず,単精度であることを明記しています. 



  13. integerが4バイトのときです.integerが8バイトの計算機を使うことになれば,integer(8)integerと書きます.