閉じる

【C言語】自然対数logと常用対数log10の計算方法

C言語で指数関数(exp)と対数関数(log, log10)を正しく使えるようになると、値の桁数やスケールの大きい問題を安定して扱えるようになります。

本記事では自然対数logと常用対数log10の違いを軸に、expの役割と使い分けmath.hの準備とコンパイル、そして初心者がつまずきやすいポイントまで丁寧に解説します。

exp, log, log10の基礎

自然対数logと常用対数log10の違い

C言語のlog自然対数を表し、底がe(約2.718281828…)です。

対してlog10常用対数で、底が10です。

最も基本的な性質は次の通りです。

  • 自然対数: log(e) = 1log(1) = 0
  • 常用対数: log10(10) = 1log10(1) = 0

表にまとめると違いがはっきりします。

関数入力の条件主な用途
log(x)ex > 0解析学、確率・統計、指数成長/減衰の解析
log10(x)10x > 0工学(デシベル)、桁数判定、10進スケールの把握
exp(x)e^x入力は任意の実数対数の逆関数、指数成長/減衰のモデル化

覚えるべき最重要ポイントは「Cのlogは自然対数」という点です。

常用対数が必要なら必ずlog10を使います

exp(指数関数)の役割

exp(x)eのx乗を返し、log(自然対数)の逆関数です。

つまり、exp(log(x)) = x(x>0)、log(exp(y)) = yが成り立ちます。

指数関数は次のような場面で頻出します。

  • 連続複利、放射性崩壊、熱や確率(正規分布)などの数式モデル
  • 浮動小数点のスケール調整(オーバーフロー・アンダーフローの回避)

使い分けの目安

  • 現象の理論式がeを用いる場合(指数分布、正規分布、微分方程式の解など)はlogexpを使います。
  • 10進の桁数やデシベルなど工学的指標にはlog10が自然です。
  • 常用対数の代わりにlogを使うのは誤りなので注意してください。どうしても変換する場合はlog10(x) = log(x) / log(10.0)が使えます。

使い方の準備 math.hとコンパイル

#include <math.h> を追加

指数・対数を使うには#include <math.h>が必須です。

関数プロトタイプやマクロが定義されます。

C言語
// math.hを使ってexp, log, log10を呼び出す最小例
#include <stdio.h>
#include <math.h>

int main(void) {
    double x = 2.0;
    printf("exp(%.1f) = %f\n", x, exp(x));
    printf("log(%.1f) = %f\n", x, log(x));
    printf("log10(%.1f) = %f\n", x, log10(x));
    return 0;
}

gccの例 gcc main.c -lm

Unix系(GCC/Clang)では、数学ライブラリのリンクが別なので-lmが必要です。

  • GCC/Clangの例: gcc main.c -lm
  • 最適化例: gcc -O2 main.c -lm

Windows(MSVC)は-lm不要

MSVC(Visual Studioのcl)では-lmは不要です。

例えば次のようにビルドできます。

  • cl /EHsc main.c

基本はdouble型で計算

標準のexploglog10doubleを入出力とします。

可変長引数であるprintfではfloatは自動でdoubleに昇格するため、%fで表示して問題ありません。

float向けexpf, logf, log10f

単精度floatを使う場合はexpflogflog10fが用意されています。

計算コストはやや低くなることがありますが、精度は落ちます

型を混在させず、式全体を同じ精度で統一するのが安全です。

必要なら拡張精度のexpllogllog10l(long double版)もあります。

基本コード例で学ぶ

expの例 eのべきの計算

expはeのべき乗を返します。

正の指数で増え、負の指数で減少します。

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

int main(void) {
    // e^x をいくつか計算
    double e1 = exp(1.0);    // e^1
    double e2 = exp(2.0);    // e^2
    double em3 = exp(-3.0);  // e^-3

    // e自体はexp(1.0)で得られます (M_Eマクロは環境依存)
    printf("e = exp(1.0)          = %.10f\n", e1);
    printf("exp(2.0)              = %.10f\n", e2);
    printf("exp(-3.0)             = %.10f\n", em3);

    // 性質の確認: exp(a)*exp(b) = exp(a+b)
    double left = exp(1.2) * exp(0.8);
    double right = exp(2.0);
    printf("exp(1.2)*exp(0.8)     = %.10f\n", left);
    printf("exp(2.0)              = %.10f\n", right);

    return 0;
}
実行結果
e = exp(1.0)          = 2.7182818285
exp(2.0)              = 7.3890560989
exp(-3.0)             = 0.0497870684
exp(1.2)*exp(0.8)     = 7.3890560989
exp(2.0)              = 7.3890560989

logの例 自然対数の計算

logは自然対数です。

log(1.0)=0log(e)=1を確認してみます。

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

int main(void) {
    double one = 1.0;
    double e = exp(1.0);     // e ≈ 2.71828...
    double half = 0.5;

    printf("log(1.0)  = %.10f\n", log(one));
    printf("log(e)    = %.10f\n", log(e));
    printf("log(0.5)  = %.10f\n", log(half));  // 負の値になります

    // 逆関係: log(exp(x)) = x
    double x = 2.5;
    printf("log(exp(%.1f)) = %.10f\n", x, log(exp(x)));

    return 0;
}
実行結果
log(1.0)  = 0.0000000000
log(e)    = 1.0000000000
log(0.5)  = -0.6931471806
log(exp(2.5)) = 2.5000000000

log10の例 常用対数の計算

log10は桁数やデシベルの計算でよく使います。

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

int main(void) {
    double a = 1000.0;
    double b = 0.001;
    double c = 2.0;

    printf("log10(1000)  = %.10f\n", log10(a));  // 3
    printf("log10(0.001) = %.10f\n", log10(b));  // -3
    printf("log10(2)     = %.10f\n", log10(c));  // 約0.3010

    // 桁数の目安: 整数nの桁数 ≈ floor(log10(n)) + 1 (n>=1)
    // 例: 12345 は 5桁
    int n = 12345;
    int digits = (int)floor(log10((double)n)) + 1;
    printf("12345の桁数の目安 = %d\n", digits);

    return 0;
}
実行結果
log10(1000)  = 3.0000000000
log10(0.001) = -3.0000000000
log10(2)     = 0.3010299957
12345の桁数の目安 = 5

printfでの出力 %fと桁数

printf%fは小数点以下6桁が既定です。

桁数は%.nfで制御できます。

指数表記が欲しい時は%eも便利です。

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

int main(void) {
    double v = exp(1.0); // e

    // 既定(6桁)
    printf("%%f     : %f\n", v);

    // 桁数の制御(3桁, 10桁)
    printf("%%.3f   : %.3f\n", v);
    printf("%%.10f  : %.10f\n", v);

    // 指数表記
    printf("%%e     : %e\n", v);
    printf("%%.5e   : %.5e\n", v);

    return 0;
}
実行結果
%f     : 2.718282
%.3f   : 2.718
%.10f  : 2.7182818285
%e     : 2.718282e+00
%.5e   : 2.71828e+00

初心者向けの注意点

logとlog10はx>0のみ

対数関数は定義域がx>0です

0や負の入力は数学的に定義されません。

実装ではlog(0.0)-inf(マイナス無限大)log(-1.0)NaNになるのが一般的です。

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

int main(void) {
    double x1 = 0.0;
    double x2 = -1.0;

    printf("log(0.0)  = %f\n", log(x1));  // -inf になることがある
    printf("log(-1.0) = %f\n", log(x2));  // nan になることがある

    // 実務では事前チェックを推奨
    double x = 10.0;
    if (x > 0.0) {
        printf("log(%.1f) = %f\n", x, log(x));
    } else {
        printf("xは正でなければなりません\n");
    }
    return 0;
}

出力例(実装依存):

log(0.0)  = -inf
log(-1.0) = nan
log(10.0) = 2.302585

0や負の数は計算できない

0や負の入力でlogやlog10を呼び出さないように、必ず事前に入力域をチェックしてください。

エラー処理を明確にしておくと、後続の計算がNaNに汚染されるのを防げます。

大きな値でinfやNaNになることがある

exp(x)はxが大きいとオーバーフローしてinfになります。

doubleでは概ねx ≳ 709.78あたりで溢れます(閾値はlog(DBL_MAX))。

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

int main(void) {
    double t1 = 700.0;
    double t2 = 1000.0;

    printf("閾値の目安 log(DBL_MAX) = %.2f\n", log(DBL_MAX));

    double y1 = exp(t1);
    double y2 = exp(t2); // ほぼ確実に +inf

    printf("exp(700)   = %e\n", y1);
    printf("exp(1000)  = %e\n", y2);

    return 0;
}
実行結果
閾値の目安 log(DBL_MAX) = 709.78
exp(700)   = 1.014232e+304
exp(1000)  = inf

自然対数と常用対数の変換 log10(x)=log(x)/log

常用対数は自然対数から次式で変換できます。

  • log10(x) = log(x) / log(10.0)
  • より一般に、log_b(x) = log(x) / log(b)(b>0, b≠1)
C言語
#include <stdio.h>
#include <math.h>

int main(void) {
    double x = 12345.0;

    double via_formula = log(x) / log(10.0); // 変換式
    double direct = log10(x);                 // 直接計算

    printf("log(x)/log(10) = %.12f\n", via_formula);
    printf("log10(x)       = %.12f\n", direct);
    printf("差(絶対値)       = %.12e\n", fabs(via_formula - direct));

    return 0;
}
実行結果
log(x)/log(10) = 4.0914910943
log10(x)       = 4.0914910943
差(絶対値)       = 0.000000000000e+00

多くの環境で差はほぼゼロですが、丸め位置の違いで極小の差が出る場合もあります。

再現性重視ならlog10を直接使う方が安心です。

丸め誤差と表示桁数に注意

浮動小数点は有限精度です。

表示桁数を増やせば「正確になる」わけではありません

内部精度(doubleは約15〜16桁)を超えて表示しても末尾はノイズになります。

結果比較は許容誤差(イプシロン)を使うのが定石です。

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

int nearly_equal(double a, double b, double eps) {
    return fabs(a - b) <= eps * fmax(1.0, fmax(fabs(a), fabs(b)));
}

int main(void) {
    double a = log(exp(20.0));    // 理論上は 20
    double b = 20.0;
    double eps = 1e-12;

    printf("a=%.17g, b=%.17g\n", a, b);
    printf("nearly_equal(a,b) = %s\n", nearly_equal(a,b,eps) ? "true" : "false");

    // 表示桁数の例
    double v = log(2.0);
    printf("log(2) 既定  : %f\n", v);
    printf("log(2) 10桁  : %.10f\n", v);
    printf("log(2) 17桁G : %.17g\n", v); // doubleの有効桁が見える
    return 0;
}
実行結果
a=20, b=20
nearly_equal(a,b) = true
log(2) 既定  : 0.693147
log(2) 10桁  : 0.6931471806
log(2) 17桁G : 0.6931471805599453

見た目の桁数と計算の正しさは別問題です。

用途に応じて適切な桁数で表示しましょう。

まとめ

本記事では、C言語のexploglog10の基本から、math.hの導入とコンパイル手順基礎的なコード例、そして初心者がつまずきやすい定義域やオーバーフロー、丸め誤差までを解説しました。

特に「Cのlogは自然対数、常用対数はlog10」という点と、log10(x)=log(x)/log(10.0)の変換式は必ず覚えておきましょう。

実装では#include <math.h>と適切なリンク(Unix系は-lm、MSVCは不要)を忘れず、対数の入力は必ずx>0を保証することが大切です。

精度や表示桁も目的に応じて調整し、安定した数値処理の基礎を身につけてください。

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

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

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

URLをコピーしました!