不能直接用 sqrt(disc) == round(sqrt(disc)) 判断,因 double 无法精确表示大于 2⁵³ 的整数,导致浮点误差使 round 结果错误;正确做法是用 llround 得近似根 r0,再在 {r0−1, r0, r0+1} 中验证 r*r==disc。

为什么不能直接用 sqrt(disc) == round(sqrt(disc)) 判断
因为 std::sqrt 返回 double,而 double 对大于 2⁵³(约 9e15)的整数无法精确表示——哪怕 disc 是完全平方数,sqrt 也可能返回 999999.999999 或 1000000.000001,round 后转 long long 仍可能错。更糟的是,static_cast<long long>(std::sqrt(x))</long> 默认截断,不是四舍五入,极易漏判。
- 错误写法:
long long r = sqrt(disc); return r * r == disc; - 正确思路:先算近似根
r0 = llround(sqrt(disc)),再在{r0-1, r0, r0+1}中暴力验证哪个满足r * r == disc - 这个 ±1 范围足够:浮点误差不会让真实整数根偏离估算值超过 1
判别式 disc = 1 + 8 * x 必须用 long long 计算
哪怕输入是 int,8 * x 也可能溢出:INT_MAX 是 2147483647,8 倍后约 1.7e10,已超出 int 范围(最大约 2.1e9),直接计算会触发未定义行为。
- 必须写成
long long disc = 1LL + 8LL * x;或强制转型:1LL + 8LL * static_cast<long long>(x)</long> - 如果
x是unsigned int或更大类型(如uint64_t),disc需对应升级为unsigned long long,且sqrt应换用std::sqrtl避免精度丢失 -
disc本身不能为负——所以函数开头必须先if (x
验证根 r 后还要检查 (r - 1) % 2 == 0 且 (r - 1) / 2 > 0
解二次方程得 n = (-1 + r) / 2,其中 r = sqrt(1 + 8x)。仅 r * r == disc 不足以保证 n 是正整数——比如 x = 10 时 disc = 81,r = 9,(9 - 1) / 2 = 4 成立;但若误取 r = 1(虽不满足 r * r == disc),(1 - 1) / 2 = 0 就不是正整数。
-
(r - 1) % 2 == 0确保能整除,避免n为半整数 -
(r - 1) / 2 > 0确保n ≥ 1,排除x = 0(T₀ = 0通常不算三角数) - 这两个条件缺一不可:漏掉
% 2检查,x = 2(disc = 17非平方数,本不该过,但若误判r=5则(5-1)/2=2会错报 true)
完整可移植实现(支持 int 和 long long 输入)
以下函数处理了溢出、浮点误差、边界值三类问题,无分支预测干扰,单次 sqrt 调用:
bool isTriangular(long long x) {
if (x (disc)));
for (long long r = r0 - 1; r = 0 && r * r == disc) {
return (r - 1) % 2 == 0 && (r - 1) / 2 > 0;
}
}
return false;
}
真正容易被忽略的是:disc 的整数平方性只是必要条件,n 是否为正整数才是判定终点——所有校验必须串行执行,少一步就可能把 2、4、5 这类非三角数错判为真。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











