閉じる

C言語の三角関数(sin, cos, tan)使い方とmath.hの基本

C言語で三角関数を正しく使うには、ヘッダの導入、コンパイル時のリンク、角度の単位(ラジアン)の理解が肝心です。

本稿ではsin、cos、tanの基本と、初心者がつまずきやすい点を、実行できるサンプルとともに丁寧に解説します。

特に度ではなくラジアンで渡す点と、tanの発散浮動小数点誤差には注意が必要です。

三角関数を使う準備

math.hをインクルードする

C言語の三角関数は標準ライブラリmath.hに宣言されています。

必ずインクルードしてから使用します。

C言語
#include <stdio.h>  // printf用
#include <math.h>   // sin, cos, tanなどの宣言

三角関数を使わずにビルドすると、未宣言の関数呼び出しとして警告やリンクエラーの原因になります。

コンパイルとリンクの指定

UNIX系環境では、数学ライブラリにリンクするため-lm指定が必要です。

WindowsのMSVCでは不要ですが、MinGWなどでは必要です。

Shell
# GCCの例(Linux, macOS)
gcc trig.c -std=c11 -O2 -Wall -Wextra -lm

# Clangの例
clang trig.c -std=c11 -O2 -Wall -Wextra -lm

# MSVCの例(Developer Command Prompt)
cl /std:c11 /W4 trig.c  # -lm相当は不要

リンクエラーが出たときは-lmの付け忘れを疑うと良いです。

引数と戻り値の型はdouble

標準のsincostanは、引数も戻り値もdoubleです。

宣言は次のとおりです。

C言語
/* math.h より(概略) */
double sin(double x);
double cos(double x);
double tan(double x);

整数リテラルやfloatを渡しても、関数呼び出し時に自動でdoubleへ拡張されます。

ただし、計算コストや精度を意識して型を合わせるのがよい実践です。

sinf, cosf, tanfなどの型別関数

float用long double用の関数も用意されています。

型に合わせて使うと無駄な型変換を避けられます。

  • sinfcosftanffloat
  • sinlcosltanllong double
C言語
#include <stdio.h>
#include <math.h>

int main(void) {
    // float版
    float deg_f = 30.0f;
    const float PI_F = 3.14159265358979323846f;
    float rad_f = deg_f * (PI_F / 180.0f);
    float sf = sinf(rad_f), cf = cosf(rad_f), tf = tanf(rad_f);

    // double版
    double deg = 30.0;
    const double PI = 3.14159265358979323846;
    double rad = deg * (PI / 180.0);
    double sd = sin(rad), cd = cos(rad), td = tan(rad);

    // long double版
    long double deg_l = 30.0L;
    const long double PI_L = 3.141592653589793238462643383279502884L;
    long double rad_l = deg_l * (PI_L / 180.0L);
    long double sl = sinl(rad_l), cl = cosl(rad_l), tl = tanl(rad_l);

    // printfではfloatはdoubleに昇格されるので%fでOK、long doubleは%Lf
    printf("float:  sin=%.7f cos=%.7f tan=%.7f\n", sf, cf, tf);
    printf("double: sin=%.10f cos=%.10f tan=%.10f\n", sd, cd, td);
    printf("long double: sin=%.15Lf cos=%.15Lf tan=%.15Lf\n", sl, cl, tl);
    return 0;
}
実行結果
float:  sin=0.5000000 cos=0.8660254 tan=0.5773503
double: sin=0.5000000000 cos=0.8660254038 tan=0.5773502692
long double: sin=0.500000000000000 cos=0.866025403784439 tan=0.577350269189626

基本はdoubleから始めて、必要に応じてfloatlong doubleを選ぶと理解しやすいです。

角度はラジアンで扱う

度からラジアンへの式

Cの三角関数は度ではなくラジアンで受け取ります。

換算式はrad = deg × π ÷ 180です。

C言語
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;

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

int main(void) {
    double deg = 60.0;
    double rad = deg2rad(deg);
    printf("%.1f度 は %.15fラジアンです\n", deg, rad);
    return 0;
}
実行結果
60.0度 は 1.047197551196598ラジアンです

πの扱い

πの定義は環境差に注意します。

  • 移植性重視…const double PI = 3.14159265358979323846;のように自前定義
  • 計算で得る…const double PI = acos(-1.0);とすれば確実にπが得られます
  • M_PI…一部環境で<math.h>が定義。MSVCでは#define _USE_MATH_DEFINESを<math.h>より前に書く必要があります
