BigDecimal数据类型加减乘除之间的一些问题
了解BigDecimal类型
BigDecimal是java中一个用于表示任意精度的实数的类,它可以表示非常大或非常小的数,同时还能保持精确度。
与其他基本数据类型不同的是,BigDecimal提供了精确的计算,支持加,减,乘,除以及取余等运算。同时BigDecimal还可以设置精度和舍入模式,从而保证计算结果的准确性。
BigDecimal设置精度和舍入的方式
首先,可以使用setScale(int newScale, RoundingMode roundingMode)设置精度和舍入模式,newScale表示要设置的精度,roundingMode表示要使用的舍入模式。
BigDecimal decimal = new BigDecimal("3.14159"); BigDecimal result = decimal.setScale(2,RoundingMode.HALF_UP);
在这个例子中,设置了结果的精度为 2,使用的舍入模式为 RoundingMode.HALF_UP
。表示采用“四舍五入”的方式进行舍入,保留两位小数.
在 Java 中,BigDecimal
类提供了多种舍入模式,用于指定进行四舍五入时的规则。以下是 RoundingMode
枚举类中定义的八种舍入模式:
UP
:向远离零的方向舍入,即“正无穷”方向,例子:3.125 舍入为 3.13。DOWN
:向靠近零的方向舍入,即“负无穷”方向,例子:-3.125 舍入为 -3.12。CEILING
:向正无穷的方向舍入,如果为正数则执行UP
舍入,如果为负数则执行DOWN
舍入,例子:3.125 舍入为 3.13,-3.125 舍入为 -3.12。FLOOR
:向负无穷的方向舍入,如果为正数则执行DOWN
舍入,如果为负数则执行UP
舍入,例子:3.125 舍入为 3.12,-3.125 舍入为 -3.13。HALF_UP
:向最接近的整数方向舍入。如果两个相邻的整数中间有距离相等,则向上舍入。例如,2.5 舍入为 3.0,-2.5 舍入为 -3.0。HALF_DOWN
:向最接近的整数方向舍入。如果两个相邻的整数中间有距离相等,则向下舍入。例如,2.5 舍入为 2.0,-2.5 舍入为 -2.0。HALF_EVEN
:向最接近的整数方向舍入。如果两个相邻的整数中间有距离相等,则向相邻的偶数舍入。例如,2.5 舍入为 2.0,3.5 舍入为 4.0。UNNECESSARY
:断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定了此类舍入模式,则抛出ArithmeticException
异常。
加法运算
public BigDecimal add(BigDecimal augend)
,其中 augend
是被加数,返回相加后的结果。
BigDecimal num1 = new BigDecimal("1.23"); BigDecimal num2 = new BigDecimal("2.34"); // 加法 BigDecimal sum = num1.add(num2); System.out.println("和:" + sum);
减法运算
public BigDecimal subtract(BigDecimal subtrahend)
,其中 subtrahend
是减数,返回相减后的结果。
// 减法 BigDecimal diff = num1.subtract(num2); System.out.println("差:" + diff);
乘法运算
public BigDecimal multiply(BigDecimal multiplicand)
,其中 multiplicand
是乘数,返回相乘后的结果。
// 乘法 BigDecimal product = num1.multiply(num2); System.out.println("积:" + product);
除法运算
public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
,其中 divisor
是除数,scale
是小数点后保留的位数,roundingMode
是舍入模式,返回相除后的结果。需要注意的是,divide()
方法在进行除法运算时,如果除不尽会抛出 ArithmeticException
异常。另外,在进行除法运算时,需要指定舍入模式,否则可能会出现精度问题。
// 除法 BigDecimal quotient = num1.divide(num2, 2, RoundingMode.HALF_UP); System.out.println("商:" + quotient);
在进行运算的时注意事项
构造器中传入字符串:BigDecimal
类的构造方法接受 String
类型的参数,而不是 double
或 float
。因为 double
或 float
类型在转换成二进制时存在精度损失,这会导致最终结果不准确。
指定精度和舍入模式:在进行除法和四舍五入运算时,需要指定精确到小数点后几位以及舍入规则,否则会产生舍入误差。精度可以通过 scale
参数指定,舍入模式可以通过 RoundingMode
枚举类中的常量指定。
避免使用 equals() 方法比较大小:BigDecimal
类的 equals()
方法只能用于判断两个对象是否相等,不能直接用于比较大小。使用compareTo() 方法进行大小比较时,要考虑正负数、相等等多种情况。
不可变性:BigDecimal
对象是不可变的,一旦创建就不能修改它的值。每个运算都创建一个新的对象,所以不要用 ==
运算符比较 BigDecimal
类型的对象。
适当缓存常用的 BigDecimal
对象:对于一些常用的小数或整数,可以将它们缓存起来,以避免重复创建 BigDecimal
对象,提升运算效率。
避免使用 floatValue() 和 doubleValue() 方法:这两个方法会将 BigDecimal
转换为 float
和 double
类型,存在精度损失。建议使用 toString()
方法或直接输出 BigDecimal
对象来查看结果
产生no value present异常时,一般是传入了空的BigDecimal对象,但一般的判断非空的方法就没什么用,比如
//报错 BigDecimal bigDecimal = list.stream().filter(fruitOrder -> (fruitOrder.getPayment() !=null)).map(FruitOrder::getPayment).reduce(BigDecimal::add).get(); //不报错 BigDecimal bigDecimal = list.stream().filter(fruitOrder -> Objects.nonNull(fruitOrder.getPayment())).map(FruitOrder::getPayment).reduce(BigDecimal::add).get();
可以使用Objects.nonNull() 进行非空判断。