C++を用いたプログラミングにおいて、数学的な演算は避けて通れない要素の一つです。

特にゲーム開発、ロボットの制御アルゴリズム、データ解析、画像処理といった分野では、三角関数(sin, cos, tan)を用いた計算が頻繁に登場します。

しかし、数学の教科書で習う「度(degree)」とプログラミング言語で扱う「ラジアン(radian)」の違いや、浮動小数点数特有の精度問題など、初心者がつまずきやすいポイントも少なくありません。

本記事では、C++の標準ライブラリである<cmath>を活用し、三角関数を正確かつ効率的に扱うための具体的な手法を詳しく解説します。

C++で三角関数を扱うための基本準備

C++で三角関数を使用するには、標準ライブラリの数学関数ヘッダーである<cmath>をインクルードする必要があります。

C言語では<math.h>が使われてきましたが、C++では名前空間の管理や関数のオーバーロード(引数の型に応じた適切な関数の呼び出し)が最適化されているため、特別な理由がない限り<cmath>を使用するのが一般的です。

<cmath>ヘッダーのインクルード

まずは、プログラムの冒頭で以下のように宣言します。

これにより、std::sinstd::cosといった関数が利用可能になります。

C++
#include <iostream>
#include <cmath> // 三角関数を使用するために必要
#include <numbers> // C++20以降の数学定数を使用する場合

C++11以降、これらの関数はfloatdoublelong doubleの各型に対してオーバーロードされています。

そのため、引数に渡す値の精度に応じて、コンパイラが自動的に最適な関数を選択してくれます。

三角関数の基本:sin, cos, tanの使い方

C++の標準ライブラリで提供されている基本的な三角関数は、std::sin(正弦)、std::cos(余弦)、std::tan(正接)の3種類です。

これらを使用する際に最も注意すべき点は、引数に渡す角度の単位が「ラジアン」であるという点です。

各関数の定義と役割

  1. std::sin(x):角度 x(ラジアン)に対する正弦を返します。戻り値の範囲は通常 -1.0 から 1.0 です。
  2. std::cos(x):角度 x(ラジアン)に対する余弦を返します。戻り値の範囲は通常 -1.0 から 1.0 です。
  3. std::tan(x):角度 x(ラジアン)に対する正接を返します。

基本的なプログラム例

以下のコードは、特定の角度(ラジアン)におけるそれぞれの値を計算して表示する例です。

C++
#include <iostream>
#include <cmath>
#include <iomanip> // 出力精度の設定用

int main() {
    // 45度をラジアンに変換した値(約0.785398)
    double angle = 0.78539816339;

    // 各関数の計算
    double s = std::sin(angle);
    double c = std::cos(angle);
    double t = std::tan(angle);

    // 結果の出力
    std::cout << std::fixed << std::setprecision(6);
    std::cout << "sin(45 deg): " << s << std::endl;
    std::cout << "cos(45 deg): " << c << std::endl;
    std::cout << "tan(45 deg): " << t << std::endl;

    return 0;
}
実行結果
sin(45 deg): 0.707107
cos(45 deg): 0.707107
tan(45 deg): 1.000000

角度の単位変換:度からラジアンへ

前述の通り、C++の三角関数はすべてラジアンを基準としています。

日常的に使われる「度(0度〜360度)」をそのまま引数に渡すと、意図しない計算結果になってしまいます。

そのため、プログラミングにおいては度数法から弧度法(ラジアン)への変換が必須となります。

変換公式

度(degree)からラジアン(radian)への変換式は以下の通りです。

ラジアン = 度 × (円周率 / 180)

逆に、ラジアンから度へ変換する場合は以下の式を用います。

度 = ラジアン × (180 / 円周率)

円周率の定義方法

C++20以前では、円周率を定義するために M_PI というマクロが使われることがありましたが、これは標準規格ではないため、環境によっては動作しません。

一般的には、std::acos(-1.0) を使用して動的に計算するか、定数として定義するのが無難でした。

しかし、C++20からは <numbers> ヘッダーにおいて std::numbers::pi が導入されました

これにより、高精度な円周率を標準的な方法で利用できるようになっています。

度からラジアンへの変換関数の実装

実務では、以下のようなヘルパー関数を用意しておくとコードの見通しが良くなります。

C++
#include <iostream>
#include <cmath>
#include <numbers>

// 度からラジアンへ変換する定数倍関数
constexpr double to_radians(double degrees) {
    return degrees * (std::numbers::pi / 180.0);
}

int main() {
    double deg = 90.0;
    double rad = to_radians(deg);

    std::cout << deg << " 度は " << rad << " ラジアンです。" << std::endl;
    std::cout << "sin(90 deg) = " << std::sin(rad) << std::endl;

    return 0;
}
実行結果
90 度は 1.5708 ラジアンです。
sin(90 deg) = 1

逆三角関数の活用:角度を求める

「辺の長さから角度を求めたい」場合には、三角関数の逆関数である逆三角関数を使用します。

C++では以下の関数が用意されています。

関数名数学表記概要
std::asin(x)arcsinサインの値から角度(ラジアン)を求める
std::acos(x)arccosコサインの値から角度(ラジアン)を求める
std::atan(x)arctanタンジェントの値から角度(ラジアン)を求める
std::atan2(y, x)arctan2座標 (x, y) から原点との角度を求める

atan2関数の重要性

逆三角関数の中でも、特に実戦で多用されるのが std::atan2 です。

通常の atan(y/x) では、x が 0 の場合にゼロ除算が発生してしまいます。

また、象限(第一象限〜第四象限)の判定を自分で行う必要があります。

一方、std::atan2(y, x) は、xとyの符号を見て適切な角度(-πからπの範囲)を自動的に返してくれるため、2Dゲームにおけるキャラクターの向き計算や、座標変換において非常に強力なツールとなります。

