目录
1.BigDecimal是什么
Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
2.BigDecimal使用
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.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的舍入模式
舍入模式 | 说明 |
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() | 返回此BigDecimal和val的最小值 | |
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应用
/** * 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.