在fortran快速上/下翻倍?

时间:2013-08-27 12:00:07

标签: fortran ieee-754 rounding-error

有没有快速的方法在Fortran中进行上/下舍入?

由于正双数的位表示的线性顺序,可以实现如下的舍入。

pinfninf是全局常量,分别为+/-无穷大

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函数和一个添加。并且它比一个乘法慢,而在第二个案例中添加一个。为什么在没有数字位的情况下转移费用如此之多?是否可以通过更快的功能替换传输或完全避免添加呼叫?

2 个答案:

答案 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更快,我无法确定您机器上发生了什么,但我可以说两件事:

  1. roundup2中的加法可能已经优化了(如果eps是参数?),所以实际上只是乘法。
  2. 如果传输确实做了任何事情,那么很容易使功能明显减慢,因为功能本身很短。如果传输只是制作多余的x副本,那甚至可能是真的。