XL Fortran の浮動小数点計算の丸め方

XL Fortran での丸め操作を理解すれば、予測可能な整合性のある結果を得るのに役立ちます。 また、スピードと正確度との妥協点を定めなければならない場合に、情報を基に決定を下すのにも 役立ちます。

MAF 演算と、中間結果に使用される高い精度のために、XL Fortran プログラムから得られる浮動小数点計算は、 一般に他の処理系の場合よりも正確になります。 XL Fortran デフォルトでの追加の精度やパフォーマンスよりも、まったく同じ結果を得ることのほうが重要な場合は、 他のシステムの浮動小数点結果の再現を参照してください。

丸めモードの選択

プログラムの丸めモードを変更するために、fpsets および fpgets ルーチンを呼び出すことが できます。 これらのルーチンは、インクルード・ファイル /opt/ibmcmp/xlf/9.1/include/fpdt.h および fpdc.h に定義されている fpstat という名前の論理値の配列を使用します。 fpstat 配列エレメントは、浮動小数点の状況レジスターおよび制御レジスターのビットに対応します。

浮動小数点の丸め制御の場合は、配列エレメント fpstat(fprn1)fpstat(fprn2) が 以下の表に記載されているとおりに設定されます。

表 15. fpsets および fpgets で使用する丸めモード・ビット

fpstat(fprn1) fpstat(fprn2) 丸めモードを使用可能にした場合
.true. .true. - 無限大方向への丸め
.true. .false. + 無限大方向への丸め
.false. .true. ゼロ方向への丸め
.false. .false. 最も近い値への丸め

たとえば、次のようになります。

      program fptest
        include 'fpdc.h'
 
        call fpgets( fpstat ) ! Get current register values.
        if ( (fpstat(fprn1) .eqv. .false.) .and. +
               (fpstat(fprn2) .eqv. .false.)) then
        print *, 'Before test: Rounding mode is towards nearest'
        print *, '             2.0 / 3.0 = ', 2.0 / 3.0
        print *, '            -2.0 / 3.0 = ', -2.0 / 3.0
        end if
 
        call fpgets( fpstat )   ! Get current register values.
        fpstat(fprn1) = .TRUE.  ! These 2 lines mean round towards
        fpstat(fprn2) = .FALSE. !   +infinity.
        call fpsets( fpstat )
        r = 2.0 / 3.0
        print *, 'Round towards +infinity:  2.0 / 3.0= ', r
 
        call fpgets( fpstat )   ! Get current register values.
        fpstat(fprn1) = .TRUE.  ! These 2 lines mean round towards
        fpstat(fprn2) = .TRUE.  !   -infinity.
        call fpsets( fpstat )
        r = -2.0 / 3.0
        print *, 'Round towards -infinity: -2.0 / 3.0= ', r
        end
! This block data program unit initializes the fpstat array, and so on.
        block data
        include 'fpdc.h'
        include 'fpdt.h'
      end

XL Fortran は、 浮動小数点状況とプロセッサーの制御レジスターを直接制御するためのいくつかのプロシージャーを提供しています。 これらのプロシージャーは、浮動小数点状況と制御レジスター (fpscr) を直接操作する インライン・マシン・インストラクションにマップされるため、fpsets および fpgets サブルーチンより効率的です。

XL Fortran は、xlf_fp_util モジュールで get_round_mode() および set_round_mode() プロシージャーを提供します。 これらのプロシージャーはそれぞれ、現行の浮動小数点丸めモードの戻しと設定を行います。

たとえば、次のようになります。

      program fptest
        use, intrinsic :: xlf_fp_util
        integer(fpscr_kind) old_fpscr
        if ( get_round_mode() == fp_rnd_rn ) then
        print *, 'Before test: Rounding mode is towards nearest'
        print *, '             2.0 / 3.0 = ', 2.0 / 3.0
        print *, '            -2.0 / 3.0 = ', -2.0 / 3.0
        end if
 
        old_fpscr = set_round_mode( fp_rnd_rp )
        r = 2.0 / 3.0
        print *, 'Round towards +infinity:  2.0 / 3.0 = ', r
 
        old_fpscr = set_round_mode( fp_rnd_rm )
        r = -2.0 / 3.0
        print *, 'Round towards -infinity: -2.0 / 3.0 = ', r
      end
 

