0

2つのfloatを使用して二重分割を実行したいと思います(Direct Computeは二重分割をサポートしていないようです)。

それは可能ですか?

これは私がこれまでに試したことです(c#コード、後でHLSLになるはずです):

int count = 7;
double value = 0.0073812398871474;
float f1 = (float)value;
float f2 = (float)((value - f1));
float r1 = f1 / count;
float r2 = f2 / count;
double result = (double)r1 + (double)r2;

0,00105446285765182(結果)

0,00105446284102106(正しい結果)

それはf1の丸めと関係があります。値が代わりの場合:

 double value = 0.0073812344471474;

その後、結果は正しいです。

4

5 に答える 5

6

浮動小数点除算を使用してカウントの逆数を計算し、ニュートン-ラフソン逆数式を使用して精度をフルダブルに改善します。

int count = 7;
double value = 0.0073812398871474;
double r = (double) (1.0f / count); // approximate reciprocal
r = r * (2.0 - count*r); // much better approximation
r = r * (2.0 - count*r); // should be full double precision by now.
double result = value * r;
于 2010-11-04T15:46:14.117 に答える
3

それは可能ですか?

はい、あなたがいる限り:

  • 避けられない精度の低下を受け入れる
  • そもそもすべてのダブルがフロートに収まるわけではないことに注意してください

アップデート

コメントを読んだ後(倍精度が必要です)、私の更新された答えは次のとおりです。

いいえ。

于 2010-11-04T13:28:26.947 に答える
3

どうやらあなたの算術エラーはすぐにはわかりません。それを詳しく説明させてください。

ダブルに大きな部分と小さな部分の2つの部分があり、それぞれが約32ビットの精度であるとします。(これは、doubleがどのように機能するかを正確に示しているわけではありませんが、私たちの目的には役立ちます。)

フロートは1つの部分しかありません。

一度に32ビットで実行していましたが、すべてを2倍にしたと想像してください。

double divisor = whatever;
double dividend = dividendbig + dividendlittle;
double bigquotient = dividendbig / divisor;

bigquotientとは何ですか?ダブルです。つまり、2つの部分があります。bigquotientは、bigquotientbig+bigquotientlittleと同じです。続行:

double littlequotient = dividendlittle / divisor;

繰り返しますが、littlequotientはlittlequotientbig+littlequotientlittleです。次に、商を追加します。

double quotient = bigquotient + littlequotient;

それをどのように計算しますか?商には2つの部分があります。quotientbigはbigquotientbigに設定されます。quotientlittleはbigquotientlittle+littlequotientbigに設定されます。littlequotientlittleは破棄されます。

ここで、フロートでそれを行うと仮定します。あなたが持っている:

float f1 = dividendbig;
float f2 = dividendlittle;
float r1 = f1 / divisor;

OK、r1とは何ですか?フロートです。したがって、それは1つの部分しかありません。r1はbigquotientbigです。

float r2 = f2 / divisor;

r2とは何ですか?フロートです。したがって、それは1つの部分しかありません。r2はlittlequotientbigです。

double result = (double)r1 + (double)r2;

それらを足し合わせると、bigquotientbig+littlequotientbigが得られます。 bigquotientlittleはどうなりましたか? そこでは32ビットの精度が失われているため、途中で32ビットが不正確になるのは当然のことです。 あなたは、32ビットで64ビット演算を概算するための正しいアルゴリズムをまったく思い付いていません。

を計算するために(big + little)/divisor、単純に行うことはできません(big / divisor) + (little / divisor)この代数の規則は、すべての除算 中に丸める場合には適用されません。

それは今明らかですか?

于 2010-11-04T15:28:33.737 に答える
1

では、次のようなものはどうですか

result = value * (double)(1f / (float)count);

そこでは、2つのフロートを分割しているだけです。必要以上にキャストがいますが、重要なのはコンセプトです。

編集:
さて、あなたは実際のものと丸みを帯びたものの違いについて心配していますよね?だからあなたがそれを正しくするまでそれを何度も繰り返してください!

double result = 0;
double difference = value;
double total = 0;
float f1 = 0;
while (difference != 0)
{
    f1 = (float)difference;
    total += f1;
    difference = value - total;
    result += (double)(f1 / count);
}

...しかし、ご存知のとおり、簡単な答えはまだ「いいえ」です。これでも、すべての丸め誤差を検出することはできません。私のテストから、それは不正確さをせいぜい1e-17に、時間の約30%まで下げます。

于 2010-11-04T14:44:00.143 に答える
0

コメントで、あなたは言います:

もちろん、精度が低下することはありません。これが私が2つのフロートを使用している理由です。精度の低下を受け入れるなら、2つのフロートをキャストして除算を行うことができます。

IEEE-754single precision値には、24桁の有効な2進数があります。値のdouble precision有効桁数は53桁です。精度を損なうことなく、倍精度値を2つの単精度値として表すことはできません。ましてや、そのような表現で算術演算を行うことはできません。

とはいえ、倍精度と単精度の変換、倍精度の減算/加算、および単精度演算のみを使用して、正しく丸められた倍精度除算を実行することは可能ですが、本当に正しく実行したい場合はかなり複雑です。実際のIEEE-754の正しい丸めが必要ですか、それとも最後の1、2ビットまで正しい答えが必要ですか?

于 2010-11-04T15:57:03.583 に答える