【C言語】atan関数とatan2関数について解説(傾きor座標から角度を求める関数)

このページでは、C言語の標準ライブラリ関数である atan 関数と atan2 関数について解説していきます。

この2つは同じような関数ですが、若干動作や引数・返却値が異なります。図形を描画するなど、2次元平面上で座標を扱うようなプログラムを作成する場合、atan2 関数の方が利用することは多いかなぁと思います。

まずは、この2つの関数それぞれについて解説し、その後にこの2つの関数間での違いについて解説していきたいと思います。

(前提知識)tan 関数とは

atan 関数と atan2 関数は tan 関数の逆関数的な関数になります。

atan 関数と atan2 関数を理解する上では、tan 関数を理解しておいた方が良いため、まずは tan 関数について簡単に説明しておきます。

tan 関数とは、一言でいえば正接を求める関数です。

もう少し直感的にいえば、下の座標の図において tan(a) の結果は y1 / x1 となります。a は、原点と座標 (x1, y1) を通過する直線と x 軸とがなす角の角度です(単位はラジアン)。

つまり、tan 関数は「角度から直線の傾きを求める関数」と考えることができます。

tan関数の意味合いを示す図

したがって、角度が分かっている状態で直線の傾きを求めたいような場合に tan 関数が活躍します。

atan 関数

tan 関数とは逆に、atan 関数は「直線の傾き」から角度を求める関数です。求められる角度の単位はラジアンです。

スポンサーリンク

atan 関数の宣言

atan 関数は math.h において下記のようにプロトタイプ宣言されています。

atan関数
#include <math.h>

double atan(double g);

引数 g には「直線の傾き」を指定します。

atan 関数の返却値

atan 関数は引数 g から求めた角度を返却します。単位は前述の通りラジアンで、-π / 2 から π / 2 の範囲の値を返却します。

atan関数の返却値の範囲を示す図

atan 関数の動作

atan 関数は、傾きを引数 g とする直線の角度を求める関数です(直線と x 軸とのなす角の角度)。

atan関数の意味合いを示す図

傾きとは x の増加量に対する y の増加量の比ですので、傾きは yの増加量 / xの増加量 によって求めることができます。

したがって、座標 (x1, y1) が与えられた時に、atan(y1 / x1) により、原点と座標 (x1, y1) を通過する直線の角度を求めるようなこともできます。

座標より求まる傾きから角度を求める様子

こんな感じで、座標もしくは直線の傾きが分かってる状態で角度を求めたいような場合に atan 関数が活躍します。

スポンサーリンク

atan2 関数

atan2 関数は atan 関数と同様に角度を求める関数になります。こちらも atan 関数と同様に、求められる角度の単位はラジアンです。

ただし atan 関数では「直線の傾き」から角度を算出するのに対し、atan2 関数は「座標」から角度を求めるという点が異なります。

atan2 関数の宣言

atan2 関数も math.h において下記のようにプロトタイプ宣言されています。

atan2関数
#include <math.h>

double atan2(double y, double x);

引数 y には指定したい座標の y 軸の値、引数 x には指定したい座標の x 軸の値を指定します。

atan2 関数の返却値

atan2 関数は引数 y と引数 x から求めた角度を返却します。単位は前述の通りラジアンで、 から π の範囲の値を返却します。

atan2関数の返却値の範囲を示す図

スポンサーリンク

atan2 関数の動作

atan2 関数は引数 yx に対し、原点と座標 (x, y) を結ぶ直線の角度を求める関数です(直線と x 軸とのなす角の角度)。

atan2関数の意味合いを示す図

原点以外の2つの座標を結ぶ直線の角度を求めたい場合は、一方の座標が原点 (0, 0) に移動するように両方の座標を並行移動させてから atan2 関数を実行します。

2つの座標から角度を求める際に平行移動が必要であることを示す図

atan 関数と atan2 関数の違い

ここまで解説してきた atan 関数と atan2 関数の最も大きな違いは「角度を算出する時に使用する値」です。

  • atan 関数は「直線の傾き」から角度を求める
  • atan2 関数は「座標」から角度を求める

