閉じる

C言語のatan2関数でx,yから角度を求める方法と使い方

2次元座標のxとyから角度(向き)を求めるとき、C言語ではatan2関数が最も確実で安全な選択です。

比から角度を戻すatanと異なり、象限を正しく判定してくれるため、負の値や0を含む場合でも期待どおりの角度が得られます。

本記事ではatan2の基本から実用例、注意点まで、初心者の方でも迷わないよう丁寧に解説します。

atan2とは?x,yから角度を求める基本

atan2の意味とメリット

atan2は、直交座標の成分(cos方向の成分x、sin方向の成分y)から、点(またはベクトル)がなす角度を一発で返す関数です。

数学的にはθ = atan2(y, x)で、x軸正方向を0とし、反時計回りを正とする角度θ(ラジアン)を返します。

最大のメリットは、象限(第1〜第4)を自動で判定し、負のxやyを含む場合でも正しい角度が返る点です。

これによりatan(y/x)のように割り算とatanを組み合わせて自前で象限分岐する必要がありません。

引数の順序は

atan2の引数順は( y, x )です。

x,yではありません。

第1引数はy、第2引数はxと覚えることが重要です。

これはtan(θ) = y/xの分子がy、分母がxであることに対応しています。

順序を取り違えると、90度ずれた誤った角度になります。

返り値の範囲は-pi〜+pi

atan2(y, x)角度をラジアンで返し、その範囲はおおむね[-π, +π]です。

一般的には-π < θ ≤ +πと理解しておくと混乱が少ないです。

たとえば、x>0かつy=0ならθ=0、x<0かつy=0ならθ=±π(実装とyの符号により+πまたは-π)になります。

次の表は、典型的な象限と返る角度の概観です。

xの符号yの符号角度の範囲(概念)
x>0y≥00〜+π/2
x<0y≥0+π/2〜+π
x<0y<0-π〜-π/2
x>0y<0-π/2〜0

ヘッダは<math.h>にある

利用には#include <math.h>が必要です。

Cの標準数値関数群に含まれます。

ビルド時の注意

  • GCC/Clang系(Linux/macOS/MinGW)では数学ライブラリをリンクするために-lmが必要です。
    例:gcc main.c -std=c17 -Wall -Wextra -O2 -lm
  • MSVC(Visual Studio)では-lmは不要です。
  • M_PIなどの円周率定数は標準Cでは必須でないため、環境により存在しないことがあります。必要なら自前でPI定数を定義するか、MSVCでは#define _USE_MATH_DEFINESの後で#include <math.h>を行います。

atan2の使い方

x,yから方位角を求める手順

点A(x1, y1)から点B(x2, y2)への向き(方位角)は、差分dx = x2 - x1dy = y2 - y1を作りatan2(dy, dx)で求めます

返るのはラジアンなので、必要に応じて度へ変換します。

C言語
// file: heading_basic.c
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;

// ラジアンを度に変換
static double rad2deg(double rad) {
    return rad * (180.0 / PI);
}

int main(void) {
    // A(1,2)からB(4,6)への向きを求める例
    const double x1 = 1.0, y1 = 2.0;
    const double x2 = 4.0, y2 = 6.0;

    const double dx = x2 - x1; // 3
    const double dy = y2 - y1; // 4

    // (0,0)ベクトルは角度が定義できないのでチェック
    if (dx == 0.0 && dy == 0.0) {
        printf("角度は定義できません(同一点)\n");
        return 0;
    }

    const double rad = atan2(dy, dx);   // 角度(ラジアン)
    const double deg = rad2deg(rad);    // 度に変換

    printf("dx=%.1f, dy=%.1f\n", dx, dy);
    printf("angle(rad)=%.10f\n", rad);
    printf("angle(deg)=%.6f\n", deg);
    return 0;
}
実行結果
dx=3.0, dy=4.0
angle(rad)=0.9272952180
angle(deg)=53.130102

ラジアンを度に変換する方法

度とラジアンの関係はdeg = rad * 180/πです。

逆にrad = deg * π/180です。

πは自前定義するのが確実です。

C言語
// file: angle_convert.c
#include <stdio.h>
#include <math.h>

// 環境によりM_PIがないため自前で定義しておくのが安全
#ifndef PI
#define PI 3.14159265358979323846
#endif

static double deg2rad(double deg) { return deg * (PI / 180.0); }
static double rad2deg(double rad) { return rad * (180.0 / PI); }

int main(void) {
    double r = deg2rad(180.0);
    double d = rad2deg(3.14159265358979323846);
    printf("180deg = %.10f rad\n", r);
    printf("pi rad = %.6f deg\n", d);
    return 0;
}
実行結果
180deg = 3.1415926536 rad
pi rad = 180.000000 deg

0や負の値を含む場合の挙動

atan2はxとyの符号から象限を自動判定します。

以下は4象限の代表点の角度を確認するサンプルです。

C言語
// file: quadrants.c
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;

