std::pow不适用于整数快速幂,因其转浮点导致精度误差且不支持取模;应手写fast_pow,核心是二进制拆分指数、每次乘法后取模、先对base取模防溢出。

为什么 pow 不能直接用于整数快速幂
标准库的 std::pow 是为浮点数设计的,输入 int 也会先转成 double,再调用浮点幂函数——这会引入精度误差(比如 pow(10, 9) 可能返回 999999999 或 1000000001),且不支持取模。竞赛、密码学或大数场景下,你真正需要的是「整数底数 + 整数指数 + 模数」的三元组合运算。
- 别对
long long调用std::pow后强制转回整型——结果不可靠 - 指数为 0 时,任何非零底数结果应为 1;底数为 0 且指数为 0 是未定义行为,需提前判断
- 如果不需要模运算,仍建议手写,避免隐式类型提升和浮点误差
手写 fast_pow 的核心循环怎么写才安全
经典二分思想:把指数拆成二进制,每次检查最低位是否为 1,是则累乘当前幂次的底数,然后底数自乘、指数右移。关键在溢出控制和边界处理。
- 必须用
long long存中间结果,哪怕输入是int——a * a容易溢出 - 模运算要放在每次乘法后:
res = (res * base) % mod,不能等最后再取模 - 右移用
exp >>= 1,别用exp /= 2(虽等价但语义不清,且对负数行为不同) - 示例片段:
long long fast_pow(long long base, long long exp, long long mod) { long long res = 1; base %= mod; // 先取模,防 base >= mod while (exp > 0) { if (exp & 1) res = (res * base) % mod; base = (base * base) % mod; exp >>= 1; } return res; }
当模数是 1 或负数时会发生什么
模数为 1 时,所有结果都应为 0(除了 0^0 这种特殊情况);但若代码没提前判断,base %= mod 会导致除零错误(因为 % 1 合法,但 % 0 不合法)。更隐蔽的是负模数——C++ 中 a % b 符号取决于被除数,不是数学意义的非负剩余。
- 模数必须大于 0,函数入口应加断言:
if (mod - 不要依赖
%得到 [0, mod) 区间结果;如需非负余数,用((x % mod) + mod) % mod - 指数为负?整数快速幂不处理负指数——那是浮点或逆元的事,别混用
递归写法 vs 迭代写法,哪个更容易出错
递归写法简洁,但栈深度随指数位数增长(最多约 64 层),一般没问题;真正危险的是忘记处理 exp == 0 的 base case,或递归调用时没对指数做整除导致死循环。
立即学习“C++免费学习笔记(深入)”;
- 迭代写法更省内存、无栈溢出风险,推荐生产环境使用
- 递归版本中,
exp / 2必须用整除(exp >> 1更安全),否则小数指数会卡住 - 两者性能差异极小,现代编译器对循环展开优化很好,不必纠结“递归更优雅”
实际用的时候,最常漏掉的是 base %= mod 这一步——尤其当 base 接近 LLONG_MAX 时,第一次 base * base 就溢出,后面全错。这个细节没有警告,也难调试。










