(n,m)
点からまでの角度を決定しようとしてい(0,0)
ます。利用できないと、0 になる可能性がarctan2
ある問題に遭遇m
します。これにより、0 による除算が発生する可能性があります。
この問題に取り組むためのエレガントで正しい解決策は何でしょうか?
(n,m)
点からまでの角度を決定しようとしてい(0,0)
ます。利用できないと、0 になる可能性がarctan2
ある問題に遭遇m
します。これにより、0 による除算が発生する可能性があります。
この問題に取り組むためのエレガントで正しい解決策は何でしょうか?
従来の象限を使用せず、線y = +/- xで定義された分岐点を使用し、CORDICのようなアルゴリズムの最初の2つのステップを使用します(たとえば、既知の角度で座標を回転し、 '回転しました):
function atan2_substitute(x,y)
{
double angle = 0;
if (x < y)
{ angle = M_PI; x = -x; y = -y; }
// this guarantees that the angle is between -135 and +45 degrees
if (x < -y)
{
angle -= M_PI/2; tmp = x; x = -y; y = tmp;
}
// this guarantees that the angle is between -45 and +45
angle += atan(y/x);
if (angle > M_PI)
angle -= 2*M_PI;
// fails at 0,0; otherwise is accurate over the entire plane
}
このようにする理由は、atan()は、1より大きい比率よりも-1と+1の間の比率y / xの方が正確である可能性が高いためです(ただし、優れたatan()アルゴリズムはこれを認識し、逆数を取る)
atan2 が利用できない場合は、ゼロ除算条件とコード内の他のすべての特殊なケースを確認する必要があります。そのように簡単です。atan2 のウィキペディアのエントリには、必要な条件がすべて含まれています。
ターゲット ハードウェアが浮動小数点演算のゼロ除算の例外をサポートしている場合は、別のオプションがあります。
例外の原因をチェックする低レベルのハンドラーをインストールし、たまたま atan 分割である場合は問題を修正します。これにより、例外がまれな場合は atan2 が高速になりますが、低レベルの調整が必要であり、移植性がありません。
テイラー級数を使用して標準を実装し、arctan(n, m)
arctan を計算する前に次のことを行います。
if (m == 0) {
if (n < 0) return Pi;
return 0;
}
別のいくつかのトリック:
1) の場合|m| < |n|
、スワップm, n
してから arctan を計算します。最後に結果を引きますPi/2
2)|m|
が に近い場合|n|
、半角公式 を使用して半角のアークタンを計算し
arctan(x) = 2*arctan(x/(1+sqrt(1+x*x)))
ます。そうでない場合、そのような値の場合、アークタンは非常にゆっくりと収束します。
のバージョンを定義しますarctan2
。マクロとしての C の例:
#define atan2(n,m) (m)==0 ? M_PI_2 : atan((n)/(m))
もちろん、 と の符号に応じて象限を見つける方法を詳しく説明できn
ますm
。
これは、atan を使用した atan2 の正しい実装だと思います (ただし、無限大は処理しません)。
float my_atan2(float y, float x)
{
if(x == 0) // might also want to use fabs(x) < 1e-6 or something like that
{
if(y > 0)
return M_PI_2;
else
return -M_PI_2;
}
else if(x > 0)
{
return atan(y/x);
}
else
{
// x < 0
if(y > 0)
return M_PI + atan(y/x);
else
return -M_PI + atan(y/x);
}
}
テスト ハーネス:
int main()
{
for(int i = -360; i <= 360; i++)
{
float x = cos(i / 180.0 * M_PI);
float y = sin(i / 180.0 * M_PI);
float good = atan2(y, x);
float mine = my_atan2(y, x);
if(fabs(good - mine) > 1e-6)
{
printf("%d %f %f %f %f\n", i, x, y, good, mine);
}
}
}