static double rad2deg(double rad) { return rad * (180.0 / PI); }

int main(void) {
    struct XY { double x, y; } cases[] = {
        { 1,  1},  // 第1象限 -> +45°
        {-1,  1},  // 第2象限 -> +135°
        {-1, -1},  // 第3象限 -> -135°
        { 1, -1},  // 第4象限 -> -45°
        {-1,  0},  // x<0, y=0 -> ±180°
        { 0, -1},  // x=0, y<0 -> -90°
        { 0,  0},  // 特殊(未定義)
    };

    for (size_t i = 0; i < sizeof(cases)/sizeof(cases[0]); ++i) {
        double x = cases[i].x, y = cases[i].y;
        if (x == 0.0 && y == 0.0) {
            printf("(0,0): 角度は定義できません\n");
            continue;
        }
        double rad = atan2(y, x);
        double deg = rad2deg(rad);
        printf("(x=% .0f, y=% .0f) -> rad=% .10f, deg=% .6f\n", x, y, rad, deg);
    }
    return 0;
}
実行結果
(x= 1, y= 1) -> rad= 0.7853981634, deg= 45.000000
(x=-1, y= 1) -> rad= 2.3561944902, deg= 135.000000
(x=-1, y=-1) -> rad=-2.3561944902, deg=-135.000000
(x= 1, y=-1) -> rad=-0.7853981634, deg=-45.000000
(x=-1, y= 0) -> rad= 3.1415926536, deg= 180.000000
(x= 0, y=-1) -> rad=-1.5707963268, deg=-90.000000
(0,0): 角度は定義できません

atanとの違い

atanは比(傾き)しか見ないため、象限が区別できません

dx<0のときにatan(dy/dx)だと180度ずれます。

C言語
// file: atan_vs_atan2.c
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;
static double rad2deg(double rad) { return rad * (180.0 / PI); }

int main(void) {
    double dx = -1.0, dy = 1.0; // 本来は+135°の方向(第2象限)

    double a_atan2 = atan2(dy, dx);        // 正しく象限を判定 -> 約+135°
    double a_atan  = atan(dy / dx);        // -1のatan -> 約-45° (象限情報が失われる)

    printf("atan2: rad=%.10f, deg=%.6f\n", a_atan2, rad2deg(a_atan2));
    printf("atan : rad=%.10f, deg=%.6f\n", a_atan,  rad2deg(a_atan));
    return 0;
}
実行結果
atan2: rad=2.3561944902, deg=135.000000
atan : rad=-0.7853981634, deg=-45.000000

象限を間違えると向き、回転、カメラ制御などで逆方向を向く重大な不具合につながります。

角度が必要なら常にatan2を使うと覚えましょう。

実用例とよく使うシーン

2D座標の向きを求める例

ゲームや可視化で、キャラクターや矢印がターゲットの方向を向く処理は典型例です。

C言語
// file: look_at_example.c
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;

static double rad2deg(double rad) { return rad * (180.0 / PI); }
static double normalize_deg(double deg) {
    // 角度を0〜360に正規化
    double r = fmod(deg, 360.0);
    if (r < 0) r += 360.0;
    return r;
}

int main(void) {
    // 自機pos -> 目標targetへ向く角度
    double pos_x = 10.0, pos_y = 5.0;
    double target_x = 4.0, target_y = 12.0;

    double dx = target_x - pos_x;
    double dy = target_y - pos_y;

    if (dx == 0.0 && dy == 0.0) {
        printf("向きなし(同一点)\n");
        return 0;
    }

    double rad = atan2(dy, dx);        // -π〜+π
    double deg = normalize_deg(rad2deg(rad)); // 0〜360度に

    printf("to target: rad=%.10f, deg(0-360)=%.6f\n", rad, deg);
    return 0;
}
実行結果
to target: rad=2.2655346020, deg(0-360)=129.870078

極座標(角度と距離)に変換する例

直交座標(x, y)から極座標(r, θ)へ変換します。

距離はhypot(x, y)が安全で、オーバーフローに強い実装になっています。

C言語
// file: to_polar.c
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;
static double rad2deg(double rad) { return rad * (180.0 / PI); }

