用 std::sqrt 转整数结果不保险,因双精度浮点无法精确表示大整数,导致边界值如1000000000000或46340²的平方根计算可能向下偏移;应改用二分查找等整数算法确保精度。

用 std::sqrt 转整数结果不保险,为什么?
直接调用 std::sqrt 后 static_cast<int></int> 或 floor 得到整数平方根,在边界值上容易出错。比如 sqrt(1000000000000) 是 1000000.0,但浮点误差可能导致结果变成 999999;sqrt(2147395600)(即 46340²)在某些编译器+平台下也可能向下偏移。这不是 bug,而是 IEEE 754 双精度无法精确表示所有大整数的必然现象。
所以「快速」不能只看函数调用快慢,得看结果是否可靠——整数平方根定义为满足 k² ≤ n 的最大整数 <code>k,必须严格满足这个不等式。
手写二分查找求整数平方根,安全又够快
对 32 位整数,最多只需 32 次比较;64 位也仅 64 次。现代 CPU 分支预测良好,实际性能远超浮点 sqrt + 类型转换的组合,且完全避免浮点误差。
- 左边界设为
0,右边界设为n(n > 1时可优化为n/2 + 1,但没必要) - 每次计算
mid = left + (right - left) / 2,避免溢出 - 判断
mid * mid :若成立,<code>mid是候选,向右缩区间;否则向左缩 - 循环结束时
left就是答案(注意退出条件是left > right,最终返回right)
示例片段:
int isqrt(int n) {
if (n <h3>
<code>std::sqrt</code> 加修正才是折中方案</h3><p>如果你追求极致简洁且输入确定在 <code>[0, 2^24)</code> 范围内(即 16777216 以内),双精度能精确表示每个整数,此时 <code>static_cast<int>(std::sqrt(n))</int></code> 是安全的。超出后就需要修正:</p>
- 先算
int k = static_cast<int>(std::sqrt(n))</int> - 再检查
(k+1)*(k+1) → 则取 <code>k+1 - 再检查
k*k > n→ 则取k-1 - 通常只需最多一次上下调整,比二分快,但逻辑多了一层
注意:k*k 可能溢出,建议用 long long 中间计算,或改用 k 这类防溢出写法。
无符号整数和 64 位数怎么处理?
对 unsigned int 或 uint64_t,二分法依然适用,但要注意:
- 右边界不能设成
n(对uint64_t来说太大),应设为1ULL (对 64 位输入足够,因为 √(2⁶⁴) = 2³²) - 比较时统一用
uint64_t类型,避免隐式提升问题 -
mid * mid易溢出,必须用mid 替代(前提是 <code>mid != 0)
没有银弹:小范围用浮点加校验,大范围或要求 100% 正确性时,老老实实二分。最易被忽略的是溢出判断——哪怕只是中间乘法,也足以让看似正确的代码在 n=2147483647 这种边界上崩掉。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











