在 fortran 中使用 selected_real_kind() 时保存常量的正确方法

Correct way to save constants while using selected_real_kind() in fortran

提问人:ferro11001 提问时间:6/3/2022 最后编辑:ferro11001 更新时间:6/3/2022 访问量:215

问:

我正在对 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() 编写这些参数?

Fortran 精密 fortran90

评论

1赞 Vladimir F Героям слава 6/3/2022
你到底在哪里使用?通常,你会这样做。它好多了。请显示完整的代码,以便我们了解您如何准确声明数字文字和常量。kind=8INTEGER, PARAMETER :: xtd = 8
0赞 Vladimir F Героям слава 6/3/2022
也就是说,如果你这样做,你就做错了。它应该是 ,但命名常量当然更好。关于你的恐惧,你必须更彻底地解释它们,但它们很可能是未经证实的。real(kind=8), parameter :: dt = 0.001real(8), parameter :: dt = 0.001_8xtd
0赞 ferro11001 6/3/2022
@VladimirF Героямслава 我担心的是,使用扩展精度写入时的数值常数 dt 是 9.9999999999999999999999958E-0004 而不是 1.00000000000000000000E-003。这是否会导致相同参数值的运行时间变慢?
1赞 steve 6/3/2022
我会谦虚地建议你需要阅读戈德堡的论文(也许,好几遍)。itu.dk/~sestoft/bachelor/IEEE754_article.pdf
0赞 Vladimir F Героям слава 6/3/2022
顺便说一句,不等同于 .1d0 表示双精度,但 kind=8 可能是其他原因。第 8 类的正确文本是 。查看 stackoverflow.com/questions/838310/fortran-90-kind-parameter1d0kind=81.0_8

答:

1赞 Federico Perini 6/3/2022 #1

几十年来,在 Fortran 标准中,许多实数/整数数据类型的选项一直在堆叠,这绝对不是最好的选项。selected_real_kind

首先,您应该打印其输出以查看正在使用的内容:

print *, 'my compiler chose real kind = ',xtd

我怀疑它会是而不是(通常选择)。换句话说,您通常会挑选数字,以确保您的编译器会选择正确的数据类型。168selected_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,它与正确使用种类不能很好地混合在一起。我也不同意你表现出的强烈反对,如果使用得当,它可能正是你所需要的工具。但我不打算反驳它,在大多数情况下更简单,而且正是需要的。d0selected_real_kindiso_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 时,我会记住它以备将来使用。