参考资料

前言

在刷算法题的时候,经常需要用到绝对值函数。一开始我以为C++的绝对值函数就是简单的abs(),直到遇到了浮点数、long long等不同类型的数据,才发现C++中的绝对值函数远比想象中复杂。

今天就来系统地梳理一下C++中绝对值函数的使用方法和注意事项,帮助大家避开我踩过的坑。

一、不同类型的绝对值函数

C++中不同数据类型有不同的绝对值函数,这是很多初学者容易踩的坑!

函数对照表

数据类型 函数名 头文件 示例
int abs() <cstdlib><cmath> abs(-5) → 5
long labs() <cstdlib> labs(-123L) → 123
long long llabs() <cstdlib> llabs(-123LL) → 123
float/double fabs() <cmath> fabs(-3.14) → 3.14
通用(C++11) abs() <cmath> 自动识别类型

二、基本使用方法

1. 整数类型

#include <cstdlib>  // 或 <cmath>
using namespace std;

int main() {
    int a = -5;
    cout << abs(a) << endl;  // 输出:5
    
    long b = -123L;
    cout << labs(b) << endl;  // 输出:123
    
    long long c = -123456789LL;
    cout << llabs(c) << endl;  // 输出:123456789
    
    return 0;
}

2. 浮点数类型

#include <cmath>
using namespace std;

int main() {
    double x = -3.14;
    cout << fabs(x) << endl;  // 输出:3.14
    
    float y = -2.5f;
    cout << fabs(y) << endl;  // 输出:2.5
    
    return 0;
}

3. C++11 通用方法(推荐)

从C++11开始,abs()函数被重载了,可以自动识别不同类型:

#include <cmath>
using namespace std;

int main() {
    // abs() 在 C++11 中重载了,可以自动识别类型
    cout << abs(-5) << endl;        // int:5
    cout << abs(-5L) << endl;       // long:5
    cout << abs(-5LL) << endl;      // long long:5
    cout << abs(-3.14) << endl;     // double:3.14
    cout << abs(-2.5f) << endl;     // float:2.5
    
    return 0;
}

三、算法竞赛常用写法

在算法竞赛中,通常会使用万能头文件bits/stdc++.h,这样就不需要担心头文件的问题了:

#include <bits/stdc++.h>
using namespace std;

int main() {
    // bits/stdc++.h 已经包含了所有头文件
    
    int a = -5;
    long long b = -123456789LL;
    double c = -3.14;
    
    cout << abs(a) << endl;    // 5
    cout << abs(b) << endl;    // 123456789
    cout << abs(c) << endl;    // 3.14
    
    return 0;
}

推荐: 在算法竞赛中,直接用 abs() 即可,编译器会自动识别类型!


四、重要注意事项

注意1:整数溢出问题(非常重要!)

这是最容易踩的坑,我自己就在这里吃过亏。

int a = INT_MIN;  // -2147483648
cout << abs(a);   // ⚠️ 未定义行为!可能输出负数

原因分析:

  • INT_MIN 的值是 -2147483648
  • INT_MAX 的值是 2147483647
  • abs(INT_MIN) 应该是 2147483648,但超出了 int 范围!
  • 结果是未定义行为,可能输出负数或其他奇怪的值

解决方法:

// 方法1:使用 long long
int a = INT_MIN;
cout << abs((long long)a) << endl;  // 强制转换为 long long

// 方法2:使用 llabs
cout << llabs(a) << endl;

// 方法3:用更大的类型存储
long long a = INT_MIN;
cout << abs(a) << endl;

注意2:浮点数要用正确的函数

// ❌ 错误:对浮点数用整数的 abs 并强制转换
double x = -3.14;
cout << abs((int)x);  // 输出:3(小数部分被截断了!)

// ✅ 正确:用 fabs 或直接用 abs(C++11)
cout << fabs(x);  // 输出:3.14
cout << abs(x);   // 输出:3.14(C++11)

注意3:避免重复计算

在一些场景下,可能需要多次使用同一个绝对值,这时候应该先计算并存储:

// ❌ 低效:计算了两次
if (abs(x - y) > 10) {
    cout << abs(x - y);
}

// ✅ 高效:只计算一次
int diff = abs(x - y);
if (diff > 10) {
    cout << diff;
}

注意4:负数取模后再求绝对值

这是一个容易混淆的地方,顺序不同结果可能不同:

int a = -7, b = 3;

// 先绝对值再取模
int result1 = abs(a) % b;  // 7 % 3 = 1

// 先取模再绝对值
int result2 = abs(a % b);  // (-7) % 3 = -1,abs(-1) = 1

在C++中,负数取模的结果符号与被除数相同,所以 -7 % 3 = -1


五、常见使用场景

场景1:计算两数之差的绝对值

int a = 5, b = 10;
int diff = abs(a - b);  // 5

这是最常见的用法,用于计算距离、差值等。

场景2:判断是否在允许范围内

int x = 7, target = 10, tolerance = 5;
if (abs(x - target) <= tolerance) {
    cout << "在允许范围内";
}

