应使用浮点误差容限判断点是否在弧线上:先通过平方距离与半径平方比较并引入比例化epsilon(eps*(r_sq+eps))避免开方误差和尺度敏感问题,再将点角度归约到[0,2π)后按跨周期区间逻辑判断是否落在起始角与终止角之间,同时处理负半径等边界情况。

用浮点误差容忍度代替“精确位于”
计算机里不存在数学意义上的“精确位于弧线”,因为 double 或 float 存在舍入误差。直接判断距离是否等于半径(dist == r)几乎永远为假。必须引入误差容限(epsilon),检查点到圆心距离与半径的差值是否落在可接受范围内。
- 典型容限选
1e-9(对double坐标常见精度足够);若坐标值很大(如 1e6 量级),需按比例放大 epsilon,例如1e-9 * max(1.0, abs(cx), abs(cy), r) - 先算平方距离避免开方——
(px - cx) * (px - cx) + (py - cy) * (py - cy),再和r * r比较,既快又少一步 sqrt 误差 - 仅满足距离条件还不够:点还必须落在指定的弧段上(起始角到终止角之间),否则只是在整圆上,而非目标弧线上
如何判断角度是否落在给定弧段区间内
弧线由圆心、半径、起始角 start_angle、终止角 end_angle(单位通常为弧度)定义。但角度是周期性的,且弧可能跨过 2π(比如从 3π/2 到 π/4),不能简单用 start_angle 判断。
- 统一把所有角度归约到
[0, 2*M_PI):用fmod(angle + 2*M_PI, 2*M_PI)处理负角和超界角 - 若
start_angle ,则弧不跨零点,直接判断归约后的 <code>angle是否在区间内 - 若
start_angle > end_angle,说明弧跨越 2π,此时合法角度是[start_angle, 2*M_PI)∪[0, end_angle],用逻辑或判断 - 注意:
atan2(py - cy, px - cx)返回的是[-M_PI, M_PI],需先加M_PI再模2*M_PI才能对齐到[0, 2*M_PI)
完整判断逻辑的 C++ 实现要点
把距离检查和角度区间检查组合起来,二者必须同时成立才算“在弧线上”。下面是一个最小可用片段的核心逻辑:
#include <cmath>
bool is_on_arc(double px, double py,
double cx, double cy, double r,
double start_angle, double end_angle,
double eps = 1e-9) {
double dx = px - cx, dy = py - cy;
double dist_sq = dx*dx + dy*dy;
double r_sq = r * r;
// 距离检查(用平方避免 sqrt)
if (std::abs(dist_sq - r_sq) > eps * (r_sq + eps)) return false;
<pre class='brush:php;toolbar:false;'>// 角度计算与归约
double angle = std::atan2(dy, dx); // [-π, π]
angle = std::fmod(angle + 2*M_PI, 2*M_PI); // → [0, 2π)
// 归约 start/end
auto norm = [](double a) { return std::fmod(a + 2*M_PI, 2*M_PI); };
double sa = norm(start_angle);
double ea = norm(end_angle);
// 区间判断
if (sa <= ea) {
return angle >= sa - eps && angle <= ea + eps;
} else {
return angle >= sa - eps || angle <= ea + eps;
}}
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 距离误差项用了
eps * (r_sq + eps),比固定eps更鲁棒,兼顾小半径和大坐标场景 - 角度容限也加在边界上(
sa - eps和ea + eps),避免因归约或 atan2 精度导致刚好卡在端点外 - 若弧是闭合的(即包含端点),当前写法已覆盖;若需开弧(排除端点),把
>=/<=改成>/<
容易被忽略的边界情况
实际使用时最容易栽在几个非显性陷阱上:
- 输入半径为负:应提前
if (r ,否则 <code>r_sq仍为正,逻辑错乱 - 起始角与终止角完全相等:此时弧退化为一个点,需额外判断
angle是否在该点角度 ±eps 内,且距离也满足——但多数业务中这种输入应视为非法,建议前置校验 - 点与圆心重合(
dx=dy=0):此时atan2(0,0)返回 0(C++ 标准规定),但该点不在任何非零半径的弧线上,距离检查会失败,无需特殊处理 - 使用
float时 epsilon 必须增大到1e-5左右,否则几乎总失败
真正难的不是公式,而是想清楚“你嘴里的‘弧线’到底指什么:是数学定义?还是绘图 API(如 Cairo、Skia)实际绘制的像素路径?后者还涉及采样、抗锯齿和设备坐标映射,那已经不是纯几何问题了。

















