必须包含#include <cmath>,传非负数且注意浮点误差;整数开方推荐二分法避免精度问题,如isqrt函数用除法防溢出,全程无浮点、无nan。

直接用 sqrt 就行,但必须包含头文件且注意类型
绝大多数情况下,sqrt 是最简单可靠的选择,但它不是“开箱即用”的——漏掉头文件或传错类型,编译器会直接报错或返回 nan。
常见错误现象:error: 'sqrt' was not declared in this scope(没加头文件),或 sqrt(2) 返回 1(整数除法干扰下误以为结果不准,其实是传了 int 导致隐式转换出问题)。
- 必须写
#include <cmath>(C++ 标准做法),不能用<math.h>(C 风格,虽可能工作但不推荐) -
sqrt重载了double、float、long double版本;传int会自动转成double,没问题;但别传负数,否则返回nan - 如果对整数开方后想取整(比如判断是否为完全平方数),别直接
static_cast<int>(sqrt(n)),浮点误差可能导致向下取整偏差——后面会说怎么绕开
sqrt 算得准不准?浮点误差在哪冒出来
它本身算法是 IEEE 754 合规的,精度足够日常使用,但“准”不等于“等于”。尤其当输入是大整数或需要精确整数判定时,sqrt 的返回值哪怕只差 1e-15,static_cast<int> 一截断就错了。
使用场景:验证 n 是否为完全平方数,或求最大整数 k 满足 k*k <= n。
立即学习“C++免费学习笔记(深入)”;
- 别这么写:
int k = static_cast<int>(sqrt(n)); if (k * k == n) {...}—— 对n = 9007199254740992这类接近double精度极限的数大概率失败 - 稳妥做法:算出
k = static_cast<int>(sqrt(n))后,检查k*k、(k+1)*(k+1)和n的关系,最多试两三个整数 - 或者干脆不用
sqrt:对整数用二分查找求整数平方根,逻辑清晰、无浮点误差,n < 2^64时最多 64 次迭代
替代方案:手写整数平方根函数比你想象中更必要
当输入确定是 unsigned long long 或你需要 100% 可预测行为(比如算法题、嵌入式环境、禁用浮点单元),sqrt 反而成了最不省心的选项。
性能影响:现代 x86 上 sqrt 是硬件指令,比纯整数二分快;但嵌入式或某些编译器优化等级下,整数版本反而更稳定、体积更小。
- 示例(安全整数版):
unsigned long long isqrt(unsigned long long n) { if (n == 0) return 0; unsigned long long lo = 1, hi = n; while (lo < hi) { unsigned long long mid = lo + (hi - lo + 1) / 2; if (mid <= n / mid) lo = mid; else hi = mid - 1; } return lo; } - 关键点:
mid <= n / mid避免mid * mid > n溢出;除法代替乘法是整数开方的通用技巧 - 这个函数返回的是 floor(√n),和
sqrt截断效果一致,但全程无浮点、无溢出、无nan
编译器和平台差异:别假设 sqrt 行为完全一致
同一个 sqrt(2.0),在 GCC/Clang/MSVC 下结果理论上一致,但启用不同优化(如 -ffast-math)或目标平台(ARM vs x86)时,中间计算路径可能不同,极少数边缘 case 会有微小偏差。
兼容性影响:Windows 上若链接旧版 MSVCRT,sqrtf(单精度)在某些输入上可能比标准慢或略不准;Linux glibc 通常更严格遵循 IEEE。
- 如果你做数值敏感任务(如科学计算中间步骤),别依赖
sqrt的最后一位比特;用std::sqrt而非 C 的::sqrt,确保走 C++ 重载 - 跨平台项目里,避免把
sqrt结果直接用于位操作或哈希种子——浮点不可控性会传染 - 调试时怀疑
sqrt有问题?先用std::cout << std::setprecision(17) << sqrt(x);看完整精度,别只看默认 6 位
浮点开方看着简单,真正卡住人的从来不是“怎么调用”,而是“怎么信它”——尤其是当你把结果喂给另一个整数逻辑的时候。










