Fortran嵌套WHERE语句

时间:2016-03-04 12:22:55

标签: nested fortran where fortran90

我有一个带有嵌套WHERE语句的Fortran 90源代码。有一个问题,但似乎很难理解到底发生了什么。我想将其转换为DO-IF结构以便进行调试。我不清楚的是如何翻译嵌套的WHERE。

所有阵列都具有相同的大小。

WHERE (arrayA(:) > 0)
    diff_frac(:) = 1.5 * arrayA(:)
    WHERE (diff_frac(:) > 2)
        arrayC(:) = arrayC(:) + diff_frac(:)
    ENDWHERE
ENDWHERE

我的选择A:

DO i=1, SIZE(arrayA)
  IF (arrayA(i) > 0) THEN
    diff_frac(i) = 1.5 * arrayA(i)
    DO j=1, SIZE(diff_frac)
      IF (diff_frac(j) > 2) THEN
          arrayC(j) = arrayC(j) + diff_frac(j)
      ENDIF
    ENDDO
  ENDIF
ENDDO

我的选项B:

DO i=1, SIZE(arrayA)
  IF (arrayA(i) > 0) THEN
    diff_frac(i) = 1.5 * arrayA(i)        
    IF (diff_frac(i) > 2) THEN
        arrayC(i) = arrayC(i) + diff_frac(i)
    ENDIF        
  ENDIF
ENDDO

谢谢

3 个答案:

答案 0 :(得分:3)

根据comp.lang.fortran中的帖子"Nested WHERE constructs“(特别是Ian的回复),似乎问题中的第一个代码转换为以下内容:

do i = 1, size( arrayA )
    if ( arrayA( i ) > 0 ) then
        diff_frac( i ) = 1.5 * arrayA( i )
    endif
enddo

do i = 1, size( arrayA )
    if ( arrayA( i ) > 0 ) then
        if ( diff_frac( i ) > 2 ) then
            arrayC( i ) = arrayC( i ) + diff_frac( i )
        endif
    endif
enddo

这与Mark的答案几乎相同,只是第二个面具部分(见下文)。 F2008文档的主要摘录如下:

  

7.2.3屏蔽数组赋值 - WHERE(第161页)

     

7.2.3.2掩码阵列分配的解释(第162页)

     

... 2. WHERE结构中的每个语句都按顺序执行。

     

... 4. mask-expr 最多只评估一次。

     

... 8.执行作为 where-body-construct 的一部分的WHERE语句时,控制掩码被建立为具有值m_c .AND。 掩模EXPR

     

... 10.如果在 where-assignment-stmt expr 变量中发生元素操作或函数引用,或者在 mask-expr 中,并且不在非附加函数引用的参数列表中,执行操作或仅针对与控制掩码的真值对应的元素计算函数。

如果我正确理解上述线程/文档,则在diff_frac( i ) > 2之后计算条件arrayA( i ) > 0,因此对应于双IF块(如果我假设Fortran中的A .and. B未指定评估的顺序)。

但是,如上面的线程所述,实际行为可能取决于编译器...例如,如果我们使用gfortran5.2,ifort14.0或Oracle fortran 12.4(没有选项)编译以下代码< / p>

integer, dimension(4) :: x, y, z
integer :: i

x = [1,2,3,4]
y = 0 ; z = 0

where ( 2 <= x )
   y = x
   where ( 3.0 / y < 1.001 )  !! possible division by zero
      z = -10
   end where
end where

print *, "x = ", x
print *, "y = ", y
print *, "z = ", z

他们都给出了预期的结果:

x =            1           2           3           4
y =            0           2           3           4
z =            0           0         -10         -10

但是如果我们用调试选项编译

gfortran -ffpe-trap=zero
ifort -fpe0
f95 -ftrap=division  (or with -fnonstd)

gfortran和ifort通过评估掩码表达式中的y(i) = 0来中止浮点异常,而f95运行时没有任何抱怨。 (根据链接线程,Cray的行为类似于gfortran / ifort,而NAG / PGI / XLF与f95类似。)

作为旁注,当我们在WHERE结构中使用“非元素”函数时,控件掩码不适用,并且所有元素都用于函数评估中(根据上面草案的第7.2.3.2节,第9节) )。例如,以下代码

integer, dimension(4) :: a, b, c

a = [ 1, 2, 3, 4 ]
b = -1 ; c = -1

where ( 3 <= a )
    b = a * 100
    c = sum( b )
endwhere

给出

a =  1 2 3 4
b =  -1 -1 300 400
c =  -1 -1 698 698

表示从b的所有元素中获得sum(b)= 698,并按顺序评估这两个语句。

答案 1 :(得分:1)

为什么不

WHERE (arrayA(:) > 0)
    diff_frac(:) = 1.5 * arrayA(:)
ENDWHERE

WHERE (diff_frac(:) > 2 .and. arrayA(:) > 0)
    arrayC(:) = arrayC(:) + diff_frac(:)
ENDWHERE

我不会说嵌套where无法完成,但我不明白为什么必须这样做。然后,如果您必须转换为do循环,则翻译非常简单。

您自己的尝试建议您将where视为一种循环结构,我认为最好将其视为一种屏蔽赋值(在语言标准中如何解释),其中每个单独的赋值发生在同一时间。现在,您可以考虑转换为do concurrent构造。

答案 2 :(得分:0)

很抱歉有点偏转这个问题,但这很有意思。我不确定我是否可以告诉嵌套where将如何编译。它甚至可能是其中一个推动信封的案例。

我同意High Performance Mark where最好被认为是一种掩蔽操作,然后我不清楚(对我而言)你的&#34; A &# 34;或&#34; B &#34;将导致。 我认为他的解决方案应该与嵌套的where相同。

我的观点:因为这甚至很难辨别,你能从头开始编写新代码吗?不要翻译它,而是删除它,忘记它,并编写代码来完成这项工作。

如果你确切地知道这段代码需要做什么,它的前后条件,那么它应该不难。如果您不知道那么算法可能过于纠结,在这种情况下,无论如何都应该重写。在这个目的和它的作用之间可能存在微妙之处。你说调试此代码。

再次,抱歉切换上下文,但我认为这可能是完全重写代码最佳服务的情况之一。

如果你想保留它并且只写循环进行调试:为什么不写它们并比较输出?
按原样使用where运行它,然后使用&#34; A &#34;运行它。相反,然后使用&#34; B &#34;。打印值。