Java中BigDecimal总结

本文详细介绍了Java中BigDecimal类用于高精度计算的原理和方法,包括构造器、加减乘除运算、比较操作以及舍入模式。通过示例代码展示了BigDecimal的使用,强调了在处理金融计算时避免精度丢失的重要性,并给出了BigDecimal在实际应用中的场景,如计算学生总分和平均分。同时,文章还提到了BigDecimal在前后端交互中可能遇到的精度问题及其解决方案。
摘要由CSDN通过智能技术生成

1.BigDecimal是什么

Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

2.BigDecimal如何使用

2.1 BigDecimal的创建

比较推荐两种创建方式:

方法类型说明
public BigDecimal(String val)构造函数使用String入参的构造函数
BigDecimal.valueOf(double val)静态方法double类型的值转换为BigDecimal类型

代码演示:

    /**
     * BigDecimal 的创建
     *
     * 优先使用 BigDecimal 中的String入参构造方法,或者使用BigDecimal.valueOf();
     */
    @Test
    public void BigDecimalConstructorExample() {
        // 使用String入参的构造方法;
        BigDecimal bigDecimal1 = new BigDecimal("0.123");
        // 或者使用 BigDecimal.valueOf() 方法,该方法内部Double.toString()方法.
        BigDecimal bigDecimal2 = BigDecimal.valueOf(0.123);
        System.out.println( bigDecimal1);
        System.out.println(bigDecimal2);
        //注意: 这里不推荐使用 double入参构造方法,会出现精度损失
        BigDecimal bigDecimal3 = new BigDecimal(0.1);
        System.out.println( bigDecimal3);
        // 输出:
        // 0.123
        // 0.123
        // 0.1000000000000000055511151231257827021181583404541015625
    }

2.2 BigDecimal的加减乘除计算

运算法则说明
加法public BigDecimal add(BigDecimal augend)
减法public BigDecimal subtract(BigDecimal subtrahend)
成法public BigDecimal multiply(BigDecimal multiplicand)
除法public BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)

代码演示:

@Test
public void BigDecimalCompute() {
	BigDecimal a = new BigDecimal("-16.5");
	BigDecimal b = BigDecimal.valueOf(3.0);
	BigDecimal add = a.add(b);
	System.out.println("加法计算: a + b =  " + add);
	BigDecimal subtract = a.subtract(b);
	System.out.println("减法计算: a - b =  " + subtract);
	BigDecimal multiply = a.multiply(b);
	System.out.println("乘法计算: a + b =  " + multiply);
	// divide(除数 , 保留小数位 , 小位数舍入方式)
	BigDecimal divide = a.divide(b, 2, RoundingMode.HALF_UP);
	System.out.println("除法计算: a / b =  " + divide);
	// 加法计算: a + b =  -13.5
	// 减法计算: a - b =  -19.5
	// 乘法计算: a + b =  -49.50
        // 除法计算: a / b =  -5.50

3.BigDecimal如何比较?

方法说明
public int compareTo(BigDecimal val)a.compareTo(b)
a > b 返回 1
a < b 返回 -1
a = b 返回 0
@Test
public void BigDecimalCompareTo() {
	BigDecimal a = new BigDecimal("0.00");
	BigDecimal b = BigDecimal.valueOf(1.6);
	BigDecimal c = BigDecimal.valueOf(1.3);
	BigDecimal d = BigDecimal.ZERO;
	
	System.out.println("a和d比较结果: " + a.compareTo(d));
	System.out.println("b和c比较结果: " + a.compareTo(b));
	System.out.println("c和d比较结果: " + b.compareTo(c));
	System.out.println("d和a比较结果: " + BigDecimal.ZERO.compareTo(a));
	
	// 注意事项: 这里不能使用 equal()方法进行比较
	if (d.equals(a)) {
		System.out.println("a = b");
	} else {
		System.out.println("a != b");
	}
	// 输出:
	// a和d比较结果: 0
	// b和c比较结果: -1
	// c和d比较结果: 1
	// d和a比较结果: 0
	// a != b
    }

4.BigDecimal的舍入模式

1.RoundingMode

这里使用,java.math.RoundingMode(枚举类)

舍入模式说明
RoundingMode.UP多余小数位直接进位, 1.2 -> 2
RoundingMode.DOWN多余小数位直接社区, 1.2 -> 1
RoundingMode.HALF_UP丢弃的分数>=0.5,向上舍去(四舍五入)
RoundingMode.HALF_DOWN丢弃的分数>0.5,向上舍去(五舍六入)
RoundingMode.HALF_EVEN如果舍弃左边的数字为奇数,则与HALF_UP模式相同,如果为偶数则与HALF_DOWN模式相同
RoundingMode.CEILING正无穷大方向舍入模式。如果值为正数,则与ROUND_UP模式相同;如果值为负数,则与ROUND_DOWN模式相同
RoundingMode.FLOOR负无穷大方向舍入模式。如果值为正数,则与ROUND_DOWN模式相同;如果值为负数,则与ROUND_UP模式相同
    /**
     * BigDecimal的舍入方式
     */
    @Test
    public void BigDecimalRoundingMode() {
        BigDecimal a = new BigDecimal("2.555");
        System.out.println("RoundingMode.UP:多余小数位直接进位=>" + a.setScale(2, RoundingMode.UP));
        System.out.println("RoundingMode.DOWN:多余小数位直接舍去=>" + a.setScale(2, RoundingMode.DOWN));
        System.out.println("RoundingMode.HALF_UP:丢弃的分数>=0.5,向上舍去(四舍五入)=>" + a.setScale(2, RoundingMode.HALF_UP));
        System.out.println("RoundingMode.HALF_DOWN:丢弃的分数>0.5,向上舍去(五舍六入)=>" + a.setScale(2, RoundingMode.HALF_DOWN));
        System.out.println("RoundingMode.HALF_EVEN:=>如果舍弃左边的数字为奇数,则与HALF_UP模式相同,如果为偶数则与HALF_DOWN模式相同" + a.setScale(2,
            RoundingMode.HALF_EVEN));
        System.out.println(
            "RoundingMode.CEILING:=>正无穷大方向舍入模式。如果值为正数,则与ROUND_UP模式相同;如果值为负数,则与ROUND_DOWN模式相同" + BigDecimal.valueOf(
                1.235).setScale(2, RoundingMode.CEILING));
        System.out.println(
            "RoundingMode.FLOOR:=>负无穷大方向舍入模式。如果值为正数,则与ROUND_DOWN模式相同;如果值为负数,则与ROUND_UP模式相同" + BigDecimal.valueOf(1.235)
                .setScale(2, RoundingMode.FLOOR));
        // RoundingMode.UP:多余小数位直接进位=>2.56
        // RoundingMode.DOWN:多余小数位直接舍去=>2.55
        // RoundingMode.HALF_UP:丢弃的分数>=0.5,向上舍去(四舍五入)=>2.56
        // RoundingMode.HALF_DOWN:丢弃的分数>0.5,向上舍去(五舍六入)=>2.55
        // RoundingMode.HALF_EVEN:=>如果舍弃左边的数字为奇数,则与HALF_UP模式相同,如果为偶数则与HALF_DOWN模式相同2.56
        // RoundingMode.CEILING:=>正无穷大方向舍入模式。如果值为正数,则与ROUND_UP模式相同;如果值为负数,则与ROUND_DOWN模式相同1.24
        // RoundingMode.FLOOR:=>负无穷大方向舍入模式。如果值为正数,则与ROUND_DOWN模式相同;如果值为负数,则与ROUND_UP模式相同1.23
    }

5.BigDecimal的常量和其他方法

代码类型说明
BigDecimal.ZERO常量值为 0,比例为 0
BigDecimal.ONE值为 1,比例为 0
BigDecimal.TEN值为 10,比例为 0
public BigDecimal abs()方法返回一个BigDecimal ,其值为此BigDecimal的绝对值,其比例为this.scale()
public BigDecimal negate()返回一个BigDecimal ,其值为(-this) ,其比例为this.scale() 。
public BigDecimal pow(int n)返回一个BigDecimal ,其值为(this n ) ,精确计算幂,精度不受限制。参数n必须在 0 到 999999999 的范围内,包括 0 到 999999999。 ZERO.pow(0)返回ONE 。
public BigDecimal max(BigDecimal val)返回此BigDecimal和val的最大值
public BigDecimal min(BigDecimal val)返回此BigDecimal和val的最小值
public BigDecimal stripTrailingZeros()1.将BigDecimal去除多余的零,可能会输出科学计数法;
2.通常配合 toPlainString()方法一起使用,输出更友好,会自动去除尾部的零;
    /**
     * BigDecimal的其他方法和常量
     */
    @Test
    public void BigDecimalOther() {
        System.out.println(BigDecimal.ZERO);
        System.out.println(BigDecimal.ONE);
        System.out.println(BigDecimal.TEN);
        // 返回一个BigDecimal ,其值为此BigDecimal的绝对值,其比例为this.scale()
        System.out.println(BigDecimal.valueOf(-1.223000).abs());
        // 返回一个BigDecimal ,其值为(-this) ,其比例为this.scale() 。
        System.out.println(BigDecimal.valueOf(1.23000).negate());
        System.out.println(BigDecimal.valueOf(-1.23000).negate());
        // 返回一个BigDecimal ,其值为(this n ) ,精确计算幂,精度不受限制。参数n必须在 0 到 999999999 的范围内,包括 0 到 999999999。 ZERO.pow(0)返回ONE 。
        System.out.println(BigDecimal.valueOf(-1.20000).pow(2));
        // 返回此BigDecimal和val的最大值
        System.out.println(BigDecimal.ZERO.max(BigDecimal.valueOf(1.2300)));
        // 返回此BigDecimal和val的最小值
        System.out.println(BigDecimal.ZERO.min(BigDecimal.valueOf(-0.1200)).stripTrailingZeros().toPlainString());
        // 0
        // 1
        // 10
        // 1.223
        // -1.23
        // 1.23
        // 1.44
        // 1.23
        // -0.12
    }

3.BigDecimal应用

1.BigDecimal实际应用过程代码演示

/**
 * BigDecimal中应用场景
 * 1.准备数据 {@link #getDataList()}
 * 2.需求:计算所有学生的总分数(计算List中所有BigDecimal) {@link #calculateTotalScore()}
 * 3.需求:计算所有学生的平均分数(无分数除外) {@link #calculateAvg()}
 * 4.需求:求最大和最小分数{@link #maxAndMinValue()}
 * 5.需求:根据分数倒序,没有分数的也要参与排序{@link #sorted()}
 *
 * @author Lobster 02
 * @since 2022/04/22
 **/
public class BigDecimalTest {
    /**
     * 1.准备数据
     */
    public List<Person> getDataList() {
        List<Person> list = new ArrayList<>();
        Person person3 = new Person("王五", 24, "深圳", new BigDecimal("59.5"));
        Person person1 = new Person("张三", 18, "北京", new BigDecimal("85.9"));
        Person person2 = new Person("李四", 32, "上海", new BigDecimal("95.5"));
        Person person4 = new Person("钱六", 26, "广州", null);
        list.add(person1);
        list.add(person2);
        list.add(person3);
        list.add(person4);
        return list;
    }

    /**
     * 2.需求:计算所有学生的总分数(计算List中所有BigDecimal)
     */
    @Test
    public void calculateTotalScore() {
        BigDecimal totalScore = getDataList().stream().map(Person::getScore).filter(Objects::nonNull).reduce(
            BigDecimal.ZERO, BigDecimal::add).setScale(1, RoundingMode.HALF_UP);
        System.out.println(totalScore);
    }

    /**
     * 3.需求:计算所有学生的平均分数(无分数除外)
     */
    @Test
    public void calculateAvg() {
        List<BigDecimal> allScore = getDataList().stream().map(Person::getScore).filter(Objects::nonNull).collect(
            Collectors.toList());
        BigDecimal avgScore = allScore.stream().reduce(BigDecimal.ZERO, BigDecimal::add).divide(
            BigDecimal.valueOf(allScore.size()), 1, RoundingMode.HALF_UP);
        System.out.println(avgScore);
    }

    /**
     * 4.需求:求最大和最小分数
     */
    @Test
    public void maxAndMinValue() {
        Optional<BigDecimal> max = getDataList().stream().map(Person::getScore).filter(Objects::nonNull).max(
            BigDecimal::compareTo);
        max.ifPresent(System.out::println);
        Optional<BigDecimal> min = getDataList().stream().map(Person::getScore).filter(Objects::nonNull).min(
            BigDecimal::compareTo);
        min.ifPresent(System.out::println);
    }

    /**
     * 5.需求:根据分数倒序,没有分数的也要参与排序
     */
    @Test
    public void sorted(){
        List<Person> list = getDataList().stream().sorted(
                Comparator.comparing(Person::getScore, Comparator.nullsFirst(Comparator.naturalOrder())).reversed())
            .collect(Collectors.toList());
       list.forEach(System.out::println);
    }
}

4.使用BigDecimal遇到的一些问题

1.BigDecimal 前后端交互失去精度

问题说明:
后端接口返回BigDecimal格式,例如0.00,前端获取到数据后,将数据解析成0;
在浏览器的控制台response返回正常,review返回不正常;
在这里插入图片描述

问题原因:
1.前端拿到JSON字符串后,转成JSON对象时出现问题.

处理办法:
1.待转换的属性字段上,加上注解即可
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize
(using = ToStringSerializer.class)

2.返回类中属性字段直接返回String.

5.参考博客

1.BigDecimal前后端交互失去精度处理办法
2.BigDecimal详解

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值