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 ) > 2
在arrayA( 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 的所有元素中获得的,两个语句依次计算。
我有一个带有嵌套 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 ) > 2
在arrayA( 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 的所有元素中获得的,两个语句依次计算。