java金融通貨処理

5107 ワード

回転:http://imtinx.iteye.com/blog/832325
「あなたが知らないかもしれないJava基礎知識(一)」では、浮動小数点演算を使うには慎重で、徹底した表現が足りないと言いましたが、floatとdoubleのタイプは主に科学と工事のために設計されています.彼らはバイナリ浮動小数点演算を実行していますが、バイナリの限界のために正確な結果が得られない場合があります. 
例えば、System.out.println(2.0-1.1)は0.9ではなく0.89999999を出力します.もちろん科学計算では関係ないです.四捨五入で問題を簡単に解決できますが、丸め誤差が生じることが禁止されている演算には適用されません. 
バイナリでは正確に10の負の乗平均値を表してはいけません.例えば、0.1というように、10進数の中で1/3を正確に表してはいけません.したがって、金融通貨のように計算する問題になればなるほど、floatとdoubleを捨ててBigDecimal類を変更しなければなりません. 
「Effective Java」という本の中でもこの原則を言及しています.floatとdoubleは科学的な計算や工程計算をするしかないです.商業計算ではjava.math.Big Decimalを使います.またStringでBigDecimalを作るには無理です.『Effective Java』の本の中の例はStringでBigDecimalを作るのに十分ですが、本ではそれを強調していません.これは小さなミスかもしれません. 
BigDecimalで上記の2.0-1.1問題を解決するコードは以下の通りです. 
Javaコード  コレクションコード 
import java.math.*;    
class BigDecimalTest     
   {    
       public static void main(String[] args)     
       {    
           //       BigDecimal      
           BigDecimal a =new BigDecimal("2.0");    
           BigDecimal b = new BigDecimal("1.1");    
           System.out.println(a.subtract(b));    
       }    
   }    
       
     :0.9
もしStringを使わずにBigDecimalを構築するなら、問題は元の通りになります. Javaコード  コレクションコード 
import java.math.*;    
    class TestB     
    {    
        public static void main(String[] args)     
        {    
            //  double   BigDecimal,        
            BigDecimal a = new BigDecimal(2.0);    
            BigDecimal b = new BigDecimal(1.1);    
            System.out.println(a.subtract(b));    
        }    
    }    
       0.899999999999999911182158029987476766109466552734375
もちろんBigDecimalも丸め方法を提供しています.以下はオンラインで淘汰された種類です.これらの問題を完璧に解決しました. Javaコード  コレクションコード
package com.morningstar.bigdecimal;  
      
    import java.math.BigDecimal;  
      
    public class Arithmetic4Double {  
        //          
        private static final int DEF_DIV_SCALE = 10;  
         
        //            ,        
        private Arithmetic4Double() {}  
      
        /** 
         *              
         * @param v1   1 
         * @param v2   2 
         * @return v1+v2   
         */  
        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 v1-v2   
         */  
        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 v1×v2   
         */  
        public static double multi(double v1,double v2) {  
            BigDecimal b1 = new BigDecimal(Double.toString(v1));  
            BigDecimal b2 = new BigDecimal(Double.toString(v2));  
            return b1.multiply(b2).doubleValue();  
        }  
      
        /** 
         *              
         *           ,        DEF_DIV_SCALE (   10 ),           。 
         * @param v1     
         * @param v2    
         * @return v1/v2   
         */  
        public static double div(double v1,double v2) {  
         BigDecimal b1 = new BigDecimal(Double.toString(v1));  
            BigDecimal b2 = new BigDecimal(Double.toString(v2));  
            return b1.divide(b2,DEF_DIV_SCALE,BigDecimal.ROUND_HALF_UP).doubleValue();  
        }  
      
        /** 
         *              
         *           ,        scale ,           。 
         * @param v1     
         * @param v2    
         * @param scale                
         * @return v1/v2   
         */  
        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();  
        }  
    }  

 
一言をまとめます.正確な演算はString構造のBig Decimalを使います.floatとdoubleを使わないでください.