CC++ & Algorithm

C++中的对数函数 log 与指数函数 exp

困难14
语言版本:C++Python
概述:用复利和反函数关系理解log和exp,并学会在C++中计算它们。

指数函数 exp 和对数函数 log:互为“最亲密”的数学朋友

你有没有听说过“指数爆炸”——比如一张纸对折42次就能从地球到月球?或者玩《我的世界》时,小村民数量每5分钟翻一番?这些都是指数函数在生活中的影子。在C++中,exp(x) 就是计算 自然常数 e 的 x 次方(e ≈ 2.71828),而 log(x) 则是它的“反操作”:告诉你“要把多少e乘起来才能得到x”。它们就像一对形影不离的好朋友,一个扩张,一个还原。下面我们就用零花钱、细菌和复利来认识它们。

一、认识指数函数 exp:翻倍、复利和细菌

exp(x) 做的事很简单:计算 e^x。e 是一个神奇的无理数,约等于 2.71828。为什么非要用e?因为自然界中很多连续增长的现象(比如细菌每时每刻都在分裂)用 e 描述最自然。

举个零花钱的例子:假设你妈妈每天给你存零花钱,但方式很特别——她按“连续复利”存钱。如果你有1元本金,年利率是100%,那么一年后你的钱会变成 e^1 ≈ 2.718元。如果年利率是200%(翻两倍),两年后就是 e^2 ≈ 7.389元。这里利率就是指数中的 x。

代码看看 exp 的威力

#include <iostream>
#include <cmath>   // 包含 exp 和 log
using namespace std;

int main() {
    double days = 5.0;                              // 翻倍次数(相当于利率)
    double factor = exp(days);                      // 计算 e^5 ≈ 148.413
    cout << "e的" << days << "次方 = " << factor << endl;

    // 细菌每20分钟翻倍,问2小时后(6次翻倍)有多少个?
    int start = 1;                                  // 初始1个细菌
    double growth = exp(6.0);                       // e^6 ≈ 403.429
    cout << "2小时后,细菌数量约为 " << start * growth << " 个" << endl;
    return 0;
}

生活小贴士:如果你在《植物大战僵尸》里种了向日葵,每个阳光产生器每10秒产出一份阳光,连续工作30秒后,总阳光量不是简单的线性,而是指数增长(因为新的向日葵又产生阳光)。exp 可以帮你估算最大产量。


二、认识对数函数 log:把“大数字”压缩成“小数字”

log(x)exp 的反函数,它回答这个问题:“e 的多少次方等于 x?” 例如 log(7.389) ≈ 2,因为 e² = 7.389。

需要特别记住:C++ 中的 log 默认是自然对数(以 e 为底)。如果你想要以 10 为底的对数(常用对数),请用 log10(x);以 2 为底用 log2(x)(C++11起支持)。

生活中的对数无处不在:

  • 声音的分贝:人耳能听到的声音强度范围极广(从轻声耳语到火箭发射),用分贝(对数尺度)来表示。比如 100 分贝是 80 分贝强度的 100 倍(因为每增加20分贝,强度×10)。
  • 地震的里氏震级:震级每增加 1 级,释放的能量约增加 31.6 倍(即 10^1.5 倍)。震级是实际能量的对数。
  • 考试排名:如果全班成绩差距很大,老师有时会用对数分数来平衡不同分数段的区分度。

代码例子:计算一个声音强度对应的分贝值。

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    double intensity = 1000.0;                      // 强度,单位倍(相对基准)
    double dB = 10 * log10(intensity);              // 分贝 = 10*log10(相对强度)
    cout << "强度 " << intensity << " 倍对应的分贝值 = " << dB << " dB" << endl;

    // 反过来,知道分贝求强度
    double dB_value = 30.0;
    double original = pow(10, dB_value / 10);       // 1000倍
    cout << dB_value << " dB 对应的强度倍数为 " << original << endl;
    return 0;
}

三、互为反函数:exp(log(x)) = xlog(exp(x)) = x

这是最核心的关系:指数和对数互为逆运算,就像加法和减法、乘法和除法。无论你先做指数再做对数,还是先对数再指数,只要参数合适,结果一定回到原数。

下面的代码验证这一点(保留了原示例并稍微扩展):

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    double x = 2.0;
    double exp_x = exp(x);              // 计算 e^2
    double log_exp = log(exp_x);        // 再取对数,应该回到x
    cout << "exp(2) = " << exp_x << endl;
    cout << "log(exp(2)) = " << log_exp << " (应该等于2)" << endl;

    double y = 10.0;
    double log_y = log(y);              // 计算 ln(10)
    double exp_log = exp(log_y);        // 再取指数,应该回到10
    cout << "log(10) = " << log_y << endl;
    cout << "exp(log(10)) = " << exp_log << " (应该等于10)" << endl;

    // 额外:试试负数不行吧?
    double negative = -5.0;
    // double bad = log(negative);      // ❌ 错误:log(负数) 会返回 NaN(非数字)
    // cout << "log(-5) = " << bad << endl;  // 取消注释会看到 nan
    cout << "注意:log()的参数不能是负数或0" << endl;

    return 0;
}

