Fortran 嵌套 WHERE 语句

Fortran nested WHERE statement

我有一个带有嵌套 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

谢谢

为什么不

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 结构。

抱歉让问题有点偏题,但这很有趣。我不确定我是否可以判断嵌套的 where 将如何编译。它甚至可能是挑战极限的案例之一。

我同意 High Performance Mark 的观点,即 where 最好被认为是一种掩蔽操作,然后(对我而言)不清楚你的“A " 或 "B" 将产生结果。 我确实认为他的解决方案应该与您的嵌套 where.

相同

我的观点:由于这甚至很难辨别,您能否从头开始编写新代码来代替它?不是翻译,而是删除,算了,写代码干活。

如果您确切知道这段代码需要做什么,它的前置条件和 post- 条件,那么应该不难。如果您不知道,那么该算法可能过于复杂,在这种情况下无论如何都应该重写。其意图和实际效果之间可能存在细微差别。你说你 正在 调试这段代码。

再次抱歉切换上下文,但我认为这可能是代码最好通过完全重写来提供的情况之一。


如果你想保留它并且只编写用于调试的循环:为什么不编写它们并比较输出?
运行 原样 where,然后 运行 用“A”代替,然后用“B”。打印值。

根据 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 Masked array assignment – WHERE (page 161)

7.2.3.2 Interpretation of masked array assignments (page 162)

... 2. Each statement in a WHERE construct is executed in sequence.

... 4. The mask-expr is evaluated at most once.

... 8. Upon execution of a WHERE statement that is part of a where-body-construct, the control mask is established to have the value m_c .AND. mask-expr.

... 10. If an elemental operation or function reference occurs in the expr or variable of a where-assignment-stmt or in a mask-expr, and is not within the argument list of a nonelemental function reference, the operation is performed or the function is evaluated only for the elements corresponding to true values of the control mask.

如果我正确理解上面的thread/documents,条件diff_frac( i ) > 2arrayA( i ) > 0之后计算,所以对应于双IF块(如果我假设A .and. B在Fortran 没有指定求值顺序)。


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

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

这意味着 sum( b ) = 698 是从 b 的所有元素中获得的,两个语句依次计算。