Java之BigDecimal詳解

1、BigDecimal概述

​ Java在java.math包中提供的API類BigDecimal,用來對超過16位有效位的數進行精確的運算。雙精度浮點型變量double能夠處理16位有效數,但在實際應用中,可能須要對更大或者更小的數進行運算和處理。通常狀況下,對於那些不須要準確計算精度的數字,咱們能夠直接使用Float和Double處理,可是Double.valueOf(String) 和Float.valueOf(String)會丟失精度。因此開發中,若是咱們須要精確計算的結果,則必須使用BigDecimal類來操做。java

​ BigDecimal所建立的是對象,故咱們不能使用傳統的+、-、*、/等算術運算符直接對其對象進行數學運算,而必須調用其相對應的方法。方法中的參數也必須是BigDecimal的對象。構造器是類的特殊方法,專門用來建立對象,特別是帶有參數的對象。git

2、BigDecimal經常使用構造函數

2.一、經常使用構造函數

  1. BigDecimal(int)bash

    建立一個具備參數所指定整數值的對象ide

  2. BigDecimal(double)函數

    建立一個具備參數所指定雙精度值的對象工具

  3. BigDecimal(long)性能

    建立一個具備參數所指定長整數值的對象spa

  4. BigDecimal(String)rest

    建立一個具備參數所指定以字符串表示的數值的對象code

2.二、使用問題分析

使用示例:

BigDecimal a =new BigDecimal(0.1);
        System.out.println("a values is:"+a);
        System.out.println("=====================");
        BigDecimal b =new BigDecimal("0.1");
        System.out.println("b values is:"+b);
複製代碼

結果示例:

a values is:0.1000000000000000055511151231257827021181583404541015625
=====================
b values is:0.1
複製代碼

緣由分析:

1)參數類型爲double的構造方法的結果有必定的不可預知性。有人可能認爲在Java中寫入newBigDecimal(0.1)所建立的BigDecimal正好等於 0.1(非標度值 1,其標度爲 1),可是它實際上等於0.1000000000000000055511151231257827021181583404541015625。這是由於0.1沒法準確地表示爲 double(或者說對於該狀況,不能表示爲任何有限長度的二進制小數)。這樣,傳入到構造方法的值不會正好等於 0.1(雖然表面上等於該值)。

2)String 構造方法是徹底可預知的:寫入 newBigDecimal(「0.1」) 將建立一個 BigDecimal,它正好等於預期的 0.1。所以,比較而言, 一般建議優先使用String構造方法。

3)當double必須用做BigDecimal的源時,請注意,此構造方法提供了一個準確轉換;它不提供與如下操做相同的結果:先使用Double.toString(double)方法,而後使用BigDecimal(String)構造方法,將double轉換爲String。要獲取該結果,請使用static valueOf(double)方法。

3、BigDecimal經常使用方法詳解

3.一、經常使用方法

  1. add(BigDecimal)

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

  2. subtract(BigDecimal)

    BigDecimal對象中的值相減,返回BigDecimal對象

  3. multiply(BigDecimal)

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

  4. divide(BigDecimal)

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

  5. toString()

    將BigDecimal對象中的值轉換成字符串

  6. doubleValue()

    將BigDecimal對象中的值轉換成雙精度數

  7. floatValue()

    將BigDecimal對象中的值轉換成單精度數

  8. longValue()

    將BigDecimal對象中的值轉換成長整數

  9. intValue()

    將BigDecimal對象中的值轉換成整數

3.二、BigDecimal大小比較

java中對BigDecimal比較大小通常用的是bigdemical的compareTo方法

int a = bigdemical.compareTo(bigdemical2)
複製代碼

返回結果分析:

a = -1,表示bigdemical小於bigdemical2;
a = 0,表示bigdemical等於bigdemical2;
a = 1,表示bigdemical大於bigdemical2;
複製代碼

舉例:a大於等於b

new bigdemica(a).compareTo(new bigdemical(b)) >= 0
複製代碼

4、BigDecimal格式化

因爲NumberFormat類的format()方法可使用BigDecimal對象做爲其參數,能夠利用BigDecimal對超出16位有效數字的貨幣值,百分值,以及通常數值進行格式化控制。

