Skip to content

BigDecimal的使用

1、BigDecimal常用构造函数

  • BigDecimal(int)

创建一个具有参数所指定整数值的对象

  • BigDecimal(double)

创建一个具有参数所指定双精度值的对象

  • BigDecimal(long)

创建一个具有参数所指定长整数值的对象

  • BigDecimal(String)

创建一个具有参数所指定以字符串表示的数值的对象

2、使用的一些坑

java
BigDecimal a = new BigDecimal(0.1);
System.out.println("a的值:"+a);
System.out.println("=====================");
BigDecimal b = new BigDecimal("0.1");
System.out.println("b的值:"+b);
sh
a的值:0.1000000000000000055511151231257827021181583404541015625
=====================
b的值:0.1

image-20231120091429089

3、BigDecimal常用方法

1、常用方法

  • add(BigDecimal)

BigDecimal对象中的值相加,返回BigDecimal对象

  • ubtract(BigDecimal)

BigDecimal对象中的值相减,返回BigDecimal对象

  • multiply(BigDecimal)

BigDecimal对象中的值相乘,返回BigDecimal对象

  • divide(BigDecimal)

BigDecimal对象中的值相除,返回BigDecimal对象

  • toString()

将BigDecimal对象中的值转换成字符串

  • doubleValue()

将BigDecimal对象中的值转换成双精度数

  • floatValue()

将BigDecimal对象中的值转换成单精度数

  • longValue()

将BigDecimal对象中的值转换成长整数

  • intValue()

将BigDecimal对象中的值转换成整数

2、BigDecimal大小比较

java中对BigDecimal比较大小一般用的是bigdemical的compareTo方法

java
int a = bigdemical.compareTo(bigdemical2);

返回结果分析:

java
a = -1,表示bigdemical小于bigdemical2;
a = 0,表示bigdemical等于bigdemical2;
a = 1,表示bigdemical大于bigdemical2;

a大于等于b

java
new bigdemica(a).compareTo(new bigdemical(b)) >= 0
java
BigDecimal a =new BigDecimal("0.10");
BigDecimal b =new BigDecimal("0.1");
System.out.println(a == b);
System.out.println(a.equals(b));
System.out.println(a.compareTo(b));
java
false
false
0

3、BigDecimal格式化

由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。

以利用BigDecimal对货币和百分比格式化为例。首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。

java
NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用
percent.setMaximumFractionDigits(3); //百分比小数点最多3位

BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额
BigDecimal interestRate = new BigDecimal("0.008"); //利率
BigDecimal interest = loanAmount.multiply(interestRate); //相乘

System.out.println("贷款金额:\t" + currency.format(loanAmount));
System.out.println("利率:\t" + percent.format(interestRate));
System.out.println("利息:\t" + currency.format(interest));
java
贷款金额:	¥15,000.48
利率:	0.8%
利息:	¥120.00

BigDecimal格式化保留2为小数,不足则补0:

java
package com.xx.bigdecimal;

import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * @Author: xueqimiao
 * @Date: 2023/11/20 09:09
 */
public class BigDecimalTest {

    public static void main(String[] args) {
        System.out.println(formatToNumber(new BigDecimal("3.435")));
        System.out.println(formatToNumber(new BigDecimal(0)));
        System.out.println(formatToNumber(new BigDecimal("0.00")));
        System.out.println(formatToNumber(new BigDecimal("0.001")));
        System.out.println(formatToNumber(new BigDecimal("0.006")));
        System.out.println(formatToNumber(new BigDecimal("0.206")));
    }

    public static String formatToNumber(BigDecimal obj) {
        DecimalFormat df = new DecimalFormat("#0.00");
        return df.format(obj).toString();
    }
}
java
3.44
0.00
0.00
0.00
0.01
0.21

4、SpringBoot应用参数格式化

java
package com.xx.vo;

import lombok.Data;

import java.io.Serializable;
import java.math.BigDecimal;

/**
 * @Author: xueqimiao
 * @Date: 2023/11/20 09:23
 */
@Data
public class BigDecimalVO implements Serializable {

    private BigDecimal score;

    private BigDecimal scoreTo;
}
java
@GetMapping("/testDecimal")
public ResultData test(){
    BigDecimalVO bigDecimalVO = new BigDecimalVO();
    bigDecimalVO.setScore(new BigDecimal("0"));
    bigDecimalVO.setScoreTo(new BigDecimal("0"));
    return new ResultData(bigDecimalVO);

}

@JsonSerialize 是 Java 中 Jackson 库提供的注解之一,用于定制对象在序列化(将对象转换为 JSON 字符串)过程中的行为。Jackson 是一个流行的 JSON 处理库,用于在 Java 对象和 JSON 数据之间进行转换。

当你在一个类或属性上使用 @JsonSerialize 注解时,你可以指定一个自定义的序列化器(serializer)。序列化器负责将对象转换为 JSON 格式,而不依赖于默认的方式。这可以用于处理一些特殊的需求,例如将对象以不同的方式序列化,或者在序列化过程中执行一些特殊的逻辑。

java
package com.xx.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;

/**
 * @Author: xueqimiao
 * @Date: 2023/11/20 09:29
 */
