初始化参数序列和参数数组有什么区别?

3
如果我要使用嵌套括号计算多项式,是声明每个常数还是声明它们为数组有区别吗?
例如,在以下两种声明方式之间有区别吗:
real(kind = c_double), parameter  :: &
            P0 =  .5,     &
            P1 =  .8,     &
            P2 = -.1,     &
            P3 = -.7,     &
            P4 = -.4,     &
            P5 = -.6, &
            P6 = -.2

并计算

x = ((((((P6 * a + P5) * a + P4) * a + P3) * a + P2) * a + P1) * a + P0)

或者

real(kind = c_double), parameter, dimension(7)  :: P = &
[.5, .8, -.1, -.7, -.4, -.6, -.2]

和计算

x = ((((((P(7) * a + P(6)) * a + P(5)) * a + P(4)) * a + P(3)) * a + P(2)) * a + P(1))

请注意常量值的精度:您应该编写例如.1d0或.1_c_double而不是.1。 - Francois Jacq
@FrancoisJacq,谢谢您。我在我的实际代码中已经这样做了。这只是为了问题的简化而缩短了代码。 - Avraham
2个回答

5

是的,有一些差异,但这些差异对您在此处使用并不重要。

这些差异源于以下事实:给定(与问题略有不同的)声明:

real, parameter :: P1=0.1, P2=0.2, P(2)=[0.1,0.2]

P1P2是常量,但P(1)P(2)不是。 P是一个常量,但这并不意味着完全相同。因此,在某些情况下可以使用P1,但不能使用P(1)

但是,在以下表达式中:

((((((P6 * a + P5) * a + P4) * a + P3) * a + P2) * a + P1) * a + P0)

常数的性质并不重要,P 中的元素同样可以被使用。

在复杂文本常量和(对于整数)字面常量中的kind参数的情况下会产生差异。此外,由于P(1)是一个数组元素,因此它可以用于一些不适用于P1的方式。


以下是与问题相关的一些注意事项:

  • 如果使用P0,则可以从0索引数组:real, parameter, dimension(0:6) :: P=[...];
  • 数组的优点之一是可以暗示元素的数量,在当前Fortran(而不是F90)中可以推断出来:real, parameter, dimension(0:*) :: P=[...]

1

我想指出,在常量表达式的部分,标准规定

常量表达式是...
(1) 常量或常量的子对象。

那么在哪些地方可以使用 P1 而不能使用 P(1)?当然,P(1) 可以在 P1 不能使用的地方使用。

在我看来,数组方法真的很有吸引力,因为数组表达式更容易理解 -- 在嵌套的表达式中可能会有一些不规则性,读者可能会忽略它们,但在数组公式中不会。

program polyval
   use ISO_C_BINDING, only:C_DOUBLE
   implicit none
   integer, parameter :: wp = C_DOUBLE
   real(wp), parameter :: P(0:*) = &
      [0.5_wp,0.8_wp,-0.1_wp,-0.7_wp,-0.4_wp,-0.6_wp,-0.2_wp]
   real(wp) a, x
   integer i
   a = 10
   x = sum([(a**i*P(i),i=0,ubound(P,1))])
   write(*,*) x
end program polyval

编辑:我认为现在Fortran编译器可能已经足够聪明,能够识别上述多项式求值的习惯用语,但显然我错了。我本以为

function poly1(x)
   use ISO_FORTRAN_ENV, only: wp=> REAL64
   real(wp) x
   real(wp) poly1
   real(wp), parameter :: P0 = 0.5_wp, P1 = 0.8_wp, P2 = -0.1_wp, &
      P3 = -0.7_wp, P4 = -0.4_wp, P5 = -0.6_wp, P6 = -0.2_wp
   poly1 = (((((P6*x+P5)*x+P4)*x+P3)*x+P2)*x+P1)*x+P0
end function poly1

并且

function poly2(x)
   use ISO_FORTRAN_ENV, only: wp=> REAL64
   real(wp) x
   real(wp) poly2
   real(wp), parameter :: P(0:6) = &
      [0.5_wp,0.8_wp,-0.1_wp,-0.7_wp,-0.4_wp,-0.6_wp,-0.2_wp]
   integer i
   poly2 = sum([(x**i*P(i),i=0,ubound(P,1))])
end function poly2

希望得到相似的代码,但是使用 gfortran -S -O3 -ffast-math -funroll-loops poly2.f90ifort /Fa /c /fast /Qipo- poly2.f90 命令都计算了 x 的幂和有效的 DOT_PRODUCT,而没有使用高效的方法。因此,在这种情况下,可能需要像汇编语言一样将表达式写成长格式,而不是使用高级语言,以获得合理的性能。

编辑: 好吧,似乎有一种情况可以使用 REAL 命名常量,但不能使用 REAL 常量表达式。

program test2
   use ISO_FORTRAN_ENV, only:wp=>REAL64
   implicit none
   complex(wp) x
   real(wp), parameter :: P1 = 4*atan(1.0_wp)
   real(wp), parameter :: P(1) = exp(1.0_wp)
   x = (P1,0)
   write(*,*) x
!   x = (P(1),0) ! Fails because literal or named constant is required
   write(*,*) x
end program test2

但是在查阅标准后,我认为这是唯一的情况。在f2003中,是否首次允许在complex-literal-constant中使用命名常量?为了完整起见,我提供一个例子,其中常量表达式可以工作,但命名常量不能:

module mymod
   use ISO_FORTRAN_ENV,only:wp=>REAL64
   implicit none
   contains
      subroutine sub(x)
         real(wp) x(*)
         write(*,*) x(1)
      end subroutine sub
end module mymod

program test3
   use mymod
   implicit none
   real(wp), parameter :: P1 = 4*atan(1.0_wp)
   real(wp), parameter :: P(1) = exp(1.0_wp)
!   call sub(P1)  ! Fails because array actual argument required
   call sub(P(1)) ! Succeeds due to sequence association
end program test3

1
我不知道你是否实际上在询问 P1 可以使用而 P(1) 不能使用的地方(这似乎是一个修辞问题,但你后面似乎没有提到它),但是 P1 是一个常量,而 P(1) 不是。 常量可以用于常量表达式无法使用的地方(例如在 complex :: z=(P1,0) 中)。 - francescalus
1
关于使用嵌套括号而非幂运算符,这是霍纳法则的实现,比使用指数函数或幂函数更高效。 - Avraham
@francescalus 我想有一种情况需要一个实数命名常量,而不是一个常量表达式。然而,我似乎找不到另一个这样的情况。当然,还有其他一些需要整数命名常量而不是常量表达式的情况,例如文字常量中的kind-param。如果我考虑到这一点,我可能会想到复数文字常量的情况。 - user5713492
1
@Avraham 我的猜测是现代Fortran编译器可以识别用于评估多项式的习语并进行优化,但我无法得到ifort或gfortran的支持。TT - user5713492

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接