以利用BigDecimal對貨幣和百分比格式化爲例。首先,建立BigDecimal對象,進行BigDecimal的算術運算後,分別創建對貨幣和百分比格式化的引用,最後利用BigDecimal對象做爲format()方法的參數,輸出其格式化的貨幣值和百分比。

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)); 
複製代碼

結果:

貸款金額: ¥15,000.48 利率: 0.8% 利息: ¥120.00
複製代碼

BigDecimal格式化保留2爲小數,不足則補0:

public class NumberFormat {
	
	public static void main(String[] s){
		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")));
    }
	/** * @desc 1.0~1之間的BigDecimal小數,格式化後失去前面的0,則前面直接加上0。 * 2.傳入的參數等於0,則直接返回字符串"0.00" * 3.大於1的小數,直接格式化返回字符串 * @param obj傳入的小數 * @return */
	public static String formatToNumber(BigDecimal obj) {
		DecimalFormat df = new DecimalFormat("#.00");
		if(obj.compareTo(BigDecimal.ZERO)==0) {
			return "0.00";
		}else if(obj.compareTo(BigDecimal.ZERO)>0&&obj.compareTo(new BigDecimal(1))<0){
			return "0"+df.format(obj).toString();
		}else {
			return df.format(obj).toString();
		}
	}
}
複製代碼

結果爲:

3.44
0.00
0.00
0.00
0.01
0.21
複製代碼

5、BigDecimal常見異常

5.一、除法的時候出現異常

java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result
複製代碼

緣由分析:

​ 經過BigDecimal的divide方法進行除法時當不整除,出現無限循環小數時,就會拋異常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

解決方法:

​ divide方法設置精確的小數點,如:divide(xxxxx,2)

6、BigDecimal總結

6.一、總結

  1. 在須要精確的小數計算時再使用BigDecimal,BigDecimal的性能比double和float差,在處理龐大,複雜的運算時尤其明顯。故通常精度的計算不必使用BigDecimal。
  2. 儘可能使用參數類型爲String的構造函數。
  3. BigDecimal都是不可變的(immutable)的, 在進行每一次四則運算時,都會產生一個新的對象 ,因此在作加減乘除運算時要記得要保存操做後的值。

6.二、工具類推薦

package com.vivo.ars.util;
import java.math.BigDecimal;

/** * 用於高精確處理經常使用的數學運算 */
public class ArithmeticUtils {
    //默認除法運算精度
    private static final int DEF_DIV_SCALE = 10;

    /** * 提供精確的加法運算 * * @param v1 被加數 * @param v2 加數 * @return 兩個參數的和 */

    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /** * 提供精確的加法運算 * * @param v1 被加數 * @param v2 加數 * @return 兩個參數的和 */
    public static BigDecimal add(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.add(b2);
    }

    /** * 提供精確的加法運算 * * @param v1 被加數 * @param v2 加數 * @param scale 保留scale 位小數 * @return 兩個參數的和 */
    public static String add(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.add(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /** * 提供精確的減法運算 * * @param v1 被減數 * @param v2 減數 * @return 兩個參數的差 */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    /** * 提供精確的減法運算。 * * @param v1 被減數 * @param v2 減數 * @return 兩個參數的差 */
    public static BigDecimal sub(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.subtract(b2);
    }

    /** * 提供精確的減法運算 * * @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 乘數 * @return 兩個參數的積 */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    /** * 提供精確的乘法運算 * * @param v1 被乘數 * @param v2 乘數 * @return 兩個參數的積 */
    public static BigDecimal mul(String v1, String v2) {
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.multiply(b2);
    }

    /** * 提供精確的乘法運算 * * @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();
    }

    /** * 提供(相對)精確的除法運算,當發生除不盡的狀況時,精確到 * 小數點之後10位,之後的數字四捨五入 * * @param v1 被除數 * @param v2 除數 * @return 兩個參數的商 */

    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /** * 提供(相對)精確的除法運算。當發生除不盡的狀況時,由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;
    }
}
複製代碼
相關文章
相關標籤/搜索