項目中BigDecimal與Double使用場景


金額要用BigDecimal

金額計算不能用doube!!!!

金額計算必須用BigDecimal,下面對比一下用double 跟BigDecimal的區別。先看一個小例子:

請看題:

示例1

問, 結果是多少? 0.01?

No! 結果是0.009999999999999998!

為什么會這樣呢? 因為float和double都是浮點數, 都有取值范圍, 都有精度范圍. 浮點數與通常使用的小數不同, 使用中, 往往難以確定.

常見的問題是定義了一個浮點數, 經過一系列的計算, 它本來應該等於某個確定值, 但實際上並不是!

double相減會轉換成二進制,因double有效位數為 16位這就會出現存儲小數位數不夠的情況,這種情況下就會出現誤差,解決方法就是使用BigDecimal,它的有效長度足夠長可存儲小數位數。

因此可代替double來進行加減乘除, 金額必須是完全精確的計算, 故不能使用double或者float, 而應該采用java.math.BigDecimal.

加減乘除

兩個BigDecimal值應該怎樣進行加減乘除呢? +, -, *, / 這樣寫嗎? 不!

請看示例:

加減乘除使用了英文的加減乘除, 即add, substract, multiply和divide

大小比較

兩個BigDecimal值怎么比較大小呢? 能用>或者<嗎? 也不可以!

兩個BigDecimal值比較使用compareTo方法, 比較結果有-1, 0, 1, 分別表示小於, 等於, 大於; 對於0, 可以使用BigDecimal.ZERO表示!

四舍五入

 

簡化BigDecimal計算的小工具類

如果我們要做一個加法運算,需要先將兩個浮點數轉為String,然后夠造成BigDecimal,在其中一個上調用add方法,傳入另一個作為參數,然后把運算的結果(BigDecimal)再轉換為浮點數。

你能夠忍受這么煩瑣的過程嗎?

網上提供的工具類Arith來簡化操作。它提供以下靜態方法,包括加減乘除和四舍五入:   

 

public   static   double   add(double   v1,double   v2)   
public   static   double   sub(double   v1,double   v2)   
public   static   double   mul(double   v1,double   v2)   
public   static   double   div(double   v1,double   v2)   
public   static   double   div(double   v1,double   v2,int   scale)   
public   static   double   round(double   v,int   scale)  

import java.math.BigDecimal;

/**
 * 進行BigDecimal對象的加減乘除,四舍五入等運算的工具類  
 * @author ameyume
 *
 */
public class Arith {

    /**
     * 由於Java的簡單類型不能夠精確的對浮點數進行運算,這個工具類提供精   
     * 確的浮點數運算,包括加減乘除和四舍五入。   
     */
    //默認除法運算精度    
    private static final int DEF_DIV_SCALE = 10;

    //這個類不能實例化    
    private Arith(){
    }

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

    /**
     * 提供(相對)精確的除法運算,當發生除不盡的情況時,精確到   
     * 小數點以后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();
    }

    /**
     * 提供精確的小數位四舍五入處理。   
     * @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));
        BigDecimal one = new BigDecimal("1");
        return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精確的類型轉換(Float)   
     * @param v 需要被轉換的數字   
     * @return 返回轉換結果
     */
    public static float convertsToFloat(double v){
        BigDecimal b = new BigDecimal(v);
        return b.floatValue();
    }

    /**
     * 提供精確的類型轉換(Int)不進行四舍五入   
     * @param v 需要被轉換的數字   
     * @return 返回轉換結果
     */
    public static int convertsToInt(double v){
        BigDecimal b = new BigDecimal(v);
        return b.intValue();
    }

    /**
     * 提供精確的類型轉換(Long)   
     * @param v 需要被轉換的數字   
     * @return 返回轉換結果
     */
    public static long convertsToLong(double v){
        BigDecimal b = new BigDecimal(v);
        return b.longValue();
    }

    /**
     * 返回兩個數中大的一個值   
     * @param v1 需要被對比的第一個數   
     * @param v2 需要被對比的第二個數   
     * @return 返回兩個數中大的一個值
     */
    public static double returnMax(double v1,double v2){
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.max(b2).doubleValue();
    }

    /**
     * 返回兩個數中小的一個值   
     * @param v1 需要被對比的第一個數   
     * @param v2 需要被對比的第二個數   
     * @return 返回兩個數中小的一個值
     */
    public static double returnMin(double v1,double v2){
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.min(b2).doubleValue();
    }

    /**
     * 精確對比兩個數字   
     * @param v1 需要被對比的第一個數   
     * @param v2 需要被對比的第二個數   
     * @return 如果兩個數一樣則返回0,如果第一個數比第二個數大則返回1,反之返回-1   
     */
    public static int compareTo(double v1,double v2){
        BigDecimal b1 = new BigDecimal(v1);
        BigDecimal b2 = new BigDecimal(v2);
        return b1.compareTo(b2);
    }

}  

  

定點數和浮點數的區別

在計算機系統的發展過程中,曾經提出過多種方法表達實數。典型的比如相對於浮點數的定點數(Fixed Point Number)。在這種表達方式中,小數點固定的位於實數所有數字中間的某個位置。

貨幣的表達就可以使用這種方式,比如 99.00 或者 00.99 可以用於表達具有四位精度(Precision),小數點后有兩位的貨幣值。由於小數點位置固定,所以可以直接用四位數值來表達相應的數值。

SQL 中的 NUMBER 數據類型就是利用定點數來定義的。還有一種提議的表達方式為有理數表達方式,即用兩個整數的比值來表達實數。

定點數表達法的缺點在於其形式過於僵硬,固定的小數點位置決定了固定位數的整數部分和小數部分,不利於同時表達特別大的數或者特別小的數。

最終,絕大多數現代的計算機系統采納了所謂的浮點數表達方式。這種表達方式利用科學計數法來表達實數,即用一個尾數(Mantissa ),一個基數(Base),一個指數(Exponent)以及一個表示正負的符號來表達實數。

比如 123.45 用十進制科學計數法可以表達為 1.2345 × 102 ,其中 1.2345 為尾數,10 為基數,2 為指數。浮點數利用指數達到了浮動小數點的效果,從而可以靈活地表達更大范圍的實數。

在MySQL中使用浮點數類型和定點數類型來表示小數。浮點數類型包括單精度浮點數(FLOAT型)和雙精度浮點數(DOUBLE型)。定點數類型就是DECIMAL型。MySQL的浮點數類型和定點數類型如下表所示:

類型名稱 字節數 負數的取值范圍 非負數的取值范圍
FLOAT 4 -3.402823466E+38~
-1.175494351E-38
0和1.175494351E-38~
3.402823466E+38
DOUBLE 8 -1.7976931348623157E+308~
-2.2250738585072014E-308
0和2.2250738585072014E-308~
1.7976931348623157E+308
DECIMAL(M,D)或DEC(M,D) M+2 同DOUBLE型 同DOUBLE型

從上表中可以看出,DECIMAL型的取值范圍與DOUBLE相同。但是,DECIMAL的有效取值范圍由M和D決定,而且DECIMAL型的字節數是M+2,也就是說,定點數的存儲空間是根據其精度決定的。

 MySQL

 BigDecimal在進行入庫時, 數據庫選擇decimal類型, 長度可以自定義, 如18; 小數點我們項目中用的是2, 保留2位小數. 此外還要注意的就是默認值, 一定寫成0.00, 不要用默認的NULL, 否則在進行加減排序等操作時, 會帶來轉換的麻煩!

`balance` decimal(18,2) DEFAULT '0.00' COMMENT '賬戶余額',

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM