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 つの加算よりも低速です。数値のビットで何もしないのに、なぜ転送コストがそんなにかかるのですか? より高速な関数で転送を置き換えるか、追加呼び出しをまったく回避することは可能ですか?