java金融货币处理

转自:http://imtinx.iteye.com/blog/832325

在《你可能不知道的Java基础知识(一)》中,我提到使用浮点运算要慎重,感觉说的不够透彻,其实float和double类型主要是为科学和工程计算而设计的。他们执行的是二进制浮点运算,由于二进制的局限性,有时候无法得到准确的结果。 


例如:System.out.println(2.0-1.1)将输出0.8999999999999999,而不是0.9,当然这在科学计算中无关紧要,通过四舍五入就可以轻松解决问题,但是在禁止出现舍入误差的运算中(比如金融计算)就不适用了。 

在二进制中无法精确地表示10的任何负数次方值,比如0.1,这和十进制中无法精确表示1/3一个道理,所以越到像金融货币计算的问题,我们不得不舍弃float和double,而改BigDecimal类。 

在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal,而且非要用String来够造BigDecimal不可!在《Effective Java》一书中的例子是用String来够造BigDecimal的,但是书上却没有强调这一点,这也许是一个小小的失误吧。 

用BigDecimal解决上述2.0-1.1问题的代码为: 

Java代码  收藏代码 

  1. import java.math.*;      
  2. class BigDecimalTest       
  3.    {      
  4.        public static void main(String[] args)       
  5.        {      
  6.            //使用字符串构造BigDecimal对象       
  7.            BigDecimal a =new BigDecimal("2.0");      
  8.            BigDecimal b = new BigDecimal("1.1");      
  9.            System.out.println(a.subtract(b));      
  10.        }      
  11.    }      
  12.          
  13.    输出:0.9  
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代码  收藏代码 
  1. import java.math.*;      
  2.     class TestB       
  3.     {      
  4.         public static void main(String[] args)       
  5.         {      
  6.             //使用double来构造BigDecimal,问题依旧       
  7.             BigDecimal a = new BigDecimal(2.0);      
  8.             BigDecimal b = new BigDecimal(1.1);      
  9.             System.out.println(a.subtract(b));      
  10.         }      
  11.     }      
  12.     输出 0.899999999999999911182158029987476766109466552734375  
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代码  收藏代码
  1. package com.morningstar.bigdecimal;    
  2.         
  3.     import java.math.BigDecimal;    
  4.         
  5.     public class Arithmetic4Double {    
  6.         //默认除法运算精度     
  7.         private static final int DEF_DIV_SCALE = 10;    
  8.            
  9.         //所有方法均用静态方法实现,不允许实例化     
  10.         private Arithmetic4Double() {}    
  11.         
  12.         /**  
  13.          * 实现浮点数的加法运算功能  
  14.          * @param v1 加数1  
  15.          * @param v2 加数2  
  16.          * @return v1+v2的和  
  17.          */    
  18.         public static double add(double v1,double v2) {    
  19.             BigDecimal b1 = new BigDecimal(Double.toString(v1));    
  20.             BigDecimal b2 = new BigDecimal(Double.toString(v2));    
  21.             return b1.add(b2).doubleValue();    
  22.         }    
  23.         /**  
  24.          * 实现浮点数的减法运算功能  
  25.          * @param v1 被减数  
  26.          * @param v2 减数  
  27.          * @return v1-v2的差  
  28.          */    
  29.         public static double sub(double v1,double v2) {    
  30.             BigDecimal b1 = new BigDecimal(Double.toString(v1));    
  31.             BigDecimal b2 = new BigDecimal(Double.toString(v2));    
  32.             return b1.subtract(b2).doubleValue();    
  33.         }    
  34.         /**  
  35.          * 实现浮点数的乘法运算功能  
  36.          * @param v1 被乘数  
  37.          * @param v2 乘数  
  38.          * @return v1×v2的积  
  39.          */    
  40.         public static double multi(double v1,double v2) {    
  41.             BigDecimal b1 = new BigDecimal(Double.toString(v1));    
  42.             BigDecimal b2 = new BigDecimal(Double.toString(v2));    
  43.             return b1.multiply(b2).doubleValue();    
  44.         }    
  45.         
  46.         /**  
  47.          * 实现浮点数的除法运算功能  
  48.          * 当发生除不尽的情况时,精确到小数点以后DEF_DIV_SCALE位(默认为10位),后面的位数进行四舍五入。  
  49.          * @param v1 被除数  
  50.          * @param v2 除数  
  51.          * @return v1/v2的商  
  52.          */    
  53.         public static double div(double v1,double v2) {    
  54.          BigDecimal b1 = new BigDecimal(Double.toString(v1));    
  55.             BigDecimal b2 = new BigDecimal(Double.toString(v2));    
  56.             return b1.divide(b2,DEF_DIV_SCALE,BigDecimal.ROUND_HALF_UP).doubleValue();    
  57.         }    
  58.         
  59.         /**  
  60.          * 实现浮点数的除法运算功能  
  61.          * 当发生除不尽的情况时,精确到小数点以后scale位,后面的位数进行四舍五入。  
  62.          * @param v1 被除数  
  63.          * @param v2 除数  
  64.          * @param scale 表示需要精确到小数点以后几位  
  65.          * @return v1/v2的商  
  66.          */    
  67.         public static double div(double v1,double v2,int scale) {    
  68.             if (scale < 0) {    
  69.                 throw new IllegalArgumentException(    
  70.                     "The scale must be a positive integer or zero");    
  71.             }    
  72.             BigDecimal b1 = new BigDecimal(Double.toString(v1));    
  73.             BigDecimal b2 = new BigDecimal(Double.toString(v2));    
  74.             return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();    
  75.         }    
  76.         
  77.         /**  
  78.          * 提供精确的小数位四舍五入功能  
  79.          * @param v 需要四舍五入的数字  
  80.          * @param scale 小数点后保留几位  
  81.          * @return 四舍五入后的结果  
  82.          */    
  83.         public static double round(double v,int scale) {    
  84.             if (scale < 0) {    
  85.                 throw new IllegalArgumentException(    
  86.                     "The scale must be a positive integer or zero");    
  87.             }    
  88.             BigDecimal b = new BigDecimal(Double.toString(v));    
  89.             BigDecimal one = new BigDecimal("1");    
  90.             return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();    
  91.         }    
  92.     }    
  93.   
  94.    
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构造的BigDecimal,不要用float和double!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值