BigDecimal 的那些坑事儿
最近查看rebate数据时,发现一个bug,主要现象是,当扣款支付宝的账号款项时,返回的是数字的金额为元,而数据库把金额存储为分,这中间要做元与分的转化,这个转化规则很简单,就是*100的,所以一开始代码很简单,如下。
1
public BigDecimal(double val)
将double表示形式转换为BigDecimal
2
public BigDecimal(int val)
将int表示形式转换为BigDecimal
3
public BigDecimal(String val)
将字符串表示形式转换为BigDecimal
通过这三个构造函数,可以把double类型,int类型,String类型构造为BigDecimal对象,在BigDecimal对象内通过BigIntegerintVal存储传递对象数字部分,通过int scale;记录小数点位数,通过int precision;记录有效位数(默认为0)。
BigDecimal的加减乘除就成了BigInteger与BigInteger之间的加减乘除,浮点数的计算也转化为整形的计算,可以大大提供性能,并且通过BigInteger可以保存大数字,从而实现真正大十进制的计算,在整个计算过程中,还涉及scale的判断和precision判断从而确定最终输出结果。
我们先看一个例子Double dd= Double.valueOf(s);BigDecimal bigD = new BigDecimal(dd);bigD = bigD.multiply(newBigDecimal(100)). divide(1, 1, BigDecimal.ROUND_HALF_UP);Long result = bigD.longValue();我们通过/1,然后设置保留小数点方式,以及设置数字保留模式,从而得到两个数乘积的小数部分。还有以下模式枚举常量摘要
ROUND_CEILING
向正无限大方向舍入的舍入模式。
ROUND_DOWN
向零方向舍入的舍入模式。
ROUND_FLOOR
向负无限大方向舍入的舍入模式。
ROUND_HALF_DOWN
向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。
ROUND_HALF_EVEN
向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。
ROUND_HALF_UP
向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。
ROUND_UNNECESSARY
用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。(默认模式)
ROUND_UP
远离零方向舍入的舍入模式。总结:
1:尽量避免传递double类型,有可能话,尽量使用int和String类型。
2:做乘除计算时,一定要设置精度和保留小数点位数。
3:BigDecimal计算时,单独放到try catch内。参考资料
IEEE 754简介: http://baike.baidu.com/view/1698149.htm
IEEE 754官方协议:http://grouper.ieee.org/groups/754/
BigDecimal函数列表:http://hi.baidu.com/logan9999/item/eeaea014677323fd9c778abd
浮点数与IEEE 754: http://www.cnblogs.com/kingwolfofsky/archive/2011/07/21/2112299.html
MathContext:http://doc.java.sun.com/DocWeb/api/all/java.math.MathContext
BigDecimal:http://doc.java.sun.com/DocWeb/api/all/java.math.BigDecimal