【Java基础系列】BigDecimal入门
一.基本介绍
1.什么是 BigDecimal?
BigDecimal
是 Java 中的一个类,用于表示任意精度的十进制数。它属于 java.math
包,并提供了高精度的浮点数运算。与基本数据类型的浮点数(如 float
和 double
)不同,BigDecimal
可以表示精确的小数,并且不会出现舍入误差。
2.BigDecimal 特点?
主要的特点包括:
-
任意精度:
BigDecimal
可以处理非常大或非常小的数字,而不会失去精度。这对于需要精确计算货币、税收等金融领域的数据非常重要。 -
不受二进制浮点数表示误差的影响: 由于二进制浮点数表示法的限制,基本数据类型的浮点数可能会导致舍入误差。
BigDecimal
使用基于十进制的表示,避免了这种误差。 -
支持精确的算术运算:
BigDecimal
提供了一系列的算术运算方法,如加法、减法、乘法和除法,这些运算可以保持高精度。 -
不可变性:
BigDecimal
对象是不可变的,一旦创建,就不能被修改。这有助于确保线程安全性。 -
丰富的方法:
BigDecimal
提供了许多用于比较、取整、取余等操作的方法。
3.使用简介
以下是一个简单的示例,演示如何使用 BigDecimal
进行精确计算:
import java.math.BigDecimal;
public class BigDecimalExample {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("10.5");
BigDecimal num2 = new BigDecimal("2.3");
// 加法
BigDecimal sum = num1.add(num2);
System.out.println("Sum: " + sum);
// 减法
BigDecimal difference = num1.subtract(num2);
System.out.println("Difference: " + difference);
// 乘法
BigDecimal product = num1.multiply(num2);
System.out.println("Product: " + product);
// 除法,指定保留小数位数和舍入模式
BigDecimal quotient = num1.divide(num2, 2, BigDecimal.ROUND_HALF_UP);
System.out.println("Quotient: " + quotient);
}
}
在这个示例中,BigDecimal
被用于执行精确的加法、减法、乘法和除法操作,并且可以通过指定保留小数位数和舍入模式来控制除法的结果。
二.使用简介
1.求和
求和
BigDecimal sum = Arrays.stream(bdArray).reduce(BigDecimal.ZERO, (p, q) -> p.add(q));
BigDecimal sum = list.stream().map(Person::getWeight)
.reduce(BigDecimal.ZERO, BigDecimal::add);
2.自定义求和
自定义求和
BigDecimal sum = map.values().stream().reduce(BigDecimal.ZERO, Utility::addWeight);
import java.math.BigDecimal;
public class Utility {
public static BigDecimal addWeight(BigDecimal w1, BigDecimal w2) {
return w1.add(w2);
}
}
3.小数计算
小数
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
public class BigDecimalSumUsingList {
public static void main(String[] args) {
Person p1 = new Person("AAA", new BigDecimal("45.23"));
Person p2 = new Person("BBB", new BigDecimal("55.43"));
Person p3 = new Person("CCC", new BigDecimal("65.21"));
Person p4 = new Person("DDD", new BigDecimal("35.73"));
List<Person> list = Arrays.asList(p1, p2, p3, p4);
BigDecimal sum = list.stream().map(Person::getWeight)
.reduce(BigDecimal.ZERO, BigDecimal::add);
System.out.println(sum);
sum = list.stream().map(p -> p.getWeight())
.reduce(BigDecimal.ZERO, (b1, b2) -> b1.add(b2));
System.out.println(sum);
sum = list.stream().map(Person::getWeight)
.reduce(BigDecimal.ZERO, Utility::addWeight);
System.out.println(sum);
}
}
BigDecimal求和
BigDecimal sum = products.stream()
.map(Product::getPrice)
.reduce(BigDecimal.ZERO, BigDecimal::add);
先相乘再累加:
final BigDecimal mgmtPrmAmt = value.stream()
.map(product -> product.getMgmtPrmAmt().multiply(new BigDecimal(product.getSalQty())))
.reduce(BigDecimal.ZERO, BigDecimal::add);
4.加减乘除
public BigDecimal add(BigDecimal value); //加
public BigDecimal subtract(BigDecimal value);//减
public BigDecimal multiply(BigDecimal value); //乘
public BigDecimal divide(BigDecimal value); //除
5.除法细节
RoundingMode类型:
- ROUND_HALF_UP:根据保留数字后一位>=5 进行四舍五入
- ROUND_UP:不管保留数字后面是大是小(0 除外)都会进 1
- ROUND_DOWN:保留设置数字,后面所有直接去除
- ROUND_HALF_DOWN:跟 ROUND_HALF_UP 差别仅在于 0.5 时会向下取整
- ROUND_HALF_EVEN:取最近的偶数
- ROUND_UNNECESSARY:不需要取整,如果存在小数位,就抛 ArithmeticException 异常
- ROUND_CEILING:如果 BigDecimal 是正的,则做 ROUND_UP 操作;如果为负,则做 ROUND_DOWN 操作 (一句话:取附近较大的整数)
- ROUND_FLOOR: 如果 BigDecimal 是正的,则做 ROUND_DOWN 操作;如果为负,则做 ROUND_UP 操作(一句话:取附近较小的整数)
BigDecimal 的 divide 方法是用于执行除法运算的,其语法如下:
#语法
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
#举例
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP);
其中,参数说明如下:
- divisor:被除数,即要将当前 BigDecimal 对象除以的数。
- scale:表示除法运算的结果要保留的小数位数。
- roundingMode:表示舍入模式,用于在执行除法运算时确定如何舍入结果。
该方法返回一个 BigDecimal 对象,表示除法运算的结果。
import java.math.BigDecimal;
/**
* @author : qinyingjie
* @version : 2.2.0
* @date : 2022/10/5 12:27
*/
class Scratch {
public static void main(String[] args) {
//Java中BigDecimal取整方法
BigDecimal bd = new BigDecimal("12.1");
long l1 = bd.setScale(0, BigDecimal.ROUND_UP).longValue(); // 向上取整
long l2 = bd.setScale(0, BigDecimal.ROUND_DOWN).longValue(); // 向下取整
System.out.println(l1);
System.out.println(l2);
}
}
要将 a 除以 b,并将结果保留 2 位小数,可以使用如下代码:
BigDecimal a = new BigDecimal("10");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b, 2, RoundingMode.HALF_UP);
System.out.println(result);
在上述代码中,RoundingMode.HALF_UP 表示使用“四舍五入”方式将结果舍入到 2 位小数。
需要注意的是,如果除数为 0,则会抛出 ArithmeticException 异常。另外,如果计算结果超出了 BigDecimal 能表示的范围,则会抛出 ArithmeticException 异常。因此,在使用 BigDecimal 进行除法运算时,需要对这些异常进行适当的处理。
6.BigDecimal 与 0 比较
BigDecimal 如何判断是否大于 0、小于 0 和等于 0
if (number.compareTo(BigDecimal.ZERO) == 0) {
System.out.println("BigDecimal对象等于0");
} else {
System.out.println("BigDecimal对象不等于0");
}
7.保留 4 位小数
取最大值并保留 4 位小数,使用的方法是一个数除以 1 等于它本身
max.setTotalSalQtyStoreRate(new BigDecimal(result.stream().mapToDouble(item -> Objects.nonNull(item.getTotalSalQtyStoreRate()) ? item.getTotalSalQtyStoreRate().doubleValue() : 0).max().getAsDouble())
.divide(new BigDecimal(1), 4, BigDecimal.ROUND_DOWN));
8.注意点
注意点:
- 小数先转为 String,不然会出现精度错误
- 比较用 compareTo,并且比较 0,用 BigDecimal.ZERO
- 除法时要确定保留小数的位数,不然有可能出现除不尽的情况
- 如果传入的字符串是一个非法的数值(null、字母、空),NumberFormatException 异常,
- 非空校验
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
roundingMode 为四舍五入的规则模型,用常量 int 来表示