C言語では三角関数だけでなく、その「逆」を求める逆三角関数も標準ライブラリで用意されています。
逆三角関数は、サインやコサインなどの値から角度を求めたいときに欠かせない機能です。
この記事では、asin・acos・atanといった逆三角関数の基本から、C言語での具体的な使い方、注意点、実用的な利用シーンまで、初心者の方にもわかりやすいように順を追って解説します。
逆三角関数とは何かを理解しよう
逆三角関数(asin, acos, atan)とは
まず、通常の三角関数について簡単におさえておきます。
三角関数には次の3つが代表的です。
- サイン
sin(x) - コサイン
cos(x) - タンジェント
tan(x)
ここでのxは角度(ラジアン)であり、出力として比(割合)の値が得られます。
これに対して逆三角関数は、三角関数の値(サイン値・コサイン値・タンジェント値)から、元の角度を求める関数です。
C言語では次の名前で定義されています。
asin(x): sinの逆関数(アークサイン、arcsin)acos(x): cosの逆関数(アークコサイン、arccos)atan(x): tanの逆関数(アークタンジェント、arctan)
少し数式で表すと、次のような関係です。
sin(θ) = xならばasin(x) = θcos(θ) = xならばacos(x) = θtan(θ) = xならばatan(x) = θ
ただし、後で説明するように、θのとりうる範囲は制限されています。
これは数学的な理由によるもので、プログラミングでも非常に重要なポイントです。
角度とラジアン(rad)の違い
C言語の数学関数では、角度は「度(°)」ではなく「ラジアン(rad)」で扱います。
この違いを理解しておかないと、計算結果が直感と合わずに混乱しがちです。
度数法とラジアンの関係は次の通りです。
- 180度 = πラジアン
- 1ラジアン ≒ 57.2958度
一般的な変換式は次のようになります。
- 度 → ラジアン
rad = deg * (M_PI / 180.0) - ラジアン → 度
deg = rad * (180.0 / M_PI)
ここでM_PIは円周率πを表すマクロです。
逆三角関数の戻り値はすべてラジアンです。
そのため、画面に「度」で表示したい場合は自分で変換する必要があります。
逆三角関数で求められる値の範囲
逆三角関数は「元の角度」を返すのですが、数学的には同じサイン値・コサイン値・タンジェント値をとる角度が複数存在します。
そのため、逆三角関数は必ず「ある決められた範囲」の角度だけを返すというルールになっています。
C言語のasin・acos・atanの値域(返ってくる角度の範囲)は次の通りです。
| 関数名 | 引数の範囲 | 戻り値(角度)の範囲 |
|---|---|---|
| asin | -1.0 ~ 1.0 | -π/2 ~ π/2 |
| acos | -1.0 ~ 1.0 | 0 ~ π |
| atan | 全実数(-∞ ~ +∞) | -π/2 ~ π/2 |
たとえば、asin(0.5)は「サインが0.5になる角度」を返しますが、実際には0.5になる角度は無数にあります。
しかし、C言語のasinは-π/2からπ/2の範囲の中でただ1つの角度だけを返すように定義されています。
C言語で使える逆三角関数の基本
math.hで定義されている逆三角関数
C言語で逆三角関数を使うには、math.hヘッダをインクルードする必要があります。
基本的な書式はすべてdouble型を引数とし、戻り値もdouble型です。
主な逆三角関数は次の通りです。
| 関数 | 説明 |
|---|---|
| asin | サイン値から角度(ラジアン)を求める |
| acos | コサイン値から角度(ラジアン)を求める |
| atan | タンジェント値から角度(ラジアン)を求める |
| atan2 | y, x成分から角度(ラジアン)を求める |
atan2は少し特別な関数で、2次元ベクトルの角度計算などでよく使われます。
後ほど詳しく解説します。
asin関数の書式と戻り値
asinの基本的な宣言は次のようになっています。
double asin(double x);
引数xはサイン値(-1.0~1.0の範囲)を表し、戻り値はラジアン単位の角度です。
戻り値の範囲は-π/2~π/2となります。
例えば、sin(π/6) = 0.5なので、asin(0.5)は約0.523598(= π/6)になります。
acos関数の書式と戻り値
acosの宣言は次の通りです。
double acos(double x);
引数xはコサイン値(-1.0~1.0の範囲)で、戻り値はラジアン単位の角度です。
戻り値の範囲は0~πです。
たとえば、cos(π/3) = 0.5なので、acos(0.5)は約1.047198(= π/3)になります。
atan関数の書式と戻り値
atanの宣言は次のようになります。
double atan(double x);
引数xはタンジェント値(任意の実数)で、戻り値はラジアン単位の角度です。
戻り値の範囲は-π/2~π/2です。
例えば、tan(π/4) = 1なので、atan(1.0)は約0.785398(= π/4)になります。
atan2関数との違いと使い分け
atan2は、2つの引数yとxから角度を求める関数です。
宣言は次の通りです。
double atan2(double y, double x);
ここでyは縦方向、xは横方向の成分と考えます。
atan(y / x)のように1つの値から角度を求めるのではなく、ベクトル(x, y)がなす角度をそのまま求めるイメージです。
atanとの主な違いは次のようになります。
| 項目 | atan | atan2 |
|---|---|---|
| 引数 | tan値(1つ) | y, x(2つ) |
| 戻り値範囲 | -π/2 ~ π/2 | -π ~ π |
| 符号と象限 | 情報が失われやすい | 第1~第4象限を正しく判定 |
| x=0のとき | y/xの計算で問題になる | 定義されており安全 |
座標から角度を求める場合はatanではなくatan2を使うのが基本です。
詳細は後半の「具体的な使いどころ」で解説します。
逆三角関数(asin, acos, atan)の計算方法と注意点
asinでサイン値から角度(ラジアン)を求める例
ここではasinを使って、サイン値から角度(ラジアンおよび度)を求める簡単な例を示します。
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
// 度数法(度)に変換するための関数
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
int main(void) {
// サイン値0.5に対応する角度を求める
double s = 0.5;
// asinでラジアンを取得
double rad = asin(s);
// 度に変換
double deg = rad_to_deg(rad);
printf("sin(theta) = %f のとき\n", s);
printf("theta (rad) = %f\n", rad); // ラジアンで表示
printf("theta (deg) = %f\n", deg); // 度で表示
return 0;
}
sin(theta) = 0.500000 のとき
theta (rad) = 0.523599
theta (deg) = 30.000000
このように、asin(0.5)の結果が約30度(π/6)であることがわかります。
表示するときだけ度数に変換し、内部の計算はラジアンのまま行うと、プログラム全体が整然としやすくなります。
acosでコサイン値から角度(ラジアン)を求める例
次にacosを使った例です。
今度はコサイン値0.5から角度を求めます。
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
int main(void) {
double c = 0.5; // cos値
double rad = acos(c); // ラジアンで角度を取得
double deg = rad_to_deg(rad);
printf("cos(theta) = %f のとき\n", c);
printf("theta (rad) = %f\n", rad);
printf("theta (deg) = %f\n", deg);
return 0;
}
想定される出力は次の通りです。
cos(theta) = 0.500000 のとき
theta (rad) = 1.047198
theta (deg) = 60.000000
cosが0.5のときの標準的な角度は60度(π/3)なので、期待通りの結果が得られています。
ここで注意したいのは、acosの戻り値の範囲は0~πということです。
たとえば、cos(60°) = cos(300°) = 0.5ですが、acos(0.5)は60度のみを返します。
複数の解を扱うような数学的問題では、この点を意識する必要があります。
atanでタンジェント値から角度(ラジアン)を求める例
atanは、斜面の傾きなどから角度を求めるときによく使われます。
ここでは、tan(theta) = 1となる角度を求めてみます。
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
int main(void) {
double t = 1.0; // tan値
double rad = atan(t); // ラジアンで角度を取得
double deg = rad_to_deg(rad);
printf("tan(theta) = %f のとき\n", t);
printf("theta (rad) = %f\n", rad);
printf("theta (deg) = %f\n", deg);
return 0;
}
出力の例です。
tan(theta) = 1.000000 のとき
theta (rad) = 0.785398
theta (deg) = 45.000000
tanが1になる標準的な角度は45度(π/4)なので、予想通りの結果になっています。
atanの戻り値の範囲は-π/2~π/2であり、この範囲外の角度は返ってきません。
関数の引数の有効範囲とエラーへの注意
逆三角関数を使うときに最も注意すべき点の1つが、引数の有効範囲です。
先ほどの表を再掲すると次の通りです。
| 関数 | 引数の有効範囲 |
|---|---|
| asin | -1.0 ~ 1.0 |
| acos | -1.0 ~ 1.0 |
| atan | 制限なし |
asinとacosは、引数が-1~1の範囲に入っていないと数学的に定義できません。
実際のプログラムでは、丸め誤差などにより、理論上は1.0のはずが1.0000001のようになってしまうケースもあります。
そのため、次のような対策がよく行われます。
- 入力値を
-1.0~1.0にクランプ(切り詰め)する
例:cst-code>if (x > 1.0) x = 1.0; など - 計算前に範囲チェックを行い、範囲外ならエラー処理や警告を出す
実際のC標準ライブラリでは、範囲外の引数に対して挙動が実装依存になる部分もあります。
特にacos・asinは引数のチェックを忘れがちなので、必ず意識しておくべきポイントです。
double型・float型での扱い方とキャスト
標準的なasin・acos・atanは引数・戻り値ともにdouble型です。
しかし、Cではfloatやlong double向けのバージョンも用意されています。
asinf(float x): 戻り値floatasinl(long double x): 戻り値long double
同様にacosf・acosl、atanf・atanlがあります。
初心者のうちは計算にはdoubleを使い、必要に応じて表示や格納時にキャストするやり方が扱いやすいです。
次の例ではfloatで保持しているサイン値から角度を求めています。
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
int main(void) {
float s_f = 0.5f; // floatのサイン値
// asinfを使う方法(結果もfloat)
float rad_f = asinf(s_f);
// asin(double)を使い、キャストする方法
double rad_d = asin((double)s_f); // doubleで計算
float rad_f2 = (float)rad_d; // floatに変換
printf("asinf での結果 (rad) = %f\n", rad_f);
printf("asin + キャストでの結果 (rad) = %f\n", rad_f2);
printf("度に変換した結果 (deg) = %f\n", rad_to_deg(rad_d));
return 0;
}
想定される出力例です。
asinf での結果 (rad) = 0.523599
asin + キャストでの結果 (rad) = 0.523599
度に変換した結果 (deg) = 30.000000
計算精度を重視する場合はdouble、それ以上の高精度が必要ならlong doubleを使うと良いです。
組み込み環境でメモリを節約したい場合などにはfloat版も有効です。
角度(度数法)への変換方法
ラジアンから度への変換はプログラム中で頻繁に登場します。
ここでは、ラジアンと度を相互に変換する汎用的な関数をまとめて用意しておきます。
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
// ラジアン → 度
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
// 度 → ラジアン
double deg_to_rad(double deg) {
return deg * (M_PI / 180.0);
}
int main(void) {
double deg = 45.0;
double rad = deg_to_rad(deg);
printf("%f 度 = %f ラジアン\n", deg, rad);
printf("%f ラジアン = %f 度\n", rad, rad_to_deg(rad));
return 0;
}
45.000000 度 = 0.785398 ラジアン
0.785398 ラジアン = 45.000000 度
このような変換関数を1か所にまとめておくと、プログラム内で角度を扱うときのミスを減らしやすいです。
内部の計算はラジアンで統一し、ユーザーとのやり取りや表示だけ度数法にすると見通しがよくなります。
逆三角関数の具体的な使いどころ
ベクトルの方向角を求める計算での利用
2次元のベクトル(x, y)があったとき、その向き(角度)を求めるのに逆三角関数はとても便利です。
とくにatan2はベクトルの方向角を安全かつ正確に求める関数としてよく使われます。
たとえば、原点(0, 0)から点(x, y)に向かうベクトルの角度を求めるコードは次のようになります。
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
int main(void) {
// 例としてベクトル(3, 4)の方向角を求める
double x = 3.0;
double y = 4.0;
// atan2(y, x)で方向角(ラジアン)を取得
double rad = atan2(y, x);
double deg = rad_to_deg(rad);
printf("ベクトル(%f, %f) の方向角:\n", x, y);
printf("theta (rad) = %f\n", rad);
printf("theta (deg) = %f\n", deg);
return 0;
}
想定される出力です。
ベクトル(3.000000, 4.000000) の方向角:
theta (rad) = 0.927295
theta (deg) = 53.130102
このベクトルは「3-4-5の直角三角形」の斜辺方向を表しており、約53.13度という結果になります。
atan2を使うことで、xが負の場合でも正しく第2象限・第3象限の角度を返してくれます。
2点間の傾きから角度を求める方法
座標(x1, y1)と(x2, y2)の2点が与えられたとき、その間を結ぶ直線の傾きの角度を求めたいことがあります。
このときもatan2が役立ちます。
2点を結ぶベクトルは(x2 - x1, y2 - y1)なので、その方向角をatan2で求めればよいのです。
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
int main(void) {
// 2点の座標
double x1 = 1.0, y1 = 2.0;
double x2 = 5.0, y2 = 7.0;
// 差分ベクトル(deltaX, deltaY)
double dx = x2 - x1;
double dy = y2 - y1;
// 2点を結ぶ直線の方向角
double rad = atan2(dy, dx);
double deg = rad_to_deg(rad);
printf("点(%f, %f) から 点(%f, %f) への方向:\n", x1, y1, x2, y2);
printf("dx = %f, dy = %f\n", dx, dy);
printf("theta (rad) = %f\n", rad);
printf("theta (deg) = %f\n", deg);
return 0;
}
点(1.000000, 2.000000) から 点(5.000000, 7.000000) への方向:
dx = 4.000000, dy = 5.000000
theta (rad) = 0.896055
theta (deg) = 51.340192
グラフ描画、ゲームプログラミング、ロボットの向きの制御など、2点間の角度を扱う場面は多数あります。
このようなときにatan(dy / dx)ではなくatan2(dy, dx)を使うことで、dxが0になるケースや象限判定の問題を避けることができます。
三角関数(sin, cos, tan)との組み合わせ方
逆三角関数は、通常の三角関数sin・cos・tanと組み合わせて使うことで、さまざまな幾何計算を行えます。
ここでは次のような流れのサンプルを示します。
- 角度を度単位で入力する
- ラジアンに変換して、
sin・cosを計算する - その結果から
atan2で元の角度を再計算してみる
#define _USE_MATH_DEFINES // M_PIを有効にするための定義
#include <stdio.h>
#include <math.h>
// 度 ⇔ ラジアン変換
double rad_to_deg(double rad) {
return rad * (180.0 / M_PI);
}
double deg_to_rad(double deg) {
return deg * (M_PI / 180.0);
}
int main(void) {
double deg_input = 30.0; // 入力角度(度)
double rad = deg_to_rad(deg_input); // ラジアンに変換
// 三角関数でx, y成分を求める(単位円を想定)
double x = cos(rad); // x成分
double y = sin(rad); // y成分
// 逆三角関数で角度を復元(ベクトルの方向角)
double rad_back = atan2(y, x);
double deg_back = rad_to_deg(rad_back);
printf("入力角度(度) = %f\n", deg_input);
printf("ラジアン = %f\n", rad);
printf("cos = %f, sin = %f\n", x, y);
printf("atan2 から求めた角度(度) = %f\n", deg_back);
return 0;
}
入力角度(度) = 30.000000
ラジアン = 0.523599
cos = 0.866025, sin = 0.500000
atan2 から求めた角度(度) = 30.000000
このサンプルでは、sin・cosで得た成分からatan2で角度を復元し、元の30度が得られていることがわかります。
このように、三角関数と逆三角関数を組み合わせることで、座標と角度を自由に行き来できるようになり、 ゲームのキャラクタ移動や物理シミュレーションなど、グラフィックス系のプログラムで非常に役立ちます。
まとめ
逆三角関数asin・acos・atanは、三角関数の値から角度(ラジアン)を求めるための重要な関数です。
C言語ではmath.hに定義されており、ラジアンで計算し、必要に応じて度に変換するという流れが基本になります。
引数の有効範囲(特にasin・acosの-1~1の制限)を意識しつつ、座標から角度を求める場面ではatanよりatan2を使うことで、安全で直感的なプログラムを書けます。
三角関数との組み合わせを理解すると、ベクトル計算やグラフィックス処理など、C言語で扱える問題の幅が大きく広がります。
