有没有快速的方法在Fortran中进行上/下舍入?
由于正双数的位表示的线性顺序,可以实现如下的舍入。
pinf
和ninf
是全局常量,分别为+/-无穷大
function roundup(x)
double precision ,intent(in) :: x
double precision :: roundup
if (isnan(x))then
roundup = pinf
return
end if
if (x==pinf)then
roundup = pinf
return
end if
if (x==ninf)then
roundup = ninf
return
end if
if (x>0)then
roundup = transfer((transfer(x,1_8)+1_8),1d0)
else if (x<0) then
roundup = transfer((transfer(x,1_8)-1_8),1d0)
else
if (transfer(x,1_8)==Z'0000000000000000')then
roundup = transfer((transfer(x,1_8)+1_8),1d0)
else
roundup = transfer((transfer(-x,1_8)+1_8),1d0)
end if
end if
end function roundup
我觉得这不是最好的方法,因为它很慢,但它几乎只使用位操作。
另一种方法是使用乘法和一些epsilon
eps = epsilon (1d0)
function roundup2(x)
double precision ,intent(in) :: x
double precision :: roundup2
if (isnan(x)) then
roundup2 = pinf
return
else if (x>=eps) then
roundup2 = x*(1d0+eps)
else if (x<=-eps) then
roundup2 = x*(1d0-eps)
else
roundup2 = eps
end if
end function roundup2
对于某些x
,两个函数都返回相同的结果(1d0,158d0),有些则不返回(0.1d0,15d0)。
第一个功能更准确,但比第二个功能慢约3.6倍 (10 ^ 9轮测试中11.1 vs 3.0秒)
print * ,x,y,abs(x-y)
do i = 1, 1000000000
x = roundup(x)
!y = roundup2(y)
end do
print * ,x,y,abs(x-y)
没有检查NaN / Infinities,第一次功能测试需要8.5秒(-20%)。
我使用圆形功能真的很难,并且在程序的配置文件中需要花费很多时间。有没有跨平台的方式来更快地绕而没有精度?
更新
该问题怀疑当时的综合报告和回合,无法重新排序。我没有提及向下舍入以保持主题简短。
提示:
第一个函数使用两个transfer
函数和一个添加。并且它比一个乘法慢,而在第二个案例中添加一个。为什么在没有数字位的情况下转移费用如此之多?是否可以通过更快的功能替换传输或完全避免添加呼叫?
答案 0 :(得分:4)
我建议您查看Fortran标准IEEE浮点内部模块(IEEE_ARITHMETIC,IEEE_FEATURES,IEEE_EXCEPTIONS)。它们提供IEEE_SET_ROUNDING_MODE,您可以在其中为后续操作设置舍入模式。理想情况下,您可以使用IEEE_GET_ROUNDING_MODE获取当前模式并保存,设置新模式,执行操作,然后恢复模式。
一些警告 - 改变处理器舍入模式本身是一个缓慢的操作,但如果你做了一次然后做了很多轮,那将是一个胜利。并非所有当前的Fortran编译器都支持IEEE内部模块,但最合理的是。您可能需要告诉编译器您正在使用IEEE环境 - 对于Intel Fortran,请使用“-fp-model strict”。
答案 1 :(得分:3)
如果我正确地理解了你想做什么,那么“最近的”内在函数不会做你想要的,如果你把它作为参数提供+/-无穷大吗?
http://gcc.gnu.org/onlinedocs/gfortran/NEAREST.html#NEAREST
如果编译器以相当好的性能实现了这一点,那么这可能会有效。如果你想让NaN舍入到Inf,你必须在包装器中添加它。
至于为什么roundup2更快,我无法确定您机器上发生了什么,但我可以说两件事: