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)<br> 7.3.0 (for Ubuntu on WSL)|

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

# Fortranの型宣言の基本
```Fortran
型([kind=]種別)[,属性,属性,...] :: 変数名[=初期値]
```
`[]`は省略可能であることを意味しています．
**型**で整数か実数か複素数か（あるいは他の型）を指定し，**種別**で精度（表現できる数値の範囲）を指定します．定数や動的割付などの情報を，**属性**という形で型の後ろに追加していきます．
種別は，**ほとんどの場合**変数のサイズ（バイト数）です．それ以外の状況を見たことはありませんが，コンパイラによってはそうでないという情報があります[^1]．
`kind=`と`=初期値`は省略可能ですが，定数を宣言する場合は初期値の代入が必須です．

宣言時の初期値代入ができない場面もあります．
また，宣言時に初期値を代入すると変数が静的変数になるので，**宣言時の初期値代入は原則としてしない**よう習慣づけた方が無難です．宣言時に値を代入するのは以下の場合くらいでしょう．
1. 定数を宣言する場合
2. 静的変数を宣言する場合

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

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

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

### 変数の基本的な属性
|属性|機能|
|:--|:--|
|`parameter`|定数であることを示す|
|`save`|静的変数であることを示す|
|`dimension(:[,:,...])`|（多次元）配列であることを示す|
|`allocatable`|動的割付けする変数・配列であることを示す|
|`pointer`|ポインタ変数であることを示す|
|`target`|ポインタ変数によって参照可能な変数（pointee）であることを示す|
|`volatile`|プログラム以外（例えばハードウェア）から変更される可能性のあることを示す[^volatile]|

### 仮引数の属性
|属性|機能|
|:--|:--|
|`intent({in,out,inout})`|関数・サブルーチンの仮引数が{読取り用,書込み用,読み書き可能}であることを示す|
|`value`|関数・サブルーチンの仮引数が値渡しであることを示す|
|`optional`|関数・サブルーチンの仮引数が省略可能であることを示す|

### アクセス制御
|属性|機能|
|:--|:--|
|`public`|変数を派生型外部あるいはモジュール外部に公開されることを示す|
|`private`|変数を派生型外部あるいはモジュール外部に公開しないことを示す|
|`protected`|変数をモジュール外部に読取り専用で公開することを示す|

### C言語との相互利用
|属性|機能|
|:--|:--|
|`bind(c)`|C言語と相互利用可能であることを示す（列挙型もしくは派生型）|

[^volatile]: それに伴ってその変数に対する最適化が抑制されるので，Fortranの場合はそれを目的に`volatile`属性を付与します．

# 整数型
整数型を例に，Fortranにおける型宣言を見てみましょう．

|範囲|表記|Intel|PGI|gfortran|備考|
|:--:|:--|:--:|:--:|:--:|:--|
||`integer`|○|○|○|コンパイルオプションでサイズを変更可能<br>Intel `/integer_size:{16,32,64}`[^iopt]<br>PGI `-i{2,4,8}`<br>gfortran `-fdefault-integer-8`[^g7]|
|$-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`|○|○|○||

[^g7]: gfortran 7.3.0では，`iso_fortran_evn`をuseしている場合にWarningが出る．

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

[^iopt]: Linux, MacOSの場合は，-integer_size {16,32,64}とする．

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

|表記|Intel|PGI|gfortran|備考|
|:--|:--:|:--:|:--:|:--|
|`integer(selected_int_kind(r=桁数))`|○|○|○|桁数`r`に応じた種別が用いられる|
|`integer(kind(x=整数))`|○|○|○|引数として渡した整数`x`を取り扱える種別が用いられる<br>標準の種別が優先される|
|`integer(int8)`|○|○|○|`iso_fortran_env`モジュールをuseする|
|`integer(int16)`|○|○|○|〃|
|`integer(int32)`|○|○|○|〃|
|`integer(int64)`|○|○|○|〃|

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

```Fortran
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ではコンパイルエラーが出ます．

```console
   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)'`|

```Fortran
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`に応じた種別が用いられる<br>PGIは`radix`をサポートしていない|
|`real(kind(x=実数))`|○|○|○|引数の実数`x`を取り扱える種別が用いられる|

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

[^2]: 内部的に2進数以外で数を保持している計算機を使う機会はあるのか？

|実数の型|桁数`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()`の実行例を以下に示します．

```Fortran
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`は正しい[^pgiquad]が`p`と`r`の要件を満たすことができないときに出るエラーのようです．

[^pgiquad]: PGIコンパイラではそもそも指定できない．

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

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

|表記|Intel|PGI|gfortran|備考|
|:--|:--:|:--:|:--:|:--|
|`real`|○|○|○|コンパイルオプションで精度を変更可能<br>`/real_size:{32,64,128}`<br>`-r8`<br>`-fdefault-real-8`[^g7]|
|`real(4)`|○|○|○||
|`real*4`|○|○|○||
|single precision|×|×|×|倍精度との比較のため|
|`real(real32)`|○|○|○|`iso_fortran_env`モジュールをuseする|

仕様の観点から種別を指定しない`real`の使用が推奨されています[^1]が，コンパイルオプションで精度を変更できてしまいます．これは移植性や互換性という観点からするとどうなんでしょう？計算機やコンパイラが変わっても精度を固定したいのであれば，種別を指定した方がよいでしょう．
gfortranの場合はさらに厄介で，コンパイルオプション次第でいくらでも精度を変更できます．

