丸め誤差

1 つの実数の丸め誤差は,その計算に限れば許容可能なほど小さいかもしれませんが,ここからは少なくとも 2 つの問題が生じます。第 1 に,2 つの正確な数値と見なした値が等しいかどうかを検証する際に,これらの数値のどちらか,または両方の浮動小数点表現の丸め誤差のために,比較が成功せず,間違った結果が得られる可能性があります。また,浮動小数点数の計算を行っているときには,丸め誤差が累積して,数値の精度が無視できないほど低下することがあります。

数値結果を慎重に検討して,丸め誤差またはその効果を最小限に抑えるようにしてください。倍精度算術計算を使うか,アルゴリズムを再構築するか,またはこの両方を行うことで改善されることがあります。たとえば,計算に線形データ項目の配列が含まれている場合,個々の配列要素から各配列の平均値を引き,これら配列の個々の要素を配列要素の標準偏差をもとに正規化することで,数値の精度の低下を抑えることができます。

次のコードは,システムによって異なる形で実行され,nx,および s に対して異なる結果を生成する可能性があります。また,ia32 システムで /fltconsistency または /nofltconsistency コンパイラ・オプションを使用すると,異なる結果が生じます。0.2 の浮動小数点表現は正確でないため,その丸め誤差が s に累積し,n の最終的な値に影響を与えます。

	  INTEGER n
	  REAL s, x
	  n = 0
	  s = 0.
	  x = 0.
	1 n = n + 1
	  x = x + 0.2
	  s = s + x
	  IF ( x .LE. 10. ) GOTO 1 ! Will you get 51 cycles?
	  WRITE(*,*) 'n = ', n, '; x = ', x, '; s = ', s

この例は,一般的なコーディング上の問題を示しています。浮動小数点変数を多くの連続した周期で持ち運び,それを使って IF 文で検証を実行するというコーディングです。このプロセスは積分計算でよく使われます。この問題にはいくつかの解決方法があります。たとえば,xs を整数指標の倍数として計算し,x を増加させる文を x = n * 0.2 に置き換えることで,丸め誤差の累積を避けることができます。周期完了の検証は「IF (n <= 50) GOTO 1」のように整数指標をもとに行うか,「DO n= 1,51」のように DO ループを使用します。どうしても周期で処理される実数変数に対して検証を行わなければならない場合は,「IF (x <= 10.001)」のように現実的な許容誤差を使用します。

浮動小数点算術は,必ずしも代数学の標準的な規則に厳密に従うわけではありません。丸め誤差を考慮に入れた場合,加算の結合律は,厳密には成り立ちません。括弧を使えば,正しい正確な答えを得るために必要な評価順序を表現することができます。生成されるコードを最適化する場合,この方法をお勧めします。さもないと,結合順序は予測不可能になります。

...\DF98\SAMPLES\TUTORIAL フォルダにある Visual Fortran サンプルASSOCN.F90 が示しているように,式「(x + y) + z」と「x + (y + z)」は,場合によっては予期しない結果をもたらすことがあります。この例は,有効けた数以上の差がある 2 つの数値を加えるときの危険性を示しています。

...\DF98\SAMPLES\TUTORIAL フォルダのサンプル INTERVAL.F90 は,計算の前後で浮動小数点コントロール・ワードの丸め精度と丸めモードを変更したときに,次の単純な式の計算結果に生じる影響を示しています。

	(q*r + s*t) / (u + v)

...\DF98\SAMPLES\TUTORIAL フォルダのサンプル EPSILON.F90 は,「1.0 + eps」などの式における丸め誤差に由来する問題を示しています。ここで,eps は 1.0 と比較するための意味のある数字です。

コンパイラは,コンパイルの際に基本丸めモード (最も近い値に丸める) を使用します。コンパイラは,最適化レベルが上がるにつれ,より多くの演算をコンパイル時に行って,実行時の演算を減らします。丸めモードを別の (最も近い値に丸める以外の) 設定にした場合,その丸めモードは,その計算が実行時に行われる場合にのみ使用されることになります。たとえば,サンプルの INTERVAL.F90/optimize:0 でコンパイルされますが,この最適化レベルでは定数の伝播とインライン展開を含むいくつかのコンパイル時の最適化が無効にされます。

関連情報