3

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

遅いので最善の方法ではないと思いますが、ほとんどビット操作しか使用しません。

別の方法は、乗算といくつかのイプシロンを使用することです 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) もあります。

最初の関数はより正確ですが、2 番目の関数よりも約 3.6 倍遅いです (10^9 ラウンドのテストで 11.1 秒対 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%)。

私はラウンド関数を非常によく使用し、プログラムのプロファイルに多くの時間がかかります。精度を落とさずに高速に丸めるクロスプラットフォームの方法はありますか?

アップデート

質問は、それらを並べ替えることができない時点でのラウンドアップとラウンドダウンの呼び出しを疑っています。トピックを短くするために、丸めについては言及しませんでした。

ヒント: 最初の関数は 2 つのtransfer関数と 1 つの加算を使用します。また、2 番目のケースでは、1 つの乗算と 1 つの加算よりも低速です。数値のビットで何もしないのに、なぜ転送コストがそんなにかかるのですか? より高速な関数で転送を置き換えるか、追加呼び出しをまったく回避することは可能ですか?

4

2 に答える 2

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" を使用してください。

于 2013-08-27T15:11:51.050 に答える
3

あなたが何をしたいのかを正しく理解している場合、引数として+/-無限大をフィードすると、「最も近い」組み込み関数はあなたが望むことをしませんか?

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

コンパイラが適切なパフォーマンスでこれを実装している場合、これは機能する可能性があります。NaN を Inf に丸めたい場合は、それをラッパーに追加する必要があります。

なぜ roundup2 が高速なのかについては、あなたのマシンで何が起こっているのかはっきりとはわかりませんが、次の 2 つのことが言えます。

  1. roundup2 の加算はおそらく最適化されているため (eps がパラメーターの場合?)、実際には乗算だけです。
  2. 転送が実際に何かを行う場合、関数自体が非常に短いため、関数の速度が著しく低下する可能性があります。転送が単に x の余分なコピーを作成するだけの場合、これは真実かもしれません。
于 2013-08-28T05:48:29.780 に答える