浮動小数点の2つの数値の平均のロバストな計算?


15

させて xy2つの浮動小数点数のこと。それらの平均を計算する正しい方法は何ですか?

単純な方法で(x+y)/2は、次の場合にオーバーフローが発生する可能性があります。xyは、大きすぎるがあります。0.5 * x + 0.5 * y多分もっと良いと思うかもしれませんが、それは2つの乗算(これは非効率かもしれません)を必要とし、それで十分かどうかわかりません。もっと良い方法はありますか?

私が遊んでいるもう一つのアイデアは (y/2)(1 + x/y) if x<=yです。しかし、繰り返しますが、これを分析し、それが私の要件を満たしていることを証明する方法がわかりません。

さらに、計算された平均がとになること>= min(x,y)を保証する必要があり<= max(x,y)ます。Don Hatchの答えで指摘されているように、この質問を提起するより良い方法は次のとおりです。つまり、xおよびyが浮動小数点数である場合、(x+y)/2?に最も近い浮動小数点数を計算する方法は?この場合、計算された平均は自動的に>= min(x,y)および<= max(x,y)です。詳細については、Don Hatchの回答を参照してください。

注:私の優先事項は堅牢な精度です。効率は消耗品です。ただし、堅牢で正確なアルゴリズムが多数ある場合は、最も効率的なアルゴリズムを選択します。


(+1)興味深い質問、驚くほど簡単ではありません。
キリル

1
過去には、浮動小数点値が計算され、中間結果のために高精度の形式で保持されていました。a + b(64ビットの倍精度)が80ビットの中間結果を生成し、これが2で除算される場合、オーバーフローを心配する必要はありません。精度の低下はそれほど明白ではありません。
JDługosz

これに対する解決策は比較的簡単に思えます(答えを追加しました)。問題は、私はプログラマーであり、コンピューターサイエンスの専門家ではないということです。そのため、この質問をさらに難しくしているのは何が欠けているのでしょうか。
–IQAndreas

2による乗算と除算のコストを心配しないでください。コンパイラはそれらを最適化します。
フェデリコポロニ

回答:


18

Highamの精度と数値アルゴリズムの安定性これらのタイプの問題をどのように分析できるかを示している。第2章、特に演習2.8を参照してください。

この回答では、Highamの本で実際に取り上げられていないものを指摘したいと思います(それについては、あまり広く知られていないようです)。これらのような単純な数値アルゴリズムの特性を証明することに興味がある場合、Haskellのsbvなどのパッケージを使用して、z3などの最新のSMTソルバー(Satisfiability Modulo Theories)の能力を使用できます。これは、鉛筆と紙を使用するよりも多少簡単です。

私は、その与えられたと仮定してい、およびIは知りたい場合、Z = X + Y / 2を満たすは、X Z Y。次のHaskellコード0xyz=(x+y)/2xzy

import Data.SBV

test1 :: (SFloat -> SFloat -> SFloat) -> Symbolic SBool
test1 fun =
  do [x, y] <- sFloats ["x", "y"]
     constrain $ bnot (isInfiniteFP x) &&& bnot (isInfiniteFP y)
     constrain $ 0 .<= x &&& x .<= y
     let z = fun x y
     return $ x .<= z &&& z .<= y

test2 :: (SFloat -> SFloat -> SFloat) -> Symbolic SBool
test2 fun =
  do [x, y] <- sFloats ["x", "y"]
     constrain $ bnot (isInfiniteFP x) &&& bnot (isInfiniteFP y)
     constrain $ x .<= y
     let z = fun x y
     return $ x .<= z &&& z .<= y

私はこれを自動的に行うことができます。ここtest1 fun提案はその全ての有限フロート用のX Y0 X Yxfun(x,y)yx,y0xy

λ> prove $ test1 (\x y -> (x + y) / 2)
Falsifiable. Counter-example:
  x = 2.3089316e36 :: Float
  y = 3.379786e38 :: Float

オーバーフローします。私はあなたの他の式を取ると仮定します:z=x/2+y/2

λ> prove $ test1 (\x y -> x/2 + y/2)
Falsifiable. Counter-example:
  x = 2.3509886e-38 :: Float
  y = 2.3509886e-38 :: Float