```Fortran
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`|○|○|○|コンパイルオプションで精度を変更可能<br>`/double_size:{64,128}`<br>`-r4`[^pgir4]<br>`-fdefault-real-8`[^fdef8]|
|`real(real64)`|○|○|○|`iso_fortran_env`モジュールをuseする|

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

[^pgir4]: [ドキュメント](https://www.softek.co.jp/SPG/Pgi/TIPS/option1.html)には書かれていますが，付与しても効果がありません．
[^fdef8]: `-fdefault-real-8`で単精度と倍精度の両方を変化させていると思われます．

```Fortran
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()`を変数宣言ごとに記述するのは煩わしいので，モジュールに種別を表す定数を定義することが行われているようです．

```Fortran
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()`等で種別を定義するのが一度だけで済みます．
また，かなり小賢しい方法で精度を切り替えることができます．（このような方法が許されるのかはわかりませんが）

```Fortran
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`として参照することを意味しています．つまり，上のプログラムでは，`dp`は`floating_point_kinds`で定義された，倍精度実数の種別を表す定数ではなく，単精度実数の種別を指すローカル変数という扱いになります．そのため，わずかな変更で`real(dp)`を倍精度実数から単精度実数に，同様の方法で`real(sp)`を単精度から倍精度に切り替えることができます．

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

```Fortran
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`|○|○|○|コンパイルオプションで精度を変更可能<br>`/real_size:{32,64,128}`<br>`-r8`<br>`-fdefault-real-8`|
|`complex(4)`|○|○|○||
|`complex*4`|×|×|×|単精度実数の型宣言との比較のため|
|`complex*8`|○|○|○||
|single complex|×|×|×|倍精度との比較のため|

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

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

```Fortran
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`|○|○|○|コンパイルオプションで精度を変更可能<br>`/double_size:{64,128}`<br>`-r4`<br>`-fdefault-real-8`|
|`complex(real64)`|○|○|○|`iso_fortran_env`モジュールをuseする|

`double complex`はコンパイルオプションで倍精度実数のサイズを変えると，それに伴って変化します．
倍精度複素数については，`double complex`ではなく，`complex(kind(0d0))`で種別を取得することが推奨されています[^1]．この統一感のなさは何でしょうか．

```Fortran
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`は数値か定数です．

```Fortran
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`の指定に`int32`や`real32`などの定数を使ったとしても，その実体はただの数字でしかなく，型の情報を持たないためです．

```Fortran
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}$のような記述も可能です．

```Fortran
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と扱うようです．

```Fortran
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=複素数)`|複素数から虚部を取り出す||
例を示しておきます．

```Fortran
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`なら偽と扱います[^logi]．真偽の2値しか取らないので，`logical(1)`でよさそうですが，Intel Fortranの古いマニュアル[^cvf]には，
> ia64 システムでは，性能を向上させるために，LOGICAL(2) や LOGICAL(1) ではなく，LOGICAL(4) (または LOGICAL(8)) を使用するようにしてください。

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

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

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

```Fortran
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`のように省略形で表されます．

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

## 型変換
IntelおよびPGIコンパイラでは，`.true.`は整数の`-1`，`.false.`は`0`に相当します．gfortranでは，`.false.`が`0`であることは共通ですが，`.true.`は`1`のようです．
論理型から整数型への変換は，関数`int()`ではできません．ビット列を異なる型に変換する関数`transfer(source=値,mold=見本[,size=配列要素数])`を使って変換します．

```Fortran
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`となっています．このとき，見本として渡した`0`は`integer`型であり，`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`の数が真[^logi]であることは共通ですが，Intelコンパイラでは，偽あるいは真となる整数を`0`あるいは`-1`に変更しているようです．

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

|コンパイラ|最下位ビットの0,1で判別|数値（非ゼロ，ゼロ）で判別|
|:--|:--|:--|
|Intel|オプションなし（標準）|/fpscomp:logicals|
|PGI|オプションなし（標準）|-Munixlogical[^pgir4]|

上記オプションをつけて実行したところ，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`であることは変わりません．マスクなどの処理に利用できますが，素直に場合分けを書いたときや，同様の処理をおこなう組み込み関数と比較して効率的かどうかは，さらなる調査が必要です．

```Fortran
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_env`を`use`して`int32`などの種別定数を使うのがよいと考えています．`logical`は種別を指定せずに宣言し，整数型と種別が同じになるようにします．
この方法は以下の利点を持っています．

- 型の表記が統一的になる
- 数値リテラルの種別指定とも親和性が高い
- ユーザが自前で定数を定義しなくてもよい[^def]
- コンパイルオプションでも型のサイズが変わらない（`logical`以外）
- `selected_{int,real}_kind()`を用いて指定する必要が生じた場合に置換で対応できる

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

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

|型|著者が適切と考える型の表記|著者が個人開発で使う簡略表記[^smpl]|
|:--|:--|:--|
|1バイト整数|`integer(int8)`|`integer(1)`|
|2バイト整数|`integer(int16)`|`integer(2)`|
|4バイト整数|`integer(int32)`|`integer` [^c]|
|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倍精度の種別はモジュールを用いて指定するのがグッドプラクティスであるという意見もいただきました．著者の考えもこれらの大部分を支持しています．

[^smpl]: あくまで著者の置かれた状況で検討した結果です．著者が対象とするアプリケーションでは，整数は4バイト以外をめったに使わない一方で，実数は倍精度を主に用いるので，単精度はrealとせず，単精度であることを明記しています．
[^c]: `integer`が4バイトのときです．`integer`が8バイトの計算機を使うことになれば，`integer(8)`を`integer`と書きます．

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

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

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