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 枚举类中定义的八种舍入模式:

  1. UP:向远离零的方向舍入,即“正无穷”方向,例子:3.125 舍入为 3.13。
  2. DOWN:向靠近零的方向舍入,即“负无穷”方向,例子:-3.125 舍入为 -3.12。
  3. CEILING:向正无穷的方向舍入,如果为正数则执行 UP 舍入,如果为负数则执行 DOWN 舍入,例子:3.125 舍入为 3.13,-3.125 舍入为 -3.12。
  4. FLOOR:向负无穷的方向舍入,如果为正数则执行 DOWN 舍入,如果为负数则执行 UP 舍入,例子:3.125 舍入为 3.12,-3.125 舍入为 -3.13。
  5. HALF_UP:向最接近的整数方向舍入。如果两个相邻的整数中间有距离相等,则向上舍入。例如,2.5 舍入为 3.0,-2.5 舍入为 -3.0。
  6. HALF_DOWN:向最接近的整数方向舍入。如果两个相邻的整数中间有距离相等,则向下舍入。例如,2.5 舍入为 2.0,-2.5 舍入为 -2.0。
  7. HALF_EVEN:向最接近的整数方向舍入。如果两个相邻的整数中间有距离相等,则向相邻的偶数舍入。例如,2.5 舍入为 2.0,3.5 舍入为 4.0。
  8. 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 类型的参数,而不是 doublefloat。因为 doublefloat 类型在转换成二进制时存在精度损失,这会导致最终结果不准确。

       指定精度和舍入模式:在进行除法和四舍五入运算时,需要指定精确到小数点后几位以及舍入规则,否则会产生舍入误差。精度可以通过 scale 参数指定,舍入模式可以通过 RoundingMode 枚举类中的常量指定。

       避免使用 equals() 方法比较大小:BigDecimal 类的 equals() 方法只能用于判断两个对象是否相等,不能直接用于比较大小。使用compareTo() 方法进行大小比较时,要考虑正负数、相等等多种情况。

       不可变性:BigDecimal 对象是不可变的,一旦创建就不能修改它的值。每个运算都创建一个新的对象,所以不要用 == 运算符比较 BigDecimal 类型的对象。

       适当缓存常用的 BigDecimal 对象:对于一些常用的小数或整数,可以将它们缓存起来,以避免重复创建 BigDecimal 对象,提升运算效率。

       避免使用 floatValue() 和 doubleValue() 方法:这两个方法会将 BigDecimal 转换为 floatdouble 类型,存在精度损失。建议使用 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() 进行非空判断。

posted on 2023-06-07 13:48  梦之泽畔  阅读(155)  评论(0编辑  收藏  举报