public class BigDecimalSerializer extends JsonSerializer<BigDecimal> {

    @Override
    public void serialize(BigDecimal value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value != null) {
            gen.writeString(new DecimalFormat("#0.00").format(value));
        }
    }
}
java
@Data
public class BigDecimalVO implements Serializable {

    private BigDecimal score;

    @JsonSerialize(using = BigDecimalSerializer.class)
    private BigDecimal scoreTo;
}

5、BigDecimal常见异常

java
public static void main(String[] args) {
    BigDecimal bigDecimal = new BigDecimal(1);
    BigDecimal bigDecimal2 = new BigDecimal(3);
    System.out.println(bigDecimal.divide(bigDecimal2));
}
java
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.base/java.math.BigDecimal.divide(BigDecimal.java:1780)
	at com.xx.bigdecimal.BigDecimalTest.main(BigDecimalTest.java:15)

通过BigDecimal的divide方法进行除法时不整除,出现无限循环小数时,就会抛异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

java
public static void main(String[] args) {
    BigDecimal bigDecimal = new BigDecimal(1);
    BigDecimal bigDecimal2 = new BigDecimal(3);
    BigDecimal result = bigDecimal.divide(bigDecimal2, 2, BigDecimal.ROUND_HALF_UP);
    System.out.println(result);
}

2表示2位小数,BigDecimal.ROUND_HALF_UP 是舍入模式,表示使用四舍五入。你可以根据你的需求选择不同的精度和舍入模式

4、BigDecimal工具类

java
package com.xx.util;

import com.xx.utils.ValidationUtil;

import java.math.BigDecimal;

/**
 * @Author: xueqimiao
 * @Date: 2023/11/20 10:15
 */
public class BigDecimalUtil {

    /**
     * 累加
     *
     * @param values
     * @return
     */
    public static BigDecimal add(BigDecimal... values) {
        if (ValidationUtil.isEmpty(values) || values.length == 0) {
            return new BigDecimal(0);
        }
        if (values.length == 1) {
            return values[0];
        } else {
            BigDecimal returnNum = ValidationUtil.isEmpty(values[0]) ? new BigDecimal(0) : values[0];
            for (int i = 1; i < values.length; i++) {
                if (ValidationUtil.isEmpty(values[i])) {
                    continue;
                }
                BigDecimal b1 = returnNum;
                BigDecimal b2 = values[i];
                returnNum = b1.add(b2);
            }
            return returnNum;
        }
    }

    /**
     * 减法
     *
     * @param param
     * @return
     */
    public static BigDecimal subtract(BigDecimal... param) {
        if (ValidationUtil.isEmpty(param) || param.length == 0) {
            return new BigDecimal(0);
        }
        BigDecimal sumLess = param[0];//被减数
        for (int i = 1; i < param.length; i++) {
            BigDecimal bigDecimal = param[i] == null ? new BigDecimal(0) : param[i];
            sumLess = sumLess.subtract(bigDecimal);
        }
        return sumLess;
    }

    /**
     * 乘以
     *
     * @param v1
     * @param v2
     * @return
     */
    public static BigDecimal multiply(BigDecimal v1, BigDecimal v2) {
        if (ValidationUtil.isEmpty(v1) || ValidationUtil.isEmpty(v2)) {
            return new BigDecimal(0);
        }
        BigDecimal multiply = v1.multiply(v2);
        return multiply;
    }

    /**
     * 除以
     *
     * @param v1
     * @param v2
     * @return
     */
    public static BigDecimal divide(BigDecimal v1, BigDecimal v2) {
        if (ValidationUtil.isEmpty(v1) || ValidationUtil.isEmpty(v2) || v2.compareTo(new BigDecimal("0")) == 0) {
            return new BigDecimal(0);
        }
        return v1.divide(v2, 3, BigDecimal.ROUND_HALF_DOWN);
    }

    /**
     * 提供精确的减法运算
     *
     * @param v1    被减数
     * @param v2    减数
     * @param scale 保留scale 位小数
     * @return 两个参数的差
     */
    public static String sub(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2, int scale) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return round(b1.multiply(b2).doubleValue(), scale);
    }

    /**
     * 提供精确的乘法运算
     *
     * @param v1    被乘数
     * @param v2    乘数
     * @param scale 保留scale 位小数
     * @return 两个参数的积
     */
    public static String mul(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
     * 定精度,以后的数字四舍五入
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 表示需要精确到小数点以后几位
     * @return 两个参数的商
     */
    public static String div(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v1);
        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理
     *
     * @param v     需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */
    public static String round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b = new BigDecimal(v);
        return b.setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 取余数
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static String remainder(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 取余数 BigDecimal
     *
     * @param v1    被除数
     * @param v2    除数
     * @param scale 小数点后保留几位
     * @return 余数
     */
    public static BigDecimal remainder(BigDecimal v1, BigDecimal v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }
        return v1.remainder(v2).setScale(scale, BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 比较大小
     *
     * @param v1 被比较数
     * @param v2 比较数
     * @return 如果v1 大于v2 则 返回true 否则false
     */
    public static boolean compare(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        int bj = b1.compareTo(b2);
        boolean res;
        if (bj > 0)
            res = true;
        else
            res = false;
        return res;
    }
}