int main(void) {
    double x = -3.0, y = 4.0;

    double r = hypot(x, y);      // 距離(>=0)
    double th = atan2(y, x);     // 角度(ラジアン, -π〜+π)

    printf("Cartesian (x=%.2f, y=%.2f)\n", x, y);
    printf("Polar     (r=%.6f, th(rad)=%.10f, th(deg)=%.6f)\n", r, th, rad2deg(th));
    return 0;
}
実行結果
Cartesian (x=-3.00, y=4.00)
Polar     (r=5.000000, th(rad)=2.2142974356, th(deg)=126.869898

ジョイスティックやマウスから角度を得る例

UIやゲームでは、入力デバイスのX,Y軸から方向を計算します。

画面座標はy軸が下向きに増える場合が多いので、数学座標と合わせるためにyの符号を反転することがよくあります。

C言語
// file: input_heading.c
#include <stdio.h>
#include <math.h>

// 0〜360度の見やすい角度に整える
static const double PI = 3.14159265358979323846;
static double rad2deg(double rad) { return rad * (180.0 / PI); }
static double normalize_deg(double deg) {
    double r = fmod(deg, 360.0);
    if (r < 0) r += 360.0;
    return r;
}

int main(void) {
    // 例: ジョイスティックの正規化入力(-1〜+1)、画面座標系(y下向き)
    double x = 0.6;
    double y_screen = 0.8;

    // 数学座標に合わせてyを反転
    double y = -y_screen;

    if (x == 0.0 && y == 0.0) {
        printf("入力がニュートラル(角度なし)\n");
        return 0;
    }

    double rad = atan2(y, x);
    double deg = normalize_deg(rad2deg(rad));

    printf("stick(x=%.2f, y=%.2f) -> heading=%.2f deg (0-360)\n", x, y_screen, deg);
    return 0;
}
実行結果
stick(x=0.60, y=0.80) -> heading=306.87 deg (0-360)

注意点とベストプラクティス

(0,0)は角度が定義できないので先にチェック

(x, y)=(0, 0)のベクトルは方向が定まりません

この入力をatan2へ渡すこと自体は可能ですが、意味のある角度ではないため、アプリ側で先に検出して分岐するのが妥当です。

度とラジアンの取り違えを防ぐ

ラジアンと度の混在はバグの温床です。

「関数の引数と戻り値がどちらの単位か」を明確に決め、名前やコメントで常に明記しましょう。

変換ヘルパーを用意して一元化すると安全です。

角度を0〜360度に正規化する

UI表示や比較を行うときは、角度を0〜360度(または0〜2π)へ正規化しておくと扱いやすくなります。

fmodを使うと簡潔です。

C言語
// file: normalize_angle.c
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;

static double normalize_rad(double rad) {
    // 0〜2πに正規化
    double two_pi = 2.0 * PI;
    double r = fmod(rad, two_pi);
    if (r < 0) r += two_pi;
    return r;
}

static double normalize_deg(double deg) {
    // 0〜360に正規化
    double r = fmod(deg, 360.0);
    if (r < 0) r += 360.0;
    return r;
}

int main(void) {
    double a1 = normalize_deg(-450.0);  // -> 270
    double a2 = normalize_rad(-3.5);    // -> 2π-3.5
    printf("normalize_deg(-450) = %.1f\n", a1);
    printf("normalize_rad(-3.5) = %.10f\n", a2);
    return 0;
}
実行結果
normalize_deg(-450) = 270.0
normalize_rad(-3.5) = 2.7831853072

doubleで計算して精度を確保する

角度計算はdoubleを使うのが基本です。

floatだと丸め誤差が大きく、微妙な差が出やすくなります。

入出力、定数、累積計算もdoubleで統一すると安定します。

関数の戻り値と単位をコメントで明記する

自作関数のコメントに単位(rad/deg)と範囲を書き、利用側の混乱を防ぎます。

C言語
// file: best_practice_utils.c
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;

// 返り値: 角度(ラジアン, 範囲は-π〜+π)
static double angle_rad_from_xy(double x, double y) {
    // 注意: (0,0)は未定義。呼び出し側で弾くか、ここで特別扱いしても良い。
    return atan2(y, x);
}

// 返り値: 角度(度, 範囲は0〜360)
static double heading_deg_from_xy(double x, double y) {
    double rad = atan2(y, x); // -π〜+π
    double deg = rad * (180.0 / PI);
    // 0〜360に正規化
    double r = fmod(deg, 360.0);
    if (r < 0) r += 360.0;
    return r;
}

int main(void) {
    double x = -1.0, y = 0.000000;
    printf("angle_rad_from_xy = %.10f rad\n", angle_rad_from_xy(x, y));
    printf("heading_deg_from_xy(0-360) = %.6f deg\n", heading_deg_from_xy(x, y));
    return 0;
}
実行結果
angle_rad_from_xy = 3.1415926536 rad
heading_deg_from_xy(0-360) = 180.000000 deg

まとめ

atan2は、xとyから角度を安全かつ確実に求めるための標準関数です。

引数の順序は( y, x )で、返り値はラジアン[-π, +π]という基本を押さえれば、象限の分岐や負の値を気にせずに扱えます。

実用上は、(0,0)の事前チェック、度↔ラジアン変換の一元化、0〜360度への正規化、そしてdoubleでの統一が重要です。

ビルド時は#include <math.h>-lm(GCC/Clang)を忘れず、M_PIがない環境では自前でπを定義しましょう。

これらのポイントを守れば、座標の向き、極座標変換、入力デバイスの角度取得など、幅広い場面で安定した角度計算が行えます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

プログラミングの基礎をしっかり学びたい方向けに、C言語の基本文法から解説しています。ポインタやメモリ管理も少しずつ理解できるよう工夫しています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!