一、java.lang.Number类
1.1、Number类的常用子类
Number 为抽象类,Java类库中,它有如下几个常用子类:
- 基本数据类型的封装类:Byte、Short、Integer、Long 、Float、Double
- 大整数、大浮点数类:BigInteger、BigDecimal
- 原子操作类:AtomicInteger、AtomicLong
1.2、Number类的通用方法
1.2.1、转数值类型
1、Number 的子类,必须提供将表示的数值转换为 byte、short、int、long、float 、double 的方法,因此,如果需要将对象转为数值类型,直接调用对应的方法即可。方法名如下图所示:
- intValue()、longValue()、floatValue()、doubleValue(),为抽象方法,子类需要自己实现具体逻辑。
- byteValue()、shortValue()方法,不是抽象方法,Number类中默认的实现逻辑是调用intValue()方法,然后强转位 byte、short类型。当然,子类也可以自己重写这两个方法。
// Number类中,byteValue()、shortValue()方法的默认实现
public byte byteValue() {
return (byte)intValue();
}
public short shortValue() {
return (short)intValue();
}
1.2.2、转字符串 toString() 方法
Byte、Short、Integer、Long 、Float、Double、BigInteger、BigDecimal、AtomicInteger、AtomicLong 这些子类都已重写 toString() 方法,都是返回当前对象数值的十进制字符串表示形式。因此,需要转字符串时,直接调用toString() 方法即可。
二、java.math.BigInteger 类
2.1、BigInteger 类的功能
2.1.1、为什么要设计BigInteger 类?
java内置的整形,最长的类型是 long(包括封装类 java.lang.Long),其只能表示 64位有符号的整数(-9223372036854775808 <= long型的整数 <= 9223372036854775807 ),并不能表示超过这个范围的其他整数,因此,为了能够表示任意的整数,才设计了BigInteger 类。
BigInteger 类,能够表示任意长度的整数,并且内置的加减乘除等算数方法,可以对其进行算数运算。
2.1.2、Java API 中的解释
- 不可变的、任意精度的 整数。
- BigInteger 提供所有 Java 的基本整数操作符的对应物,并提供 java.lang.Math 的所有相关方法。另外,BigInteger 还提供以下运算:模算术、GCD 计算、质数测试、素数生成、位操作以及一些其他操作。
- 【特别注意】当为任何输入参数传递 null 对象引用时,此类中的所有方法和构造方法都将抛出
NullPointerException
。 - 【特别注意】BigInteger 是不可变的,对象内的属性 signum、mag 两个参数都是 final 修饰的,也就是说一旦创建对象,就不能再改变其数值。BigInteger定义的加减乘除等运算的方法,其结果都是返回一个新的BigInteger对象。
2.2、BigInteger的底层实现
2.2.1、BigInteger类,是如何表示任意大小的整数的?
BigInteger类定义了 signum、mag 两个字段,分别来表示“整数的正负号”、“整数的值”。
属性字段 | 数据类型 | 特征 | 功能说明 |
signum | int | final 修饰, 不可变。 | 记录BigInteger值的正负号。 -1表示负数,0表示零,1表示正数。 |
mag | int[] | final 修饰, 不可变。 | 用于表示BigInteger的值。 该字段值,计算方式:
|
// BigInteger 类源码
public class BigInteger
extends Number
implements Comparable<BigInteger> {
/**
* The signum of this BigInteger: -1 for negative, 0 for zero, or
* 1 for positive. Note that the BigInteger zero <i>must</i> have
* a signum of 0. This is necessary to ensures that there is exactly one
* representation for each BigInteger value.
中文翻译:
此BigInteger的符号:-1表示负,0表示零,1表示正。
请注意,BigInteger零必须具有0的符号。
这对于确保每个BigInteger值只有一个表示是必要的。
*/
final int signum;
/**
* The magnitude of this BigInteger, in <i>big-endian</i> order: the
* zeroth element of this array is the most-significant int of the
* magnitude. The magnitude must be "minimal" in that the most-significant
* int ({@code mag[0]}) must be non-zero. This is necessary to
* ensure that there is exactly one representation for each BigInteger
* value. Note that this implies that the BigInteger zero has a
* zero-length mag array.
中文翻译:
此BigInteger的大小,按 “大端序” 顺序:此数组的第零个元素是大小的最高有效整数。
幅度必须为“最小”,因为最高有效整数( mag[0] )必须为非零。
这对于确保每个BigInteger值只有一个表示是必要的。
请注意,这意味着BigInteger零的mag数组长度为零。
*/
final int[] mag;
}
2.2.2、什么是 “Big-Endian 大端”,什么是 “Little-Endian 小端”?
- Big-Endian 大端 :高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
- Little-Endian 小端 :低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
2.2.3、mag 数组中,元素的存储逻辑
对于计算机而言,其实是没有数据类型的概念的,都是0101010101等二进制位,数据类型是编程语言自己规定的。由于Java语言,基本数据类型最大的只能表示到 long 型,为了表示更大的数,因此 Java中的 BigInteger类,通过 int[] 数组来组合表示大整数。
mag数组中的元素,存储逻辑如下:
- 首先将BigInteger值的绝对值,转换成二进制数。
- 然后从右向左,每隔32位的二进制数分为一组,并将每组的32位二进制数 转换为十进制整数。
- 最后,将每组转换后的十进制数,从左到右依次赋值给 mag 数组。
如下图所示,大整数 27670116110564327424,转为二进制、并从右到左每隔32位分为一组,即:[1 , 10000000000000000000000000000000, 00000000000000000000000000000000],最后将每组二进制数转为十进制数,即为:[1, -2147483648, 0] , 这就是 mag 数组的值。
2.2.4、代码实践验证:mag数组元素值
大整数 | 二进制数,按32位分组 | 转十进制后的mag数组元素值 |
4294967299 | 1, 00000000000000000000000000000011 | [1, 3] |
429496729999 | 1100100, 00000000000000000000000110001111 | [100, 399] |
27670116110564327424 | 1, 10000000000000000000000000000000, 00000000000000000000000000000000 | [1 -2147483648, 0] |
-27670116110564327424 |
debug模式,查看对象中的属性值,如下:
2.2、常用方法
2.2.1、构造方法
功能 | 构造方法名 | 使用说明 |
将String转为大整数 | BigInteger(String val) | 将 BigInteger 的十进制字符串表示形式转换为 BigInteger。 |
2.2.2、算数运算方法
功能 | 方法名 | 使用说明 |
返回此对象的正负号 | signum() | -1表示负数,0表示零,1表示正数。 |
转基本数据类型 | intValue()、longValue()、floatValue()、doubleValue() 等等 | 详见 1.2.1 章节。 |
转字符串 | toString() | 详见 1.2.2 章节。 |
两个整数比大小 | compareTo(BigInteger val) | 当前 BigInteger 对象 在数值上小于、等于或大于 参数 val 时,分别返回 -1,0,或 1。 |
获取两个整数的最大值 | max(BigInteger val) | 返回此 BigInteger 和 val 的最大值。 |
获取两个整数的最小值 | min(BigInteger val) | 返回此 BigInteger 和 val 的最小值。 |
绝对值 | abs() | 取绝对值 |
加 | add(BigInteger val) | 返回其值为 (this + val) 的 BigInteger。 |
减 | subtract(BigInteger val) | 返回其值为 (this - val) 的 BigInteger。 |
乘 | multiply(BigInteger val) | 返回其值为 (this * val) 的 BigInteger。 |
除 | divide(BigInteger val) | 返回其值为 (this / val) 的 BigInteger。 |
取余 | mod(BigInteger m) | 返回其值为 (this mod m) 的 BigInteger。 |
求最大公约数 | gcd(BigInteger val) | 返回一个 BigInteger,其值是 abs(this) 和 abs(val) 的最大公约数。 |
& 与运算 | and(BigInteger val) | 返回其值为 (this & val) 的 BigInteger。 |
| 或运算 | or(BigInteger val) | 返回其值为 (this | val) 的 BigInteger。 |
非运算 | not() | 返回其值为 (~this) 的 BigInteger。 |
三、java.math.BigDecimal 类
3.1、BigDecimal 类 的功能及作用
3.1.1、为什么要设计 BigDecimal 类 ?
由于现代计算机中采用了浮点数来表示小数,这种表示法会存在精度丢失的问题。这种精度丢失在诸如金融(如:商城系统、银行系统)、航天等需要高精度运算的场景下是无法接受的,为了保证精度,JDK中推出了BigDecimal类型,用来进行浮点数的精确计算。
1、代码实践:使用浮点数,计算商品总价,精度丢失的场景
public static void main(String[] args) {
// 商品价格
double price1 = 19.99;
double price2 = 9.99;
double price3 = 4.99;
// 商品数量
int quantity1 = 2;
int quantity2 = 3;
int quantity3 = 1;
// 计算每种商品的小计
double subtotal1 = price1 * quantity1;
double subtotal2 = price2 * quantity2;
double subtotal3 = price3 * quantity3;
// 计算总价
// 正确的值,应该是 74.94,但此处计算的结果是 74.93999999999998
double total = subtotal1 + subtotal2 + subtotal3;
// 输出结果
System.out.println("商品1小计:" + subtotal1);
System.out.println("商品2小计:" + subtotal2);
System.out.println("商品3小计:" + subtotal3);
System.out.println("购物车总价:" + total);
}
日志打印结果如下:
2、代码实践:使用BigDecimal,计算商品总价,保证精度
public static void main(String[] args) {
// 商品价格
BigDecimal price1 = new BigDecimal("19.99"); //注意,此处入参是 String类型
BigDecimal price2 = new BigDecimal("9.99");
BigDecimal price3 = new BigDecimal("4.99");
// 商品数量
int quantity1 = 2;
int quantity2 = 3;
int quantity3 = 1;
// 计算每种商品的小计
BigDecimal subtotal1 = price1.multiply(new BigDecimal(quantity1));
BigDecimal subtotal2 = price2.multiply(new BigDecimal(quantity2));
BigDecimal subtotal3 = price3.multiply(new BigDecimal(quantity3));
// 计算总价
// 正确的值,应该是 74.94,此处计算的结果也是 74.94
BigDecimal total = subtotal1.add(subtotal2).add(subtotal3);
// 输出结果
System.out.println("商品1小计:" + subtotal1);
System.out.println("商品2小计:" + subtotal2);
System.out.println("商品3小计:" + subtotal3);
System.out.println("购物车总价:" + total);
}
日志打印结果如下:
3.1.2、Java API 中的解释
- 不可变的、任意精度的、有符号的 十进制数。
- 【特别注意】BigDecimal 是不可变的,对象内的属性 intVal、intCompact 、scale 三个参数都是 final 修饰的,也就是说一旦创建对象,就不能再改变其数值。BigDecimal 定义的加减乘除等运算的方法,其结果都是返回一个新的 BigDecimal 对象。
- BigDecimal 由任意精度的整数非标度值 (unscaledValue) 和 整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负 scale 次幂。因此,BigDecimal 表示的数值是 (unscaledValue × 10-scale)。
- 【名词解释】非标度值unscaledValue,就是 BigDecimal 的绝对值 在去掉小数点后的整数表示。标度 scale,就是 BigDecimal 的小数位数。 如 "123.456" ,非标度值是 "123456",标度是3。如 "-1000.12",非标度值是"100012", 标度是2。
- BigDecimal 类提供多种方法,如:算术运算、标度操作、舍入、比较、哈希算法、格式转换。
- BigDecimal 类让用户能完全控制舍入行为。
- 【特别注意】如果未指定舍入模式,并且无法表示准确结果,则抛出一个异常。
- 在任何情况下,可以使用 RoundingMode 枚举类的枚举值(如:RoundingMode.HALF_UP),为舍入控制提供八种舍入模式。也可以通过向该操作提供适当的 MathContext 对象,对已选择的精度和舍入模式执行计算。
3.2、BigDecimal 的底层实现
3.2.1、源码:BigDecimal类,定义的属性
/**
* The scale of this BigDecimal,
* as returned by {@link #scale}.
中文翻译:
此BigDecimal的小数位数(标度),由{scale}返回。
*/
private final int scale;
/**
* The unscaled value of this BigDecimal,
* as returned by {@link #unscaledValue}.
中文翻译:
此BigDecimal的非标度值,由{unscaledValue}返回。
*/
private final BigInteger intVal;
/**
* If the absolute value of the significand of this BigDecimal is
* less than or equal to {@code Long.MAX_VALUE}, the value can be
* compactly stored in this field and used in computations.
中文翻译:
如果此BigDecimal的有效位的绝对值(非标度值unscaledValue)小于或等于Long类型的最大值(Long.MAX_VALUE),
则该值可以紧凑地存储在此字段中并用于计算。
*/
private final transient long intCompact;
/**
* Sentinel value for {@link #intCompact} indicating the
* significand information is only available from {@code intVal}.
中文翻译:
表示有效位信息的{intCompact}的哨兵值,仅可从{intVal}获得。
*/
static final long INFLATED = Long.MIN_VALUE;
private static final BigInteger INFLATED_BIGINT
= BigInteger.valueOf(INFLATED);
3.2.2、源码:BigDecimal类,其中一个构造方法
// 构造方法源码
public BigDecimal(BigInteger unscaledVal, int scale) {
this.intVal = unscaledVal;
// 如果非标度值unscaledVal,在long类型范围内,则 intCompact = unscaledVal;
// 如果非标度值unscaledVal,超过了long类型范围,则 intCompact = Long.MIN_VALUE。
this.intCompact = compactValFor(unscaledVal);
this.scale = scale;
}
/**
* Returns the compact value for given {@code BigInteger}, or
* INFLATED if too big. Relies on internal representation of
* {@code BigInteger}.
中文翻译:
返回给定的 BigInteger参数 b 的紧凑值。
如果太大(超过long类型的整数范围),则返回INFLATED(值为:Long.MIN_VALUE)。
*/
private static long compactValFor(BigInteger b) {
int[] m = b.mag;
int len = m.length;
if (len == 0)
return 0;
int d = m[0];
if (len > 2 || (len == 2 && d < 0))
return INFLATED;
long u = (len == 2)?
(((long) m[1] & LONG_MASK) + (((long)d) << 32)) :
(((long)d) & LONG_MASK);
return (b.signum < 0)? -u : u;
}
3.2.3、源码:BigDecimal类,其中一个加法运算
//当对象的intCompact不等于INFLATED(值为 -9223372036854775808)时,就用对象的intCompact进行计算。
//当intCompact等于 INFLATED 时,就用对象的 intVal 来计算。
public BigDecimal add(BigDecimal augend) {
if (this.intCompact != INFLATED) {
if ((augend.intCompact != INFLATED)) {
return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
} else {
return add(this.intCompact, this.scale, augend.intVal, augend.scale);
}
} else {
if ((augend.intCompact != INFLATED)) {
return add(augend.intCompact, augend.scale, this.intVal, this.scale);
} else {
return add(this.intVal, this.scale, augend.intVal, augend.scale);
}
}
}
3.2.4、底层存储结构总结
综合以上3节的源码,可以得出以下结论:
scale | 此BigDecimal的小数位数(标度)。 |
intVal 属性 | 此BigDecimal的非标度值unscaledVal。 【特别注意】使用 BigDecimal(String val) 构造方法进行实例化时,当非标度值在long类型范围内时,intVal 可能是null (参考3.2.5章节)。 |
intCompact | 如果非标度值unscaledVal,在long类型范围内,则 intCompact = unscaledVal; 如果非标度值unscaledVal,超过了long类型范围,则 intCompact = Long.MIN_VALUE。 |
算数运算规则 | 通过 add(BigDecimal augend) 加法运算的源码,可以看出:
|
其他说明 | 其实继续跟踪 add() 方法的底层源码时,当两个BigDecimal 对象的标度不一样时,会计算出二者最大的标度值,然后将两个BigDecimal转为同一标度的数后,在进行一系列算数运算。 |
3.2.5、代码验证验证:通过 debug 查看对象中的属性值
public static void main(String[] args) {
String s1 = "123.999";
String s2 = "276701161105643274.24";
BigInteger i1 = new BigInteger(s1.replace(".",""));
BigInteger i2 = new BigInteger(s2.replace(".",""));
// 构造方法一:使用 BigInteger 实例化
BigDecimal b_i1 = new BigDecimal(i1, 3);
BigDecimal b_i2 = new BigDecimal(i2, 3);
// 构造方法二:使用 String 实例化
BigDecimal b_s1 = new BigDecimal(s1);
BigDecimal b_s2 = new BigDecimal(s2);
}
3.3、精度控制、舍入模式
3.3.1、JAVA API 中的描述
由于同一数值可以有不同的表示形式(具有不同的标度),因此运算和舍入的规则必须同时指定数值结果和结果表示形式中所用的标度。
一般情况下,当准确结果(在除法中,可能有无限多位)比返回的数值具有更多位数时,舍入模式和精度设置确定操作如何返回具有有限位数的结果。
对于所有算术运算符,运算的执行方式是,首先计算准确的中间结果,然后,使用选择的舍入模式将其舍入为精度设置(如有必要)指定的位数。如果不返回准确结果,则将丢弃准确结果的某些数位。当舍入增加了返回结果的大小时,前导数字“9”的进位传播可能会创建新的数位。例如,将值 999.9 舍入为三位数字,则在数值上等于一千,表示为 100×101。在这种情况下,新的 "1" 是返回结果的前导数位。
除了逻辑的准确结果外,每种算术运算都有一个表示结果的首选标度。下表列出了每个运算的首选标度。
3.3.2、枚举 RoundingMode ,指定的几种舍入模式
为可能丢弃精度的数值操作指定一种舍入行为。每种舍入模式都指示如何计算返回舍入结果位数的最低有效位。如果返回的位数比表示精确数值结果所需的位数少,则舍弃的位数称为舍弃部分,而不管这些位数对数值的作用如何。换句话说,假设是一个数值,舍弃部分的绝对值可能大于 1。
3.3.3、BigDecimal保留两位小数
参考我的另一篇博客: BigDecimal保留两位小数-CSDN博客
3.4、常用方法
3.4.1、构造方法
功能 | 构造方法名 | 方法说明 |
String作为入参 | 使用构造方法 实例化: new | 将 BigDecimal 的字符串表示形式转换为 BigDecimal。 |
double、float做为入参 | 使用静态方法valueOf 实例化: BigDecimal.valueOf(double val) | 使用 |
【特别注意】BigDecimal虽然能杜绝运算过程中的精度丢失,但无法杜绝初始化过程中的精度丢失。构造方法 BigDecimal(double val) 就会存在精度丢失的情况(因为入参double类型在计算机的内存中以二进制存储,本来就存在精度丢失的情况)。而静态方法 BigDecimal.valueOf(double val),是先将double类型转为字符串形式,然后再通过 BigDecimal(String val)
进行实例化,所以,不会丢失精度。
// 静态方法 valueOf() 的源码
public static BigDecimal valueOf(double val) {
return new BigDecimal(Double.toString(val));
}
3.4.2、设置精度 和 舍入模式
1、同时指定标度 和 舍入模式
方法名:
public BigDecimal setScale(int newScale, RoundingMode roundingMode){
......
}
public BigDecimal setScale(int newScale,int roundingMode){
......
}
参数:
- newScale - 要返回的 BigDecimal 值的标度。
- roundingMode - 要应用的舍入模式。
方法解释:
返回 BigDecimal,其标度为 newScale 参数指定值,其非标度值通过此 BigDecimal 的非标度值乘以或除以十的适当次幂来确定,以维护其总值。如果该操作减少标度,则非标度值必须被除(而不是乘),并且该值可以更改;在这种情况下,将指定的舍入模式应用到除法中。
【特别注意】由于 BigDecimal 对象是不可变的,因此此方法的调用不会 导致初始对象被修改,setScale 返回具有适当标度的对象;返回的对象不一定是新分配的。(即:原对象并不会改变,setScale() 方法 返回的是一个新的 BigDecimal 对象)。
2、只指定标度,不指定舍入模式
方法名:
public BigDecimal setScale(int newScale){
......
}
解释:
返回一个 BigDecimal,其标度为 newScale 参数指定值,其值在数值上等于此 BigDecimal 的值。如果这不可能,则抛出 ArithmeticException。
此调用通常用于增加标度,在这种情况下,可以保证存在指定标度和正确值的 BigDecimal。如果调用方知道 BigDecimal在其小数部分的结尾有足够多的零(即其整数值中的十的因子),则该调用也可用于减少标度,以允许重新标度,而不更改其值。
此方法返回与 setScale 的两个参数版本相同的结果,但是,为调用方省去了指定舍入模式的麻烦(舍入模式不影响结果)。
3.4.3、算数运算方法
功能 | 方法名 | 使用说明 |
转基本数据类型 | intValue()、longValue()、floatValue()、doubleValue() 等等 | 详见 1.2.1 章节。 |
转字符串 | toString() | 详见 1.2.2 章节。 |
两个对象比大小 | compareTo(BigDecimal val) | 当前 BigDecimal 对象 在数值上小于、等于或大于 参数 val 时,分别返回 -1,0,或 1。 |
获取两个整数的最大值 | max(BigDecimal val) | 返回此 BigDecimal 和 val 的最大值。 |
获取两个整数的最小值 | min(BigDecimal val) | 返回此 BigDecimal 和 val 的最小值。 |
绝对值 | abs() | 返回 BigDecimal,其值为此 BigDecimal 的绝对值, 其标度为 this.scale()。 |
取反 | negate() | 返回 BigDecimal,其值为 (-this), 其标度为 this.scale()。 |
加 | add(BigDecimal augend) | 返回一个 BigDecimal,其值为 (this + augend), 其标度为 max(this.scale(), augend.scale())。 |
减 | subtract(BigDecimal subtrahend) | 返回一个 BigDecimal,其值为 (this - subtrahend), 其标度为 max(this.scale(), subtrahend.scale())。 |
乘 | multiply(BigDecimal multiplicand) | 返回一个 BigDecimal,其值为 (this × multiplicand), 其标度为 (this.scale() + multiplicand.scale())。 |
除 | 参考下面的“3.4.4、除法运算”章节。 | |
取余 | remainder(BigDecimal divisor) | 返回其值为 (this % divisor) 的 BigDecimal。 |
【特别注意】当判断两个BigDecimal的值是否相等时,要用compareTo() 去比较,而不要用 equals()方法。因为,compareTo() 只比较值是否相等,而equals()会比较值与标度是否完全相同。
3.4.4、除法运算
1、除法方法
方法名 | 方法说明 | |
三个参数 | divide(BigDecimal divisor, int scale, RoundingMode roundingMode) | 返回一个 BigDecimal,其值为 (this / divisor),其标度为指定标度。
|
divide(BigDecimal divisor, int scale, int roundingMode) | ||
两个参数 | divide(BigDecimal divisor, int roundingMode) | 返回一个 BigDecimal,其值为 (this / divisor),其标度为 this.scale()。 |
一个参数 | divide(BigDecimal divisor) | 返回一个 BigDecimal,其值为 (this / divisor), 其首选标度为 (this.scale() - divisor.scale()); 如果无法表示准确的商值(因为它有无穷的十进制扩展),则抛出 ArithmeticException。 |
2、三个参数的除法,方法解释
public BigDecimal divide(BigDecimal divisor,
int scale,
RoundingMode roundingMode){
......
}
- 第一个参数 divisor ,用来指定 此 BigDecimal 要除以的值。
- 第二个参数 scale ,用于指定运算结果中应保留的小数位数。这个参数对于控制计算结果的精度非常重要。通过设置不同的保留小数位数,可以满足不同应用场景对精度的需求。例如,如果设置保留一位小数,那么计算结果将按照舍入模式 将小数舍入到最接近的数值,保留一位小数。这种灵活性使得BigDecimal在处理金融计算等需要高精度计算的场景中非常有用。
- 第三个参数 roundingMode ,用来指定多种舍入模式(参见“3.3、精度控制、舍入模式”章节)。这些舍入模式包括但不限于直接省略多余的小数(ROUND_DOWN)、直接进位(ROUND_UP)、四舍五入(ROUND_HALF_UP)等,以及更复杂的舍入模式,如:ROUND_HALF_EVEN(在5的情况下,根据前一位的奇偶性决定舍入方向)和ROUND_UNNECESSARY(断言操作具有精确结果,否则抛出异常)。这些舍入模式的选择进一步增加了BigDecimal在处理复杂计算时的灵活性和准确性。
---------------------------------------------------------------------
-------------------------------参考资源-----------------------------
---------------------------------------------------------------------
查看 Java API 文档 + 阅读 java 源码 + 编写代码进行验证
同时,参考以下优秀博客的内容:
BigInteger的底层数据结构_biginteger底层结构-CSDN博客
BigInteger底层原理实现_biginterger底层-CSDN博客
详解BigDecimal_bigdecimal底层实现-CSDN博客
Java之API详解之BigDecimal类的详细解析_java中bigdecimal底层存储方式-CSDN博客