動作しません(段階的なアンダーフローのため:、これはすべての演算がbase-2であるために直感的ではない場合があります)。(x/2)×2x

ここで試してください:z=x+(yx)/2

λ> prove $ test1 (\x y -> x + (y-x)/2)
Q.E.D.

動作します!これQ.E.D.は、上記で定義されているように、プロパティがすべてのフロートに対して保持されることの証明ですtest1

何ほぼ同じ、これだけに限定さ(代わりの0 X yの)?バツy0バツy

λ> prove $ test2 (\x y -> x + (y-x)/2)
Falsifiable. Counter-example:
  x = -3.1300826e34 :: Float
  y = 3.402721e38 :: Float

さて、がオーバーフローした場合、z = x + y / 2 x / 2 )はどうですか?yバツz=バツ+y/2バツ/2

λ> prove $ test2 (\x y -> x + (y/2 - x/2))
Q.E.D.

したがって、ここで試した式の中で、ようです(証明もあります)。SMTソルバーアプローチは、鉛筆と紙を使用して浮動小数点エラー解析を行うよりも、単純な浮動小数点式に関する疑いに答えるはるかに迅速な方法のように思えます。バツ+y/2バツ/2

最後に、精度と安定性の目標は、パフォーマンスの目標としばしば対立します。パフォーマンスについては、特にコンパイラがこれを機械命令に変換するという重い作業を引き続き行うため、よりも優れた方法を実際には見ていません。バツ+y/2

PSこれはすべて単精度IEEE754浮動小数点演算を使用しています。私がチェック倍精度演算(交換とを用いて)、そしてそれはあまりにも動作します。バツバツ+y/2バツ/2ySFloatSDouble

PPSコードでこれを実装する際に心に留めておくべきことの1つは、コンパイラフラグ-ffast-math(一部の形式のフラグは一部の一般的なコンパイラではデフォルトでオンになっていることがあります)はIEEE754算術演算を行わないため、上記の証明が無効になることです。連想加算の最適化などを有効にするフラグを使用する場合、以外の操作を行う意味はありません。バツ+y/2

PPPS条件なしの単純な代数式のみを見て少し夢中になりました。Don Hatchは厳密に優れています。


2
つかまっている; x <= yの場合(x> = 0であるかどうかに関係なく)、x +(y / 2-x / 2)が良い方法であると主張しましたか?答えが正確に表現できる場合、次の場合に間違った答えを与えるので、正しくないように思えます:x = -1、y = 1 + 2 ^ -52(1より大きい表現可能な最小数)、その場合、答えは2 ^ -53です。Pythonで確認: >>> x = -1.; y = 1.+2.**-52; print `2**-53`, `(x+y)/2.`, `x+(y/2.-x/2.)`
ドンハッチ

2
不平等があることを保証する:@DonHatch私は、「堅牢性」についての質問に答えることを目的とした保持していないと何もアンダー/オーバーフローし続けます。重要なことに、あなたが与える例は、大きな条件数の意味で不安定です:x yの小さな相対的摂動は、結果に大きな相対的変化、すなわち壊滅的なキャンセルを引き起こします。それ以外はあなたの言うとおりです:x + y / 2は常に小さな相対誤差で正しく丸められた結果を生成します。私自身はx + yバツバツ+y/2yバツyバツ+y/2バツ+y/2
キリル

8

最初に、すべての場合で最も正確な答えを提供するメソッドがある場合、必要な条件を満たすことを確認します。(私が言うことを注意最も正確な答えではなく、2人の受賞者が存在する可能性があるため、最も正確な答えを。)証明:もし、逆に、あなたは正確ん-AS-可能答えていないことを、必要な条件を満たすの(どちらの場合がより良い答え、矛盾であるか)、または(どちらの場合がより良い答え、矛盾であるか)のいずれかを意味します。answer<min(x,y)<=max(x,y)min(x,y)min(x,y)<=max(x,y)<answermax(x,y)

だから、あなたの質問は、最も正確な可能な答えを見つけることに要約されると思う。全体を通してIEEE754算術を想定して、以下を提案します。