ただし、直線の傾きは座標から算出することができるので、結局 atan 関数でも座標から角度を求めることができると言えそうです。

ですが、座標から算出した傾きを引数とした場合、atan 関数から得られる角度が意図しないものになることがあります。

例えば下記の2つの直線と x 軸とがなす角の角度を求めることについて考えてみましょう。

  • 原点と座標 (100, 100) を結ぶ直線
  • 原点と座標 (-100, -100) を結ぶ直線

まず前者の場合、求めたい角度と直線は下の図のように表すことができます。

座標(100,100)から求めたい角度を示す図

さらに後者の場合、求めたい角度と直線は下の図のように表すことができます。

座標(-100,-100)から求めたい角度を示す図

図を見ても分かるように、前者と後者の場合とで求めたい角度は異なるはずです。より具体的にいえば、2つの角度は π(180度)の差があるはずです。

実際に、atan2(100, 100)atan2(-100, -100) の結果は下記のように異なります。約 3.14、つまり π の差があることが確認できます。

  • atan2(100, 100)  :約 0.785398π/4
  • atan2(-100, -100):約 -2.356194-3π/4

では atan(100 / 100)atan(-100 / -100) の結果はどうでしょう?結果は下記のようになりました。

  • atan(100 / 100)  :約 0.785398π/4
  • atan(-100 / -100):約 0.785398π/4

結果が同じになってしまいましたね…。

両者では同じ引数を 1 を指定して atan 関数を実行することになるので、同じ結果になるのは当然ですね(100 / 100-100 / -100 も実行結果は 1)。

ただ、上の図を見ても分かるように、2つの直線から求めたい角度は異なるはずです。つまりこの場合、atan 関数では本当に求めたかった角度が求められなかったことになります。

こんな感じで atan 関数の場合、座標から計算した傾きを引数に指定すると、意図しない結果となる場合があります。

atan2 関数の場合、引数で座標そのものを指定することができるため、その座標の点が原点から見てどの方向に存在するかどうかを判断して角度を算出することができます。要は x と y それぞれの符号を考慮して角度を算出してくれます。

atan関数とatan2関数との違い1

その一方で atan 関数の場合、引数では直線の傾きしか指定できないので、その直線の傾きの算出元となった座標が原点から見てどの方向に存在するかどうかを判断することができません。

atan関数とatan2関数との違い2

判断できないので、とにかく atan 関数は -π/2 から π/2 の範囲の値を返却するようになっています。

先程の例でいえば、傾きが 1 の直線が x 軸となす角の角度の候補として π/4-3π/4 の2つがありますが、後者は範囲外の値になるため、前者の値が返却されるようになっています。

こういった違いがあるため、単純に1次元的な値から角度を求めたいような場合は atan 関数でも良いと思いますが、二次元平面上の座標から角度を求めるような場合は atan2 関数の方が無難な場合が多いと思います。

例えば、マウスでクリックされた位置の方向をキャラクターに向かせるようなアプリやゲームを作ろうと思うと、atan 関数だとマウスでクリックされた位置とは反対の方向を向いてしまう可能性があります。

atan関数を使うとゲームやアプリが上手く動作しなくなる例

あとは図形などを描画するようなときも、座標から角度を求めるのであれば atan2 関数を用いた方が良いと思います。

atan 関数と atan2 関数の使用例と注意点

最後に atan 関数と atan2 関数の簡単な使用例とちょっとした注意点について解説しておきます。

スポンサーリンク

使用例

下記は、次の2つの直線と x 軸とがなす角の角度を atan 関数と atan2 関数それぞれで求めるプログラム例になります。

  • 原点と座標 (-100, 50) を結ぶ直線
  • 原点と座標 (100, 50) を結ぶ直線
atanとatan2の使用例
#include <stdio.h>
#include <math.h>

