Apache Commons Geometry 配置参数详解
详解 Apache Commons Geometry 1.0 的精度与容差体系(Precision.EPSILON、DoubleEquivalence、绝对误差比较)及 Maven 坐标、编译目标、JVM 堆等工程参数,均附真实回显验证。
Apache Commons Geometry 配置参数详解
目标:系统梳理 Apache Commons Geometry 1.0 的全部可配置项——几何精度/容差(
Precision.EPSILON、DoubleEquivalence、绝对/相对误差比较)、构造参数与工程侧 Maven/JVM 参数,并给出每个参数的真实运行回显。以下命令与输出均已在 Ubuntu 22.04(JDK 17.0.20.1 + Maven 3.6.3)实测通过。
一、环境信息
本节先固定本次实测所用软硬件基线,便于读者逐项对照复现。
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- JDK:OpenJDK 17.0.20.1
- 构建工具:Apache Maven 3.6.3
- 目标库:Apache Commons Geometry 1.0(
org.apache.commons:commons-geometry-euclidean:1.0) - 示例工程目录:
/opt/geo-demo(含GeometryCheck.java与demo/PrecisionDemo.java等) - 依赖 classpath 文件:
/opt/geo-demo/cp.txt(由mvn dependency:build-classpath生成)
Apache Commons Geometry 是纯 Java 库,没有独立的配置文件。它的“配置”分为三类,本文按此展开:
| 配置类别 | 载体 | 典型参数 | 生效方式 |
|---|---|---|---|
| 几何精度/容差 | 代码(构造器参数) | Precision.EPSILON、Precision.doubleEquivalenceOfEpsilon(...) | 创建 Line/Plane/Area 等对象时传入 |
| 数值比较 | 代码(静态方法) | Precision.equals(...)、compareTo(...)、round(...) | 直接调用 |
| 工程侧 | pom.xml / JVM 启动参数 | maven.compiler.source、-Xmx | 构建与运行期 |
二、前置条件
本文所有示例均为代码级演示,需要先具备可编译运行 Geometry 的工程。满足以下条件即可:
- JDK 17(Geometry 1.0 要求 Java 8+,本文用 17):
java -version实测为17.0.20.1。 - Maven 3.6.3:用于解析依赖与编译。
- 依赖已就绪:工程
pom.xml已引入commons-geometry-euclidean:1.0,详见《Ubuntu 22.04 安装 Apache Commons Geometry 指南》第 4.3 节。
验证依赖可用(列出传递依赖树):
cd /opt/geo-demo
mvn -B dependency:tree
输出节选(实测首尾):
[INFO] \- org.apache.commons:commons-geometry-euclidean:jar:1.0:compile
[INFO] +- org.apache.commons:commons-geometry-core:jar:1.0:compile
[INFO] +- org.apache.commons:commons-numbers-core:jar:1.0:compile
[INFO] +- org.apache.commons:commons-numbers-arrays:jar:1.0:compile
[INFO] +- org.apache.commons:commons-numbers-angle:jar:1.0:compile
[INFO] \- org.apache.commons:commons-numbers-quaternion:jar:1.0:compile
[INFO] BUILD SUCCESS
精度相关类全部位于 commons-numbers-core(Geometry 的精度依赖),坐标 org.apache.commons:commons-numbers-core:1.0。
三、参数查看与修改方式
几何精度参数在代码中传入,没有命令行查看入口。本库唯一的“参数清单”就是 Precision 类的公开 API,可用 javap 直接查看真实签名,作为修改依据。
查看 Precision 类的公开常量与方法:
javap -cp /opt/geo-jars/commons-numbers-core-1.0.jar org.apache.commons.numbers.core.Precision
输出:
Compiled from "Precision.java"
public final class org.apache.commons.numbers.core.Precision {
public static final double EPSILON;
public static final double SAFE_MIN;
public static int compareTo(double, double, double);
public static int compareTo(double, double, int);
public static boolean equals(float, float);
public static boolean equalsIncludingNaN(float, float);
public static boolean equals(float, float, float);
public static boolean equalsIncludingNaN(float, float, float);
public static boolean equals(float, float, int);
public static boolean equalsIncludingNaN(float, float, int);
public static boolean equals(double, double);
public static boolean equalsIncludingNaN(double, double);
public static boolean equals(double, double, double);
public static boolean equalsWithRelativeTolerance(double, double, double);
public static boolean equalsIncludingNaN(double, double, double);
public static boolean equals(double, double, int);
public static boolean equalsIncludingNaN(double, double, int);
public static double round(double, int);
public static double round(double, int, java.math.RoundingMode);
public static double representableDelta(double, double);
public static org.apache.commons.numbers.core.Precision$DoubleEquivalence doubleEquivalenceOfEpsilon(double);
static {};
}
查看 DoubleEquivalence(几何对象内部使用的比较器接口)的方法签名:
javap -cp /opt/geo-jars/commons-numbers-core-1.0.jar org.apache.commons.numbers.core.Precision\$DoubleEquivalence
输出:
public interface org.apache.commons.numbers.core.Precision$DoubleEquivalence {
public default boolean eq(double, double);
public default boolean eqZero(double);
public default boolean lt(double, double);
public default boolean lte(double, double);
public default boolean gt(double, double);
public default boolean gte(double, double);
public default double signum(double);
public abstract int compare(double, double);
}
要点:javap 输出即“参数清单”的权威来源。几何对象(Line、Plane、Segment、ConvexArea 等)的构造器/工厂方法都接收一个 Precision.DoubleEquivalence 参数,传入不同的容差即修改几何判定行为。
四、参数总览
本文涉及的参数汇总如下,后文逐类详解:
| 参数/常量 | 类型 | 默认值(实测) | 作用 |
|---|---|---|---|
Precision.EPSILON | double 常量 | 1.1102230246251565E-16 | 机器精度阈值(2^-53),equals 无参比较的默认容差 |
Precision.SAFE_MIN | double 常量 | 2.2250738585072014E-308 | 最小安全规格化数,representableDelta 的下限保护 |
Precision.doubleEquivalenceOfEpsilon(eps) | 工厂方法 | 无(参数必填) | 构造基于绝对误差的 DoubleEquivalence |
DoubleEquivalence.eq / eqZero / lt / lte / gt / gte / signum / compare | 接口方法 | 由 eps 决定 | 几何比较谓词 |
Precision.equals(double, double, double) | 静态方法 | 第三个参数必填 | 绝对误差比较 |
Precision.equalsWithRelativeTolerance(...) | 静态方法 | 参数必填 | 相对误差比较 |
Precision.compareTo(double, double, double) | 静态方法 | 参数必填 | 带容差的三态比较 |
Precision.round(double, int) | 静态方法 | 参数必填 | 按小数位四舍五入 |
Precision.representableDelta(double, double) | 静态方法 | 参数必填 | 计算给定目标下可表示的增量 |
maven.compiler.source / target | pom 属性 | 工程自定(本文 17) | 编译目标字节码版本 |
-Xmx | JVM 参数 | 默认取机器可用堆 | 限制堆内存(本文示例 256 MB) |
💡 技术拓展:浮点比较策略不止 Commons Numbers 的“绝对误差”一种。常见策略对比——
策略 原理 适用场景 绝对误差 |a-b| ≤ eps 数值量级已知且稳定 相对误差 |a-b| ≤ max(|a|,|b|)·eps 数值量级跨度大(如 1e-6 与 1e6 混算) ULP 误差 |a-b| ≤ n·ulp 与二进制表示精度对齐 精确比较 a == b 无误差传播的整数值 本文默认采用绝对误差(
doubleEquivalenceOfEpsilon),因为几何示例坐标量级小且稳定;若坐标跨量级混算,可改用Precision.equalsWithRelativeTolerance。
五、核心参数分类详解
5.1 精度常量:EPSILON 与 SAFE_MIN
Precision.EPSILON 是机器精度阈值,等于 2^-53(约 1.11e-16),是 Math.ulp(1.0) 的一半;SAFE_MIN 是最小规格化正数。实测输出:
cd /opt/geo-demo
java -Xmx256m -cp "target/classes:$(cat cp.txt)" demo.PrecisionDemo
输出节选:
[const] Precision.EPSILON = 1.1102230246251565E-16
[const] Precision.SAFE_MIN = 2.2250738585072014E-308
[eps] eps=1e-10 eq(1.0,1.0) = true , eps=1e-4 eq(1.0,1.0) = true
解读:EPSILON 用作“无参数默认容差”的参考基准;实际使用中按业务误差预算选择更大的容差(如 1e-10、1e-6)。SAFE_MIN 主要在 representableDelta 内部作为下界保护,一般不需要手动设置。
5.2 容差工厂:doubleEquivalenceOfEpsilon
所有几何对象都通过 Precision.doubleEquivalenceOfEpsilon(eps) 获得比较器,eps 决定“两个数相差多少算相等”。实测对比不同容差对点相等判断的影响:
java -Xmx256m -cp "target/classes:$(cat cp.txt)" demo.PrecisionDemo
输出节选:
[pt-eq-strict] 1,1 vs 1,1.00001 = false
[pt-eq-loose] 1,1 vs 1,1.00001 = true
[zero-strict] eqZero(1e-5,0) = false
[zero-loose] eqZero(1e-5,0) = true
解读:同一对点 (1,1) 与 (1, 1.00001),容差 1e-10 时判定不等,容差 1e-4 时判定相等;同理 1e-5 在严格容差下不算“零”、在宽松容差下算“零”。容差越大,越多的浮点噪声被吞掉,但过大会把本应区分的位置合并,需按业务坐标量级选择。
5.3 DoubleEquivalence 谓词:eqZero / lt / gte / signum / compare
比较器除 eq 外还提供带容差的大小判断与符号函数。实测输出:
java -Xmx256m -cp "target/classes:$(cat cp.txt)" demo.Precision3Demo
输出:
[signum] signum(-2.5) = -1.0
[signum] signum(0.0) = 0.0
[lt] lt(1.0, 1.000001)= false
[gte] gte(1.0, 1.000001)= true
[cmp] compare(1.0, 1.000001) = 0
解读(容差 1e-6):1.0 与 1.000001 的差正好落在容差边界内,因此 lt 为 false、gte 为 true、compare 返回 0(视为相等)。若差值大于容差,compare 返回 -1/1。这类谓词被 Geometry 内部用于“点是否在直线上”“两线是否平行”等判定。
5.4 静态比较方法:equals / equalsWithRelativeTolerance / compareTo
不构造对象时可直接用静态方法做数值比较,支持绝对误差与相对误差两种口径。实测输出:
java -Xmx256m -cp "target/classes:$(cat cp.txt)" demo.Precision2Demo
输出:
[abs] equals(1.0, 1.000000000001, 1e-9) = true
[abs] equals(1.0, 1.000000000001, 1e-12) = false
[rel] equalsWithRelativeTolerance(1000000.0, 1000001.0, 1e-6) = true
[cmp] compareTo(1.0, 1.000000001, 1e-6) = 0
[cmp] compareTo(1.0, 1.000000001, 1e-10) = -1
[rd] round(1.23456789, 4) = 1.2346
[ulp] Math.ulp(1.0) = 2.220446049250313E-16
解读:
equals(a, b, eps):绝对误差比较,1.0与1.000000000001在容差 1e-9 下相等、在 1e-12 下不等。equalsWithRelativeTolerance(a, b, eps):相对误差比较,1000000.0与1000001.0相差 0.0001%,小于相对容差 1e-6(即 0.0001%),判定相等——这种口径适合大数比较。compareTo(a, b, eps):三态比较,返回 -1/0/1。round(d, n):按小数位四舍五入。
5.5 representableDelta:可表示的增量
representableDelta(reference, delta) 返回“在 reference 量级下实际可表示的 delta 步长”,用于理解浮点表示对容差的下限影响。实测输出:
[repr] representableDelta(1.0, 1e-3) = 9.999999999998899E-4
[repr] representableDelta(1e6, 1e-3) = 0.0010000000474974513
[ulpE6] Math.ulp(1000000.0) = 1.1641532182693481E-10
解读:在 1.0 量级,1e-3 可被精确表示;在 1e6 量级,由于 ulp(1e6) ≈ 1.16e-10,1e-3 会被量化到最近的表示值(0.0010000000474974513)。坐标越大,浮点表示越粗,这是几何计算在大坐标场景下需要放宽容差的原因。
5.6 构造参数:precision 传入几何对象
Line/Plane/Segment/ConvexArea 等全部通过工厂方法接收 precision。以 2D 直线为例,平行判定直接受容差影响。实测输出:
java -Xmx256m -cp "target/classes:$(cat cp.txt)" demo.PrecisionDemo
输出节选:
[parallel-loose] isParallel = true
[parallel-strict] isParallel = false
[side] offset(0,5) = -5.0
[side] offset(0,-5)= 5.0
解读:两条相差 1e-5 的“几乎平行”直线,在容差 1e-4 下判定平行,在容差 1e-10 下判定不平行;offset 的符号表示点在直线的哪一侧。3D 的 Planes.fromPoints、Planes.fromPointAndNormal 同样接收 precision 参数,行为一致。
5.7 工程侧参数:Maven 编译目标与编码
pom.xml 中的 maven.compiler.source/target 决定编译字节码版本(本文 17),project.build.sourceEncoding 决定源码编码:
grep -E "compiler.source|compiler.target|sourceEncoding" /opt/geo-demo/pom.xml
输出:
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
解读:source/target=17 需 JDK 17 编译;UTF-8 保证含中文注释的源码可正常编译,否则 javac 会报 unmappable character。
5.8 JVM 运行参数
Geometry 库本身轻量,运行期通过 -Xmx 控制堆内存。本文示例统一使用 256 MB 堆:
java -Xmx256m -cp "target/classes:$(cat cp.txt)" demo.Geometry2DDemo
输出节选(实测首尾):
[v-add] a.add(b) = (4.0, 6.0)
...
[region-u] union area = 18.0
解读:-Xmx256m 将最大堆限制为 256 MB,对几何示例(少量对象)完全够用且避免默认堆过大浪费内存;生产按数据规模可放宽到 512 MB。
六、生产配置模板
按“16GB 内存服务器、普通磁盘、批量几何计算服务”场景给出推荐配置:
| 配置项 | 推荐值 | 理由 |
|---|---|---|
| JDK | 17 LTS | 长期支持,Geometry 1.0 实测正常 |
| Maven 编译目标 | 17 | 与 JDK 一致 |
| 几何容差 | 1e-10(坐标 ≤ 1e4);1e-6(坐标 ≥ 1e5) | 平衡噪声与精度,见 5.5 |
| 大数比较 | equalsWithRelativeTolerance(a, b, 1e-6) | 避免绝对误差误判 |
| JVM 堆 | -Xms256m -Xmx512m | 服务型进程常见下限/上限 |
| 源码编码 | UTF-8 | 中文环境必需 |
将容差、坐标量级与堆参数写入工程的方式:
mkdir -p /opt/geo-app/src/main/java
示例 pom.xml 关键片段:
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-geometry-euclidean</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
启动脚本中固定堆参数:
java -Xms256m -Xmx512m -cp "target/classes:$(cat cp.txt)" demo.Geometry2DDemo
七、常见问题(FAQ)
Q1:容差选多大合适?
没有通用值,按坐标量级定:小坐标(如 < 1e4)用 1e-10;大坐标(如经纬度 1e6 量级)用 1e-6 或更大,否则 representableDelta 会放大误差(见 5.5 实测)。先跑一轮真实数据,把误判比例降到业务可接受范围。
Q2:Precision.DOUBLE 为什么编译报错?
Commons Numbers 1.0 的 Precision 类没有 DOUBLE 常量,只有 EPSILON(1.1102230246251565E-16)与 SAFE_MIN。需要“默认机器精度”时直接用 Precision.doubleEquivalenceOfEpsilon(Precision.EPSILON)。可用 javap 核对类成员(见三章)。
Q3:两个点明明相等,eq 却返回 false?
大概率是容差太小或坐标量级太大。先打印两者差值并与容差对比;大坐标场景改用相对误差比较(5.4 节)。
Q4:mvn compile 报 invalid target release: 17?
Maven 运行在低于 17 的 JDK 上。确认 mvn -version | grep "Java version",若为 8,设 JAVA_HOME 指向 JDK 17(CentOS 8 常见,见《CentOS 8 安装 Apache Commons Geometry 指南》Q1)。
Q5:运行时报 NoClassDefFoundError: Precision?
classpath 缺 commons-numbers-core。Maven 工程用 mvn dependency:build-classpath 生成完整 classpath;手动 jar 需补齐 5 个 commons-numbers-* 依赖。
八、参考链接
- Apache Commons Geometry 官方站点:https://commons.apache.org/proper/commons-geometry/
- Commons Numbers Precision 文档:https://commons.apache.org/proper/commons-numbers/javadocs/api-release/org/apache/commons/numbers/core/Precision.html
- Maven 中央仓库 commons-numbers-core:https://repo1.maven.org/maven2/org/apache/commons/commons-numbers-core/
- Apache Commons Geometry 用户指南:https://commons.apache.org/proper/commons-geometry/userguide/index.html