在C++中,%
是取模运算符(modulus operator),用于计算两个数相除后的余数。虽然它的用法看起来简单,但在实际编程中有一些需要注意的细节和易错点。以下是关键注意事项:
1. 操作数必须为整数类型
%
只能用于整数类型(如 int
, long
, char
等),不能用于浮点数(float
、double
)。
错误示例:
double a = 5.5, b = 2.0; int c = a % b; // 编译错误!
解决方法:用 fmod()
函数处理浮点数取模:
#include <cmath> double c = fmod(a, b); // 正确
2. 负数的取模结果
C++中 %
的结果符号与被除数(左操作数)一致,这是易错点!
示例:
cout << 5 % 3; // 2 cout << -5 % 3; // -2(符号与被除数-5一致) cout << 5 % -3; // 2(符号与被除数5一致) cout << -5 % -3; // -2(符号与被除数-5一致)
注意:数学上余数通常定义为非负数,但C++遵循“商向零截断”规则。若需要非负结果,需手动调整:
int mod = (a % b + b) % b; // 确保结果非负
3. 除数为零的未定义行为
如果 b == 0
,a % b
会导致未定义行为(UB),可能引发运行时崩溃。
安全写法:
if (b != 0) { int res = a % b; } else { // 处理除零错误 }
4. 取模与除法的关系
%
和 /
满足以下关系(除非 b == 0
):
(a / b) * b + (a % b) == a
但注意:当 a
或 b
为负数时,/
是向零截断的整数除法。
5. 取模运算的优先级
%
和 *
、/
优先级相同,高于 +
、-
,但低于括号。
易错示例:
int x = 10 + 5 % 3; // 等价于 10 + (5 % 3) = 12 int y = (10 + 5) % 3; // 15 % 3 = 0
建议:复杂表达式显式加括号。
6. 大数取模的溢出问题
对 int
类型的大数取模时,可能先发生溢出。
示例:
int a = 1e9, b = 1e9 + 7; int res = (a * a) % b; // a*a 溢出后再取模,结果错误!
解决方法:用 long long
或逐步取模:
long long res = (1LL * a * a) % b;
7. 取模的常见用途
-
循环索引:
i % n
实现数组循环访问。 -
哈希函数:
key % capacity
计算哈希桶位置。 -
判断奇偶性:
n % 2 == 0
为偶数。 -
周期性操作:如时钟计算
(hours + offset) % 12
。
总结表
场景 | 注意事项 |
---|---|
操作数类型 | 仅限整数,浮点数用 fmod() |
负数取模 | 结果符号与被除数一致,需手动调整非负 |
除数为零 | 未定义行为,必须检查 |
优先级 | 与 * 、/ 相同,注意括号 |
大数运算 | 用 long long 避免溢出 |
数学期望 vs C++行为 | C++结果可能为负,数学定义通常非负 |
最佳实践:
-
检查除数非零。
-
处理负数时显式调整结果(如
(a % b + b) % b
)。 -
大数运算用
long long
。