2013-08-27 28 views
2

在Fortran中有快速的方法嗎?在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 

我覺得這是沒有這樣做,因爲它的速度慢的最好辦法全局常量,但它幾乎只使用位操作。

的另一種方法是使用乘法和一些小量 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倍(11.1 VS3.0秒上10^9輪試驗)

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 /無窮大第一功能測試需要8.5秒(-20%)。

我使用循環功能真的很難,它需要很多時間在程序的配置文件。是否有跨平臺的方式可以更快速地完成並且沒有精確性?

更新

的問題,在沒有能力對它們重新排序的時間嫌疑人圍捕和ROUNDDOWN的電話。我沒有提到輪換,以保持話題的簡短。

提示: 第一個函數使用兩個transfer函數和一個加法。在第二種情況下,它比一個乘法和一個加法要慢。爲什麼轉移成本這麼多,當它不做任何數字的位?是否可以用更快的功能代替傳輸或者完全避免添加呼叫?

+0

您是否嘗試過使用nint? – cup

+0

我需要「四捨五入」到最接近的代表double而不是int。標題可能有點誤導,但從身體清楚。 – Sergei

回答

2

如果我正確地理解了你想要做的事情,那麼「最近的」內在函數是不是做了你想要的,如果你把它作爲參數來餵食+/-無窮大呢?

http://gcc.gnu.org/onlinedocs/gfortran/NEAREST.html#NEAREST

這可能會實現,如果編譯器實現了這個有不俗的表現。如果你想讓NaN輪到Inf,你必須將它添加到包裝中。

至於爲什麼roundup2快,我不能告訴肯定發生了什麼事情你的機器上,但我可以說兩兩件事:

  1. 在roundup2加入可能是優化掉了(如果每股收益一個參數?),所以實際上只是一個乘法。
  2. 如果轉移確實做了任何事情,那很可能會顯着減慢功能,因爲功能本身很短。如果轉移只是製造了多餘的x副本,那甚至可能是真的。
+0

謝謝,NEAREST與平常的雙打很好,但它比我的機器上的第一個'roundup'慢三倍。 – Sergei

+0

奇怪的是它比你慢得多,因爲它可能使用類似[nextafter](http://svn.open64.net/svnroot/open64/trunk/osprey/libacml_mv/src/nextafter.c)的實現,它本身非常類似於你的第一次綜合報道。 – 2013-08-28 14:31:45

3

我建議您看看Fortran標準IEEE浮點內在模塊(IEEE_ARITHMETIC,IEEE_FEATURES,IEEE_EXCEPTIONS)。這些提供了IEEE_SET_ROUNDING_MODE,您可以在其中設置後續操作的舍入模式。理想情況下,您可以使用IEEE_GET_ROUNDING_MODE獲取當前模式並保存,設置新模式,執行操作,然後恢復模式。

一些注意事項 - 改變處理器舍入模式本身就是一個緩慢的操作,但如果你這樣做了一次,然後做了很多回合,那將是一場勝利。並非所有當前的Fortran編譯器都支持IEEE內在模塊,但最合理的模塊應該是。您可能需要告訴編譯器您正在使用IEEE環境 - 對於英特爾Fortran,請使用「-fp-model strict」。

+0

感謝您的建議,但它不適合我的需求。我不能重新排序整批作業。可能有一些比特函數的技巧? – Sergei

+0

你不必散裝做它們。爲什麼不嘗試編寫使用標準功能的函數並查看它對您的工作方式?我認爲它必須比你提議的其他事情更快。 FP值的位操作非常棘手 - 我應該知道,因爲我寫過(並修復了錯誤)代碼。 –

+0

我需要同時使用綜合和倒數。如上所述,更改舍入模式非常昂貴。我不能那樣做。 – Sergei