提问人:ferro11001 提问时间:6/3/2022 最后编辑:ferro11001 更新时间:6/3/2022 访问量:215
在 fortran 中使用 selected_real_kind() 时保存常量的正确方法
Correct way to save constants while using selected_real_kind() in fortran
问:
我正在对 real(kind=8) 使用 selected_real_kind() 来检查我需要多少额外的处理器时间来提高数值精度。我使用的参数集是:
module param
implicit none
!extended double precision
INTEGER, PARAMETER :: xtd = SELECTED_REAL_KIND(16, 40)
save
!List of Input paramters and constants
real(xtd), parameter :: PI =4.0_xtd*atan(1.0_xtd)
integer, parameter :: L = 512
integer, parameter :: steps = 160000
real(xtd), parameter :: dt = 0.001_xtd
real(xtd), parameter :: lambda = 1.0_xtd
real(xtd), parameter :: mu = 0.0_xtd
real(xtd), parameter :: a = dt*(lambda+mu), b = dt*(lambda-mu)
end module param
打印这些参数时的输出是:
Pi = 3.14159265358979323851
L = 512
steps = 160000
dt = 9.99999999999999999958E-0004
lambda = 1.00000000000000000000
mu = 0.00000000000000000000
a = dt*(Lambda+Mu) = 9.99999999999999999958E-0004
b = dt*(Lambda-Mu) = 9.99999999999999999958E-0004
双精度real(kind=8)的代码,
module param
implicit none
save
!List of Input paramters and constants
real(kind=8), parameter :: PI =4.d0*atan(1.d0)
integer, parameter :: L = 512
integer, parameter :: steps = 160000
real(kind=8), parameter :: dt = 0.001d0
real(kind=8), parameter :: lambda = 1.0d0
real(kind=8), parameter :: mu = 0.0d0
real(kind=8), parameter :: a = dt*(lambda+mu), b = dt*(lambda-mu)
end module param
其输出为
Pi = 3.1415926535897931
L = 512
steps = 160000
dt = 1.0000000000000000E-003
lambda = 1.0000000000000000
mu = 0.0000000000000000
a = dt*(Lambda+Mu) = 1.0000000000000000E-003
b = dt*(Lambda-Mu) = 1.0000000000000000E-003
我担心数值常数的这种差异可能会导致边际精确常数的运行时间变慢。有没有更好的方法来使用 selected_real_kind() 编写这些参数?
答:
1赞
Federico Perini
6/3/2022
#1
几十年来,在 Fortran 标准中,许多实数/整数数据类型的选项一直在堆叠,这绝对不是最好的选项。selected_real_kind
首先,您应该打印其输出以查看正在使用的内容:
print *, 'my compiler chose real kind = ',xtd
我怀疑它会是而不是(通常选择)。换句话说,您通常会挑选数字,以确保您的编译器会选择正确的数据类型。16
8
selected_real_kind(15,307)
selected_real_kind
这是丑陋的、糟糕的、不安全的。它可能在分散的代码中工作,但几乎肯定会在任何非基本代码中引起头痛。我的建议是永远不要使用它:改用提供的数据类型:iso_fortran_env
use iso_fortran_env, only: real32,real64,real128
integer, parameter :: x64 = real64
integer, parameter :: x32 = real32
real(x64) :: pi64 = acos(-1.0_x64) ! this is a double pi
real(x32) :: pi32 = acos(-1.0_x32) ! this is a float pi
print *, '32-bit pi = ',pi32
print *, '64-bit pi = ',pi64
在您的例子中,您使用的是四精度(128 位浮点数),这比双精度的计算密集度要高得多,只是为了多获得一位数。
评论
3赞
Ian Bush
6/3/2022
我认为可以通过对常量使用种类的简短讨论来改进答案 - 操作使用的是 Fortran77,它与正确使用种类不能很好地混合在一起。我也不同意你表现出的强烈反对,如果使用得当,它可能正是你所需要的工具。但我不打算反驳它,在大多数情况下更简单,而且正是需要的。d0
selected_real_kind
iso_fortran_env
0赞
Vladimir F Героям слава
6/3/2022
is fortran env 常量说的是存储大小,而不是数值精度或 C 等效值(浮点/双精度)。
0赞
Vladimir F Героям слава
6/3/2022
但最重要的是,在我的阅读中,问题根本不是关于种类数字的来源,而是关于实际使用它以及它引起的数字结果的差异。对于所讨论的编译器,如果值 8 是直接输入的,或者是使用 intrisic 函数输入的,或者是 intrisic 函数的结果,则不应有任何输出差异。我什至可以理解为什么有人会直接将种类数设置为 8,例如在某些特定于编译器的测试中。我讨厌的是直接将幻数放在代码中,而不是一个命名的常量。如果程序员知道他们为什么选择值 8,那就这样吧。real64
0赞
ferro11001
6/3/2022
好的,我将放弃使用 d0 作为常量,并使用带有适当整数参数的 selected_real_kind 来实现双精度或其修改。由于iso_fortran_env与 fortran-2003 或更高版本兼容,因此当我使用 fortran-90 时,我会记住它以备将来使用。
评论
kind=8
INTEGER, PARAMETER :: xtd = 8
real(kind=8), parameter :: dt = 0.001
real(8), parameter :: dt = 0.001_8
xtd
1d0
kind=8
1.0_8