3つの倍精度配列があるとしましょう。
real*8, dimension(n) :: x, y, z
として初期化されます
x = 1.
y = (/ (1., i=1,n) /)
z = (/ (1. +0*i, i=1,n) /)
すべての配列のすべての要素をに初期化する必要があります1
。ifort
(16.0.0 20150815)、これは、いずれかの意図したとおりに動作n
宣言された精度の範囲内です。つまり、次のように初期化するn
と
integer*4, parameter :: n
である限りn < 2147483647
、初期化はすべての宣言で意図したとおりに機能します。
gfortran
(4.8.5 20150623レッドハット4.8.5-16)、初期化が失敗したためy
、長いほど(一定の引数を持つ配列内包)n>65535
、独立その精度。AFAIK65535
は、の最大値でありunsigned short int
、別名unsigned int*2
、の範囲内にありますinteger*4
。
以下はMWEです。
program test
implicit none
integer*4, parameter :: n = 65536
integer*4, parameter :: m = 65535
real*8, dimension(n) :: x, y, z
real*8, dimension(m) :: a, b, c
integer*4 :: i
print *, huge(n)
x = 1.
y = (/ (1., i=1,n) /)
z = (/ (1.+0*i, i=1,n) /)
print *, x(n), y(n), z(n)
a = 1.
b = (/ (1., i=1,m) /)
c = (/ (1.+0*i, i=1,m) /)
print *, a(m), c(m), c(m)
end program test
gfortran
(gfortran test.f90 -o gfortran_test
)でコンパイルすると、次のように出力されます。
2147483647
1.0000000000000000 0.0000000000000000 1.0000000000000000
1.0000000000000000 1.0000000000000000 1.0000000000000000
ifort
(ifort test.f90 -o ifort_test
)でコンパイルすると、次のように出力されます。
2147483647
1.00000000000000 1.00000000000000 1.00000000000000
1.00000000000000 1.00000000000000 1.00000000000000
何が得られますか?
コンパイラが配列コンストラクタを処理する方法には、確かに大きな違いがあります。n<=65535
オブジェクトファイル(またはいくつかの中間表現)に格納されている[1.、1.、1。、...]の実際の配列があるためです。
より大きな配列の場合、コンパイラはループを生成します。
(*(real(kind=8)[65536] * restrict) atmp.0.data)[offset.1] = 1.0e+0;
offset.1 = offset.1 + 1;
{
integer(kind=8) S.2;
S.2 = 0;
while (1)
{
if (S.2 > 65535) goto L.1;
y[S.2] = (*(real(kind=8)[65536] * restrict) atmp.0.data)[S.2];
S.2 = S.2 + 1;
}
L.1:;
}
私には、最初に一時配列の1つの要素のみを設定し、次に(ほとんど未定義の)一時配列をにコピーするように見えますy
。そしてそれは間違っています。Valgrindは、初期化されていないメモリの使用状況も報告します。
デフォルトの実数については、
while (1)
{
if (shadow_loopvar.2 > 65536) goto L.1;
(*(real(kind=4)[65536] * restrict) atmp.0.data)[offset.1] = 1.0e+0;
offset.1 = offset.1 + 1;
shadow_loopvar.2 = shadow_loopvar.2 + 1;
}
L.1:;
{
integer(kind=8) S.3;
S.3 = 0;
while (1)
{
if (S.3 > 65535) goto L.2;
y[S.3] = (*(real(kind=4)[65536] * restrict) atmp.0.data)[S.3];
S.3 = S.3 + 1;
}
L.2:;
}
これで2つのループができました。1つは一時配列全体を設定し、もう1つはそれをコピーしてy
すべてが正常です。
この問題は、この質問を読んだGCC開発者によって修正されました。バグはhttps://gcc.gnu.org/bugzilla/show_bug.cgi?id=84931で追跡されています
彼らはまた、問題が型変換に関連していることを確認しました。コンストラクターにはデフォルトの精度が1.
あり、単精度配列では型変換はありませんが、倍精度配列の場合は型変換があります。それがこれら2つのケースの違いを引き起こしました。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加