Skip to content

浮点数与精度问题 ​

特殊值: ​

  • +0.0 -0.0
  • 在常见的 IEC 60559/IEEE 754 浮点实现中,非零数除以 ±0.0 可能得到 ±inf,对负数开平方等无效运算可能得到 NaN。
  • NaN 与任何数(包括自身)做有序比较或相等比较都返回 false,只有 != 返回 true。

这些行为依赖实现和浮点环境,不能把主动除以零或制造 NaN 当作可移植的分支技巧。应在运算前检查分母、方向向量和定义域,必要时再使用 std::isfinite、std::isinf、std::isnan 检查结果。

  1. 若问题能用整数解决则不用浮点数
  2. 除非时限紧张,否则使用 long double
  3. 减少数学库函数的调用
  4. 进行浮点数比较时,加入容限(误差)eps

下文公式中的 =0、>0 和 <0,若使用浮点数实现,均应通过与数据尺度匹配的误差比较完成;若输入是整数,点积、叉积等判定可使用足够宽的整数类型精确计算,并注意中间乘法溢出。

点 ​

(x,y),横坐标:x,纵坐标:y。

向量: ​

坐标系中一个点对应一个向量,通常使用向量表示一个点。

点积: ​

点积是一个值:

​a→⋅b→=axbx+ayby

​几何意义:

a→⋅b→=|a→|×|b→|×cos⁡θ
  • 向量的长度:|a→|=a→⋅a→
  • 向量的夹角:cos⁡θ=a→⋅b→|a→|×|b→|,要求 a→,b→ 均为非零向量
  • a→ 在 b→ 方向上的标量投影:a→⋅b→|b→|,要求 b→≠0→;当 a→,b→ 均非零时,它也等于 |a→|cos⁡θ
  • a→ 在 b→ 上的向量投影:projb→a→=a→⋅b→b→⋅b→b→,要求 b→≠0→
  • 向量垂直:a→⋅b→=0

叉积: ​

向量的叉积仅在 2,3 维坐标系中存在,更高维通常没有意义。

  • 2 维是一个标量:a→×b→=axby−aybx
  • 3 维是一个向量:a→×b→=(aybz−azby,−axbz+azbx,axby−aybx)

​几何意义:

a→×b→=|a→|×|b→|×sin⁡θk^
  • 平行四边形面积:|a→×b→|=|a→|×|b→|×|sin⁡θ|
  • 向量平行:a→×b→=0→
  • to-left 测试

to-left 测试 ​

​判断点 P 在有向直线 AB 左侧/右侧上。

{AB→×AP→>0P 在有向直线 AB 左侧AB→×AP→<0P 在有向直线 AB 右侧AB→×AP→=0P 在有向直线 AB 上

向量逆时针旋转 ​

​a→ 逆时针旋转 θ:

(ax,ay)→(cos⁡θax−sin⁡θay,sin⁡θax+cos⁡θay)

线段 ​

a→,b→ 记录线段两端点。

判断点是否在线段上: ​

  • 点 P 在 AB 所在直线上:PA→×PB→=0
  • 点 P 在 AB 之间:
    • 若线段包含端点,则 PA→⋅PB→≤0;若只判断开线段内部,才使用 <0
    • min(Ax,Bx)≤Px≤max(Ax,Bx)∧min(Ay,By)≤Py≤max(Ay,By)

判断两条线段是否相交: ​

  • 若只判断严格相交,则要求点 A 和点 B 在直线 CD 的严格不同侧,且点 C 和点 D 在直线 AB 的严格不同侧。
  • 若把端点接触和共线重叠也视为相交,还要分别判断四个端点是否落在另一条线段上;点在线段上的判定使用上一节的闭区间条件。

直线 ​

通常使用点向式表示:直线上的一点 P 和方向向量 v→ 表示一条直线,参数方程为 X→=P→+tv→。方向向量必须满足 v→≠0→。

求点 A 到直线的距离: ​

d=|v→×PA→||v→|

求点 A 在直线上的投影点 B: ​

B→=P→+(A→−P→)⋅v→v→⋅v→v→,v→≠0→.

两直线交点: ​

设两条直线分别为 P→1+tv→1 和 P→2+sv→2,且两个方向向量均非零。令

D=v→1×v→2.

当 D≠0 时,两直线有唯一交点,参数和交点分别为

t=(P→2−P→1)×v→2D=v→2×(P→1−P→2)D,Q→=P→1+tv→1.

参数 t 可能为负,不能对分子取绝对值。当 D=0 时:若 (P→2−P→1)×v→1≠0,两直线平行且不重合;否则两直线重合,没有唯一交点。浮点实现中可按向量长度缩放容限,例如比较 |D| 与 ε|v→1||v→2|,而不是先执行除法。接口应分别返回“唯一交点、平行、重合、方向无效”等状态,不能依赖除以零产生的 inf 或 NaN 区分。

多边形 ​

由点集表示。

  • 一般按逆时针顺序
  • 不一定满足凸性
  • 注意第一个点与最后一个点的处理

多边形的面积: ​

先定义有向二倍面积

S2=∑i=0n−1P→i×P→(i+1)modn.

多边形的普通面积为

S=|S2|2.

特别地,三角形的面积:|a→×b→|2

多边形的方向: ​

在标准笛卡尔坐标系中,若顶点沿一个不自交多边形的边界依次给出,则:

  • S2>0:逆时针;
  • S2<0:顺时针;
  • S2=0:退化,不能据此确定方向。

对自交多边形,S2/2 表示带符号的代数面积,不能直接用来描述整个图形的普通面积或统一方向。

判断点是否在多边形内 ​

射线法 ​

从查询点 P 向 x 轴正方向引出射线。首先使用“点在线段上”的闭区间判定检查 P 是否落在任意一条边上;边界点可以单独返回 BOUNDARY,也可以按题意统一归入内部或外部。

对非边界点,边 AB 仅在以下两种情况之一成立时贡献一次穿越:

  • Ay≤Py<By 且 AB→×AP→>0;
  • By≤Py<Ay 且 AB→×AP→<0。

这相当于纵坐标区间包含较低端点、排除较高端点,水平边不计数。射线经过普通穿越顶点时贡献一次;经过局部极小点时两条边均贡献,经过局部极大点时两条边均不贡献,因此不会错误改变交点数的奇偶性。穿越次数为奇数时 P 在奇偶填充意义下位于内部,否则位于外部。

回转数法 ​

回转数:闭合曲线绕查询点的有向总圈数。

​遵循非零规则:当回转次数为 0 时,点在曲线外部。

  • 一种实现方法:计算相邻两边夹角(有方向)的和。
  • 另一种实现方法是沿用射线法的半开规则:向上穿越贡献 +1,向下穿越贡献 −1。这种写法避免了反三角函数;只有在坐标为整数、叉积使用足够宽的整数类型且中间运算不溢出时,方向判定才没有浮点误差。浮点坐标仍需使用稳健的误差比较。

对简单多边形,奇偶规则与非零回转数规则给出相同的内外结果;对自交多边形,两者分别对应奇偶填充和非零填充,结果可能不同。

判断点是否在凸多边形内 ​

  • n 次 to-left 测试 O(n)。

  • 二分 O(log⁡n)。

上述方法要求多边形凸、非退化,且顶点按同一方向沿边界给出;二分实现还需单独约定点落在边或顶点上的返回结果。