直接比较sqrt(n)与取整值易错,因double精度不足(仅精确到2^53≈9e15),大数如9999999999999999会误判;正确做法是取整后检查r−1、r、r+1三个候选值平方是否等于n。

用 sqrt 判断完全平方数,为什么直接比较 sqrt(n) 和它的取整值容易出错?
因为 sqrt 返回 double,浮点数精度在大整数(比如接近 LLONG_MAX)时无法精确表示整数,static_cast 可能向下舍入一格。例如 n = 9999999999999999,sqrt 计算结果可能是 99999999.99999999,强制转 long long 后变成 99999999,平方后远小于原数,但实际它本就是完全平方数(100000000²)。
正确做法是:取整后向上、向下各试一个候选值,再平方比对:
long long r = static_cast(std::sqrt(n)); for (long long x : {r - 1, r, r + 1}) { if (x >= 0 && x * x == n) return true; } return false;
std::sqrt 的参数类型和溢出风险必须手动控制
std::sqrt 对 int 会隐式转成 double,而 double 能精确表示的整数上限约是 2^53 ≈ 9e15。一旦 n > 9e15,sqrt(n) 的输入就已失真,后续判断必然不可靠。
- 对
long long类型输入,先做范围检查:if (n > 9007199254740992LL) {...} - 更稳妥的方式是避免
sqrt:用二分查找在[0, n]或[0, min(n, 1LL 内找平方根(n很大时上界可设为sqrt(LLONG_MAX)约3e9) - 注意
x * x可能溢出,比较前应加if (x > 0 && n / x 做除法防溢出
使用 std::sqrt 前必须包含头文件并处理负数
std::sqrt 在 中声明,不包含会编译失败或触发隐式声明警告。且 sqrt 对负数返回 NaN,static_cast 后行为未定义 —— 必须先判负:
立即学习“C++免费学习笔记(深入)”;
#include... if (n < 0) return false; long long r = static_cast (std::sqrt(n));
另外,std::sqrt 有多个重载,传 int 调用的是 double sqrt(double),不是 float 版,这点无需额外指定,但不能依赖 sqrtf。
整数二分法判断完全平方数更安全、无精度问题
当需要 100% 正确性(尤其处理大整数或写库函数时),放弃 sqrt 是最干净的选择。核心逻辑是找满足 x² ≤ n 的最大 x,再验证 x² == n:
if (n < 0) return false;
long long lo = 0, hi = n;
while (lo <= hi) {
long long mid = lo + (hi - lo) / 2;
if (mid > 3037000499LL) { hi = mid - 1; continue; } // 防 mid*mid 溢出
long long sq = mid * mid;
if (sq == n) return true;
if (sq < n) lo = mid + 1;
else hi = mid - 1;
}
return false;3037000499LL 是 sqrt(LLONG_MAX) 的下取整,超过它平方必溢出。这个边界值比每次做除法判断更高效,也更直观。
浮点 sqrt 看似简单,但边界 case 太多;整数二分写一次,后面所有输入都稳。真正上线或做算法题时,别省那几行代码。