场景3:计算曼哈顿距离

在网格问题中经常用到:

int x1 = 1, y1 = 2;
int x2 = 4, y2 = 6;
int manhattan_distance = abs(x1 - x2) + abs(y1 - y2);  // 7

场景4:判断奇偶性(适用于负数)

int n = -7;
if (abs(n) % 2 == 1) {
    cout << "奇数";
} else {
    cout << "偶数";
}

场景5:数组中找最接近的值

int target = 50;
int arr[] = {10, 30, 45, 60, 80};
int closest = arr[0];
int min_diff = abs(arr[0] - target);

for (int i = 1; i < 5; i++) {
    int diff = abs(arr[i] - target);
    if (diff < min_diff) {
        min_diff = diff;
        closest = arr[i];
    }
}
cout << "最接近的值:" << closest;  // 45

六、函数性能对比

虽然在大多数情况下性能差异不大,但了解一下还是有帮助的:

函数 适用类型 返回类型 头文件 推荐度
abs(int) int int <cstdlib> ⭐⭐⭐
labs(long) long long <cstdlib> ⭐⭐
llabs(long long) long long long long <cstdlib> ⭐⭐
fabs(double) float/double double <cmath> ⭐⭐⭐
abs(T) (C++11) 任意数值类型 对应类型 <cmath> ⭐⭐⭐⭐⭐

七、记忆技巧

记忆口诀

整数 abs,浮点 fabs
C++11 后都用 abs
小心溢出 INT_MIN
竞赛直接 abs 最稳

推荐使用方式

#include <bits/stdc++.h>
using namespace std;

// C++11 后统一用 abs(),编译器自动识别类型
int a = abs(-5);           // int
long long b = abs(-5LL);   // long long
double c = abs(-3.14);     // double

八、完整示例代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    // 1. 整数绝对值
    int x = -10;
    cout << "abs(-10) = " << abs(x) << endl;  // 10
    
    // 2. long long 绝对值
    long long y = -123456789LL;
    cout << "abs(-123456789LL) = " << abs(y) << endl;  // 123456789
    
    // 3. 浮点数绝对值
    double z = -3.14159;
    cout << "abs(-3.14159) = " << abs(z) << endl;  // 3.14159
    
    // 4. 计算差值
    int a = 5, b = 15;
    cout << "差值 = " << abs(a - b) << endl;  // 10
    
    // 5. 注意溢出(正确做法)
    int min_val = INT_MIN;
    cout << "abs(INT_MIN) = " << abs((long long)min_val) << endl;
    
    // 6. 曼哈顿距离
    int x1 = 1, y1 = 2, x2 = 4, y2 = 6;
    cout << "曼哈顿距离 = " << abs(x1-x2) + abs(y1-y2) << endl;  // 7
    
    return 0;
}

九、常见错误示例

错误1:浮点数用错函数

// ❌ 错误
double x = -3.14;
int result = abs(x);  // 可能得到 3,小数丢失

// ✅ 正确
double result = abs(x);  // 3.14(C++11)
// 或
double result = fabs(x);  // 3.14

错误2:溢出未处理

// ❌ 危险
int x = INT_MIN;
int result = abs(x);  // 未定义行为

// ✅ 安全
long long result = abs((long long)x);

错误3:重复计算

// ❌ 低效
for (int i = 0; i < n; i++) {
    if (abs(arr[i] - target) < 10) {
        sum += abs(arr[i] - target);
    }
}

// ✅ 高效
for (int i = 0; i < n; i++) {
    int diff = abs(arr[i] - target);
    if (diff < 10) {
        sum += diff;
    }
}

总结

最佳实践

  1. C++11 及以后:统一使用 abs(),简单好记,编译器自动处理类型
  2. 注意整数溢出INT_MIN 的绝对值会溢出,需要转换为 long long
  3. 算法竞赛#include <bits/stdc++.h> + abs() 搞定一切
  4. 避免重复计算:将绝对值结果存储在变量中,不要重复调用
  5. 浮点数:C++11 后用 abs()fabs() 都可以

何时使用何种函数

  • 一般情况:直接用 abs()(C++11)
  • 明确是浮点数:用 fabs()abs()
  • ⚠️ 可能溢出:先转换为 long long 再用 abs()
  • ⚠️ C++98/03:整数用 abs()/labs()/llabs(),浮点用 fabs()

快速查询表

// 推荐写法(C++11)
abs(-5)        // int
abs(-5LL)      // long long  
abs(-3.14)     // double

// 注意溢出
abs((long long)INT_MIN)  // 安全

// 旧版本兼容
abs(int_val)       // int
labs(long_val)     // long
llabs(ll_val)      // long long
fabs(double_val)   // double

后记

绝对值函数虽然看起来简单,但在实际使用中有不少细节需要注意。特别是整数溢出的问题,我自己就因为这个在比赛中吃过亏。

希望这篇文章能帮助大家避开这些坑,在刷题和比赛中更加得心应手!

如果有任何问题或者补充,欢迎在评论区讨论~

祝大家刷题顺利! 💪✨