if max(abs(x),abs(y)) >= 1.:
    return x/2. + y/2.
else:
    return (x+y)/2.

これが最も正確な答えを与えるという私の議論は、やや退屈なケース分析です。ここに行く:

  • ケースmax(abs(x),abs(y)) >= 1.

    • サブケースxもyも非正規化されない:この場合、計算された答えx/2.+y/2.は同じ仮数を操作(x+y)/2するため、オーバーフローを防ぐために拡張指数を仮定した場合の計算とまったく同じ答えを返します。この答えは丸めモードに依存する場合がありますが、いずれにしても、IEEE754が可能な限り最良の答えであることx+yが保証されています(計算結果は数学的なx + yの最適な近似であることが保証されており、2による除算は正確です)場合)。
    • サブケースxは非正規化されます(などabs(y)>=1):

      answer = x/2. + y/2. = y/2. since abs(x/2.) is so tiny compared to abs(y/2.) = the exact mathematical value of y/2 = a best possible answer.

    • サブケースyは非正規化されます(そのためabs(x)>=1)。

  • ケースmax(abs(x),abs(y)) < 1.
    • 計算されたサブケースはx+y非正規化または非正規化と「偶数」のいずれかです。計算はx+y正確ではないかもしれませんが、IEEE754により数学x + yの可能な限り最良の近似であることが保証されます。この場合、式の後続の2による除算(x+y)/2.は正確であるため、計算された答え(x+y)/2.は数学的(x + y)/ 2の可能な限り最適な近似になります。
    • サブケース計算をx+y非正規化し、「奇数」:この場合、Xのうちの正確に1つにおいて、Yはまた、非正規化アンドなければならない「奇数」、xの他方を意味し、Yは、反対の符号を有する非正規化され、そして計算されたように、x+yIS正確に数学的なx + yであるため、計算(x+y)/2.されたものはIEEE754によって数学(x + y)/ 2の可能な限り最良の近似であることが保証されます。

「非正規化」と言ったとき、私は本当に何か他のものを意味していることに気付きました-つまり、数字が近づくにつれて互いに近い数字、つまり非正規化された数字の範囲の約2倍の数字の範囲、つまり、en.wikipedia.org / wiki / Denormal_numberの図の最初の8ティック程度です。重要なのは、これらの「奇数」のものが、それらを2で割ることが正確ではない唯一の数字であるということです。これを明確にするために、答えのこの部分を言い換える必要があります。
ドンハッチ

flopバツy=opバツy1+δ|δ|あなたはバツ/2+y/2バツ+y/2常に正しく丸められ、オーバーフロー/アンダーフローはありません。残されているのは、オーバーフロー/アンダーフローを何も表示しないことです。これは簡単です。
キリル

@キリル私は少し迷っています...どこから来たのですか?また、「2による除算が非正規数に対して正確である」というのはまったく真実ではないと思います。正確なステートメントは、「abs(x)が最大の非正規数の少なくとも2倍である限り、x / 2は正確です」というようなものです。
ドンハッチ

3

binary64(倍精度)計算に例示されるIEEE-754バイナリ浮動小数点形式の場合、S。Boldoは、以下に示す単純なアルゴリズムが正しく丸められた平均を提供することを正式に証明しました。

