If statement 即使在不可到达的if条件下,0的对数也会导致错误
我在Fortran 90中实现了以下代码,我希望使用某些参数的对数计算参数tau,前提是这些参数在适当的范围内If statement 即使在不可到达的if条件下,0的对数也会导致错误,if-statement,fortran,fortran90,gfortran,If Statement,Fortran,Fortran90,Gfortran,我在Fortran 90中实现了以下代码,我希望使用某些参数的对数计算参数tau,前提是这些参数在适当的范围内 MODULE nrtype INTEGER, PARAMETER :: SP=KIND(1.0) INTEGER, PARAMETER :: DP=KIND(1.0d0) INTEGER, PARAMETER :: I4B=SELECTED_INT_KIND(9) INTEGER, PARAMETER :: I2B=SELECTED_INT_KIND(4
MODULE nrtype
INTEGER, PARAMETER :: SP=KIND(1.0)
INTEGER, PARAMETER :: DP=KIND(1.0d0)
INTEGER, PARAMETER :: I4B=SELECTED_INT_KIND(9)
INTEGER, PARAMETER :: I2B=SELECTED_INT_KIND(4)
INTEGER, PARAMETER :: I1B=SELECTED_INT_KIND(2)
INTEGER, PARAMETER :: SPC=KIND((1.0,1.0))
INTEGER, PARAMETER :: DPC=KIND((1.0D0,1.0D0))
INTEGER, PARAMETER :: LGT=KIND(.TRUE.)
END MODULE
MODULE parameters
USE nrtype
REAL(DP), PARAMETER :: beta=.98_dp
REAL(DP), PARAMETER :: maxtol=1.0e-6_dp
REAL(DP), PARAMETER :: theta=1.0_dp
REAL(DP), PARAMETER :: delta=0.0_dp
END MODULE
PROGRAM mainp
USE parameters
USE nrtype
IMPLICIT NONE
INTEGER(I4B) :: tau
REAL(DP) :: taustar
IF (theta > 1.0_dp .AND. (delta > 0.0_dp .AND. delta < 1.0_dp)) THEN
taustar=LOG(maxtol/(beta*(1.0_dp-delta)*(theta-1.0_dp)))/LOG(beta*delta)
tau=CEILING(taustar,REAL(DP))
ENDIF
IF (theta > 1.0_dp .AND. delta==0.0_dp) THEN
tau=1
ELSEIF (theta == 1.0_DP .OR. delta==1.0_DP) THEN
tau=0
ENDIF
END PROGRAM main
我在代码上犯了什么错误?看起来好像没有识别if语句。表达式LOG(beta*delta)
在delta为0常量时被gfortran视为无效。它是否位于代码中不可访问的部分并不重要。它是无效的。你不能把它放在if条件下或其他任何地方
解决方案可能是不将delta
声明为参数,或者使用一些预处理器。其他编译器可能会接受它(我尝试了ifort)。我不确定这个标准。当delta为0常量时,gfortran认为表达式LOG(beta*delta)
无效。它是否位于代码中不可访问的部分并不重要。它是无效的。你不能把它放在if条件下或其他任何地方
解决方案可能是不将delta
声明为参数,或者使用一些预处理器。其他编译器可能会接受它(我尝试了ifort)。我不确定该标准。(虽然这是一个很长的注释…)Mac OSX10.9上的gfortran-6.1在使用-fdump fortran original
或-fdump fortran optimized
编译时提供了以下信息:
code:
IF .false.
ASSIGN mainp:taustar (/ log[(((/ 9.9999999999999995e-7_8
(parens (* (* 9.7999999999999998e-1_8
(parens 1.0000000000000000_8))
(parens 0_8))))))] log[((0_8))])
ASSIGN mainp:tau ceiling[((mainp:taustar) (8.00000000))]
ENDIF
IF .false.
ASSIGN mainp:tau 1 ELSE
IF .true.
ASSIGN mainp:tau 0
ENDIF
ENDIF
所有-O0
、-O2
、-O3
和-O5
的结果都相同
ifort-14.0编译程序时没有抱怨无法访问的log
行(同时抱怨tau=CEILING(…)
)。第三个IF块对应于theta==1.0_DP.或。已执行增量==1.0_DP
Oracle fortran 12.4有点有趣,因为它给出了如下警告:
taustar=LOG(maxtol/(beta*(1.0_dp-delta)*(theta-1.0_dp)))/LOG(beta*delta)
^ ^ ^
"test.f90", Line = 31, Column = 27: WARNING: A divisor of zero was detected in an expression.
"test.f90", Line = 31, Column = 65: WARNING: Evaluation of this constant expression produced a NaN or other abnormal value.
"test.f90", Line = 31, Column = 74: WARNING: The argument is not in the valid range for this intrinsic.
但第三个IF块再次被执行
另外,如果我将delta
声明为一个常用变量(不使用参数
属性),gfortran也会在移除上限(…)
后执行第三个if块(这一行似乎有问题,原因不同)
一种解决方法是将常量分配给局部变量一次,然后将它们传递给LOG(…)/LOG(…)
(从某种意义上说,是为了欺骗gfortran!):
(虽然这有点像一条长评论…)Mac OSX10.9上的gfortran-6.1在使用-fdump fortran original
或-fdump fortran optimized
编译时提供了以下信息:
code:
IF .false.
ASSIGN mainp:taustar (/ log[(((/ 9.9999999999999995e-7_8
(parens (* (* 9.7999999999999998e-1_8
(parens 1.0000000000000000_8))
(parens 0_8))))))] log[((0_8))])
ASSIGN mainp:tau ceiling[((mainp:taustar) (8.00000000))]
ENDIF
IF .false.
ASSIGN mainp:tau 1 ELSE
IF .true.
ASSIGN mainp:tau 0
ENDIF
ENDIF
所有-O0
、-O2
、-O3
和-O5
的结果都相同
ifort-14.0编译程序时没有抱怨无法访问的log
行(同时抱怨tau=CEILING(…)
)。第三个IF块对应于theta==1.0_DP.或。已执行增量==1.0_DP
Oracle fortran 12.4有点有趣,因为它给出了如下警告:
taustar=LOG(maxtol/(beta*(1.0_dp-delta)*(theta-1.0_dp)))/LOG(beta*delta)
^ ^ ^
"test.f90", Line = 31, Column = 27: WARNING: A divisor of zero was detected in an expression.
"test.f90", Line = 31, Column = 65: WARNING: Evaluation of this constant expression produced a NaN or other abnormal value.
"test.f90", Line = 31, Column = 74: WARNING: The argument is not in the valid range for this intrinsic.
但第三个IF块再次被执行
另外,如果我将delta
声明为一个常用变量(不使用参数
属性),gfortran也会在移除上限(…)
后执行第三个if块(这一行似乎有问题,原因不同)
一种解决方法是将常量分配给局部变量一次,然后将它们传递给LOG(…)/LOG(…)
(从某种意义上说,是为了欺骗gfortran!):
此错误在编译时抛出,而不是在运行时抛出
由于beta和delta是常量,编译器在程序运行之前会选择一个快捷方式并计算日志。因为它是一个零的日志,所以它抛出了一个错误,这是应该的
我不知道你想做什么。编译器是正确的,这将始终是一个零日志。确实要将增量设置为常量吗?此错误是在编译时而不是运行时引发的
由于beta和delta是常量,编译器在程序运行之前会选择一个快捷方式并计算日志。因为它是一个零的日志,所以它抛出了一个错误,这是应该的
我不知道你想做什么。编译器是正确的,这将始终是一个零日志。您确定要将delta设为常数吗?所有Fortran问题都使用tag。您将自己限制在Fortran 90(近30年历史)范围内有什么特殊原因吗?现代Fortran具有内在的模块ieee_算术
,以提高在此类问题上的鲁棒性。所有Fortran问题都使用tag。您将自己限制为Fortran 90(近30年历史)有什么特殊原因吗?现代Fortran具有内在的模块ieee_算术
,以提高对此类问题的鲁棒性。非常感谢您,我很抱歉之前没有添加Fortran标记。您是如何得出这一结论的?我只是认为这是一个编译器错误。除了类型之外,当调用过程时,对要记录的实际参数值的限制也适用——这需要执行包含过程引用的语句,而这不应该发生。不过,从我使用该编译器的经验来看,该示例还存在其他问题,可能会阻止它进行编译。这是可能的,它是允许的,但编译器不接受它。这就是为什么在《非常感谢你,我为之前没有添加fortran标记而道歉》中所有的麻烦(解决方法)的原因。你是如何得出这个结论的?我只是认为这是一个编译器错误。除了类型之外,当调用过程时,对要记录的实际参数值的限制也适用——这需要执行包含过程引用的语句,而这不应该发生。不过,从我使用该编译器的经验来看,该示例还存在其他问题,可能会阻止它进行编译。这是可能的,它是允许的,但编译器不接受它。这就是为什么所有的麻烦
code:
IF .false.
ASSIGN mainp:tmp 0_8
ASSIGN mainp:arg1 (/ 9.9999999999999995e-7_8 mainp:tmp)
ASSIGN mainp:arg2 0_8
ASSIGN mainp:taustar (/ __log_r8[[((mainp:arg1))]] __log_r8[[((mainp:arg2))]])
ENDIF