Arrays 在Fortran数组构造函数中引起问题的浮点
我有点被一些与Fortran数组构造函数相关的错误所困扰。也许你能帮我理解错误?我特别有两个问题(见下文) 我最简单的工作示例是:Arrays 在Fortran数组构造函数中引起问题的浮点,arrays,fortran,fortran90,Arrays,Fortran,Fortran90,我有点被一些与Fortran数组构造函数相关的错误所困扰。也许你能帮我理解错误?我特别有两个问题(见下文) 我最简单的工作示例是: program debug use ieee_arithmetic implicit none integer :: ii real, parameter :: KZERO = 1 real, parameter :: LAMBDA = 1.3 integer, parameter :: pad = 5
program debug
use ieee_arithmetic
implicit none
integer :: ii
real, parameter :: KZERO = 1
real, parameter :: LAMBDA = 1.3
integer, parameter :: pad = 5
integer, parameter :: nsh = 60 + 2*pad
integer, parameter :: top=nsh-pad, bot=pad+1
real(kind=8), parameter :: dt2 = 1.0D-5/2
real(kind=8), parameter :: VK_SS = 6.0D-10
real(kind=8), parameter :: VM_SS = 6.0D-05
real(kind=8), parameter,dimension(nsh) :: k = [(KZERO*LAMBDA**(ii-pad), ii=1, nsh)]
real(kind=8), parameter,dimension(nsh) :: NUK_K = [(-dt2*VK_SS*k(ii)**2, ii=1, nsh)]
real(kind=8), parameter,dimension(nsh) :: NUK_M = [(-dt2*VM_SS*k(ii)**2, ii=1, nsh)]
real(kind=8), parameter, dimension(nsh) :: A = [(.0,ii=1,pad), ( REAL(exp(NUK_K(ii))), ii=bot, top), (.0,ii=1,pad)]
real(kind=8), parameter, dimension(nsh) :: B = [(.0,ii=1,pad), ( REAL( NUK_M(ii) ), ii=bot, top), (.0,ii=1,pad)]
!real(kind=8), parameter, dimension(nsh) :: C = [(.0,ii=1,pad), ( REAL(exp(NUK_M(ii))), ii=bot, top), (.0,ii=1,pad)]
print *,A
print *,'-------------------------'
print *,B
print *,'-------------------------'
!print *,C
end program debug
第一个问题:我为什么需要在定义了A、B和C的构造函数中使用REAL()进行类型转换?如果没有,则会出现以下错误:
/opt/intel/composer_xe_2011_sp1.9.293/bin/intel64/ifort -o debug debug.f90
debug.f90(23): error #7113: Each ac-value expression in an array-constructor must have the same type and type parameters. [EXP]
real(kind=8), parameter, dimension(nsh) :: A = [(.0,ii=1,pad), ( exp(NUK_K(ii)), ii=bot, top), (.0,ii=1,pad)]
--------------------------------------------------------------------^
…但是do循环元素已经是真正的=8了,因为NUK_K是
第二个问题:当我取消对定义C的行的注释时(也是通过数组构造函数),我得到以下错误:
/opt/intel/composer_xe_2011_sp1.9.293/bin/intel64/ifort -o debug debug.f90
debug.f90(25): warning #7919: The value was too small when converting to REAL(KIND=4); the result is zero.
real(kind=8), parameter, dimension(nsh) :: C = [(.0,ii=1,pad), ( REAL(exp(NUK_M(ii))), ii=bot, top), (.0,ii=1,pad)]
--------------------------------------------------------------------^
debug.f90(25): error #7768: This operation on this data type is currently inaccurate.
real(kind=8), parameter, dimension(nsh) :: C = [(.0,ii=1,pad), ( REAL(exp(NUK_M(ii))), ii=bot, top), (.0,ii=1,pad)]
--------------------------------------------------------------------^
…警告是正常的,因为我猜这意味着编译器建议exp(-[large number])->0,对吗?
但我如何处理错误“此数据类型上的操作当前不准确”
我希望你能帮助我,因为我已经在这个问题上花了很长时间了
编辑
首先,非常感谢您提供的有用答案!我非常感激。然而,不准确的问题仍然存在。有什么想法吗?我最初的猜测是使用四元组而不是双元组,然后将结果转换为双元组(我的CPU本机不支持四元组)。这也行不通。我新的最小工作示例是(注意,我删除了零填充以使示例更简单):
但这仍然会产生错误
/opt/intel/composer_xe_2011_sp1.9.293/bin/intel64/ifort -o debug debug.f90
debug.f90(30): error #7768: This operation on this data type is currently inaccurate.
real(df), parameter, dimension(nsh) :: C = [ ( exp(NUK_M(ii)) , ii=1, nsh) ]
--------------------------------------------------^
第一个问题: 您试图将一个数组
(.0,ii=1,pad)
,它是默认类型real(4)
,与数组(real(exp(NUK(ii)),ii=bot,top)
,它是real(8)
。最简单的解决方案是将第一个数组也变成实数(8)
数组,例如用0.0d0
替换浮点数.0
(双精度数字的科学表示法)
例如:
real(kind=8), parameter, dimension(nsh) :: A = [(0.0d0,ii=1,pad), ( exp(NUK_K(ii)), ii=bot, top), (0.0d0,ii=1,pad)]
real(kind=8), parameter, dimension(nsh) :: B = [(0.0d0,ii=1,pad), ( NUK_M(ii) , ii=bot, top), (0.0d0,ii=1,pad)]
real(kind=8), parameter, dimension(nsh) :: C = [(0.0d0,ii=1,pad), ( exp(NUK_M(ii)), ii=bot, top), (0.0d0,ii=1,pad)]
第二个问题:
默认情况下,real(num)
不会将数字num
键入real(8)
,而是键入默认种类real(4)
。如果要将其类型转换为real(8)
,则必须使用符号real(num,kind=8)
显式执行此操作
换句话说,你一直在做的是,从双精度到单精度浮点显式地类型转换
(exp(NUK_M(ii)),ii=bot,top)
,然后用一个单精度的零数组连接它,然后再将结果类型转换到一个双精度数组。这就是编译器抱怨精度损失的原因。对于可能感兴趣的人,请跟进:
我决定做一个骇人的解决方案来解决关于内在函数exp()精度的问题。
问题是我希望计算exp(-[very large number]),它变得太接近零,甚至四元浮点也无法表示大(负)指数。问题是,我不能简单地将这些条目设置为零,因为这会干扰整个问题的数值。
因此,我所做的是用exp(-7.0D2)替换exp(-7.0D2),这接近使用double表示的极限 这两个问题是相关的,但不是以一种很容易让你找到其他资源的方式。本质上,思考“当我忽略左边的东西时,右边的东西是如何评估的?”。数组构造函数不知道
nuk_k
的类型;求幂运算不知道c
的类型。因此,查找构造的数组的类型,使用显式种类常量的real()
的结果是丑陋和不可移植的。此外,如果你有一个种类常数,比如说rp
,那么real(rp)
比real(kind=8)
还要短,你可以省略kind=
。谢谢你的回答,并建议了定义不同种类实数的正确方法。我同意,这样比较好。然而,不准确的问题仍然存在,你可以在我编辑的答案中看到。有什么想法吗?你不能保证0.0d0是一个实物值为8的实数,我可以向你展示一些不真实的情况。此外,您不能保证reals的类型值为8对于您正在使用的编译器是有效的,我可以再次给出一个示例。在这个时代,您不应该使用d0,也不应该对种类值使用显式常量。更确切地说,请参阅一点简介默认种类是默认种类,只有在某些编译器中它是4。它是在现实世界中可以遇到的其他几种编译器中的第一种。此外,大多数编译器可以设置为使用8字节实数作为默认类型,然后双精度
(或1d0
)将为16字节,尽管在gfortran中kind=8
仍将是8字节
。换句话说,gfortran可以设置为使用kind=8
作为双精度,有些人确实使用此设置(即使在这里的问题中)。我在回答中没有使用kind变量的原因是,根据他/她提供的示例代码,我试图尽可能具体地解决OPs问题。但我同意,一般来说,使用种类变量是更好的做法。(在我自己的代码中,我总是定义dp=real64
,其中real64
是从iso\u fortran\u env
导入的,并且会为常量编写0.0\u dp
而不是0.0d0
。)非常感谢您的回答。它们非常有用,我采用了速记方法来定义浮点类型。然而,不准确的问题仍然存在,正如您可以从我上面编辑的问题中看到的。有什么想法吗?为什么不定义自己的exp_new(x),为x<7.0D2调用内在exp(x),并为x>7.0D2使用简单的泰勒展开?
real(kind=8), parameter, dimension(nsh) :: A = [(0.0d0,ii=1,pad), ( exp(NUK_K(ii)), ii=bot, top), (0.0d0,ii=1,pad)]
real(kind=8), parameter, dimension(nsh) :: B = [(0.0d0,ii=1,pad), ( NUK_M(ii) , ii=bot, top), (0.0d0,ii=1,pad)]
real(kind=8), parameter, dimension(nsh) :: C = [(0.0d0,ii=1,pad), ( exp(NUK_M(ii)), ii=bot, top), (0.0d0,ii=1,pad)]