大数与精确计算
学习目标
- 根据范围和精度要求选择基本类型、
BigInteger或BigDecimal。 - 使用
BigInteger完成任意精度整数运算和比较。 - 使用
BigDecimal从字符串构造金额,明确除法舍入和比较规则。 - 使用
Math的检查、舍入和取整 API,识别普通运算的溢出边界。
核心知识点
专业术语
- 溢出(overflow):结果超出基本整数类型可表示范围,普通运算可能静默回绕。
- 任意精度整数(arbitrary-precision integer):
BigInteger按需要扩展存储,不受long范围限制。 - 十进制定点数(decimal fixed-point):
BigDecimal用无穷精度的十进制小数表达,适合金额和计量。 - 舍入模式(rounding mode):除法或缩放无法精确表示时,使用
RoundingMode规定保留哪一侧。 - 数值相等与表示相等:
BigDecimal("1.0")与BigDecimal("1.00")的compareTo相等,但equals可能不同。
白话解释与边界
普通 int/long 像固定大小的盒子,装不下时可能从最大值绕回最小值;Math.addExact 等 API 会在溢出时抛异常。BigInteger 像可扩大的整数纸带,适合订单号、计数器和密码学相关整数,但对象和计算成本更高。
BigDecimal 应从字符串、整数或 valueOf 构造,不要把二进制浮点近似直接带进金额。除不尽时必须指定舍入策略;金额比较通常用 compareTo,格式展示使用 setScale 和 toPlainString。它们都是不可变对象,每次运算都会返回新值。
常用用法
BigInteger.valueOf:从 long 创建大整数
已有 long 值时用 BigInteger.valueOf 创建大整数,避免先转换为文本。
import java.math.BigInteger;
BigInteger fromLong = BigInteger.valueOf(9_000_000_000L);
// 初始状态:fromLong 当前为 BigInteger.valueOf(9_000_000_000L)。
// 作用:已有 long 值时用 BigInteger.valueOf 创建大整数,避免先转换为文本。
BigInteger next = fromLong.add(BigInteger.ONE);
// 初始状态:next 当前为 fromLong.add(BigInteger.ONE)。
int digits = fromLong.toString().length();
System.out.println(fromLong);
// 输出:9000000000
System.out.println(next);
// 输出:9000000001
System.out.println(digits);
// 输出:10BigInteger 构造器:从文本创建大整数
数值超出 long 范围或来自协议文本时使用构造器,并明确进制与格式错误处理。
// 语义:数值超出 long 范围或来自协议文本时使用构造器,并明确进制与格式错误处理。
// 初始状态:value 初始为 new BigInteger("12345678901234567890");hex 初始为 new BigInteger("ff", 16)。
import java.math.BigInteger;
BigInteger value = new BigInteger("12345678901234567890");
// 初始状态:value 的初始值为 new BigInteger("12345678901234567890")。
// 作用:数值超出 long 范围或来自协议文本时使用构造器,并明确进制与格式错误处理。
// 关键变化:new BigInteger("12345678901234567890") 从十进制文本创建 value,精确保存该整数。
BigInteger hex = new BigInteger("ff", 16);
// 关键变化:new BigInteger("ff", 16) 按十六进制解析文本,hex 变为 255。
System.out.println(value);
// 输出:12345678901234567890
System.out.println(hex);
// 输出:255BigInteger.add:大整数加法
使用 add 精确相加两个大整数,结果是新对象,原值不变。
import java.math.BigInteger;
BigInteger principal = new BigInteger("10000000000000000000");
// 初始状态:principal 当前为 new BigInteger("10000000000000000000")。
BigInteger result = principal.add(BigInteger.valueOf(2));
// 初始状态:result 当前为 principal.add(BigInteger.valueOf(2))。
// 作用:使用 add 精确相加两个大整数,结果是新对象,原值不变。
System.out.println(result);
// 输出:10000000000000000002
System.out.println(principal);
// 输出:10000000000000000000BigInteger.subtract:大整数减法
使用 subtract 精确计算两个大整数的差,结果不会修改被减数。
import java.math.BigInteger;
BigInteger value = new BigInteger("10000000000000000000");
// 初始状态:value 当前为 new BigInteger("10000000000000000000")。
BigInteger result = value.subtract(BigInteger.ONE);
// 初始状态:result 当前为 value.subtract(BigInteger.ONE)。
// 作用:使用 subtract 精确计算两个大整数的差,结果不会修改被减数。
System.out.println(result);
// 输出:9999999999999999999
System.out.println(value);
// 输出:10000000000000000000BigInteger.multiply:大整数乘法
使用 multiply 精确计算大整数乘积,并限制不可信输入造成的结果规模。
import java.math.BigInteger;
BigInteger value = new BigInteger("10000000000000000000");
// 初始状态:value 当前为 new BigInteger("10000000000000000000")。
BigInteger result = value.multiply(BigInteger.valueOf(3));
// 初始状态:result 当前为 value.multiply(BigInteger.valueOf(3))。
// 作用:使用 multiply 精确计算大整数乘积,并限制不可信输入造成的结果规模。
System.out.println(result);
// 输出:30000000000000000000
System.out.println(value);
// 输出:10000000000000000000BigInteger.divide:计算大整数的商
divide 执行向零截断的整数除法,除数为零会抛 ArithmeticException。
import java.math.BigInteger;
BigInteger value = new BigInteger("17");
// 初始状态:value 当前为 new BigInteger("17")。
BigInteger quotient = value.divide(BigInteger.valueOf(5));
// 初始状态:quotient 当前为 value.divide(BigInteger.valueOf(5))。
// 作用:divide 执行向零截断的整数除法,除数为零会抛 ArithmeticException。
System.out.println(quotient);
// 输出:3
System.out.println(value);
// 输出:17BigInteger.remainder:计算大整数余数
使用 remainder 取得与整数除法配套的余数,结果的符号可能为负。
import java.math.BigInteger;
BigInteger value = new BigInteger("17");
// 初始状态:value 当前为 new BigInteger("17")。
BigInteger remainder = value.remainder(BigInteger.valueOf(5));
// 初始状态:remainder 当前为 value.remainder(BigInteger.valueOf(5))。
// 作用:使用 remainder 取得与整数除法配套的余数,结果的符号可能为负。
System.out.println(remainder);
// 输出:2
System.out.println(value);
// 输出:17BigInteger.compareTo:比较大整数
compareTo 返回负数、零或正数,适合比较超出基本整数范围的值。
import java.math.BigInteger;
BigInteger amount = new BigInteger("-20");
// 初始状态:amount 当前为 new BigInteger("-20")。
int order = amount.compareTo(BigInteger.ZERO);
// 初始状态:order 当前为 amount.compareTo(BigInteger.ZERO)。
// 作用:compareTo 返回负数、零或正数,适合比较超出基本整数范围的值。
System.out.println(order < 0);
// 输出:true
System.out.println(order == 0);
// 输出:falseBigInteger.signum:读取大整数符号
只关心负数、零或正数时使用 signum,结果分别为 -1、0、1。
import java.math.BigInteger;
BigInteger negative = new BigInteger("-20");
// 初始状态:negative 当前为 new BigInteger("-20")。
int sign = negative.signum();
// 初始状态:sign 当前为 negative.signum()。
// 作用:只关心负数、零或正数时使用 signum,结果分别为 -1、0、1。
System.out.println(sign);
// 输出:-1
System.out.println(BigInteger.ZERO.signum());
// 输出:0BigInteger.pow:计算非负整数次幂
pow 计算非负整数次幂,大指数可能生成巨大对象,调用前应限制输入。
import java.math.BigInteger;
BigInteger value = BigInteger.valueOf(12);
// 初始状态:value 当前为 BigInteger.valueOf(12)。
BigInteger square = value.pow(2);
// 初始状态:square 当前为 value.pow(2)。
// 作用:pow 计算非负整数次幂,大指数可能生成巨大对象,调用前应限制输入。
System.out.println(square);
// 输出:144
System.out.println(value);
// 输出:12BigInteger.gcd:计算最大公约数
使用 gcd 计算两个大整数绝对值的最大公约数。
import java.math.BigInteger;
BigInteger left = BigInteger.valueOf(12);
// 初始状态:left 当前为 BigInteger.valueOf(12)。
BigInteger right = BigInteger.valueOf(18);
// 初始状态:right 当前为 BigInteger.valueOf(18)。
BigInteger result = left.gcd(right);
// 输入:result 的初始值为 left.gcd(right)。
// 作用:使用 gcd 计算两个大整数绝对值的最大公约数。
System.out.println(result);
// 输出:6
System.out.println(left);
// 输出:12BigDecimal.valueOf:从 double 创建小数
已有 double 值时用 BigDecimal.valueOf,避免 new BigDecimal(double) 暴露二进制近似。
import java.math.BigDecimal;
BigDecimal value = BigDecimal.valueOf(0.20);
// 初始状态:value 当前为 BigDecimal.valueOf(0.20)。
// 作用:已有 double 值时用 BigDecimal.valueOf,避免 new BigDecimal(double) 暴露二进制近似。
String plain = value.toPlainString();
// 初始状态:plain 当前为 value.toPlainString()。
// 关键变化:value.toPlainString() 把 value=0.20 转成非科学计数法文本,plain 变为 "0.2"。
System.out.println(plain);
// 输出:0.2
System.out.println(value.scale());
// 输出:1BigDecimal 字符串构造器:创建精确小数
十进制文本来自金额或协议边界时使用字符串构造器,精确保留文本表达的数值与 scale。
// 语义:十进制文本来自金额或协议边界时使用字符串构造器,精确保留文本表达的数值与 scale。
// 初始状态:value 初始为 new BigDecimal("0.10");plain 初始为 value.toPlainString()。
import java.math.BigDecimal;
BigDecimal value = new BigDecimal("0.10");
// 初始状态:value 的初始值为 new BigDecimal("0.10")。
// 作用:十进制文本来自金额或协议边界时使用字符串构造器,精确保留文本表达的数值与 scale。
// 关键变化:new BigDecimal("0.10") 按字符串创建精确小数,value 保留数值 0.10 和 scale=2。
String plain = value.toPlainString();
// 关键变化:value.toPlainString() 把 value=0.10 转成非科学计数法文本,plain 变为 "0.10"。
System.out.println(plain);
// 输出:0.10
System.out.println(value.scale());
// 输出:2BigDecimal.add:精确小数加法
使用 add 精确计算十进制小数之和,结果和输入都是不可变对象。
import java.math.BigDecimal;
BigDecimal price = new BigDecimal("19.90");
// 初始状态:price 当前为 new BigDecimal("19.90")。
BigDecimal total = price.add(new BigDecimal("1.10"));
// 初始状态:total 当前为 price.add(new BigDecimal("1.10"))。
// 作用:使用 add 精确计算十进制小数之和,结果和输入都是不可变对象。
System.out.println(total);
// 输出:21.00
System.out.println(price);
// 输出:19.90BigDecimal.subtract:精确小数减法
使用 subtract 精确计算十进制小数之差,并在业务边界另行约定 scale。
import java.math.BigDecimal;
BigDecimal amount = new BigDecimal("20.00");
// 初始状态:amount 当前为 new BigDecimal("20.00")。
BigDecimal result = amount.subtract(new BigDecimal("1.25"));
// 初始状态:result 当前为 amount.subtract(new BigDecimal("1.25"))。
// 作用:使用 subtract 精确计算十进制小数之差,并在业务边界另行约定 scale。
System.out.println(result);
// 输出:18.75
System.out.println(amount);
// 输出:20.00BigDecimal.multiply:精确小数乘法
使用 multiply 精确计算十进制小数乘积,金额结果仍应按领域规则设置 scale。
import java.math.BigDecimal;
BigDecimal price = new BigDecimal("19.90");
// 初始状态:price 当前为 new BigDecimal("19.90")。
BigDecimal quantity = new BigDecimal("3");
// 初始状态:quantity 当前为 new BigDecimal("3")。
BigDecimal total = price.multiply(quantity);
// 作用:使用 multiply 精确计算十进制小数乘积,金额结果仍应按领域规则设置 scale。
// 关键变化:price.multiply(quantity) 用 price=19.90 乘 quantity=3,total 变为 59.70。
System.out.println(total);
// 输出:59.70
System.out.println(price);
// 输出:19.90BigDecimal.divide:指定舍入模式除法
除不尽且未指定舍入时可能抛 ArithmeticException;金额场景必须和业务约定小数位及舍入模式,不能默认 HALF_UP 永远正确。
import java.math.BigDecimal;
import java.math.RoundingMode;
BigDecimal average = new BigDecimal("10").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP);
// 初始状态:average 当前为 new BigDecimal("10").divide(new BigDecimal("3"), 2, RoundingMode.HALF_UP)。
// 作用:除不尽且未指定舍入时可能抛 ArithmeticException。
System.out.println(average);
// 输出:3.33BigDecimal.compareTo:按数值比较
金额和阈值比较通常使用 compareTo;equals 同时比较数值和 scale,可能把 1.0 与 1.00 判为不等。
import java.math.BigDecimal;
BigDecimal left = new BigDecimal("1.0");
// 初始状态:left 当前为 new BigDecimal("1.0")。
BigDecimal right = new BigDecimal("1.00");
// 初始状态:right 当前为 new BigDecimal("1.00")。
System.out.println(left.compareTo(right) == 0);
// 输出:true
// 作用:金额和阈值比较通常使用 compareTo。BigDecimal.setScale:固定小数位
setScale 按指定 RoundingMode 返回新对象;舍入模式应由领域规则明确规定。
import java.math.BigDecimal;
import java.math.RoundingMode;
BigDecimal amount = new BigDecimal("12.345");
// 初始状态:amount 当前为 new BigDecimal("12.345")。
BigDecimal rounded = amount.setScale(2, RoundingMode.HALF_EVEN);
// 初始状态:rounded 当前为 amount.setScale(2, RoundingMode.HALF_EVEN)。
// 作用:setScale 按指定 RoundingMode 返回新对象。
System.out.println(rounded);
// 输出:12.34BigDecimal.toPlainString:输出非科学计数法文本
toPlainString 避免科学计数法,适合要求普通十进制形式的协议和展示。
import java.math.BigDecimal;
BigDecimal value = new BigDecimal("1E+3");
// 初始状态:value 当前为 new BigDecimal("1E+3")。
String plain = value.toPlainString();
// 初始状态:plain 当前为 value.toPlainString()。
// 作用:toPlainString 避免科学计数法,适合要求普通十进制形式的协议和展示。
System.out.println(plain);
// 输出:1000
System.out.println(value.toString());
// 输出:1E+3BigDecimal.stripTrailingZeros:去除尾随零
展示不要求固定小数位时用 stripTrailingZeros 去掉尾零;结果可能采用科学计数法。
import java.math.BigDecimal;
BigDecimal value = new BigDecimal("1000.00");
// 初始状态:value 当前为 new BigDecimal("1000.00")。
BigDecimal stripped = value.stripTrailingZeros();
// 初始状态:stripped 当前为 value.stripTrailingZeros()。
// 作用:展示不要求固定小数位时用 stripTrailingZeros 去掉尾零。
System.out.println(stripped.toPlainString());
// 输出:1000
System.out.println(stripped.scale());
// 输出:-3Math.addExact:检查加法溢出
Math.addExact 在整数加法溢出时抛 ArithmeticException,避免静默回绕。
int left = 20;
// 初始状态:left 当前为 20。
int right = 22;
// 初始状态:right 当前为 22。
int total = Math.addExact(left, right);
// 作用:Math.addExact 在整数加法溢出时抛 ArithmeticException,避免静默回绕。
// 关键变化:Math.addExact(left, right) 检查 left=20 与 right=22 未溢出,total 变为 42。
boolean positive = total > 0;
System.out.println(total);
// 输出:42
System.out.println(positive);
// 输出:trueMath.multiplyExact:检查乘法溢出
Math.multiplyExact 在整数乘法溢出时抛 ArithmeticException,适合严格计数计算。
int left = 6;
// 初始状态:left 当前为 6。
int right = 7;
// 初始状态:right 当前为 7。
int product = Math.multiplyExact(left, right);
// 作用:Math.multiplyExact 在整数乘法溢出时抛 ArithmeticException,适合严格计数计算。
boolean positive = product > 0;
System.out.println(product);
// 输出:42
System.out.println(positive);
// 输出:trueMath.floorDiv:向下取整除法
floorDiv 向负无穷方向取整,适合负数参与的周期、页码或时间桶计算。
int dividend = -7;
// 初始状态:dividend 当前为 -7。
int divisor = 3;
// 初始状态:divisor 当前为 3。
int quotient = Math.floorDiv(dividend, divisor);
// 作用:floorDiv 向负无穷方向取整,适合负数参与的周期、页码或时间桶计算。
// 关键变化:Math.floorDiv(dividend, divisor) 用 dividend=-7、divisor=3 向负无穷取整,quotient 变为 -3。
System.out.println(quotient);
// 输出:-3
System.out.println(dividend / divisor);
// 输出:-2Math.floorMod:计算向下取整余数
floorMod 返回与 floorDiv 配套的余数,正除数时结果保持非负。
int dividend = -7;
// 初始状态:dividend 当前为 -7。
int divisor = 3;
// 初始状态:divisor 当前为 3。
int remainder = Math.floorMod(dividend, divisor);
// 作用:floorMod 返回与 floorDiv 配套的余数,正除数时结果保持非负。
// 关键变化:Math.floorMod(dividend, divisor) 用 dividend=-7、divisor=3 计算向下取整余数,remainder 变为 2。
System.out.println(remainder);
// 输出:2
System.out.println(dividend % divisor);
// 输出:-1不常用但需要知道
BigInteger.mod:非负模运算
被除数和模数都必须满足 mod 的非负约束,结果始终非负;如果需要保留被除数符号,比较 remainder 与 remainder() 的语义差异。
import java.math.BigInteger;
BigInteger remainder = new BigInteger("7").mod(BigInteger.valueOf(5));
// 输入:remainder 的初始值为 new BigInteger("7").mod(BigInteger.valueOf(5))。
// 作用:通过 BigInteger.mod 非负模运算。
System.out.println(remainder);
// 输出:2BigInteger.isProbablePrime:概率素性判断
返回概率意义上的判断,certainty 越大误判概率越低;密码学用途应使用经过审计的库和协议,不能把它当成完整密钥生成方案。
import java.math.BigInteger;
boolean prime = BigInteger.valueOf(101).isProbablePrime(20);
// 输入:prime 的初始值为 BigInteger.valueOf(101).isProbablePrime(20)。
// 作用:通过 BigInteger.isProbablePrime 概率素性判断。
System.out.println(prime);
// 输出:trueBigInteger.probablePrime:生成大素数
示例使用普通 Random 仅为演示;安全密钥场景应使用 SecureRandom,并由密码学库管理参数和生命周期。
import java.math.BigInteger;
import java.util.Random;
BigInteger prime = BigInteger.probablePrime(32, new Random(1));
// 输入:prime 的初始值为 BigInteger.probablePrime(32, new Random(1))。
// 作用:通过 BigInteger.probablePrime 生成大素数。
System.out.println(prime.bitLength());
// 输出:32BigDecimal.divideAndRemainder:同时取得商和余数
用于需要同时计算整数商和剩余量的分配逻辑;金额按份额分配还需要额外定义尾差归属。
import java.math.BigDecimal;
BigDecimal[] result = new BigDecimal("10.5").divideAndRemainder(new BigDecimal("3"));
// 输入:result 的初始值为 new BigDecimal("10.5").divideAndRemainder(new BigDecimal("3"))。
// 作用:通过 BigDecimal.divideAndRemainder 同时取得商和余数。
System.out.println(result[0] + ", " + result[1]);
// 输出:3, 1.5Math.nextUp:访问更大的相邻浮点值
Math.nextUp 返回浮点表示中相邻的更大值,适合测试数值算法上边界。
double value = 1.0;
// 初始状态:value 当前为 1.0。
double next = Math.nextUp(value);
// 作用:通过 Math.nextUp 访问更大的相邻浮点值。
System.out.println(next > value);
// 输出:true
System.out.println(next);
// 输出:1.0000000000000002Math.nextDown:访问更小的相邻浮点值
Math.nextDown 返回浮点表示中相邻的更小值,适合测试数值算法下边界。
double value = 1.0;
// 初始状态:value 当前为 1.0。
double previous = Math.nextDown(value);
// 作用:通过 Math.nextDown 访问更小的相邻浮点值。
System.out.println(previous < value);
// 输出:true
System.out.println(previous);
// 输出:0.9999999999999999Math.round:四舍五入到整数
Math.round 返回最接近的整数;需要十进制小数位规则时使用 BigDecimal.setScale。
double value = 3.6;
// 初始状态:value 当前为 3.6。
long rounded = Math.round(value);
// 作用:通过 Math.round 四舍五入到整数。
// 关键变化:Math.round(value) 将 value=3.6 四舍五入,rounded 变为 4。
System.out.println(rounded);
// 输出:4
System.out.println(Math.round(-2.5));
// 输出:-2Math.ceil:向正无穷方向取整
Math.ceil 返回不小于参数的最小整数值,返回类型仍为浮点数。
double value = 2.1;
// 初始状态:value 当前为 2.1。
double result = Math.ceil(value);
// 作用:通过 Math.ceil 向正无穷方向取整。
// 关键变化:Math.ceil(value) 将 value=2.1 向正无穷取整,result 变为 3.0。
System.out.println(result);
// 输出:3.0
System.out.println(Math.ceil(-2.9));
// 输出:-2.0Math.floor:向负无穷方向取整
Math.floor 返回不大于参数的最大整数值,返回类型仍为浮点数。
double value = 2.9;
// 初始状态:value 当前为 2.9。
double result = Math.floor(value);
// 作用:通过 Math.floor 向负无穷方向取整。
// 关键变化:Math.floor(value) 将 value=2.9 向负无穷取整,result 变为 2.0。
System.out.println(result);
// 输出:2.0
System.out.println(Math.floor(-2.1));
// 输出:-3.0Math.random:生成普通随机数
适合简单模拟或演示,不能用于令牌、验证码、密钥和抽奖安全性;安全随机使用 SecureRandom 并定义可审计策略。
double sample = Math.random();
// 输入:sample 的初始值为 Math.random()。
// 作用:通过 Math.random 生成普通随机数。
System.out.println(sample >= 0.0 && sample < 1.0);
// 输出:true简单案例
import java.math.BigDecimal;
import java.math.RoundingMode;
public class PreciseMoneyDemo {
public static void main(String[] args) {
BigDecimal price = new BigDecimal("19.90");
BigDecimal count = new BigDecimal("3");
BigDecimal discount = new BigDecimal("1.00");
BigDecimal total = price.multiply(count).subtract(discount)
.setScale(2, RoundingMode.HALF_UP);
System.out.println("total=" + total.toPlainString());
// 输出:total=58.70
System.out.println("under60=" + (total.compareTo(new BigDecimal("60.00")) < 0));
// 输出:under60=true
}
}案例把字符串构造、运算、舍入、格式化和比较串成金额边界流程;真实结算还要明确币种、税率、尾差、溢出上限和持久化 scale。
易混点
new BigDecimal(0.1)会保留二进制浮点误差,金额使用字符串或BigDecimal.valueOf。BigDecimal.equals比较 scale,金额数值比较通常用compareTo;展示位数则另用setScale。BigDecimal.divide除不尽时必须指定精度和舍入,否则可能抛ArithmeticException。int/long普通运算可能静默溢出,Math.addExact等检查 API 才会主动报错。BigInteger、BigDecimal不可变,add、multiply的返回值必须接住,不能期待原对象被修改。
课后小问
为什么
new BigDecimal("1.0").equals(new BigDecimal("1.00"))可能为false,而compareTo为零? 答案:equals同时比较数值和 scale,compareTo只比较数值大小。 解析:金额阈值、排序和去重通常先确定业务是“数值相等”还是“格式也相等”,不要混用两个契约。为什么
Math.addExact(Integer.MAX_VALUE, 1)比普通+更适合金额计数边界? 答案:addExact在溢出时抛出ArithmeticException,普通+可能静默回绕成负数。 解析:边界值一旦回绕,后续校验可能继续传播错误;若范围本身可能超过long,应直接设计为BigInteger或BigDecimal。
本节小结
- 需要超出基本类型范围的整数使用
BigInteger,需要十进制精度的金额使用BigDecimal。 - 大数对象不可变,除法、舍入和比较必须把返回值和契约写清楚。
BigDecimal从字符串或valueOf构造,比较优先compareTo,展示使用明确 scale。Math的检查、取整和浮点边界 API 解决特定问题,不能替代金额建模和安全随机策略。
快速回顾
- 能解释整数溢出与
BigInteger的适用边界。 - 能从字符串构造金额并选择
RoundingMode。 - 能区分
BigDecimal.equals与compareTo。 - 能使用
Math.addExact、floorDiv等 API 处理边界计算。
阿源的知识库