BigDecimal은 사용하기 쉽습니다.

계속 만들고 성장을 가속화하십시오! 오늘은 "너겟 데일리 뉴플랜 · 6월 업데이트 챌린지"에 참가한 첫날입니다. 클릭하시면 이벤트 내용을 보실 수 있습니다.

BigDecimal을 사용하는 이유

  • 1. float 및 double 유형의 주요 설계 목표는 과학 및 공학 계산을 위한 것입니다. 그들은 이진 부동 소수점 산술 을 수행합니다. 이 연산은 광범위한 숫자 값에 대해 높은 정확도로 빠른 근사값을 제공하도록 신중하게 설계되었습니다. 그러나 그것들 은 완전히 정확한 결과를 제공하지 않습니다 .
  • 2. 이동 단말기로서 백엔드 인터페이스가 자주 호출되며 리턴 매개변수 유형은 백엔드에 의해 결정됩니다. double을 받아서 사용하면 다음과 같은 문제가 발생할 수 있습니다.
    public void dfFormat() {
        double number =1000000000000.0;
        //变成了科学计数法
        log("number直接打印->"+number);
        log("numberString.valueOf->"+String.valueOf(number));
    }
复制代码

출력 결과

number 직접 인쇄 -> 1.0E12 , 여기서 직접 과학적 표기법이 됩니다.

numberString.valueOf-> 1.0E12 , String 으로의 변환도 과학적 표기법입니다.

해결책

  • 1. 디스플레이에만 후속 처리(소수점, 더하기, 빼기, 곱하기 및 나누기 등 유지)가 필요하지 않은 경우 백엔드가 String 유형을 반환하도록 할 수 있습니다.

  • 2. BigDecimal 사용

    • 1. double 대신 BigDecimal을 직접 사용하여 데이터를 수신합니다.
    • 2. double을 BigDecimal로 변환합니다.
    public void dfFormat() {
        double number =1000000000000.0;
        //toPlainString
        log("numberDecimal-> "+BigDecimal.valueOf(number).toPlainString());
    }
复制代码

인쇄 결과:

numberDecimal-> 1000000000000,从结果可以看出,数据显示正常了。
复制代码

double 을 BigDecimal 로 변환하려면 new BigDecimal( number) 대신 BigDecimal.valueOf(number) 를 사용하십시오 .

BigDecimal을 String 으로 변환하고 toString( ) 대신 toPlainString() 을 사용 하십시오 .

시공방법

BigDecimal提供的比较常用的是下面几种方法:

  • 1.public BigDecimal(String val):将 String 表示形式转换成 BigDecimal(推荐使用)
  • 2.public BigDecimal(int val):将 int 表示形式转换成 BigDecimal
  • 3.public BigDecimal(double val):将 double 表示形式转换为 BigDecimal(不推荐使用)
    public void dfFormat() {
        BigDecimal a = new BigDecimal("10");
        BigDecimal b = new BigDecimal(5);
        BigDecimal k = new BigDecimal(-16.0034);
    }
复制代码

输出结果

 a-> 10
 b-> 5
 k-> -16.00339999999999918145476840436458587646484375
复制代码

原因:float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该用于要求精确结果的场合。

类型转换

注意:double转BigDecimal使用new BigDecimal()的方式会造成精度丢失。怎么解决?看下面的类型转换。

类型转换

double 转 BigDecimal

精度丢失解决方案:

  • 1:new BigDecimal(String.valueOf(number2));
  • 2:BigDecimal.valueOf(number2);
    public void dfFormat() {
  double number2 =-16.0034;
        log("number2直接打印->"+number2);
        //double转BigDecimal导致丢失精度
        log("number2Decimal-> "+new BigDecimal(number2).toPlainString());

        //精度丢失解决方案:
        log("精度丢失String.valueOf-> "+new BigDecimal(String.valueOf(number2)).toPlainString());
        log("精度丢失BigDecimal.valueOf-> "+BigDecimal.valueOf(number2).toPlainString());
    }
复制代码

输出结果

