跳转至

cmath常用函数

在C++算法竞赛中,处理数学相关的逻辑绝大多数依赖于 <cmath> 头文件(部分依赖 <numeric><cstdlib>)。和 <algorithm> 一样,熟练调用这些标准库函数可以帮你避开很多底层的精度坑。

以下是竞赛中最常用的数学函数分类总结与核心用法:

一、 幂次与开方

1. sqrt()cbrt() * 作用: sqrt(x) 计算平方根,cbrt(x) 计算立方根(C++11加入)。 * 返回值: 均为浮点数(double)。 * 代码示例:

double a = sqrt(16.0); // 4.0
double b = cbrt(27.0); // 3.0

2. pow() * 作用: 计算 x 的 y 次方 pow(x, y)。 * ⚠️ 竞赛巨坑: pow 的返回值是浮点数!绝对不要用它来计算大整数的乘方(比如 pow(2, 30)),极易因为浮点数精度丢失导致结果偏差(比如变成 1073741823.999... 被截断成错的整数)。 * 正确替代方案: * 算 2 的次幂:直接用位运算 1LL << y。 * 算整数次幂并取模:手写快速幂(Quick Pow)

3. hypot() * 作用: 计算直角三角形的斜边长,即 sqrt(x*x + y*y)。 * 优势: 在计算两点间距离时非常好用,且内部做了优化,能避免 x*x + y*y 在求和前就发生整型溢出

double dist = hypot(dx, dy); 

二、 取整与绝对值

1. abs()fabs() * 作用: 求绝对值。 * 区别: * abs():原本在 <cstdlib> 中用于整型 int。但在 C++11 之后,<cmath> 对其进行了重载,支持浮点数。 * fabs():专门用于求浮点数的绝对值。竞赛中处理 double 时,用 fabs() 是最稳妥的习惯。

2. ceil(), floor(), round() * 作用: * ceil(x):向上取整(天花板,找大于等于 x 的最小整数)。 * floor(x):向下取整(地板,找小于等于 x 的最大整数)。 * round(x):四舍五入。 * 返回值: 它们返回的依然是浮点数,如果赋给 int 会发生隐式转换。

三、 指数与对数

1. log(), log10(), log2() * 作用: * log(x):计算以 \(e\) 为底的自然对数 (\(\ln x\))。 * log10(x):计算以 10 为底的对数。 * log2(x):计算以 2 为底的对数。 * 竞赛场景: 经常用于计算一个数字的位数,或者在数据结构(如 RMQ 问题的 ST 表)中预处理对数数组。

2. exp() * 作用: 计算 \(e^x\)

四、 三角函数与 \(\pi\)

1. sin(), cos(), tan() * 注意: C++ 中所有三角函数的参数都是弧度制,不是角度制。

2. asin(), acos(), atan(), atan2() * 作用: 反三角函数。 * 高频用法 (atan2): atan2(y, x) 用于计算点 (x, y) 与原点连线和 x 轴正半轴的夹角(极角),范围是 (-pi, pi]。在极角排序(计算几何经典问题)中必用。 * 获取高精度 \(\pi\) 的标准姿势: 竞赛中一般不手写 3.1415926,而是利用反余弦函数生成:

const double PI = acos(-1.0);

五、 现代 C++ 补充 (在 <numeric> 头文件中)

如果你使用的是 C++17 或以上标准(现在各大 OJ 基本都支持):

1. std::gcd()std::lcm() * 作用: 计算最大公约数(GCD)和最小公倍数(LCM)。 * 优势: 直接取代了手写欧几里得算法 return b == 0 ? a : gcd(b, a % b); 或者非标准的 __gcd

#include <numeric>
int g = std::gcd(12, 18); // 6
int l = std::lcm(12, 18); // 36


💡 竞赛实战避坑指南:

  1. 浮点数比较的 eps 陷阱: 计算机存储浮点数有精度误差。永远不要用 a == b 来比较两个浮点数!必须引入一个极小值 eps(通常取 1e-9)。

    const double eps = 1e-9;
    if (fabs(a - b) < eps) { ... } // 判断 a == b
    if (a - b > eps) { ... }       // 判断 a > b
    if (a - b < -eps) { ... }      // 判断 a < b
    

  2. 整数除法向上取整的优雅写法: 如果你要把整数 A 除以 B 并向上取整,不要ceil((double)A / B),这会引入不必要的浮点数运算和潜在精度问题。 正确写法(要求 A 和 B 均为正数): (A + B - 1) / B

  3. 当心 NaN (Not a Number): 如果你对负数求 sqrt(),或者求 acos(x)x 超过了 [-1, 1] 的范围(通常是因为浮点误差变成了 1.000000001),函数会返回 NaN。后续任何与 NaN 的运算结果都会变成乱码。在求反三角函数前,最好对 x 稍微做一下截断保护。