C++
#include <iostream>
#include <cmath>
#include <numbers>

int main() {
    double y = 1.0;
    double x = -1.0;

    // y=1, x=-1 は第二象限なので、角度は135度(3π/4)になるはず
    double angle_rad = std::atan2(y, x);
    double angle_deg = angle_rad * (180.0 / std::numbers::pi);

    std::cout << "座標 (" << x << ", " << y << ") の角度は " 
              << angle_deg << " 度です。" << std::endl;

    return 0;
}
実行結果
座標 (-1, 1) の角度は 135 度です。

応用:双曲線関数と特殊な計算

C++の<cmath>には、通常の三角関数以外にも、物理シミュレーションや統計学で利用される双曲線関数も含まれています。

双曲線関数のラインナップ

  • std::sinh(x):双曲線正弦(ハイパボリックサイン)
  • std::cosh(x):双曲線余弦(ハイパボリックコサイン)
  • std::tanh(x):双曲線正接(ハイパボリックタンジェント)

これらは懸垂線(カテナリー曲線)の計算や、ディープラーニングの活性化関数(特にtanh)などで利用されます。

hypot関数の利便性

直角三角形の斜辺の長さを求める際、ピタゴラスの定理 sqrt(xx + yy) を記述するのが一般的ですが、C++には std::hypot(x, y) という便利な関数が存在します。

これを使うことで、計算途中のオーバーフローやアンダーフローを防ぎつつ、精度良く斜辺を求めることが可能です。

C++
#include <iostream>
#include <cmath>

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

    // 斜辺の長さを計算
    double distance = std::hypot(x, y);

    std::cout << "底辺 3, 高さ 4 の直角三角形の斜辺は " << distance << " です。" << std::endl;

    return 0;
}
実行結果
底辺 3, 高さ 4 の直角三角形の斜辺は 5 です。

浮動小数点数による計算誤差と注意点

三角関数の計算には doublefloat が使われますが、コンピュータ内部での表現には限界があるため、必ず計算誤差が伴います。

1. ゼロにならない「ゼロ」

理論上、sin(π) は 0 になります。

しかし、プログラム上で計算すると、完全な 0 にはならず、極めて小さな値(例:1.22e-16 など)になることが多々あります。

そのため、if (std::sin(angle) == 0.0) といった厳密な比較は避け、一定の許容誤差(イプシロン)を設けて判定するのがベストプラクティスです。

2. tan(90度) の扱い

数学において tan(90°) は定義されません(無限大に発散)。

C++でこれに近い値を計算すると、非常に大きな値が返されるか、実行環境によっては特殊な数値表現(NaN: Not a Number)に近い振る舞いを見せることがあります。

入力値のバリデーションを事前に行うことが推奨されます。

3. 精度の選択

精度がそれほど重要でないリアルタイム性の高いアプリケーション(モバイルゲームなど)では、double ではなく float を使用することで、計算負荷を軽減できる場合があります。

その際は std::sinf(x)std::cosf(x) といった float 版の関数(あるいはオーバーロードされた std::sin)を適切に選びましょう。

実践例:物体の円運動シミュレーション

最後に、三角関数を応用して、二次元平面上での円運動の座標を計算する実践的な例を紹介します。

物体が一定の速度で円周上を移動する場合、その座標は三角関数を用いて以下のように表せます。

  • x = 中心X + 半径 * cos(角度)
  • y = 中心Y + 半径 * sin(角度)
C++
#include <iostream>
#include <cmath>
#include <numbers>
#include <vector>

struct Point {
    double x, y;
};

int main() {
    double centerX = 0.0;
    double centerY = 0.0;
    double radius = 10.0;
    int steps = 8; // 8方向の座標を計算

    std::cout << "円周上の座標を計算します(半径10):" << std::endl;

    for (int i = 0; i < steps; ++i) {
        // 現在のステップを角度(度)に変換
        double degrees = i * (360.0 / steps);
        // 度からラジアンに変換
        double radians = degrees * (std::numbers::pi / 180.0);

        // 座標の計算
        double posX = centerX + radius * std::cos(radians);
        double posY = centerY + radius * std::sin(radians);

        std::cout << "角度: " << degrees << " deg -> 座標: (" 
                  << posX << ", " << posY << ")" << std::endl;
    }

    return 0;
}
実行結果
円周上の座標を計算します(半径10):
角度: 0 deg -> 座標: (10, 0)
角度: 45 deg -> 座標: (7.07107, 7.07107)
角度: 90 deg -> 座標: (6.12323e-16, 10)
角度: 135 deg -> 座標: (-7.07107, 7.07107)
角度: 180 deg -> 座標: (-10, 1.22465e-15)
角度: 225 deg -> 座標: (-7.07107, -7.07107)
角度: 270 deg -> 座標: (-1.83697e-15, -10)
角度: 315 deg -> 座標: (7.07107, -7.07107)

この結果からわかるように、90度や180度の地点で座標が完全に 0 にならず、非常に小さな値(e-15など)になっていることが確認できます。

これが先ほど述べた浮動小数点演算の特性です。

まとめ

C++での三角関数計算は、標準ライブラリの<cmath>を活用することで、非常にシンプルかつ強力に行うことができます。

重要なポイントを整理すると、まず角度の単位は常にラジアンで扱うこと、そしてC++20以降であればstd::numbers::piを利用して精度の高い円周率を定義することです。

また、単なる sincos だけでなく、座標から角度を逆算する atan2 や、誤差に強い斜辺計算を行う hypot といった周辺関数の存在を知っておくことで、プログラムの堅牢性は大きく向上します。

浮動小数点特有の誤差に注意を払いながら、これらの関数を適切に組み合わせて、高度な数学的処理を実装していきましょう。