为什么不能对负数或0取对数? 因为任何正数的指数都不可能等于负数或0(e^x 永远 > 0),所以 log(负数或0) 没有意义。C++中会返回一个特殊值 NaN(Not a Number),继续计算可能会导致程序异常。


四、新手最容易犯的 3 个错误

错误1:忘了包含头文件 <cmath>

如果不写 #include <cmath>,直接使用 explog,编译器会报错“未定义标识符”。

错误2:误把 log 当作以 10 为底的常用对数

很多人从数学课习惯 log 表示常用对数,但在 C++ 中 log(x) 是自然对数。如果需要以 10 为底,请用 log10(x);以 2 为底用 log2(x)

错误3:对负数和 0 取对数

double invalid = log(-3.0);   // 返回 nan
double zero = log(0.0);       // 返回 -inf(负无穷)

编程时要先检查参数是否为正数,可以这样保护:

double safe_log(double num) {
    if (num <= 0) {
        cout << "错误:数字必须大于0" << endl;
        return -1;  // 或返回一个错误标记
    }
    return log(num);
}

五、完整可运行的示例:模拟零花钱的复利增长

假设你有一笔初始零花钱 本金 = 100 元,银行按 年利率 5% 连续复利 计算。那么 t 年后的总金额公式为:金额 = 本金 × e^(利率 × t)。反过来,如果想达到某个目标金额,需要多少年?用对数求解。

#include <iostream>
#include <cmath>
using namespace std;

int main() {
    double principal = 100.0;              // 本金 100 元
    double rate = 0.05;                    // 年利率 5%
    double year = 10.0;                    // 存 10 年

    // 计算 10 年后的本息和
    double amount = principal * exp(rate * year);
    cout << "存 " << year << " 年后总金额 = " << amount << " 元" << endl;

    // 假如想达到 200 元(翻倍),需要多少年?
    double target = 200.0;
    double years_needed = log(target / principal) / rate;   // 反推年数
    cout << "想要达到 " << target << " 元,需要 " << years_needed << " 年" << endl;

    // 检查:把算出的年数代回去
    double check = principal * exp(rate * years_needed);
    cout << "验证:经过 " << years_needed << " 年,金额 = " << check << " 元" << endl;

    return 0;
}

输出结果(近似):

存 10 年后总金额 = 164.872 元
想要达到 200 元,需要 13.8629 年
验证:经过 13.8629 年,金额 = 200 元

这个例子完美展示了 explog 如何配合解决实际问题:用 exp 正向计算,用 log 反向求解。


六、总结与相关指引

  • exp(x):计算 e^x,用于模拟连续增长(复利、细菌、放射性衰变)。
  • log(x):计算自然对数 ln(x),用于压缩大范围数据或反向推算指数中的指数。
  • 记住它们互为反函数:exp(log(x)) = x(x>0),log(exp(x)) = x
  • 若要计算其他底数的指数或对数,可以用换底公式:log_a(b) = ln(b) / ln(a),或者直接使用 pow(a, b) 计算 a^b。

相关知识点

  • pow 函数 计算任意底数的幂,比如 pow(10, 3) = 1000
  • 三角函数 sincostan 也在 <cmath> 中,它们与指数函数通过欧拉公式联系。
  • 数学常数 M_EM_PI 定义在 <cmath> 中(需要 #define _USE_MATH_DEFINES 预处理),可以直接使用。

下次你听到“指数增长”或者“对数刻度”,别忘了打开 C++ 的 <cmath>,用 explog 来和数字玩耍——就像用加法和减法一样自然!

例题精讲

1单选题

在C++中,使用<cmath>头文件后,函数log(x)返回的是?

A以10为底的对数
B以e为底的对数(自然对数)
C以2为底的对数
D以任意底数的对数,底数由参数指定
2单选题

以下哪个表达式正确计算了e的a次方(其中e是自然常数)?

Aexp(a)
Blog(a)
Cpow(exp(1), a)
Dpow(2.71828, a)
3判断题

在C++中,log(1)的返回值为0。

4填空题
计算一个正数的自然对数后,再取指数,应回到原数。补全以下代码:
#include <cmath>
using namespace std;
double x = 5.0;
double y = log(x);
double z = exp(y);
// 此时z的值等于___
5填空题
以下程序从键盘读取一个正数,输出其自然对数,保留两位小数。补全代码:
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
int main() {
    double x;
    cin >> x;
    double result = log(___);
    cout << fixed << setprecision(2) << result << endl;
    return 0;
}