【本当にあった怖い話】桁移動 kP の呪い

#FORMAT 桁移動書式 *k* P
カーニガン＆プローガー著「プログラム書法」は、1970年代に書かれたプログラミングのグッド・プラクティス本の古典で、例題は主に FORTRAN66 と PL/I で与えられています。古典としての評価のほか FORTRAN に対するグッド・プラクティス指南本が余りないため、Fortran 界隈では未だにそこそこ読まれていたりします。
![02085.jpg](https://qiita-image-store.s3.amazonaws.com/0/44522/bbdce3e2-7106-53ae-101d-68984de7487f.jpeg)
https://www.kyoritsu-pub.co.jp/bookdetail/9784320020856

この本の中では、E (Engineering) 書式に対して、桁移動書式 *k* P を用いて 1 桁ずらして見易くすることが何度も繰り返し推奨されています。
###例１
```fortran
    program ex1
        implicit none
        real :: pi = 4 * atan(1.0)
        print '(a,   e13.6 /)', ' pi=', pi
        print '(a, 1pe13.6 /)', ' pi=', pi
    end program ex1
```
```
 pi= 0.314159E+01

 pi= 3.141593E+00

```
確かに、0.314..E+01 という形式より 1P で１桁ずらして 3.14..E+00 とした方が見易い気がします。FORTRAN66 や FORTRAN77 では、浮動書数点数は E (engineering) か F (fixed point) しか選べないので、E 書式を使う時は常に 1PE にすればいいような気がしてきます。

#心霊プログラム
これはかつて私の意識が高すぎて 1P を乱用した結果できてしまった、呪いの心霊プログラムである。

###例２
```fortran
    program ex2
        implicit none
        real :: x, y, a, b
        x = 3.146
        y = 3.14
        !
        print *, x, y
        write(9, '(1pe10.3, f10.3)'), x, y
        rewind(9)
        !
        read (9, '(1pe10.3, f10.3)'), a, b
        print *, a, b
    end program ex2
```
```
   3.146000       3.140000
   3.146000       3.140000
```
一見、何の変哲もないプログラムである。

実数変数 x,y に値を与え、それを装置番号９番のファイルに書き出している。変数 x は見易い 1PE 書式で書き出し、変数 y は素直に F 書式で書き出している。

ファイルを巻き戻した後、実数変数 a,b に書き出した値を読み込んで、表示を行っている。読み込みのための書式は、書き出しのものをコピペして同一のものになっている。

x,y に与えた値と a,b に読み込んだ値は一致しており、意図したとおりの結果が得られている。特に変わった点は無いように思える。

では、ここで書き出したファイル fort.9 の中身を見てみよう。

```
 3.146E+00    31.400
```  
**お分かりいただけただろうか？**　
一つ目の数値は、意図通りに 3.146E+00 となっている。**しかし二つ目の数値は、本来の値 3.14 の 10 倍の 31.4 となっている。**これはどういうことなのか？ファイルから読み込んで表示した結果は正しい値であった。それにもかかわらず、ファイル中の値がおかしくなっている。これは何らかの呪いによるものであろうか？

**REPLAY**
ではもう一度、（プログラムを少し変えて）見てみよう。
###例３

```fortran
    program ex3
        implicit none
        real :: x, y, a, b
        x = 3.146
        y = 3.14
        !
        print *, x, y
        print *
        write(*, '(1pe10.3, f10.3)'), x, y
        write(9, '(1pe10.3, f10.3)'), x, y
        rewind(9)
        !
        read (9, '(1pe10.3, f10.3)'), a, b
        print *
        print *, a, b
        print *
        write(*, '(1pe10.3, f10.3)'), a, b
    end program ex3
```
```
   3.146000       3.140000

 3.146E+00    31.400

   3.146000       3.140000

 3.146E+00    31.400
```  
書式をつけて書き出された数値は、ファイル中の値と同じく二つ目の値が１０倍の値になっていた。

かつてこの怪奇現象に遭遇した私の感じた恐怖と焦りを感じ取っていただきたい。

##解説
文法書によれば、書式中の桁移動 *k* P は、同じ書式中では別の値が指定されない限り他の指定子にも適用され続けるという。つまり、今の場合二番目の変数は、見かけ上の F10.3 ではなく 1PF10.3 の書式で読み書きされているのと同じになっている。そして、E 書式の場合は 1PE のように桁移動させても、桁移動の分だけ指数部が調整されて値は元のままに保たれるが、F 書式の場合には *k* PF のように桁移動させると、元の値から 10^k だけスケールされた値になってしまうのである。

今のプログラムの場合、書き出しで 10 倍された値は、読み込みの時には逆に 1/10 にされてプログラム中の変数では元の値を回復していたのである。 

*k* P 指定子で桁をずらした場合には、直後に 0P 指定子によって桁ずらしを元に戻さないと、呪いによって祟られるのである。

##呪いは消えず
Fortran90 以降の　Modern Fortran においては、ES 書式が加わったためわざわざ危険な桁移動をさせる必要はなくなっている。
###例４

```fortran
    program ex4
        implicit none
        real :: pi = 4 * atan(1.0)
        print '(a,   e13.6 /)', ' pi=', pi
        print '(a, 1pe13.6 /)', ' pi=', pi
        print '(a,  es13.6 /)', ' pi=', pi
    end program ex4
```
```
 pi= 0.314159E+01

 pi= 3.141593E+00

 pi= 3.141593E+00

```
しかしながら、「プログラム書法」にとりついた桁移動の呪いは、成仏することなくさまよい続けており、以下の知恵袋の質問に見られるように、未だ祟られる人間が絶えないという。

>
Yahoo 知恵袋　https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13169482653
　
**fortranのwrite文で出力される値がおかしくなり･･･**
fortranのwrite文で出力される値がおかしくなります。
fortran90にてwrite文を使用して、計算結果を外部ファイルに書き込むプログラムを作っています。 
しかし、write文で出力される桁数がおかしくなります。
　云々

本記事によって、一人でも多くの人が呪われた心霊プログラムの恐怖から救われることを祈ってやまない。

#教訓
桁移動 *k* P は止めた方がいい。特に 1PE, 1PG の類。ES を使うのです。