精度丢失String.valueOf-> -16.0034
精度丢失BigDecimal.valueOf-> -16.0034
复制代码

BigDecimal 转 String

    public void dfFormat() {
        double number =1000000000000.0;
        //toString和toPlainString
        log("toString-> "+new BigDecimal(String.valueOf(number)).toString());
        log("toString-> "+BigDecimal.valueOf(number).toString());
        log("toPlainString-> "+ new BigDecimal(String.valueOf(number)).toPlainString());
        log("toPlainString-> "+BigDecimal.valueOf(number).toPlainString());
复制代码

两种方式对比toString和toPlainString的区别,打印结果:

toString-> 1.0E+12
toString-> 1.0E+12
toPlainString-> 1000000000000
toPlainString-> 1000000000000
复制代码

注意:很多人肯定习惯使用toString()了,但在BigDecimal这里就需要注意了,要使用toPlainString()。

BigDecimal 转 double/int/long等

        //如:BigDecimal转double
        BigDecimal b = new BigDecimal(5);
        //doubleValue-> 5.0
        log("doubleValue-> "+b.doubleValue());
复制代码

加减乘除取余

BigDecimal 提供的方法:

    public BigDecimal add(BigDecimal augend) {}//加
    public BigDecimal subtract(BigDecimal subtrahend) {}//减
    public BigDecimal multiply(BigDecimal multiplicand) {}//乘
    public BigDecimal divide(BigDecimal divisor) {}//除(不推荐使用)
    public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {}//除(推荐使用)
    public BigDecimal[] divideAndRemainder(BigDecimal divisor) {}//取余
复制代码
        //加减乘除
        BigDecimal a = new BigDecimal("10");
        BigDecimal b = new BigDecimal(5);
        log("10 + 5-> "+a.add(b).toPlainString());
        log("10 - 5-> "+a.subtract(b).toPlainString());
        log("10 * 5-> "+a.multiply(b).toPlainString());
        log("10 / 5-> "+a.divide(b).toPlainString());
        
        BigDecimal e = BigDecimal.valueOf(5.34178);
        BigDecimal f = BigDecimal.valueOf(3.12456);
        //e.divide(f)直接报错
//        e("e / f-> "+e.divide(f).toPlainString());
        log("e / f-> "+e.divide(f,3,BigDecimal.ROUND_HALF_UP).toPlainString());
        //取余
        BigDecimal []h = e.divideAndRemainder(f);
        log("e / f 取余 整-> "+h[0]+"余->"+h[1]);
        BigDecimal []j = a.divideAndRemainder(b);
        log("10 / 5 取余 整-> "+j[0]+"余->"+j[1]);
复制代码

输出结果

10 + 5-> 15
10 - 5-> 5
10 * 5-> 50
10 / 5-> 2
e / f-> 1.710
e / f 取余 整-> 1余->2.21722
10 / 5 取余 整-> 2余->0
复制代码

注意:

  • 1.为什么不推荐使用divide(BigDecimal divisor)?当 BigDecimal除法可能出现不能整除的情况,就会发送运行时异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result。
  • 2.跟String一样, BigDecimal页是不可变的,在进行每一步运算时,都会产生一个新的对象。

divide

    e.divide(f,3,BigDecimal.ROUND_HALF_UP)
复制代码

参数解释:

  • divisor:除数
  • scale:表示小数点保留位数
  • roundingMode:表示舍入模式

舍入模式

我们一般舍入模式采取的是ROUND_HALF_UP,默认的也是ROUND_HALF_UP

  • ROUND_CEILING //向正无穷方向舍入
  • ROUND_UP //不管保留数字后面是大是小 (0 除外) 都会进1
  • ROUND_DOWN //舍去制,截断操作,后面所有数字直接去除。结果会向原点方向对齐
  • ROUND_FLOOR //向负无穷方向舍入
  • ROUND_HALF_EVEN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
  • ROUND_HALF_UP //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 3.15保留一位小数结果为3.2,传说中的四舍五入。
  • ROUND_HALF_DOWN //양변(거리)이 같지 않으면 가장 가까운 변으로 반올림합니다. 그렇다면 반올림합니다(예: 3.15에서 소수점 이하 한 자리까지) 결과는 3.1입니다.
  • ROUND_UNNECESSARY // 계산 결과가 정확하며 반올림 모드가 필요하지 않습니다.

크기 비교

        //比较大小
        BigDecimal u = new BigDecimal("1");
        //BigDecimal.ZERO==0;
        if(u.compareTo(BigDecimal.ZERO)==-1){
            log("u 小于 0");
        }
        if(u.compareTo(BigDecimal.ZERO)==0){
            log("u 等于 0");
        }
        if(u.compareTo(BigDecimal.ZERO)==1){
            log("u 大于 0");
        }
        BigDecimal um = new BigDecimal("-1");
        BigDecimal uzero = new BigDecimal("0");
        if(um.compareTo(BigDecimal.ZERO)==-1){
            log("um 小于 0");
        }
        if(uzero.compareTo(BigDecimal.ZERO)==0){
            log("uzero 等于 0");
        }
复制代码

출력 결과

 u 大于 0
 um 小于 0
 uzero 等于 0
复制代码

BigDecimal은 0, 1, 10의 세 가지 상수를 제공합니다. 위의 BigDecimal.ZERO(0)과 같은 방법을 사용하십시오.

    //0
    public static final BigDecimal ZERO = new BigDecimal(0, 0);
    //1
    public static final BigDecimal ONE = new BigDecimal(1, 0);
    //10
    public static final BigDecimal TEN = new BigDecimal(10, 0);

复制代码

형식(DecimalFormat)

예를 들어, 우리는 일반적으로 예약된 소수 자릿수, 0 채우기 및 0 제거를 사용합니다.


        DecimalFormat df2 = new DecimalFormat("######0.##");
        DecimalFormat df3 = new DecimalFormat("######0.###");
        //保留2位小数,不补零
        log(df2.format(3.1415927));//3.14
        log(df2.format(3.1));//3.14
        //保留3位小数,不补零
        log(df3.format(3.1415927));//3.142,默认四舍五入
        log(df2.format(3.1));//3.14
        DecimalFormat df22 = new DecimalFormat("######0.00");
        DecimalFormat df32 = new DecimalFormat("######0.000");
        //保留2位小数,补零
        log(df22.format(3.2));//3.20
        //保留3位小数,补零
        log(df32.format(3.2));//3.200
        //去掉BigDecimal后无用的零
        BigDecimal g = new BigDecimal("0.1000");
        log(g.stripTrailingZeros().toPlainString());//0.1
        
        double pi = 3.1415927;
        System.out.print(pi);
        //取一位整数
        log(new DecimalFormat("0").format(pi));//3
        //取一位整数和两位小数
        log(new DecimalFormat("0.00").format(pi));//3.14
        //取两位整数和三位小数,整数不足部分以0填补。
        log(new DecimalFormat("00.000").format(pi));// 03.142
        //取所有整数部分
        log(new DecimalFormat("#").format(pi));//3
        //以百分比方式计数,并取两位小数
        log(new DecimalFormat("#.##%").format(pi));//314.16%
        long c = 299792458;//光速
        //每三位以逗号进行分隔。
        log(new DecimalFormat(",###").format(c)); //299,792,458
        // 将格式嵌入文本光速大小为每秒299,792,458米。
        log(new DecimalFormat("光速大小为每秒,###米。").format(c)); 
复制代码

요약

  • 1. double을 풀 수 없을 때 BigDecimal을 사용하십시오.
  • 2. 매개변수 유형이 String인 생성자를 사용해 보십시오.
  • 3. double을 BigDecimal로 변환할 때 new BigDecimal()을 사용하지 마십시오.
  • 4. BigDecimal을 String으로 변환할 때 toString() 대신 toPlainString()을 사용한다.
  • 5. 분할 운용에 있어서는 무진장 분할의 경우를 고려할 필요가 있다.

추천

출처juejin.im/post/7101919280924459016