02、大数 -- BigDecimal、BigInteger

一、概述


  • 如果基本的 整数浮点数 精度不足以满足需求,可以使用 java.math 包中两个很有用的类:
    • BigInteger 和 BigDecimal。
  • 这两个类可以处理包含任意长度 数字序列数值

二、BigInteger


1、用途

  • BigInteger 类可实现任意精度整数运算。如:
  • 超出long范围(-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807)的大整数

2、典型场景

  • 大数阶乘(如:1000!)。
  • 密码学中的大数运算(如:RSA算法)。
  • 精确的整数计算(如:财务或科学计算)。
public class BigIntegerTest {
    public static void main(String[] args) {
        // 使用静态的 valueOf 方法可以将一个【普通的数】转换为【大数】。
        BigInteger c = BigInteger.valueOf(100);
        // 输出:100
        System.out.println(c);

        // 对于更长的数,可以使用一个带 【字符串参数】 的构造器:
        BigInteger a = new BigInteger("123456789012345678901234567890");
        // 输出:123456789012345678901234567890
        System.out.println(a);
        BigInteger b = new BigInteger("987654321098765432109876543210");
        // 输出:987654321098765432109876543210
        System.out.println(b);

        // 加法
        BigInteger sum = a.add(b);
        // 输出:1111111110111111111011111111100
        System.out.println(sum);

        // 乘法
        BigInteger product = a.multiply(b);
        // 输出:121932631137021795226185032733622923332237463801111263526900
        System.out.println(product);
    }
}

三、BigDecimal


1、用途:

  • BigDecimal 类可实现任意精度浮点数运算。避免 double/float 的精度丢失问题。

2、典型场景:

  • 金融计算(如:货币金额)。
  • 科学计算需要精确小数位的场景。
  • 避免浮点数舍入误差(如:0.1 无法精确表示为二进制浮点数)。

3、注意事项:

  • 1、避免使用 double 初始化:直接用 double 会导致精度丢失
public class BigDecimalTest {
    public static void main(String[] args) {
        // 不精确的数 -- 丢失精度的值(实际值≈0.10000000000000000555...)
        BigDecimal d1 = new BigDecimal(0.1);
        
        // 输出:0.1000000000000000055511151231257827021181583404541015625
        System.out.println(d1);

        // 精确的数 -- 未丢失精度的值
        BigDecimal d2 = new BigDecimal("0.1");
        // 输出:0.1
        System.out.println(d2);
    }
}

  • 2、 精度与标度管理:乘法和除法需显式设置精度,避免累积误差
    • 通过 MathContextsetScale() 方法,显式控制精度标度
BigDecimal x = new BigDecimal("1.234");
BigDecimal y = new BigDecimal("5.678");
// 保留两位小数
BigDecimal product = x.multiply(y).setScale(2, RoundingMode.HALF_UP); 
  • 3、 舍入模式(舍弃、向上/向下取整、四舍五入):
    • 必须指定舍入模式,否则可能抛出 ArithmeticException :
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
// 错误:未指定舍入模式
// BigDecimal result = a.divide(b); 
// 正确:必须指定舍入模式,确保结果可控。
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP); // 结果为0.33
  • 4、 等值比较:使用 compareTo() 而非 equals() :
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
a.equals(b); // false(精度不同)
a.compareTo(b) == 0; // true(数值相等)
  • 5、不可变性(Immutability)
    • 所有运算生成新对象原始值不变,避免并发修改导致的意外精度损失
BigDecimal a = new BigDecimal("10.5");

// a 的值仍为 10.5
BigDecimal b = a.add(new BigDecimal("2.3"));

四、BigDecimal 进行运算时,保证精度不丢失的原理


1、十进制存储结构

  • BigDecimal 内部通过两个核心组件表示数值:
    • 未缩放值 (Unscaled Value) / 未缩放的整数
      • 一个任意精度整数 (BigInteger),存储数值的有效数字。
    • 标度(Scale)
      • 一个 32 位整数,表示小数点后位数(即:小数点的位置)。
  • 所有运算均基于 BigInteger 的整数运算,完全避免浮点数二进制精度丢失

  • 如:数值 123.45 表示为:
    • 未缩放值 = 12345
    • 标度 = 2(即 12345 × 10⁻²)
  • 这种设计直接以十进制形式****存储数值避免了二进制浮点数精度丢失问题
    • 如:0.1 无法用二进制进行精确表示

2、精确的运算规则

  • BigDecimal 的运算通过整数操作实现,确保每一步计算精确可控:
    • 如:123.45(标度 2),67.8(标度 1) 两个数字。

1)、加/减法
  • 标度对齐:将两个操作数的标度调整为相同值
    • 123.45 (标度 2) + 67.8 (标度 1) → 转换为 67.80 (标度 2)。
  • 整数运算:直接对未缩放值进行加减
    • 12345 + 6780 = 19125 → 结果 191.25 (标度 2)。

2)、乘法
  • 未缩放值 相乘:直接对未缩放值进行乘法
    • 12345 × 678 = 8364810
  • 标度相加:结果的标度为两个操作数 标度之和
    • 标度 2 + 1 = 3 → 结果 8364.810。

3)、除法
  • 显式指定 精度舍入模式:必须定义结果的小数位数舍入规则,否则抛出异常。
    • 如:1 ÷ 3 指定标度为 2,舍入模式为 HALF_UP → 结果 0.33。

你可能感兴趣的:(#,java常用类,java,开发语言,青少年编程,后端)