Skip to content

大数与精确计算 ​

学习目标 ​

  • 根据范围和精度要求选择基本类型、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 创建大整数,避免先转换为文本。

java
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);
// 输出:10

BigInteger 构造器:从文本创建大整数 ​

数值超出 long 范围或来自协议文本时使用构造器,并明确进制与格式错误处理。

java
// 语义:数值超出 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);
// 输出:255

BigInteger.add:大整数加法 ​

使用 add 精确相加两个大整数,结果是新对象,原值不变。

java
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);
// 输出:10000000000000000000

BigInteger.subtract:大整数减法 ​

使用 subtract 精确计算两个大整数的差,结果不会修改被减数。

java
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);
// 输出:10000000000000000000

BigInteger.multiply:大整数乘法 ​

使用 multiply 精确计算大整数乘积,并限制不可信输入造成的结果规模。

java
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);
// 输出:10000000000000000000

BigInteger.divide:计算大整数的商 ​

divide 执行向零截断的整数除法,除数为零会抛 ArithmeticException。

java
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);
// 输出:17

BigInteger.remainder:计算大整数余数 ​

使用 remainder 取得与整数除法配套的余数,结果的符号可能为负。

java
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);
// 输出:17

BigInteger.compareTo:比较大整数 ​

compareTo 返回负数、零或正数,适合比较超出基本整数范围的值。

java
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);
// 输出:false

BigInteger.signum:读取大整数符号 ​

只关心负数、零或正数时使用 signum,结果分别为 -1、0、1。

java
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());
// 输出:0

BigInteger.pow:计算非负整数次幂 ​

pow 计算非负整数次幂,大指数可能生成巨大对象,调用前应限制输入。

java
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);
// 输出:12

BigInteger.gcd:计算最大公约数 ​

使用 gcd 计算两个大整数绝对值的最大公约数。

java
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);
// 输出:12

BigDecimal.valueOf:从 double 创建小数 ​

已有 double 值时用 BigDecimal.valueOf,避免 new BigDecimal(double) 暴露二进制近似。

java
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());
// 输出:1

BigDecimal 字符串构造器:创建精确小数 ​

十进制文本来自金额或协议边界时使用字符串构造器,精确保留文本表达的数值与 scale。

java
// 语义:十进制文本来自金额或协议边界时使用字符串构造器,精确保留文本表达的数值与 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());
// 输出:2

BigDecimal.add:精确小数加法 ​

使用 add 精确计算十进制小数之和,结果和输入都是不可变对象。

java
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.90

BigDecimal.subtract:精确小数减法 ​

使用 subtract 精确计算十进制小数之差,并在业务边界另行约定 scale。

java
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.00

BigDecimal.multiply:精确小数乘法 ​

使用 multiply 精确计算十进制小数乘积,金额结果仍应按领域规则设置 scale。

java
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.90

BigDecimal.divide:指定舍入模式除法 ​

除不尽且未指定舍入时可能抛 ArithmeticException;金额场景必须和业务约定小数位及舍入模式,不能默认 HALF_UP 永远正确。

java
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.33

BigDecimal.compareTo:按数值比较 ​

金额和阈值比较通常使用 compareTo;equals 同时比较数值和 scale,可能把 1.0 与 1.00 判为不等。

java
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 返回新对象;舍入模式应由领域规则明确规定。

java
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.34

BigDecimal.toPlainString:输出非科学计数法文本 ​

toPlainString 避免科学计数法,适合要求普通十进制形式的协议和展示。

java
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+3

BigDecimal.stripTrailingZeros:去除尾随零 ​

展示不要求固定小数位时用 stripTrailingZeros 去掉尾零;结果可能采用科学计数法。

java
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());
// 输出:-3

Math.addExact:检查加法溢出 ​

Math.addExact 在整数加法溢出时抛 ArithmeticException,避免静默回绕。

java
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);
// 输出:true

Math.multiplyExact:检查乘法溢出 ​

Math.multiplyExact 在整数乘法溢出时抛 ArithmeticException,适合严格计数计算。

java
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);
// 输出:true

Math.floorDiv:向下取整除法 ​

floorDiv 向负无穷方向取整,适合负数参与的周期、页码或时间桶计算。

java
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);
// 输出:-2

Math.floorMod:计算向下取整余数 ​

floorMod 返回与 floorDiv 配套的余数,正除数时结果保持非负。

java
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() 的语义差异。

java
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);
// 输出:2

BigInteger.isProbablePrime:概率素性判断 ​

返回概率意义上的判断,certainty 越大误判概率越低;密码学用途应使用经过审计的库和协议,不能把它当成完整密钥生成方案。

java
import java.math.BigInteger;

boolean prime = BigInteger.valueOf(101).isProbablePrime(20);
// 输入:prime 的初始值为 BigInteger.valueOf(101).isProbablePrime(20)。
// 作用:通过 BigInteger.isProbablePrime 概率素性判断。
System.out.println(prime);
// 输出:true

BigInteger.probablePrime:生成大素数 ​

示例使用普通 Random 仅为演示;安全密钥场景应使用 SecureRandom,并由密码学库管理参数和生命周期。

java
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());
// 输出:32

BigDecimal.divideAndRemainder:同时取得商和余数 ​

用于需要同时计算整数商和剩余量的分配逻辑;金额按份额分配还需要额外定义尾差归属。

java
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.5

Math.nextUp:访问更大的相邻浮点值 ​

Math.nextUp 返回浮点表示中相邻的更大值,适合测试数值算法上边界。

java
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.0000000000000002

Math.nextDown:访问更小的相邻浮点值 ​

Math.nextDown 返回浮点表示中相邻的更小值,适合测试数值算法下边界。

java
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.9999999999999999

Math.round:四舍五入到整数 ​

Math.round 返回最接近的整数;需要十进制小数位规则时使用 BigDecimal.setScale。

java
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));
// 输出:-2

Math.ceil:向正无穷方向取整 ​

Math.ceil 返回不小于参数的最小整数值,返回类型仍为浮点数。

java
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.0

Math.floor:向负无穷方向取整 ​

Math.floor 返回不大于参数的最大整数值,返回类型仍为浮点数。

java
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.0

Math.random:生成普通随机数 ​

适合简单模拟或演示,不能用于令牌、验证码、密钥和抽奖安全性;安全随机使用 SecureRandom 并定义可审计策略。

java
double sample = Math.random();
// 输入:sample 的初始值为 Math.random()。
// 作用:通过 Math.random 生成普通随机数。
System.out.println(sample >= 0.0 && sample < 1.0);
// 输出:true

简单案例 ​

java
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 的返回值必须接住,不能期待原对象被修改。

课后小问 ​

  1. 为什么 new BigDecimal("1.0").equals(new BigDecimal("1.00")) 可能为 false,而 compareTo 为零? 答案:equals 同时比较数值和 scale,compareTo 只比较数值大小。 解析:金额阈值、排序和去重通常先确定业务是“数值相等”还是“格式也相等”,不要混用两个契约。

  2. 为什么 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 处理边界计算。