XL Fortran は、ieee_arithmetic モジュールで ieee_get_rounding_mode() および ieee_set_rounding_mode() プロシージャーを提供します。 これらの移植可能プロシージャーはそれぞれ、現行の浮動小数点丸めモードの検索と設定を行います。

たとえば、次のようになります。

      program fptest
        use, intrinsic :: ieee_arithmetic
        type(ieee_round_type) current_mode
        call ieee_get_rounding_mode( current_mode )
        if ( current_mode == ieee_nearest ) then
        print *, 'Before test: Rounding mode is towards nearest'
        print *, '             2.0 / 3.0 = ', 2.0 / 3.0
        print *, '            -2.0 / 3.0 = ', -2.0 / 3.0
        end if
 
        call ieee_set_rounding_mode( ieee_up )
        r = 2.0 / 3.0
        print *, 'Round towards +infinity:  2.0 / 3.0 = ', r
 
        call ieee_set_rounding_mode( ieee_down )
        r = -2.0 / 3.0
        print *, 'Round towards -infinity: -2.0 / 3.0 = ', r
      end

注:

  1. 拡張精度の浮動小数点値は、最も近い値への丸めモード以外で使用してはなりません。
  2. スレッド・セーフティーおよび再入可能性に関しては、インクルード・ファイル /opt/ibmcmp/xlf/9.1/include/fpdc.h の中に、トリガー定数 IBMT で保護された THREADLOCAL ディレクティブが入っています。 呼び出しコマンド xlf_rxlf90_r、および xlf95_r を実行すると、 デフォルトで -qthreaded コンパイラー・オプションがオンになり、結果としてトリガー定数 IBMT が暗黙指定されます。 スレッド・セーフティーの考慮されていないコードでファイル /opt/ibmcmp/xlf/9.1/include/fpdc.h を組み込む場合は、トリガー定数に IBMT を指定してはなりません。

丸め誤差の最小化

丸め誤差や、計算結果におけるその他の予期しないわずかな誤差を処理するいくつかの方法があります。 以下の方法のうちの 1 つまたは複数の方法を考慮してみる必要があるでしょう。

全体にわたる丸めの最小化

丸め操作 (特にループの場合) によってコードのパフォーマンスは低下するので、計算の精度に対してよくない 影響を与えることがあります。 倍精度計算の一時結果を保管するときは、単精度変数ではなく倍精度を使うようにし、計算の最終結果が 得られるまで丸め操作を遅らせるよう配慮してください。

実行時までの丸めの遅延

コンパイラーは、可能なら、コンパイル中に浮動小数点の式を評価します。 したがって、その結果作成されたプログラムは、実行時の不必要な計算で実行スピードが遅くなることがありません。 しかし、コンパイラーの評価による結果は、実行時の計算結果と正確に一致しない場合があります。 これらの計算を実行時まで遅らせるには、-qfloat オプションの nofold サブオプションを指定してください。

それでも結果は同じにならない場合があります。 たとえば、DATA および PARAMETER ステートメント内の計算は、やはりコンパイル時に実行されるからです。

fold/nofold が原因で結果に最も大きな差が出るのは、拡張精度計算を実行したり、 -O オプションでコンパイルされたりする (またはこの両方) プログラムの場合です。

丸めモードでの整合性の確保

デフォルトで設定されている丸めモードを、最も近い値への丸めモードから変更することができます。 (例を参照してください。) これを行う場合は、プログラムのすべての 丸め操作で同じモードを使用するように 注意しなければなりません。

無限大への丸めモードを一貫して使用した場合は、たとえば次のコマンドを使用して、 丸めモードの選択の例のようなプログラムをコンパイルできます。

xlf95 -qieee=plus -qfloat=rrm changes_rounding_mode.f
IBM Copyright 2003