我有一个带有嵌套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
谢谢
答案 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;。打印值。