如何比较 Fortran 中的两个双精度实数?
How to compare two double precision real numbers in Fortran?
我编写了一个 Fortran 子程序来计算椭圆轨道上两点之间的飞行时间 (TOF)。显然这个 TOF 必须是正数。我用不同的数据测试了我的子例程,但在某些情况下,尽管我编写了一种可能的方法来解决这个问题,但我得到了负面结果。
这是我的子程序:
!*************************************************************
subroutine TOF_between_2TA(e,xn,theta1,theta2,delta_t)
!*************************************************************
! Compute time of flight bewteen two points (point 1 and point 2) on elliptical orbits.
!*************************************************************
! Input:
! xn = mean motion, any units
! e = eccentricity
! theta1 = true anomaly of first point, in interval [0, twopi]
! theta2 = true anomaly of second point, in interval [0, twopi]
!
! Output:
! delta_t = time of flight between two points (same units as given by xn)
!*************************************************************
implicit none
! Arguments
double precision, intent(in) :: e,xn,theta1,theta2
double precision, intent(out) :: delta_t
! Locals
integer :: i
double precision :: xe,sxe,cxe,cth,sth,den,theta
double precision, dimension(2) :: theta_vec,xm_vec
!! To get positive time intervals, theta_vec must be sorted in ascending order
if (theta1 < theta2) then
theta_vec = [theta1, theta2] ! Case theta1 < theta2
elseif (theta2 < theta1) then
theta_vec = [theta2, theta1] ! Case theta1 > theta2
endif
do i=1,2
theta = theta_vec(i)
cth = cos(theta)
sth = sin(theta)
den = 1.0 + e*cth
cxe = (e + cth)/den
sxe = sqrt(1.0-e*e)*sth/den
xe = atan2(sxe,cxe)
! atan2 returns angles in interval -pi, +pi, we need angles in interval [0,2pi]
xe = mod(xe,twopi)
if (xe .lt. 0.0_pr) xe = xe + twopi
xm_vec(i) = xe - e*sxe
enddo
delta_t= (xm_vec(2)-xm_vec(1)) / xn
return
end subroutine TOF_between_2TA
您能否建议我一种方法来增加我的 soubrutine 的稳健性并保护我免受不良结果(即负数)的影响?
我几乎可以肯定问题是当我尝试比较变量 theta1 和 theta2 时,比较两个实数的最聪明的方法是什么?
编辑 - 经过多次测试,我在这里报告工作例程:
!*************************************************************
subroutine TOF_since_pericenter(e,xn,theta,delta_t_peri)
!*************************************************************
! Time of Flight since pericenter: From true anomaly to time
!*************************************************************
! Input:
! xn = mean motion, any units
! e = eccentricity
! theta = true anomaly, in interval [0, twopi]
!
! Output:
! delta_t_peri = time since pericenter (same units as given by xn)
!*************************************************************
implicit none
! Arguments
double precision, intent(in) :: e,xn,theta
double precision, intent(out) :: delta_t_peri
! Locals
double precision :: xe,sxe,cxe,cth,sth,xm,den
cth = cos(theta)
sth = sin(theta)
den = 1.d0 + e*cth
cxe = (e + cth)/den
sxe = sqrt(1.d0 - e*e)*sth/den
xe = atan2(sxe,cxe)
! atan2 returns angles in interval [-pi, +pi], we need angles in interval [0,2pi]
xe = mod(xe,twopi)
if (xe .lt. 0.d0) xe = xe + twopi
xm = xe - e*sxe
delta_t_peri = xm/xn
return
end subroutine ToF_since_pericenter
!*************************************************************
subroutine TOF_between_2TA(e,xn,theta_1,theta_2,delta_t)
!*************************************************************
! Compute time of flight required by point 1 to reach the point 2 on elliptical orbits.
! The point 1 is the moving object and it rotates counterclockwise on its orbit
!*************************************************************
! Input:
! xn = mean motion, any units
! e = eccentricity
! theta_1 = true anomaly of asteroid, in interval [0, twopi]
! theta_2 = true anomaly of intersection point, in interval [0, twopi]
!
! Output:
! delta_t = time of flight between two points (same units as given by xn)
!*************************************************************
implicit none
! Arguments
double precision, intent(in) :: e,xn,theta_1,theta_2
double precision, intent(out) :: delta_t
! Locals
integer :: i
double precision :: T,delta_t1_peri,delta_t2_peri
double precision, parameter :: small = 1.d-30
! Compute orbital period
T = twopi / xn
call TOF_since_pericenter(e,xn,theta_1,delta_t1_peri)
call TOF_since_pericenter(e,xn,theta_2,delta_t2_peri)
delta_t = delta_t2_peri - delta_t1_peri
if (delta_t .gt. small) then
delta_t = delta_t
elseif (delta_t .lt. -small) then
delta_t = delta_t + T
endif
return
end subroutine TOF_between_2TA
!*************************************************************
接受进一步的评论和改进建议。
您不需要进行明确的比较。您可以将 theta_vec
构造为
theta_vec = [min(theta1, theta2), max(theta1, theta2)]
这具有无分支的优势,因此 运行 比比较快得多。 (并不是说这可能与代码的整体性能相关)。
我还应该指出行
xe = mod(xe,twopi)
if (xe .lt. 0.d0) xe = xe + twopi
可以换成
xe = modulo(xe, twopi)
利用 modulo(a, p)
的输出总是 non-negative 如果 p
为正,这与 mod
的输出不同。
我编写了一个 Fortran 子程序来计算椭圆轨道上两点之间的飞行时间 (TOF)。显然这个 TOF 必须是正数。我用不同的数据测试了我的子例程,但在某些情况下,尽管我编写了一种可能的方法来解决这个问题,但我得到了负面结果。
这是我的子程序:
!*************************************************************
subroutine TOF_between_2TA(e,xn,theta1,theta2,delta_t)
!*************************************************************
! Compute time of flight bewteen two points (point 1 and point 2) on elliptical orbits.
!*************************************************************
! Input:
! xn = mean motion, any units
! e = eccentricity
! theta1 = true anomaly of first point, in interval [0, twopi]
! theta2 = true anomaly of second point, in interval [0, twopi]
!
! Output:
! delta_t = time of flight between two points (same units as given by xn)
!*************************************************************
implicit none
! Arguments
double precision, intent(in) :: e,xn,theta1,theta2
double precision, intent(out) :: delta_t
! Locals
integer :: i
double precision :: xe,sxe,cxe,cth,sth,den,theta
double precision, dimension(2) :: theta_vec,xm_vec
!! To get positive time intervals, theta_vec must be sorted in ascending order
if (theta1 < theta2) then
theta_vec = [theta1, theta2] ! Case theta1 < theta2
elseif (theta2 < theta1) then
theta_vec = [theta2, theta1] ! Case theta1 > theta2
endif
do i=1,2
theta = theta_vec(i)
cth = cos(theta)
sth = sin(theta)
den = 1.0 + e*cth
cxe = (e + cth)/den
sxe = sqrt(1.0-e*e)*sth/den
xe = atan2(sxe,cxe)
! atan2 returns angles in interval -pi, +pi, we need angles in interval [0,2pi]
xe = mod(xe,twopi)
if (xe .lt. 0.0_pr) xe = xe + twopi
xm_vec(i) = xe - e*sxe
enddo
delta_t= (xm_vec(2)-xm_vec(1)) / xn
return
end subroutine TOF_between_2TA
您能否建议我一种方法来增加我的 soubrutine 的稳健性并保护我免受不良结果(即负数)的影响? 我几乎可以肯定问题是当我尝试比较变量 theta1 和 theta2 时,比较两个实数的最聪明的方法是什么?
编辑 - 经过多次测试,我在这里报告工作例程:
!*************************************************************
subroutine TOF_since_pericenter(e,xn,theta,delta_t_peri)
!*************************************************************
! Time of Flight since pericenter: From true anomaly to time
!*************************************************************
! Input:
! xn = mean motion, any units
! e = eccentricity
! theta = true anomaly, in interval [0, twopi]
!
! Output:
! delta_t_peri = time since pericenter (same units as given by xn)
!*************************************************************
implicit none
! Arguments
double precision, intent(in) :: e,xn,theta
double precision, intent(out) :: delta_t_peri
! Locals
double precision :: xe,sxe,cxe,cth,sth,xm,den
cth = cos(theta)
sth = sin(theta)
den = 1.d0 + e*cth
cxe = (e + cth)/den
sxe = sqrt(1.d0 - e*e)*sth/den
xe = atan2(sxe,cxe)
! atan2 returns angles in interval [-pi, +pi], we need angles in interval [0,2pi]
xe = mod(xe,twopi)
if (xe .lt. 0.d0) xe = xe + twopi
xm = xe - e*sxe
delta_t_peri = xm/xn
return
end subroutine ToF_since_pericenter
!*************************************************************
subroutine TOF_between_2TA(e,xn,theta_1,theta_2,delta_t)
!*************************************************************
! Compute time of flight required by point 1 to reach the point 2 on elliptical orbits.
! The point 1 is the moving object and it rotates counterclockwise on its orbit
!*************************************************************
! Input:
! xn = mean motion, any units
! e = eccentricity
! theta_1 = true anomaly of asteroid, in interval [0, twopi]
! theta_2 = true anomaly of intersection point, in interval [0, twopi]
!
! Output:
! delta_t = time of flight between two points (same units as given by xn)
!*************************************************************
implicit none
! Arguments
double precision, intent(in) :: e,xn,theta_1,theta_2
double precision, intent(out) :: delta_t
! Locals
integer :: i
double precision :: T,delta_t1_peri,delta_t2_peri
double precision, parameter :: small = 1.d-30
! Compute orbital period
T = twopi / xn
call TOF_since_pericenter(e,xn,theta_1,delta_t1_peri)
call TOF_since_pericenter(e,xn,theta_2,delta_t2_peri)
delta_t = delta_t2_peri - delta_t1_peri
if (delta_t .gt. small) then
delta_t = delta_t
elseif (delta_t .lt. -small) then
delta_t = delta_t + T
endif
return
end subroutine TOF_between_2TA
!*************************************************************
接受进一步的评论和改进建议。
您不需要进行明确的比较。您可以将 theta_vec
构造为
theta_vec = [min(theta1, theta2), max(theta1, theta2)]
这具有无分支的优势,因此 运行 比比较快得多。 (并不是说这可能与代码的整体性能相关)。
我还应该指出行
xe = mod(xe,twopi)
if (xe .lt. 0.d0) xe = xe + twopi
可以换成
xe = modulo(xe, twopi)
利用 modulo(a, p)
的输出总是 non-negative 如果 p
为正,这与 mod
的输出不同。