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>0 | y≥0 | 0〜+π/2 |
| x<0 | y≥0 | +π/2〜+π |
| x<0 | y<0 | -π〜-π/2 |
| x>0 | y<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 - x1、dy = y2 - y1を作りatan2(dy, dx)で求めます。
返るのはラジアンなので、必要に応じて度へ変換します。
// 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です。
πは自前定義するのが確実です。
// 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象限の代表点の角度を確認するサンプルです。
// 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度ずれます。
// 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座標の向きを求める例
ゲームや可視化で、キャラクターや矢印がターゲットの方向を向く処理は典型例です。
// 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)が安全で、オーバーフローに強い実装になっています。
// 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の符号を反転することがよくあります。
// 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を使うと簡潔です。
// 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)と範囲を書き、利用側の混乱を防ぎます。
// 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がない環境では自前でπを定義しましょう。
これらのポイントを守れば、座標の向き、極座標変換、入力デバイスの角度取得など、幅広い場面で安定した角度計算が行えます。
