1. 程式人生 > >java中金額(浮點表示)的計算

java中金額(浮點表示)的計算

 java中進行金額的計算經常浮點數丟失精度,造成這種問題的原因應該與cpu對浮點數的計算方式有關,有下面的介紹:
從原理上來講,任何一門語言對於浮點數的計算都是不精確的。
因為現在的Computer都是基於二進位制數來儲存計算的。
例如計算8+3時,Computer會轉換為二進位制的加法1000+11=1011,然後再轉換為十進位制數為11。
這種演算法對於整數來說是不會產生誤差的(如果不超過計算範圍);
而對於浮點數計算有時就會產生誤差。因為有的浮點數轉換成為二進位制時是一個無窮迴圈小數。
例如十進位制的0.4,轉換成為二進位制為0.0110011001100110....,這樣,在0.4+0.3時就不能準確的算出是0.7,
而是經過一些舍入處理才能得出正確結果,但經過多次運算誤差產生的較大時,
即使經過一些舍入處理也不能得到精確的結果了。
既然這樣,那麼在java中為了保持精度需要怎樣做呢?
一句話:用BigDecimal進行運算;BigDecimal的建構函式有下面幾種:
BigDecimal(BigInteger val)
          Translates a BigInteger into a BigDecimal.
BigDecimal(BigInteger unscaledVal, int scale)
          Translates a BigInteger unscaled value and an int scale into a BigDecimal.
BigDecimal(double val)
          Translates a double into a BigDecimal.
BigDecimal(String val)
          Translates the String representation of a BigDecimal into a BigDecimal.
按照jdk幫助文件說明,如果採用double型別建構函式的話有可能丟失精度,推薦採用String型別的建構函式.
Note: the results of this constructor can be somewhat unpredictable.
One might assume that new BigDecimal(.1) is exactly equal to .1, but it is actually equal to
.1000000000000000055511151231257827021181583404541015625. This is so because .1 cannot be represented
exactly as a double (or, for that matter, as a binary fraction of any finite length).
Thus, the long value that is being passed in to the constructor is not exactly equal to .1,
appearances notwithstanding.

在用的過程中寫了一個幫助類Amount,以解決在日常開發中遇到的金額計算問題:

package com.langchen.domain;

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

/**
 * 金額型別. 支援自身的四則運算,並將this返回.
 
*/

publicclass Amount implements Serializable {

    
private BigDecimal value;

    
/**
     * 提供預設精度10
     
*/

    
privateint scale =10;

    
/**
     * double型別建構函式
     * 
     * 
@param value
     
*/

    
public Amount(double value) {
        
this.value =new BigDecimal(Double.toString(value));
    }


    
/**
     * String型別建構函式
     * 
     * 
@param value
     
*/

    
public Amount(String value) {
        
this.value =new BigDecimal(value);
    }


    
/**
     * 取得BigDecimal的值
     * 
     * 
@return
     
*/

    
public BigDecimal getValue() {
        
returnthis.value;
    }


    
/**
     * 兩個double型別的數值相加
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble add(double v1, double v2) {
        Amount a1 
=new Amount(v1);
        Amount a2 
=new Amount(v2);
        
return add(a1, a2);
    }


    
/**
     * 兩數相除
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble div(double v1, double v2) {
        Amount a1 
=new Amount(v1);
        Amount a2 
=new Amount(v2);
        
returnthis.divide(a1, a2);
    }


    
/**
     * 相減
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble sub(double v1, double v2) {
        Amount a1 
=new Amount(v1);
        Amount a2 
=new Amount(v2);
        
returnthis.subtract(a1, a2);
    }


    
/**
     * 相乘
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble mul(double v1, double v2) {
        Amount a1 
=new Amount(v1);
        Amount a2 
=new Amount(v2);
        
returnthis.multiply(a1, a2);
    }


    
/**
     * 兩個Amount型別的資料進行相加
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble add(Amount v1, Amount v2) {
        
return v1.getValue().add(v2.getValue()).doubleValue();
    }


    
/**
     * 兩個Amount型別變數相除
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble divide(Amount v1, Amount v2) {
        
if (scale <0{
            
thrownew IllegalArgumentException("精度指定錯誤,請指定一個>=0的精度");
        }

        
return v1.getValue().divide(v2.getValue(), scale,
                BigDecimal.ROUND_HALF_UP).doubleValue();
    }


    
/**
     * 兩數相乘
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble multiply(Amount v1, Amount v2) {
        
return v1.getValue().multiply(v2.getValue()).doubleValue();
    }


    
/**
     * 兩數相減
     * 
     * 
@param v1
     * 
@param v2
     * 
@return
     
*/

    
publicdouble subtract(Amount v1, Amount v2) {
        
return v1.getValue().subtract(v2.getValue()).doubleValue();
    }


    
/**
     * 返回value的浮點數值
     * 
     * 
@return
     
*/

    
publicdouble doubleValue() {
        
returnthis.getValue().doubleValue();
    }

    
/**
     * 設定精度
     * 
@param scale
     
*/

    
publicvoid setScale(int scale) {
        
this.scale = scale;
    }

}