一、BigDecimal概述
BigDecimal 是 Java 中用于高精度计算的类,属于 java.math 包。它主要用于处理需要极高精度的数值运算。
记住:在金融计算、价格计算等需要精确小数运算的场景中,BigDecimal 是唯一正确的选择。
二、double、float 类型的坑
double result = 0.1 + 0.2; // 结果为 0.30000000000000004
float a = 1;
float b = 0.9f;
System.out.println(a - b); // 结果为 0.100000024
三、BigDecimal优越性
- 高精度数值计算:BigDecimal是 Java 中用于高精度数值计算的类。它主要用于处理需要精确表示的十进制数,特别是在金融、货币计算等对精度要求极高的领域。双精度浮点型变量double 虽然可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理,BigDecimal 就可以用来对超过16位有效位的数进行精确的运算。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以开发中,如果我们需要精确计算的结果,则必须使用BigDecimal类来操作。
- 不可变对象:BigDecimal对象是不可变的,这意味着一旦创建,其数值就不能被修改(即线程安全)。对BigDecimal对象进行的任何操作(如加法、减法、乘法、除法等)都会返回一个新的BigDecimal对象,而原对象保持不变。这种不可变性有助于确保数据的一致性和线程安全性。
当程序中涉及到金额时,必须使用 BigDecimal 而不是 double 或 float 。
double result = 0.1 + 0.2; // 结果为 0.30000000000000004
BigDecimal bd1 = new BigDecimal("0.1");
BigDecimal bd2 = new BigDecimal("0.2");
BigDecimal sum = bd1.add(bd2); // 结果为 0.3
四、BigDecimal 的常用方法
4.1 构造方法
- new BigDecimal(int val):创建一个具有参数所指定整数值的对象。
- new BigDecimal(double val):(不推荐使用,因为存在精度丢失问题)创建一个具有参数所指定双精度值的对象。
- new BigDecimal(long val):创建一个具有参数所指定长整数值的对象
- new BigDecimal(String val):(推荐使用)创建一个具有参数所指定以字符串表示的数值的对象。
BigDecimal b_int = new BigDecimal(10);
BigDecimal b_double = new BigDecimal(0.1);
BigDecimal b_float = new BigDecimal(0.1f);
BigDecimal b_string = new BigDecimal("0.1");
BigDecimal b_valueof = BigDecimal.valueOf(0.1);
System.out.println("b_int:" + b_int); // b_int:10
System.out.println("b_double:" + b_double); // b_double:0.1000000000000000055511151231257827021181583404541015625
System.out.println("b_float:" + b_float); // b_float:0.100000001490116119384765625
System.out.println("b_string:" + b_string); // b_string:0.1
System.out.println("b_valueof:" + b_valueof); // b_valueof:0.1
使用构造函数时,推荐用 int 或 String 做参数,如果不满足,则建议使用 BigDecimal.valueOf 方法,该方法可接受 int、long、double、float 做参数,但注意不接受 String。
4.2 算术运算方法
- BigDecimal add(BigDecimal augend) 加法运算。
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("3.20");
BigDecimal sum = a.add(b); // 13.70
- BigDecimal subtract(BigDecimal subtrahend) 减法运算
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("3.20");
BigDecimal difference = a.subtract(b); // 7.30
- BigDecimal multiply(BigDecimal multiplicand) 乘法运算
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("3.20");
BigDecimal product = a.multiply(b); // 33.600
- BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode) 除法运算
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("3.20");
// 四舍五入保留2位小数
BigDecimal quotient1 = a.divide(b, 2, RoundingMode.HALF_UP); // 3.28
// 使用默认舍入模式
BigDecimal quotient2 = a.divide(b, RoundingMode.HALF_UP);
- BigDecimal remainder(BigDecimal divisor) 取余运算
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3.2");
BigDecimal remainder = a.remainder(b); // 0.9
- BigDecimal[] divideAndRemainder(BigDecimal divisor) 除法取余
BigDecimal a = new BigDecimal("10.5");
BigDecimal b = new BigDecimal("3.2");
BigDecimal[] results = a.divideAndRemainder(b);
// results[0] = 3 (商)
// results[1] = 0.9 (余数)
- BigDecimal abs() 绝对值
BigDecimal a = new BigDecimal("-10.50");
BigDecimal absolute = a.abs(); // 10.50
- BigDecimal pow(int n) 幂运算
BigDecimal a = new BigDecimal("2.5");
BigDecimal power = a.pow(3); // 15.625
- BigDecimal setScale(int newScale, RoundingMode roundingMode) 四舍五入
BigDecimal a = new BigDecimal("3.1415926");
// 四舍五入保留3位小数
BigDecimal rounded = a.setScale(3, RoundingMode.HALF_UP); // 3.142
// 直接截断
BigDecimal truncated = a.setScale(3, RoundingMode.DOWN); // 3.141
- int compareTo(BigDecimal val) 比较运算
BigDecimal a = new BigDecimal("10.50");
BigDecimal b = new BigDecimal("10.5");
int result = a.compareTo(b);
// result = 0 (相等)
// result < 0 (a < b)
// result > 0 (a > b)
五、BigDecimal遇到的问题
5.1 相除精度丢失的问题
问题:两数相除,如果9/3=3整除没问题,但是10/3=0.33333333…除不尽,这里不能让电脑一直除不尽,所以BigDecimal做出一些限制;
必须按照(数,保留小数位(最好要合理限制最大精度),舍入方式)来操作,否则就会抛出异常,例如:
public static void main(String[] args) {
BigDecimal a = new BigDecimal(10);
BigDecimal b = new BigDecimal(3);
BigDecimal c = a.divide(b);
}
执行:抛出
Exception in thread “main” java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1616)
5.2 BigDecimal初始化时入参使用String类型,如果不是数字会抛异常NumberFormatException
// java.lang.NumberFormatException
BigDecimal val = new BigDecimal("a");
System.out.println(val);
5.3 用 equals 判断相等
BigDecimal x = new BigDecimal("1.0");
BigDecimal y = new BigDecimal("1.00");
System.out.println(x.equals(y)); //false 尽管 1.0 和 1.00 的数值相等,但精度不一样,equals 判定为不同。