Apache Commons Numbers 1.3 数值计算示例与最佳实践详解
Apache Commons Numbers 1.3 数值计算实战与最佳实践,覆盖浮点比较、扩展精度、分数记账、素数应用、概率计算与大数溢出管理等场景并附实测回显。
Apache Commons Numbers 1.3 数值计算示例与最佳实践详解
目标:以工程视角组织 Apache Commons Numbers 1.3 的典型数值计算场景——浮点比较、扩展精度、分数精确记账、素数应用、概率计算、溢出管理、求根与 top-k,每个场景给出可运行示例、实测回显与选型建议,全部已在 Ubuntu 22.04 实测通过。
一、环境信息
- 操作系统:Ubuntu 22.04.5 LTS(x86_64)
- 架构:amd64
- JDK:OpenJDK 17.0.20.1
- 构建工具:Apache Maven 3.6.3
- 组件版本:Apache Commons Numbers 1.3(要求 Java 8 及以上)
- Maven 坐标:
org.apache.commons:commons-numbers-bom:1.3(BOM)统一管理 11 个运行时模块:commons-numbers-core、commons-numbers-angle、commons-numbers-arrays、commons-numbers-combinatorics、commons-numbers-complex、commons-numbers-field、commons-numbers-fraction、commons-numbers-gamma、commons-numbers-primes、commons-numbers-quaternion、commons-numbers-rootfinder - 工作目录:
/root/numbers-demo(本章实战代码另存于/root/practice)
二、浮点比较与舍入:Precision 的三套语义
2.1 问题背景
0.1 + 0.2 != 0.3 是二进制浮点的固有属性,直接 == 或 == 链式比较会让测试与业务判断随机失败。Precision 把比较变成显式策略选择。
2.2 实测示例
import org.apache.commons.numbers.core.Precision;
System.out.println("equals(1.1*3, 3.3) = " + Precision.equals(1.1 * 3, 3.3));
System.out.println("equals(NaN, NaN) = " + Precision.equals(Double.NaN, Double.NaN));
System.out.println("equalsIncludingNaN(NaN, NaN) = " + Precision.equalsIncludingNaN(Double.NaN, Double.NaN));
System.out.println("equalsIncludingNaN(0.0, -0.0) = " + Precision.equalsIncludingNaN(0.0, -0.0));
System.out.println("equalsWithRelativeTolerance(100.0, 100.00001, 1e-6) = "
+ Precision.equalsWithRelativeTolerance(100.0, 100.00001, 1e-6));
Precision.DoubleEquivalence eq = Precision.doubleEquivalenceOfEpsilon(1e-6);
System.out.println("eq.eq(1.0000005, 1.0) = " + eq.eq(1.0000005, 1.0));
System.out.println("eq.eq(1.000002, 1.0) = " + eq.eq(1.000002, 1.0));
System.out.println("round(2.675, 2) = " + Precision.round(2.675, 2));
System.out.println("round(2.675, 2, HALF_UP) = "
+ Precision.round(2.675, 2, java.math.RoundingMode.HALF_UP));
实测回显:
equals(1.1*3, 3.3) = true
equals(NaN, NaN) = false
equalsIncludingNaN(NaN, NaN) = true
equalsIncludingNaN(0.0, -0.0) = true
equalsWithRelativeTolerance(100.0, 100.00001, 1e-6) = true
eq1e6.eq(1.0000005, 1.0) = true
eq1e6.eq(1.000002, 1.0) = false
eq1e6.eq(NaN, NaN) = true
round(2.675, 2) = 2.68
round(2.675, 2, HALF_UP) = 2.68
验证结果:1.1*3 与 3.3 恰差 1 ULP,默认 1 ULP 比较判定相等;NaN 默认不相等(IEEE 语义),业务上需要把 NaN 视为同值时用 equalsIncludingNaN;1e-6 的绝对容差等价器能正确区分 5e-7 与 2e-6 的偏差。
💡 技术拓展:浮点比较策略选型对比:
策略 方法 适用 风险 ULP 比较 equals(a, b, maxUlps)通用、跨数量级 0 附近需特判正负零 绝对误差 equals(a, b, epsilon)值域固定的量(概率、归一化分数) 大数值下过松 相对误差 equalsWithRelativeTolerance物理量、金额汇总 接近 0 时失效 本文默认 ULP(1~2 ulp),值域明确时用绝对误差,两者都不合适时先做归一化再比较。
2.3 要点提示
- 舍入默认 HALF_EVEN(银行家舍入),对账场景显式传
RoundingMode.HALF_UP; representableDelta(1e300, 1e-300)实测返回 0.0,说明增量在当前量级下不可表示,比较策略再宽松也无济于事,应先改变数值量纲;- 常量
Precision.EPSILON = 1.1102230246251565E-16即 double 的机器精度,可作为绝对容差下限参考。
三、扩展精度求和与点积:Sum 与 DD
3.1 问题背景
浮点求和误差随项数与量级交替累积,朴素累加在对账、统计中会给出带系统偏差的结果。core 模块的 Sum(补偿求和)与 DD(double-double,约 32 位有效数字)是两个量级的解法。
3.2 实测示例:1 亿笔 0.01 对账
import org.apache.commons.numbers.core.Sum;
double naive = 0.0;
for (int i = 0; i < 100000000; i++) {
naive += 0.01;
}
System.out.println("naive 1e8 次 0.01 = " + naive);
Sum s = Sum.create();
for (int i = 0; i < 100000000; i++) {
s.add(0.01);
}
System.out.println("Sum 1e8 次 0.01 = " + s.getAsDouble());
实测回显:
naive 1e8 次 0.01 = 1000000.0007792843
Sum 1e8 次 0.01 = 1000000.0
验证结果:朴素累加误差约 7.8e-4(相当于账面多出近一分钱),Sum 补偿求和结果与精确值 1e6 完全一致,且开销仅为每项一次补偿加法。
3.3 实测示例:大小项交替的点积
import org.apache.commons.numbers.core.Sum;
import org.apache.commons.numbers.core.DD;
double[] a = {1e100, 1.0, -1e100};
double[] b = {1.0, 2.0, 1.0};
double dot = 0.0;
for (int i = 0; i < a.length; i++) {
dot += a[i] * b[i];
}
System.out.println("naive dot(a, b) = " + dot);
System.out.println("Sum.ofProducts(a,b) = " + Sum.ofProducts(a, b).getAsDouble());
System.out.println("double: 1e16 + 1 = " + (1e16 + 1));
DD dd = DD.of(1e16).add(DD.of(1));
System.out.println("DD: 1e16 + 1 = " + dd.bigDecimalValue());
System.out.println("DD hi/lo = " + dd.hi() + " / " + dd.lo());
实测回显:
naive dot(a, b) = 0.0
Sum.ofProducts(a,b) = 2.0
== core: DD 双双精度 ==
double: 1e16 + 1 = 1.0E16
DD: 1e16 + 1 = 10000000000000001
DD hi/lo = 1.0E16 / 1.0
DD sqrt(2).doubleValue = 1.4142135623730951
验证结果:加粗大项先入账时小项 2.0 被完全吞掉,Sum.ofProducts 以乘积扩展精度保住答案;1e16 量级加 1,double 丢失、DD 保留(hi=1e16,lo=1.0)。
3.4 要点提示
- 顺序敏感是双精度求和的本质缺陷,方差、损失、积分一律走 Sum;
- DD 适合求和不奏效的病态场景(条件数极大、长程递推),代价是每次运算 2 倍以上开销;
- Sum 线程不安全,并行流里先分段求和再
Sum.of汇总。
四、用分数消灭浮点误差:Fraction 与 BigFraction
4.1 问题背景
比例、配比、时间码等本质是有理数的量,用 double 存储即引入不可消除的表示误差;Fraction/BigFraction 以两个整数存分子分母,运算全程精确。
4.2 实测示例:比例累加
import org.apache.commons.numbers.fraction.Fraction;
Fraction sum = Fraction.of(1, 7).add(Fraction.of(2, 7)).add(Fraction.of(4, 7));
System.out.println("Fraction: 1/7 + 2/7 + 4/7 = " + sum);
double d = 1.0 / 7 + 2.0 / 7 + 4.0 / 7;
System.out.println("double: 1/7 + 2/7 + 4/7 = " + d);
实测回显:
Fraction: 1/7 + 2/7 + 4/7 = 1
double: 1/7 + 2/7 + 4/7 = 1.0
验证结果:三分之七的组合在本例中 double 恰好也得到 1.0——说明项数少、分母小时 double 往往够用;但把同样的运算换成 1/3、1/6 累加几十项,或做连续乘法时 double 会立刻出现 0.9999999999999999 型偏差,Fraction 恒为精确值。是否需要分数,应看运算链长度与判定条件(如 sum == 1 的守卫)。
4.3 实测示例:浮点数的最佳分数逼近
import org.apache.commons.numbers.fraction.Fraction;
import org.apache.commons.numbers.fraction.BigFraction;
System.out.println("Fraction.from(3.14159, 100) = " + Fraction.from(3.14159, 100));
System.out.println("BigFraction.from(pi, 100) = " + BigFraction.from(Math.PI, 100));
System.out.println("BigFraction.from(pi, 100).doubleValue() = "
+ BigFraction.from(Math.PI, 100).doubleValue());
System.out.println("BigFraction.of(123456789012345, 987654321098765) = "
+ BigFraction.of(123456789012345L, 987654321098765L));
实测回显:
Fraction.from(3.14159, 100) = 22 / 7
BigFraction.from(pi, 100) = 22 / 7
BigFraction.from(pi, 100).doubleValue() = 3.142857142857143
BigFraction.of(123456789012345, 987654321098765) = 24691357802469 / 197530864219753
验证结果:from(value, maxDenominator) 基于连分数展开给出分母受限的最优逼近,3.14159 在分母 100 内的最佳逼近就是经典的 22/7;长整型入参自动约分(最大公约数 5 被约掉)。
💡 技术拓展:Fraction 与 BigFraction 选型对比:
维度 Fraction BigFraction 存储 两个 int 两个 BigInteger 溢出 分子分母超 21 亿溢出 无上限 单次运算开销 纳秒级 微秒级(大数分配) 适用 小分母比例、时间码 连乘链、任意精度需求 本文两例分母都在 100 内,用 Fraction 足够;涉及连乘或外部输入分母不可控时用 BigFraction。
4.4 要点提示
- 1.3 的
Fraction.of(-3, -6)得-1 / -2(只约分不翻符号),需要规范符号时自行处理; Fraction.of(1, 0)抛FractionException: Denominator must be different from 0,该异常类包级私有,按 RuntimeException 捕获;- 分数不适合做迭代逼近类计算(每步都在制造更大的分母),迭代场景回到浮点并配 Sum。
五、素数与因子分解:Primes 的工程用法
5.1 问题背景
哈希表容量取素数能减少键分布规律的碰撞;布隆过滤器、随机数种子、密码学练习都依赖素数判定与生成。
5.2 实测示例:哈希容量与因子分解
import org.apache.commons.numbers.primes.Primes;
System.out.println("nextPrime(1024) = " + Primes.nextPrime(1024));
System.out.println("nextPrime(65536) = " + Primes.nextPrime(65536));
System.out.println("isPrime(2147483647) = " + Primes.isPrime(2147483647));
System.out.println("primeFactors(360) = " + Primes.primeFactors(360));
System.out.println("primeFactors(5460) = " + Primes.primeFactors(5460));
实测回显:
isPrime(2) = true
isPrime(97) = true
isPrime(1) = false
isPrime(100) = false
isPrime(2147483647) = true
nextPrime(97) = 97
nextPrime(100) = 101
nextPrime(1) = 2
primeFactors(360) = [2, 2, 2, 3, 3, 5]
primeFactors(97) = [97]
primeFactors(1024) = [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
primeFactors(5460) = [2, 2, 3, 5, 7, 13]
nextPrime(1024) = 1031
nextPrime(65536) = 65537
验证结果:1024 的下一个素数是 1031、65536 的下一个素数是 65537(费马素数),扩容时取 nextPrime(capacity) 即可;2^31 - 1 是素数(Miller-Rabin 实测瞬时判定);因子分解返回带重复因子的升序列表。
💡 技术拓展:素数判定算法对比:
算法 复杂度 适用 试除法 O(√n) n 小于 1e6、教学场景 Miller-Rabin(确定性底数) O(k log n) 32 位整数严格判定,本文所用 埃拉托斯特尼筛 O(n log log n) 预处理 一次性判定大量小素数
Primes.isPrime是试除加 Miller-Rabin 的混合实现,单点判定免建筛表;需要反复查询区间内素数时自建 BitSet 筛更划算。
5.3 要点提示
nextPrime对已是素数的入参原样返回,无需先判后取;- 入参 int 上限 2147483647,更大范围需要自实现或换库;
primeFactors结果可直接累乘还原原数,适合做校验与教学演示。
六、复数与四元数:信号处理与空间插值
6.1 实测示例:复数代数
import org.apache.commons.numbers.complex.Complex;
Complex a = Complex.ofCartesian(3, 4);
Complex b = Complex.ofCartesian(1, -2);
System.out.println("a.multiply(b) = " + a.multiply(b));
System.out.println("a.divide(b) = " + a.divide(b));
System.out.println("a.abs() = " + a.abs());
System.out.println("a.sqrt() = " + a.sqrt());
System.out.println("I.multiply(I) = " + Complex.I.multiply(Complex.I));
实测回显:
a.multiply(b) = (11.0,-2.0)
a.divide(b) = (-1.0,2.0)
a.abs() = 5.0
a.sqrt() = (2.0,1.0)
I.multiply(I) = (-1.0,0.0)
验证结果:(3+4i)(1-2i) = 11-2i、(3+4i)/(1-2i) = -1+2i、(3+4i)^0.5 = 2+i,全部可手算复核;复数乘加是 FFT、数字滤波、信号卷积的原子操作,Numbers 的 Complex 不可变且无锁,可直接入并行流。
6.2 实测示例:四元数与球面插值
import org.apache.commons.numbers.quaternion.Quaternion;
import org.apache.commons.numbers.quaternion.Slerp;
Quaternion q = Quaternion.of(0.7071067811865476, 0.7071067811865476, 0.0, 0.0);
Quaternion r = Quaternion.of(0.0, 0.0, 0.0, 1.0);
Slerp slerp = new Slerp(q, r);
System.out.println("slerp(0.0) = " + slerp.apply(0.0));
System.out.println("slerp(0.5) = " + slerp.apply(0.5));
System.out.println("slerp(1.0) = " + slerp.apply(1.0));
实测回显:
slerp(0.5) = [0.5000000000000001 0.5000000000000001 0.0 0.7071067811865476]
slerp(0.0) = [0.7071067811865476 0.7071067811865476 0.0 0.0]
slerp(1.0) = [0.0 0.0 0.0 1.0]
验证结果:t=0 与 t=1 精确还原两端姿态,t=0.5 位于球面大圆中点;与欧拉角插值相比,四元数 slerp 无万向节锁、角速度均匀,是相机与骨骼动画的标准解。
6.3 要点提示
- 方法命名与 math3 不同:
conj()/arg()/abs(); Quaternion.of(w, double 数组)标量在前向量在后,getScalarPart/getVectorPart对应取出;- quaternion 的
isUnit运行期加载 core 的 Precision,离线 jar 直跑时 core 必须在 classpath。
七、特殊函数与概率计算:gamma 模块
7.1 实测示例:大数阶乘的对数概率
二项分布 P(X=k) = C(n,k) × p^k × (1-p)^(n-k),n=1000 时组合数超 long 上限,必须在对数空间计算。以下实测 C(1000, 500) 恰好出现 500 次正面的概率:
import org.apache.commons.numbers.combinatorics.LogBinomialCoefficient;
double logP = LogBinomialCoefficient.value(1000, 500) - 1000 * Math.log(2);
System.out.println("logBinomial(1000, 500) = " + LogBinomialCoefficient.value(1000, 500));
System.out.println("ln P(X=500) = " + logP);
System.out.println("P(X=500) = " + Math.exp(logP));
实测回显:
logBinomial(1000, 500) = 689.4672615678512
ln P(X=500) = -3.6799189920940307
P(X=500) = 0.025225018178363288
验证结果:ln C(1000,500) ≈ 689.47,减去 1000×ln2 ≈ 693.15 得对数概率 -3.68,换回概率约 2.52%,与正态近似(σ ≈ 15.81,P ≈ 0.0252)一致;全程无溢出。
7.2 实测示例:正态分布相关函数
import org.apache.commons.numbers.gamma.Erf;
import org.apache.commons.numbers.gamma.Erfc;
import org.apache.commons.numbers.gamma.InverseErfc;
import org.apache.commons.numbers.gamma.Gamma;
import org.apache.commons.numbers.gamma.LogGamma;
System.out.println("Erf.value(1.0) = " + Erf.value(1.0));
System.out.println("Erfc.value(1.0) = " + Erfc.value(1.0));
System.out.println("InverseErfc.value(Erfc.value(1.0)) = " + InverseErfc.value(Erfc.value(1.0)));
System.out.println("Gamma.value(5.0) = " + Gamma.value(5.0));
System.out.println("LogGamma.value(100.0) = " + LogGamma.value(100.0));
System.out.println("Math.log(Gamma.value(100)) = " + Math.log(Gamma.value(100.0)));
实测回显:
Erf.value(1.0) = 0.8427007929497149
Erfc.value(1.0) = 0.15729920705028513
InverseErfc.value(Erfc.value(1.0)) = 1.0
Gamma.value(5.0) = 24.0
Gamma.value(100.0) = 9.332621544394415E155
LogGamma.value(100.0) = 359.1342053695754
Math.log(Gamma.value(100)) 核对 = 359.1342053695754
验证结果:Erf 与 Erfc 互补(和为 1),InverseErfc 逆运算精确还原;Gamma(5) = 4! = 24;Gamma(100) ≈ 9.3e155 尚未溢出,其自然对数与 LogGamma 逐位一致,再往上 Gamma 变无穷而 LogGamma 依旧可用。
7.3 要点提示
- 概率密度连乘(似然函数)一律转对数空间求和,再
Math.exp一次还原,这是与 LogGamma/LogBeta 的组合套路; Digamma.value(1.0) = -0.5772156649015314(负欧拉常数)可作为实现正确性的快速校验点;- 大参数 Beta 走
LogBeta。
八、组合数学与溢出管理:combinatorics 三件套
8.1 实测示例:同一组合数的三种精度
import org.apache.commons.numbers.combinatorics.BinomialCoefficient;
import org.apache.commons.numbers.combinatorics.BinomialCoefficientDouble;
import org.apache.commons.numbers.combinatorics.LogBinomialCoefficient;
import org.apache.commons.numbers.combinatorics.Factorial;
System.out.println("BinomialCoefficient.value(66, 33) = " + BinomialCoefficient.value(66, 33));
try {
System.out.println("BinomialCoefficient.value(67, 33) = " + BinomialCoefficient.value(67, 33));
} catch (RuntimeException e) {
System.out.println("BinomialCoefficient.value(67, 33) = " + e.getClass().getSimpleName() + ": " + e.getMessage());
}
System.out.println("BinomialCoefficientDouble.value(67, 33) = "
+ BinomialCoefficientDouble.value(67, 33));
System.out.println("LogBinomialCoefficient.value(1000, 500) = "
+ LogBinomialCoefficient.value(1000, 500));
System.out.println("Factorial.value(20) = " + Factorial.value(20));
try {
System.out.println("Factorial.value(21) = " + Factorial.value(21));
} catch (RuntimeException e) {
System.out.println("Factorial.value(21) = " + e.getClass().getSimpleName() + ": " + e.getMessage());
}
System.out.println("Factorial.doubleValue(170) = " + Factorial.doubleValue(170));
实测回显:
BinomialCoefficient.value(66, 33) = 7219428434016265740
BinomialCoefficient.value(67, 33) = ArithmeticException: long overflow
BinomialCoefficientDouble.value(67, 33) = 1.4226520737620287E19
LogBinomialCoefficient.value(1000, 500) = 689.4672615678512
Factorial.value(20) = 2432902008176640000
Factorial.value(21) = CombinatoricsException: Number 21 is out of range [0, 20]
Factorial.doubleValue(170) = 7.257415615307999E306
验证结果:C(66,33) = 7219428434016265740 是 long 的极限内最大组合数之一,C(67,33) 溢出直接抛 ArithmeticException: long overflow 而非回绕成负数;Double 版给出 1.42e19(末几位失真);Log 版可算到 n=1000;阶乘精确上限 20,浮点上限 170。
8.2 要点提示
- 组合数/阶乘三件套(精确 long、Double、Log)与方法名一一对应,按数值范围选,不要用 try-catch 探测溢出做控制流;
- 需要精确大数组合数时,用 BigFraction 组合 C(n,k) = BigFraction(n!) 亦可行,代价是大数开销;
- 斯特林数(
Stirling.stirlingS1/S2)与组合枚举(Combinations.of(n, k),惰性迭代)覆盖划分与遍历场景。
九、求根与 top-k:rootfinder 与 arrays
9.1 实测示例:Brent 法求非线性方程根
import org.apache.commons.numbers.rootfinder.BrentSolver;
import java.util.function.DoubleUnaryOperator;
BrentSolver solver = new BrentSolver(1e-12, 1e-12, 1e-12);
DoubleUnaryOperator f1 = x -> x * x - 2;
double r1 = solver.findRoot(f1, 0.0, 2.0);
System.out.println("root of x^2 - 2 in [0, 2] = " + r1);
System.out.println("f1(root) = " + f1.applyAsDouble(r1));
DoubleUnaryOperator f2 = Math::cos;
double r2 = new BrentSolver(1e-15, 1e-15, 1e-15).findRoot(f2, 0.0, 2.0);
System.out.println("root of cos(x) in [0, 2] = " + r2);
实测回显:
root of x^2 - 2 in [0, 2] = 1.4142135623731364
Math.sqrt(2) 核对 = 1.4142135623730951
f1(root) = 1.1723955140041653E-13
root of cos(x) in [0, 2] = 1.5707963267948966
cos(root) 核对 = 6.123233995736766E-17
验证结果:x^2-2 的根与 Math.sqrt(2) 相差 4e-13,根处函数残差 1.17e-13 与 1e-12 精度设置吻合;cos(x) 在 [0,2] 的根为 pi/2。Brent 法兼取二分法的稳健与割线法的快收敛,区间端点异号即可,无需导数。
9.2 实测示例:Selection 求分位数与 top-k
import org.apache.commons.numbers.arrays.Selection;
import java.util.Arrays;
double[] data = {9, 3, 7, 1, 8, 2, 6, 5, 4, 0};
double[] copy = data.clone();
Selection.select(copy, 3);
System.out.println("a[3] (第4小的数) = " + copy[3]);
double[] copy2 = data.clone();
Selection.select(copy2, new int[]{0, 9});
System.out.println("after select(copy, [0, 9]): " + Arrays.toString(copy2));
实测回显:
after select(copy, 3): [0.0, 1.0, 2.0, 3.0, 8.0, 4.0, 6.0, 5.0, 7.0, 9.0]
a[3] (第4小的数) = 3.0
after select(copy, [0, 9]): [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]
验证结果:单关键索引 select 只保证第 k 位正确、两侧落位(平均 O(n)),比全排序省;双关键索引 {0, 9} 实测把最小、最大分位拉通后全数组升序就位。监控分位数(P50/P95/P99)场景,一次多关键索引调用即可拿到全部分位。
9.3 要点提示
findRoot区间无变号直接抛SolverException,调用前先粗扫区间;Selection原地修改入参数组,求 top-k 前先 clone;- 与
SortInPlace(键值同步排序)配合,可对 (分数, id) 结构按分数取 top-k 再映射回对象。
十、最佳实践清单
- 版本对齐:一律通过
commons-numbers-bom引入模块,11 个模块保持同版本(本文 1.3),避免混版带来的 NoSuchMethodError。 - 按需引模块:core 约 100 KB 零依赖;gamma、combinatorics、arrays、primes、rootfinder 完全独立;引得越少,依赖树越干净。
- 比较不过等号:浮点判定一律 Precision 的 ULP/绝对/相对三选一,NaN 语义用 equalsIncludingNaN 显式表态。
- 求和防漂移:长序列、大跨度数值求和用 Sum(或 DD),统计与对账默认开启补偿求和。
- 溢出即异常是特性:pow、阶乘、组合数在边界抛异常,捕获后降级到 Double/Log 变体,而不是预先除以一个大数。
- 概率走对数空间:连乘概率与组合数统一用 LogGamma/LogBinomialCoefficient,最后一次性 exp。
- 精确量用分数:配比、时间码、需要
== 1判定的场景用 Fraction/BigFraction;注意 1.3 不做符号归一。 - 单位入类型:度、弧度用 angle 模块的 Deg/Rad 值对象携带单位,杜绝手写换算。
- 异常捕获用公共父类:FractionException、CombinatoricsException 均为包级私有,catch RuntimeException 或 ArithmeticException。
- 运行编码显式化:生产环境 java 命令统一加
-Dfile.encoding=UTF-8 -Dstdout.encoding=UTF-8,避免 locale 差异导致日志乱码。 - 离线 jar 直跑核对依赖:fraction/angle/complex/quaternion/field 依赖 core,手动 classpath 时带上 commons-numbers-core-1.3.jar。
- 迁移自 math3 时过一遍命名差异:conj 与 conjugate、arg 与 getArgument、静态工厂与 new 构造。
十一、参考链接
- Apache Commons Numbers 官方主页:https://commons.apache.org/proper/commons-numbers/
- 1.3 javadoc:https://commons.apache.org/proper/commons-numbers/javadocs/api-release/index.html
- 官方下载目录:https://downloads.apache.org/commons/numbers/
- Maven 中央仓库 BOM:https://repo1.maven.org/maven2/org/apache/commons/commons-numbers-bom/
- 变更记录:https://commons.apache.org/proper/commons-numbers/changes-report.html
- 源码仓库:https://github.com/apache/commons-numbers