C言語
#include <stdio.h>
#define _USE_MATH_DEFINES  // MSVCでM_PIを有効化
#include <math.h>

int main(void) {
#ifdef M_PI
    printf("M_PI = %.17f\n", M_PI);
#else
    const double PI = acos(-1.0);
    printf("PI (from acos) = %.17f\n", PI);
#endif
    return 0;
}
実行結果
M_PI = 3.14159265358979312

自前定義かacos(-1.0)で得る方法が移植性の面で安心です。

代表角のラジアン値

よく使う角度とラジアンの関係をまとめます。

度からラジアンへ即座に変換できるとコーディングが安定します。

度(deg)πの倍数表現ラジアン近似値
000.000000
30π/60.523599
45π/40.785398
60π/31.047198
90π/21.570796
180π3.141593
2703π/24.712389
3606.283185

sin, cos, tanの基本的な使い方

sinの使い方

正弦sin(x)は対象角の縦方向成分を返します。

例として30度の正弦は0.5です。

C言語
#include <stdio.h>
#include <math.h>

int main(void) {
    const double PI = 3.14159265358979323846;
    double x = PI / 6.0;  // 30度
    printf("sin(π/6) = %.10f\n", sin(x));
    return 0;
}
実行結果
sin(π/6) = 0.5000000000

cosの使い方

余弦cos(x)は横方向成分です。

60度の余弦は0.5になります。

C言語
#include <stdio.h>
#include <math.h>

int main(void) {
    const double PI = 3.14159265358979323846;
    double x = PI / 3.0;  // 60度
    printf("cos(π/3) = %.10f\n", cos(x));
    return 0;
}
実行結果
cos(π/3) = 0.5000000000

tanの使い方

正接tan(x)sin(x)/cos(x)に等しい値です。

45度の正接は1です。

C言語
#include <stdio.h>
#include <math.h>

int main(void) {
    const double PI = 3.14159265358979323846;
    double x = PI / 4.0;  // 45度
    printf("tan(π/4) = %.10f\n", tan(x));
    return 0;
}
実行結果
tan(π/4) = 1.0000000000

基本サンプルコード

よく使う角度を配列で回して、度→ラジアン変換と三角関数の組を一気に表示するサンプルです。

C言語
#include <stdio.h>
#include <math.h>

/* πの自前定義。移植性のために定数で持ちます */
static const double PI = 3.14159265358979323846;

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

int main(void) {
    double degrees[] = {0.0, 30.0, 45.0, 60.0};  // 90度はtanが発散するため別節で解説
    size_t n = sizeof degrees / sizeof degrees[0];

    for (size_t i = 0; i < n; ++i) {
        double d = degrees[i];
        double r = deg2rad(d);
        double s = sin(r);
        double c = cos(r);
        double t = tan(r);
        printf("%6.1f度 -> rad=% .10f  sin=% .10f  cos=% .10f  tan=% .10f\n",
               d, r, s, c, t);
    }
    return 0;
}
実行結果
   0.0度 -> rad= 0.0000000000  sin= 0.0000000000  cos= 1.0000000000  tan= 0.0000000000
  30.0度 -> rad= 0.5235987756  sin= 0.5000000000  cos= 0.8660254038  tan= 0.5773502692
  45.0度 -> rad= 0.7853981634  sin= 0.7071067812  cos= 0.7071067812  tan= 1.0000000000
  60.0度 -> rad= 1.0471975512  sin= 0.8660254038  cos= 0.5000000000  tan= 1.7320508076

環境により末尾の桁がわずかに異なる場合があります。

戻り値の範囲

範囲を理解しておくと不思議な値に戸惑いません

  • sin(x)cos(x)…理論上は[-1, 1]。ただし浮動小数点誤差でごくわずかに外れるように見えることがあります
  • tan(x)全実数範囲に広がります。cos(x)=0の点(±π/2, ±3π/2, …)で発散し、実装によってはinfを返すことがあります

初心者がつまずきやすいポイント

tan(90度付近)は発散に注意

90度(π/2)ではcosが0になり、tanは定義できません

近い角度でも非常に大きな値になります。

C言語
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;
static double deg2rad(double deg) { return deg * (PI / 180.0); }

