If statement 即使在不可到达的if条件下,0的对数也会导致错误

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

我在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)
    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