Keep Fortran Great!
NaN と三分岐 GOTO 文
実数は全順序集合なので、任意の2数を持ってくれば、大・小・等しいの三つの関係の内必ず一つが成り立ちますが、IEEE754 の代数系には普通の数の他に ±∞ や NaN (Not a Number) があって、このうち NaN には順序が無いため、比較する2数のうちのどちらか(もしくは両方)が NaN のとき、三つの関係のどれも成り立たない状況が生じます。
上古 FORTRAN の三分岐 GOTO 文に、この NaN が来た時どうなるか見てみます。三分岐 GOTO 文では、変数が負、0.0、正のいずれかによって行き先が決定されます。
ソース・プログラム
NaN を 0.0 / 0.0 で生成します。gfortran のコンパイル時チェックを避けるため、zero = 0.0 を使っています。
参考記事:implicit_none さん
https://qiita.com/implicit_none/items/a96d5bdb1a0cf05b6f03
program NaNtest
implicit none
real :: xnan, ynan, zero = 0.0
xnan = zero / zero
ynan = xnan
print *, xnan, ynan
print *, xnan == ynan, xnan /= ynan
!
print *, xnan < ynan, xnan == ynan, xnan > ynan
!
if (xnan) 1, 2, 3
1 print *, 1
goto 99
2 print *, 2
goto 99
3 print *, 3
99 continue
!
print *, 'SIGN NaN', sign(1.0, xnan)
end program NaNtest
実行結果
NaN は自分自身との比較でもイコールになりません。二つの NaN どうしの大小等比較を見てみるとすべて偽になっています。
三分岐 GOTO 文では三番目の正に来ています。
しかし、NaN の符号を見てみると、Intel Fortran と gfortran で結果が分かれます。Intel は正、gfortran は負になっています。gfortran の場合、三分岐 goto の場合と矛盾しています。まぁそもそも本来 NaN には符号が無いはずなので、意味が無いんですがw
intel fortran
NaN NaN
F
F T
F F F
3
SIGN NaN 1.000000
gfortran
NaN NaN
F
F T
F F F
3
SIGN NaN -1.00000000
弁明
一度 NaN が出ると、それ以降の計算は全部 NaN になりそうな気がしますが、sign 関数で符号を見ると普通の数に戻ってくるという事実があって、長年謎に思っていたのですが、三分岐 GOTO 文などと整合させるためではないかと思って、そういう結論を導こうとしていたのですが、gfortran の結果で主張が崩れてしまいましたw
NaN の二進内部表現の符号ビットを見ると、NaN の内部表現は一意ではないので、正の場合も負の場合も可能なのですが、Intel Fortran で調べた限りでは、符号ビットの内容に関わらず sign 関数では常に正扱いになっていました。それで上記のストーリーを考えたのですが、gfortran で思いっきり矛盾してしまいましたw
gfortran での NaN の内部表現と sign 関数の関係はまだ調べていないので、追って調べてみたいと思います。KEEP FORTRAN GREAT!
補足
ソース・プログラム
NaN をビット列で生成して、符号ビットを変えたうえで、sign 関数に入れてみます。
program hello
real :: xnan
xnan = transfer(B'01111111100000000000000000000001', xnan)
print *, xnan
print *, sign(1.0, xnan)
!
xnan = transfer(B'11111111100000000000000000000001', xnan)
print *, xnan
print *, sign(1.0, xnan)
end program hello
gfortran での実行結果
gfortran では、NaN の符号ビットの逆の符号になっているようです。
NaN
1.00000000
NaN
-1.00000000
補足 H31.4.10
gfortran の三分岐は NaN の符号ビットに依らずに、正に相当する三番目に飛んでいます。