int main(void) {
    double d1 = 89.999;         // 90度に非常に近い
    double r1 = deg2rad(d1);
    double t1 = tan(r1);

    double d2 = 90.0;
    double r2 = deg2rad(d2);

    printf("tan(%.3f度) = %.3f\n", d1, t1);

    // cosがほぼ0であれば、発散と見なして直接tanを使わない
    if (fabs(cos(r2)) < 1e-12) {
        printf("tan(%.1f度) は発散(非定義)です\n", d2);
    } else {
        printf("tan(%.1f度) = %.6f\n", d2, tan(r2));
    }
    return 0;
}
実行結果
tan(89.999度) = 57295.780
tan(90.0度) は発散(非定義)です

分母となるcos(x)が0に近いかを判定して回避するのが実務では重要です。

度とラジアンの取り違え

よくあるミスは度のままsinに渡すことです。

sin(90)は1ではありません(90ラジアンの正弦)。

C言語
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;
static double deg2rad(double deg) { return deg * (PI / 180.0); }

int main(void) {
    double wrong = sin(90.0);               // 誤り: 90はラジアン
    double right = sin(deg2rad(90.0));      // 正しい: 90度→ラジアンへ変換

    printf("sin(90.0)        = %.10f  (誤り)\n", wrong);
    printf("sin(deg2rad(90)) = %.10f  (正しい)\n", right);
    return 0;
}
実行結果
sin(90.0)        = 0.8939966636  (誤り)
sin(deg2rad(90)) = 1.0000000000  (正しい)

常に入力の単位を明示し、必要ならdeg2radヘルパを用意して統一しましょう。

浮動小数点の誤差

理論上0になるはずの値が0にならないのはよくある現象です。

sin(π)は0のはずですが、丸め誤差でごく小さな値になります。

C言語
#include <stdio.h>
#include <math.h>
#include <float.h>

int main(void) {
    const double PI = 3.14159265358979323846;
    double s = sin(PI);
    printf("sin(π) = %.20f\n", s);

    // しきい値(イプシロン)で近似比較する
    double eps = 1e-12;
    if (fabs(s) < eps) {
        printf("sin(π)は誤差内で0とみなせます (|s| < %.0e)\n", eps);
    } else {
        printf("sin(π)は0から有意に離れています\n");
    }
    return 0;
}
実行結果
sin(π) = 0.00000000000000024492
sin(π)は誤差内で0とみなせます (|s| < 1e-12)

等号比較==で浮動小数点を直接比べないのが鉄則です。

許容誤差内で比較しましょう。

角度の正規化

角度を加算・減算していくと、範囲がどんどん広がって扱いづらくなります。一般には[0, 2π)[-π, π)に正規化します。

C言語
#include <stdio.h>
#include <math.h>

static const double PI = 3.14159265358979323846;
static const double TWO_PI = 2.0 * 3.14159265358979323846;

/* [0, 2π) に正規化する簡易版。極端に大きな値には回数が増える点に注意 */
double normalize_rad_0_2pi(double rad) {
    while (rad >= TWO_PI) rad -= TWO_PI;
    while (rad < 0.0)     rad += TWO_PI;
    return rad;
}

/* [-π, π) に正規化 */
double normalize_rad_mpi_pi(double rad) {
    rad = normalize_rad_0_2pi(rad);
    if (rad >= PI) rad -= TWO_PI;
    return rad;
}

int main(void) {
    double a = 15.0 * TWO_PI + PI / 6.0;  // たくさん周回した角度
    printf("元: %.6f, [0,2π): %.6f, [-π,π): %.6f\n",
           a, normalize_rad_0_2pi(a), normalize_rad_mpi_pi(a));
    return 0;
}
実行結果
元: 95.105652, [0,2π): 0.523599, [-π,π): 0.523599

性能や巨大値対応が必要ならfmodを使う方法もありますが、詳細は別記事で扱います。

まとめ

本稿ではmath.hをインクルードし、ラジアンで三角関数を呼ぶという基礎から、πの扱い型別関数(sinf/cosf/tanf, sinl/cosl/tanl)代表角のラジアン、そしてsin/cos/tanの基本的な使い方を解説しました。

特にtan(90度付近)の発散度とラジアンの取り違え浮動小数点誤差は初心者が陥りやすいポイントです。

これらを避けるために、度→ラジアン変換ヘルパを用意し、入力単位を明示必要に応じた誤差許容で比較する習慣を身につけましょう。

まずはdoubleと基本サンプルから始め、慣れてきたらfloat/long doubleや正規化などの工夫を取り入れると、より堅牢で読みやすいコードになります。

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

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

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

URLをコピーしました!