web-dev-qa-db-ja.com

arctanはどのように実装されますか?

ライブラリの多くの実装は、すべてのアーク関数のFPATAN命令にまで及びます。 FPATANはどのように実装されていますか? 1ビットの符号、Mビットの仮数、Nビットの指数があるとすると、この数の逆正接を取得するためのアルゴリズムは何ですか? FPUがそれを行うので、そのようなアルゴリズムがあるはずです。

14

三角関数にはかなり醜い実装があり、ハッキーでビットをいじくり回します。実際に使われているアルゴリズムを説明できる人をここで見つけるのはかなり難しいと思います。

Atan2の実装は次のとおりです。 https://sourceware.org/git/?p=glibc.git;a=blob;f=sysdeps/ieee754/dbl-64/e_atan2.c;h=a287ca6656b210c77367eec3c46d72f18476d61d;hb = HEAD

編集:実際に私はこれを見つけました: http://www.netlib.org/fdlibm/e_atan2.c これははるかに簡単ですが、おそらくそのために遅くなります(?)。

FPUは一部の回路でこれらすべてを実行するため、CPUがこのすべての作業を実行する必要はありません。

7
typ1232

X86プロセッサでのFPATAN命令の実装は、通常、独自仕様です。 arctanまたは他の(逆)三角関数を計算するために、一般的なアルゴリズムは3段階のプロセスに従います。

  1. 完全な入力ドメインを狭い間隔にマッピングするための引数の削減
  2. 狭い区間(一次近似区間)でのコア近似の計算
  3. 最終結果を生成するための引数の削減に基づく中間結果の拡張

