Android - 金額、利率精確計算BigDecimal,附帶工具類

需求:

Double和float類型,進行加減乘除操作以後,得到的結果小數點後會有好多位。

例如:0.06-0.01 = 0.049999999999999996

所以在涉及到金額或者利率換算時候,必須使用BigDecimal類型進行計算。

BigDecimal:

在這種情況下我們就需要用到java.math包下面的BigDecimal類,BigDecimal主要用於高精度的數據計算,例如計算金額的時候,還有工程測量計算的時候。BigDecimal的提供了add(),subtract(),multiply()和divide()四種方法,分別爲加減乘除,一般計算包含小數的用法爲

BigDecimal b1 = new BigDecimal(a1);
BigDecimal b2 = new BigDecimal(a2);
System.out.println(b1.subtract(b2).doubleValue());

其中a1和a2的類型可以爲String,Double,int,long等等,我在實際用的時候發現了一個問題,當a1和a2的類型爲Double的時候,得到的結果仍然丟失了精度,例如:

BigDecimal b1 = new BigDecimal(0.06);
BigDecimal b2 = new BigDecimal(0.01);
System.out.println(b1.subtract(b2).doubleValue());

得到的結果仍然爲0.049999999999999996
可是如果參數類型爲String,結果就是正確的,就像這樣:

BigDecimal b1 = new BigDecimal("0.06");
BigDecimal b2 = new BigDecimal("0.01");
System.out.println(b1.subtract(b2).doubleValue());

得到的結果爲0.05。

經過測試發現,原來在new BigDecimal(0.06)的時候,得到b1的結果爲0.059999999999999997779553950749686919152736663818359375,而b2的值爲0.01000000000000000020816681711721685132943093776702880859375,這時候就已經精度丟失了,最後得到的結果很顯然也會丟失精度,而使用new BigDecimal("0.06")得到的b1仍然爲0.06。

所以如果大家要對小數進行精確計算的話,new BigDecimal()的參數一定要用String類型的。

工具類:

package com.baofu.yunfutong.util;


import java.math.BigDecimal;

/**
 * Android 精確算法
 * 適用於金額、利率換算
 */
public class BigDecimalUtils {
    /**
     * 提供精確的加法運算
     *
     * @param v1 被加數
     * @param v2 加數
     * @param scale 保留scale 位小數
     * @return 兩個參數的和
     */
    public static String add(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("保留的小數位數必須大於零");
        }
        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 減數
     * @param scale 保留scale 位小數
     * @return 兩個參數的差
     */
    public static String sub(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("保留的小數位數必須大於零");
        }
        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 String mul(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("保留的小數位數必須大於零");
        }
        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 String div(String v1, String v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("保留的小數位數必須大於零");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        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("保留的小數位數必須大於零");
        }
        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("保留的小數位數必須大於零");
        }
        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("保留的小數位數必須大於零");
        }
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.remainder(b2).setScale(scale, BigDecimal.ROUND_HALF_UP).toString();
    }

    /**
     * 比較大小
     *
     * @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);
        if (bj > 0)
            return true;
        else
            return false;
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章