Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是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
}
运算法则 | 说明 | |
---|---|---|
加法 | 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
方法 | 说明 |
---|---|
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
}
这里使用,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
}
代码 | 类型 | 说明 |
---|---|---|
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
}
/**
* 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);
}
}
问题说明:
后端接口返回BigDecimal格式,例如0.00,前端获取到数据后,将数据解析成0;
在浏览器的控制台response返回正常,review返回不正常;
问题原因:
1.前端拿到JSON字符串后,转成JSON对象时出现问题.
处理办法:
1.待转换的属性字段上,加上注解即可
@JsonFormat(shape = JsonFormat.Shape.STRING)
@JsonSerialize(using = ToStringSerializer.class)
2.返回类中属性字段直接返回String.
1.BigDecimal前后端交互失去精度处理办法
2.BigDecimal详解