Arduboyで狙い撃つぜ!(atan2テーブルの作り方) その2

例によって例のごとく、atan2は重い。
テーブル化したいのだが……。

その前に、atan2について調べてみた。

ターゲットを狙い撃つ場合、そのターゲットまでのx,yの距離は分かる。
であれば、tanの逆関数であるatanを使えば良さそうだが、
x=0のとき、つまり、90度、270度の時にゼロ割が発生してしまう。
atanだと90度、270度の時に場合分けが必要になって、使いづらい。

atan2はその辺りが考慮されており、単にyとxを与えれば、角度が返ってくる。
なんて素晴らしい。

そこで、atanでatan2と同じような動作を再現して、中身を想像してみた。

atanは、-PI/2〜PI/2ラジアンを返すので、(-90〜90度ということ)

  • xが正の場合は、atanの値をそのまま使う。
  • xが負の場合は、yの符号をチェックして、第2象限、第3象限に場合分け。
  • 90度、270度、x=0かつy=0の場合は、特殊処理。

ということになった。


Pythonで動作を確認してみた。

#                 PI/2
#                  |
#     atan(y/x)+PI | atan(y/x)
#                  |
# PI --------------+-------------- 0
#                  |
#     atan(y/x)-PI | atan(y/x)
#                  |
#                -PI/2

import math

def atan2(y, x):
    if x > 0:
        # 第1、第4象限
        return math.atan(y / x)
    elif x < 0:
        if y < 0:
            # 第2象限
            return math.atan(y / x) - math.pi
        else:
            # 第3象限
            return math.atan(y / x) + math.pi
    else:
        if y > 0:
            # 90度
            return math.pi / 2
        elif y < 0:
            # 270度
            return -math.pi / 2
        else:
            # x,yが0
            return 0


以下のようにして、
数学ライブラリのatan2と自作のatan2を比較してみた。

#                 PI/2
#                  |
#     atan(y/x)+PI | atan(y/x)
#                  |
# PI --------------+-------------- 0
#                  |
#     atan(y/x)-PI | atan(y/x)
#                  |
#                -PI/2

import math
import random
import sys

def atan2(y, x):
    if x > 0:
        # 第1、第4象限
        return math.atan(y / x)
    elif x < 0:
        if y < 0:
            # 第2象限
            return math.atan(y / x) - math.pi
        else:
            # 第3象限
            return math.atan(y / x) + math.pi
    else:
        if y > 0:
            # 90度
            return math.pi / 2
        elif y < 0:
            # 270度
            return -math.pi / 2
        else:
            # x,yが0
            return 0

# テストコード
for i in range(100000):
    x = random.randint(-sys.maxsize-1, sys.maxsize)
    y = random.randint(-sys.maxsize-1, sys.maxsize)
    v1 = math.atan2(y, x)
    v2 = atan2(y, x)

    # v1,v2の値が異なっていれば表示する(わずかな誤差は許容)
    if round(v1 - v2, 15) != 0:
        print(str(x) + "\t" + str(y))