gfortran中隐含的数组构造函数的奇怪初始化行为

时间:2018-03-16 23:26:27

标签: arrays constructor fortran intel gfortran

我们说我有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 Red Hat 4.8.5-16)中,只要y,<{1}},n>65535(具有常量参数的数组理解)的初始化失败>独立的精确度。 AFAIK,65535unsigned 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

使用gfortrangfortran test.f90 -o gfortran_test)进行编译,输出:

  2147483647
   1.0000000000000000        0.0000000000000000        1.0000000000000000     
   1.0000000000000000        1.0000000000000000        1.0000000000000000

使用ifortifort test.f90 -o ifort_test)进行编译,输出:

2147483647
   1.00000000000000        1.00000000000000        1.00000000000000     
   1.00000000000000        1.00000000000000        1.00000000000000     

是什么给出了?

1 个答案:

答案 0 :(得分:4)

编译器如何处理数组构造函数确实存在很大差异。对于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:;
    }

在我看来,首先它只设置一个临时数组的一个元素,然后将(大部分未定义的)临时数组复制到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:;
    }

我们现在有两个循环,一个设置整个临时数组,第二个循环复制到y,一切都很好。

结论:编译错误。

这个问题由阅读此问题的GCC开发人员修复。该错误在https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84931

进行跟踪

他们还发现问题与类型转换有关。构造函数具有默认精度1.,并且对于单精度数组,没有类型转换,但对于双精度数组,存在一些类型转换。这导致了这两种情况的不同。