逆トリグ(およびSQRT)関数のフローティングポイント算術の丸めエラーを説明するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/4171239

質問

私は、緯度と経度がラジアンにある形状(大きさ、緯度、経度)の2つのベクトルを表すいくつかの二重値をとるかなり複雑な関数を持っています。関数の目的は、指定された角度で最初のベクトルを2番目のベクトルを回転させ、結果のベクトルを返すことです。コードが論理的に正しく、機能していることをすでに確認しています。

関数の予想される目的はグラフィックス用です。そのため、二重精度は必要ありません。ただし、ターゲットプラットフォームでは、フロート(SINF、COSF、ATAN2F、ASINF、ACOSF、SQRTF特異的に)を使用するTrig(およびSQRT)関数がフロートよりもダブルでより速く動作します(おそらく、そのような値を計算する命令が実際に必要になる可能性があるためダブル;フロートが渡された場合、値はダブルにキャストする必要があります。これには、より多くのメモリを持つエリア、つまりオーバーヘッドの領域にコピーする必要があります。その結果、関数に含まれるすべての変数は2倍の精度です。

問題は次のとおりです。私は自分の関数を最適化しようとしているので、毎秒より多くの回数と呼ばれるようにします。したがって、私は罪、Cos、SQRTなどの呼びかけを、それらの関数の浮動小数点バージョンへの呼び出しと交換しました。これは、ほぼすべての入力に対して機能します。ただし、入力ベクトルが標準ユニットベクトル(I、J、またはK)と平行に近い場合、さまざまな機能の丸めエラーが十分に構築されており、SQRTFまたは逆トリグ関数への後の呼び出しを引き起こすのに十分なものになります(ASINF、ACOSF、 atan2f)引数を渡す かろうじて これらの関数のドメインの外。

ですから、私はこのジレンマを残しています。二重精度関数のみを呼び出して問題を避けることができます(そして、1秒あたり約1,300,000のベクトル操作の制限になります)、または何か他のものを考え出すことができます。最終的に、逆トリグ関数への入力を消毒してエッジケースの世話をする方法を望んでいます(SQRTのために些細なことです:ABSを使用するだけです)。分岐は選択肢ではありません。単一の条件付きステートメントでさえ、あらゆるオーバーヘッドが追加されているため、パフォーマンスの向上が失われます。

それで、何かアイデアはありますか?

編集:誰かがダブルスとフローティングポイント操作を使用して混乱を表明しました。実際にすべての値をダブルサイズのコンテナ(つまり、フロートサイズのコンテナに保存する場合よりも、すべての値を保存する場合、関数ははるかに高速です。ただし、浮動点精度トリグ操作は、明らかな理由で二重精度トリグ操作よりも高速です。

役に立ちましたか?

解決

基本的に、aを見つける必要があります 数値的に安定しています 問題を解決するアルゴリズム。この種のことに対する一般的なソリューションはありません。そのような概念を使用して、特定のケースに対して行う必要があります 条件番号 個々のステップの場合。そして、根本的な問題自体が条件付けされていない場合、実際には不可能かもしれません。

他のヒント

単一精度の浮動小数点は、本質的にエラーを導入します。したがって、すべての比較がEpsilon因子を使用してある程度の「スロップ」を持つように数学を構築する必要があり、限られたドメインを持つ関数への入力を消毒する必要があります。

前者は分岐するときに十分に簡単です

bool IsAlmostEqual( float a, float b ) { return fabs(a-b) < 0.001f; } // or
bool IsAlmostEqual( float a, float b ) { return fabs(a-b) < (a * 0.0001f); } // for relative error

しかし、それは厄介です。クランプドメイン入力は少し難しいですが、より良いです。重要なのは使用することです 条件付き移動演算子, 、一般的にはそのようなことをします

float ExampleOfConditionalMoveIntrinsic( float comparand, float a, float b ) 
{ return comparand >= 0.0f ? a : b ; }

単一のOPで、ブランチを発生させることなく。

これらはアーキテクチャによって異なります。 x87フローティングポイントユニットでは、 fcmov条件付きムーブop, 、しかし、それは以前に設定されている条件フラグに依存するため、それは不器用です。したがって、それは遅いです。また、CMOVに固有の一貫したコンパイラはありません。これが、可能な限りSSE2スカラー数学を支持してX87フローティングポイントを回避する理由の1つです。

条件付きの動きは、SSEでは 比較演算子 少しだけで。これは、スカラー数学でも望ましいです。

// assuming you've already used _mm_load_ss to load your floats onto registers 
__m128 fsel( __m128 comparand, __m128 a, __m128 b ) 
{
    __m128 zero = {0,0,0,0};
    // set low word of mask to all 1s if comparand > 0
    __m128 mask = _mm_cmpgt_ss( comparand, zero );  
    a = _mm_and_ss( a, mask );    // a = a & mask 
    b = _mm_andnot_ss( mask, b ); // b = ~mask & b
    return _mm_or_ss( a, b );     // return a | b
    }
}

SSE2スカラー数学が有効になっている場合、コンパイラはより優れていますが、これは優れていますが、これは優れていますが、これは優れていますが、優れていません。コンパイラフラグでそれを行うことができます /arch:sse2 MSVCまたは -mfpmath=sse GCCで。

PowerPCおよび他の多くのRISCアーキテクチャについて fsel() ハードウェアオペコードであるため、通常はコンパイラ内固有のものです。

を見たことがありますか グラフィックプログラミングブラックブック それとも、計算をGPUに渡しますか?

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top