Sylvie Boldo、「浮動小数点平均を計算するプログラムの形式的検証」。では正式なエンジニアリング手法に関する国際会議、PP。17-32。Springer、Cham、2015年(ドラフトオンライン

バツ+y/2バツ/2+y/2binary64C[29672970]C 特定のユースケースに最適なパフォーマンスを提供するため。

これにより、次のISO-C99コード例が生成されます。

double average (double x, double y) 
{
    const double C = 1; /* 0x1p-967 <= C <= 0x1p970 */
    return (C <= fabs (x)) ? (x / 2 + y / 2) : ((x + y) / 2);
}

最近のフォローアップ作業で、S。Boldoと共著者は、Fused Multiply-Add(FMA)操作とよく知られた精度倍増ビルディングブロック(TwoSum):

Sylvie Boldo、Florian Faissole、およびVincent Tourneur、「10進浮動小数点数の正しい平均を計算するための正式に証明されたアルゴリズム」。では第25回IEEEコンピュータの演算に関するシンポジウム(ARITH 25)、2018年6月、頁69-75。(オンライン下書き


2

パフォーマンスに関しては非常に効率的ではないかもしれませんが、(1)いずれの数値もいずれxy(オーバーフローなしを超えないことを確認し、(2)浮動小数点を「正確」に保つ非常に簡単な方法があります可能(および(3)、追加のボーナスとして、減算が使用されている場合でも、値が負の数値として格納されることはありません。

float difference = max(x, y) - min(x, y);
return min(x, y) + (difference / 2.0);

実際、正確性を追求したい場合は、その場で分割を行う必要さえありません。の値を返すだけでmin(x, y)differenceこれを使用して論理的に単純化したり、後で操作したりできます。


今私が理解しようとしているのは、この同じ答えを3 つ以上のアイテムで機能させ、すべての変数を最大数よりも低く保ち、1つの除算演算のみを使用して精度を維持する方法です。
IQAndreas

@beckoうん、少なくとも2回は除算を行うことになります。また、あなたが与えた例は答えを間違ったものにするでしょう。の平均を想像してください2,4,9、それはの平均と同じではありません3,9
IQAndreas

あなたは正しい、私の再帰は間違っていた。精度を落とさずに今すぐ修正する方法がわかりません。
becko

これが最も正確な結果をもたらすことを証明できますか?つまり、xおよびyが浮動小数点である場合、計算により(x+y)/2?に最も近い浮動小数点が生成されます。
-becko

1
x、yが最小および最大の表現可能な数値である場合、これはオーバーフローしませんか?
ドンハッチ

1

より高い精度に変換し、そこに値を追加してから元に戻します。

高精度ではオーバーフローが発生しないはずであり、両方が有効な浮動小数点範囲内にある場合、計算された数値も内部になければなりません。

そして、それはそれらの間にあるべきであり、最悪の場合、精度が十分でない場合、大きい数の半分に過ぎません。


これはブルートフォースアプローチです。おそらく動作しますが、中程度の高精度を必要としない分析を探していました。また、どの程度の中間精度が必要かを見積もることができますか?いずれにせよ、この答えを削除しないでください(+1)。答えとしては受け入れません。
becko

1

理論的にx/2は、仮数から1を引くことで計算できます。

ただし、特に浮動小数点数の形式がわからない場合は、このようなビット演算を実際に実装することは必ずしも簡単ではありません。

これを行うことができる場合、操作全体が3つの加算/減算に削減され、大幅に改善されるはずです。


0

@Roland Heathと同じ方向に考えていましたが、まだコメントできません。

x/2指数から1を減算することで計算できます(仮数ではなく、仮数から1を減算2^(value_of_exponent-length_of_mantissa)すると、全体の値から減算されます)。

一般的なケースの制限なしに、を仮定しましょうx < y。(場合はx > y、変数を再ラベル付け、。もしx = y(x+y) / 2自明です。)

  • に変換(x+y) / 2しますx/2 + y/2。これは、2つの整数減算(指数から1を減算)によって実行できます。
    • ただし、表現によっては指数に下限があります。1を引く前に指数がすでに最小の場合、この方法では特殊なケース処理が必要になります。最小の指数は、表現x可能x/2なものより小さくなります(仮数が暗黙の先行1で表現されると仮定)。
    • の指数から1を引く代わりに、仮数を1だけ右xにシフトしますx(暗黙の先行1があれば、それを追加します)。
    • yの指数から1を減算します(最小でない場合)。最小の場合(仮数のためにyはxよりも大きい)、仮数を1つ右にシフトします(暗黙の先頭1があれば追加します)。
    • xの指数に従って、新しい仮数を右にシフトしますy
    • 仮数がx完全にシフトアウトされていない限り、カマキリの整数加算を実行します。両方の指数が最小の場合、先頭の指数はオーバーフローします。これは問題ありません。オーバーフローは暗黙の先頭の指数になるはずだからです。
  • 1つの浮動小数点追加。
    • ここでは特別なケースは考えられません。上記のシフトにも適用される丸めを除きます。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.