引数の削減は通常、MathWorld( http://mathworld.wolfram.com/InverseTangent.html )などのさまざまな標準参照で検索できるよく知られた三角関数公式に基づいています。 arctanの計算では、一般的に使用されるIDは次のとおりです。

  • arctan(-x)= -arctan(x)
  • arctan(1/x)= 0.5 * pi-arctan(x)[x> 0]
  • arctan(x)= arctan(c)+ arctan((x --c)/(1 + x * c))

最後のアイデンティティは、値のテーブルの構築に役立つことに注意してくださいarctan(i/2n)、i = 1 ... 2n、これにより、追加のテーブルストレージを犠牲にして、任意に狭い一次近似間隔を使用できます。これは、空間と時間の間の古典的なプログラミングのトレードオフです。

コア間隔の近似は、通常、十分な次数のミニマックス多項式近似です。有理数近似は、浮動小数点除算のコストが高いため、通常、最新のハードウェアでは競合しません。また、2つの多項式の計算に加えて除算によって生じる誤差のために、追加の数値誤差が発生します。

ミニマックス多項式近似の係数は、通常、Remezアルゴリズムを使用して計算されます( http://en.wikipedia.org/wiki/Remez_algorithm )。 MapleやMathematicaのようなツールには、そのような近似を計算するための機能が組み込まれています。多項式近似の精度は、すべての係数が正確に表現可能なマシン番号であることを確認することで改善できます。私が知っている唯一のツールには、このための組み込み機能があります。これは、fpminimax()関数を提供するSollya( http://sollya.gforge.inria.fr/ )です。 。

多項式の評価は通常、効率的で正確なホーナー法( http://en.wikipedia.org/wiki/Horner%27s_method )、またはエストリン法( http ://en.wikipedia.org/wiki/Estrin%27s_scheme )およびホーナー法。 Estrinのスキームでは、スーパースカラープロセッサが提供する命令レベルの並列性をうまく利用できますが、全体的な命令数にはわずかな影響があり、精度にはほとんど影響しません(常にではありません)。

FMA(融合積和)を使用すると、丸めステップの数が減り、減算キャンセルに対する保護が提供されるため、いずれかの評価スキームの精度とパフォーマンスが向上します。 FMAは、GPUや最近のx86CPUを含む多くのプロセッサで使用されています。標準Cおよび標準C++では、FMA操作はfma()標準ライブラリ関数として公開されますが、ハードウェアサポートを提供しないプラットフォームでエミュレートする必要があるため、これらのプラットフォームでは速度が低下します。

プログラミングの観点から、近似と引数の削減に必要な浮動小数点定数をテキスト表現からマシン表現に変換するときに、変換エラーのリスクを回避したいと考えています。 ASCIIから浮動小数点への変換ルーチンは、トリッキーなバグが含まれていることで有名です(例: http://www.exploringbinary.com/php-hangs-on-numeric-value-2-2250738585072011e-308/ )。標準Cによって提供されるメカニズムの1つ(notC++は、独自の拡張としてのみ利用可能です)は、浮動小数点定数を16進数として指定することです。基になるビットパターンを直接表現するリテラル。複雑な変換を効果的に回避します。

以下は、上記の設計原理と手法の多くを示す倍精度arctan()を計算するためのCコードです。この迅速に構築されたコードは、他の回答で指摘されている実装の洗練度に欠けていますが、2 ulps未満のエラーで結果を提供するはずです。これは、さまざまなコンテキストで十分な場合があります。すべての中間ステップに1024ビット浮動小数点演算を使用するRemezアルゴリズムの簡単な実装を使用して、カスタムミニマックス近似を作成しました。 Sollyaまたは同様のツールを使用すると、数値的に優れた近似値が得られると思います。

double my_atan (double x)
{
    double a, z, p, r, s, q, o;
    /* argument reduction: 
       arctan (-x) = -arctan(x); 
       arctan (1/x) = 1/2 * pi - arctan (x), when x > 0
    */
    z = fabs (x);
    a = (z > 1.0) ? 1.0 / z : z;
    /* evaluate minimax polynomial approximation */
    s = a * a; // a**2
    q = s * s; // a**4
    o = q * q; // a**8
    /* use Estrin's scheme for low-order terms */
    p = fma (fma (fma (-0x1.53e1d2a25ff34p-16, s, 0x1.d3b63dbb65af4p-13), q,
                  fma (-0x1.312788dde0801p-10, s, 0x1.f9690c82492dbp-9)), o,
             fma (fma (-0x1.2cf5aabc7cef3p-7, s, 0x1.162b0b2a3bfcep-6), q, 
                  fma (-0x1.a7256feb6fc5cp-6, s, 0x1.171560ce4a483p-5)));
    /* use Horner's scheme for high-order terms */
    p = fma (fma (fma (fma (fma (fma (fma (fma (fma (fma (fma (fma (p, s,
        -0x1.4f44d841450e1p-5), s,
         0x1.7ee3d3f36bb94p-5), s, 
        -0x1.ad32ae04a9fd1p-5), s,  
         0x1.e17813d66954fp-5), s, 
        -0x1.11089ca9a5bcdp-4), s,  
         0x1.3b12b2db51738p-4), s,
        -0x1.745d022f8dc5cp-4), s,
         0x1.c71c709dfe927p-4), s,
        -0x1.2492491fa1744p-3), s,
         0x1.99999999840d2p-3), s,
        -0x1.555555555544cp-2) * s, a, a);
    /* back substitution based on argument reduction */
    r = (z > 1.0) ? (0x1.921fb54442d18p+0 - p) : p;
    return copysign (r, x);
}
19
njuffa

要約:それは難しい。また、時々SOをぶらぶらしているEricPostpischilとStephenCanonは、それが非常に得意です。

多くの特殊機能の通常のアプローチは次のとおりです。

  • NaN、無限大、および符号付きゼロを特殊なケースとして処理します。
  • 数値が大きすぎて結果が_M_PI_に丸められる場合は、_M_PI_を返します。このしきい値をMと呼びます。
  • 何らかの種類の引数削減IDがある場合は、それを使用して引数をより適切な範囲にします。 (これは注意が必要な場合がありますsinおよびcosの場合、これは正しい範囲に到達するように2piの正確な値。)
  • _[0,M)_を有限の数の間隔に分割します。 チェビシェフ近似 を使用して、各区間でかなり高次のアークタンを作成します。 (これはオフラインで行われ、通常、これらの実装で見られるすべての魔法の数のソースです。また、Remezの交換アルゴリズムを使用してChebyshev近似をわずかに厳密にすることができますが、これが大いに役立つケースはわかりません。 。)
  • 引数がどの間隔にあるかを把握し(ifsなどを使用するか、テーブルのインデックス作成のトリックを使用して)、その間隔でChebyshevシリーズを評価します。

ここでは、いくつかのプロパティが特に望ましいです。

  • arctanの実装は単調である必要があります。つまり、_x < y_の場合、arctan(x) <= arctan(y)です。
  • arctan実装は、常に正しい答えから1ulp以内の答えを返す必要があります。これは相対誤差限界であることに注意してください。

これらの2つの特性が成り立つように、チェビシェフ級数を評価することは完全に簡単ではありません。ここでは、2つのdoubleを使用して単一の値の異なる部分を表すトリックが一般的です。次に、実装が単調であることを示すいくつかのケースワークがおそらくあります。また、ゼロに近い場合、Chebyshev近似ではなくarctanのテイラー近似---相対誤差限界を求めており、ホーナーの法則を使用して級数を評価すると機能するはずです。

読むためのatan実装を探しているなら、fdlibmは現在glibcにあるものよりも厄介ではないようです。引数の削減は、必要に応じてtan(a+b) = (tan(a) + tan(b)) / (1 - tan(a) tan(b))に_0.5_、_1_、または_1.5_を使用して、トリガーID tan(a)に基づいているようです。

7
tmyklebu