int main(void) {
    int x, y;
    double a;

    y = 50;

    for (x = -100; x <= 100; x += 200) {
        a = atan((double)y / (double)x);
        printf("[atan ]原点と座標(%d,%d)を結ぶ直線の角度:%f\n", x, y, a);
    }

    for (x = -100; x <= 100; x += 200) {
        a = atan2(y, x);
        printf("[atan2]原点と座標(%d,%d)を結ぶ直線の角度:%f\n", x, y, a);
    }

    return 0;
}

実行結果は下記のようになります。

[atan ]原点と座標(-100,50)を結ぶ直線の角度:-0.463648
[atan ]原点と座標(100,50)を結ぶ直線の角度:0.463648
[atan2]原点と座標(-100,50)を結ぶ直線の角度:2.677945
[atan2]原点と座標(100,50)を結ぶ直線の角度:0.463648

注意点

このページの最後として、atan 関数と atan2 関数のちょっとした注意点をまとめておきます。

atan 関数と atan2 関数の返却値の単位はラジアン

atan 関数と atan2 関数の返却値の単位は “度” ではなく “ラジアン” です。

これらの関数の返却値を “度” で表したい場合、下記ページで解説しているように、返却値に対して 180 / π を掛けてやる必要があります。

度とラジアンの単位変換の方法の解説ページアイキャッチ度(度数法)⇔ラジアン(弧度法)の変換

atan 関数の返却値は -π/2 から π/2 の範囲の値のみ

また、atan 関数と atan2 関数の違い で解説したように、atan 関数の返却値は -π/2 から π/2 の範囲の値のみとなります。

そのため座標から直線の傾きを計算して atan 関数を実行したとしても、原点とその座標を結ぶ直線の傾きになるとは限らないので注意してください。

この現象は、上記の 使用例 の結果からも確認できると思います。

atan 関数の引数を計算する際の型に注意

また、atan 関数の引数の値を座標から割り算で計算する場合は型に注意してください。

特に画像を扱うような場合、座標 xy を表す変数には整数型が用いられることが多いです。

その一方で、C言語では 整数型 / 整数型 の結果は 整数型 であり、小数点以下の値が捨てられてしまいます。

例えば xy がそれぞれ int 型の変数であるとすると、 x = 100y = 90 の場合に y / x をそのまま行うと結果が 0 になり、求める傾きとして意図しないものになってしまいます。

このような場合、小数点以下の値が捨てられないよう、キャスト演算子を用いて double 型に変換してから割り算する必要があります。例えばこの例であれば、(double)y / (double)x を実行すれば計算結果は 0.9 となり意図した結果を得ることができます。

atan2 関数の第1引数は y 座標

atan2 関数では第1引数が y 座標であることに注意してください。ついつい x 座標の方を第1引数に指定してしまうことが多いです。

例えば EXCEL でも atan2 と同様の関数が用意されており、この EXCEL の関数の場合は x 座標の方が第1引数になるらしいです…。この辺りとも混同しないように注意してください。

縦軸の正方向の向きと角度の関係に注意

このページでは、縦軸(y 軸)の正方向を上方向として解説してきましたが、プログラミングで座標を扱う場合は、縦軸の正方向が下方向であることが結構多いです。

例えば画像データなどは、原点が左上で縦軸の正方向を下方向として扱うことが多いです。

縦軸の正方向を上方向とした場合、角度の正方向は反時計回りになりますが、縦軸の正方向を下方向とした場合、角度の正方向は時計回りになるので注意してください。

縦軸の正方向によって角度の正方向が反対になる例

まとめ

このページでは、C言語の標準ライブラリ関数である atan 関数と atan2 関数について解説しました!

両者ともに角度を求めるための関数になりますが、atan 関数は「直線の傾き」から、atan2 関数は「座標」から角度を求めるという違いがあります。

また、この違いがあるため、引数や返却値も異なります。

さらに、atan 関数では直線の傾きのみから角度を算出するため、座標値 x と y の符号を考慮せずに角度が求められてしまうことに注意してください。

二次元平面上の座標から角度を求めたいような場合は、atan2 関数の方が適していることが多いと思います。