关于BigDecimal 和 double 类型保存金钱,以及精度问题,银行家舍入法
1、 BigDecimal 类型数据 的创建,构造函数 有
1 public BigDecimal(BigInteger intVal, long val, int scale, int prec); 2 3 public BigDecimal(char[] in, int offset, int len) ; 4 5 public BigDecimal(String val); 6 7 public BigDecimal(double val); 8 9 public BigDecimal(BigInteger val); 10 11 public BigDecimal(int val); 12 13 public BigDecimal(long val);
其中 构造方法
public BigDecimal(double val)
可能是不可预测的,因为double类型数据 有精度问题, 比如 double a = 0.1, new BigDecimal(a); 构造出来的BigDecimal 是不等于 0.1的, 解决方法是 将其转为String 或者用 。
1 double a = 5.5D; 2 double b = 4.5D; 3 double c = 1.465D; 4 double d = 1.455D; 5 BigDecimal decimal = new BigDecimal(c).setScale(2, RoundingMode.HALF_EVEN); //构造的BigDecimal 不等于 1.465, 不精确 :1.4650000000000000799360577730112709105014801025390625 6 BigDecimal decimal2 = BigDecimal.valueOf(c).setScale(2, RoundingMode.HALF_EVEN); //这样构造的BigDecimal 才精确 7 BigDecimal decimal3 = new BigDecimal(c+"").setScale(2, RoundingMode.HALF_EVEN); //这样构造的BigDecimal 才精确 8 9 10 System.out.println(new BigDecimal(a).setScale(0, RoundingMode.HALF_EVEN).doubleValue()); 11 System.out.println(new BigDecimal(b).setScale(0, RoundingMode.HALF_EVEN).doubleValue()); 12 System.out.println(new BigDecimal(c+"").setScale(2, RoundingMode.HALF_EVEN).doubleValue()); 13 System.out.println(new BigDecimal(d).setScale(2, RoundingMode.HALF_EVEN).doubleValue());
2、 BigDecimal 跟String 对象一样是不可变对象, 设置保留小数位数后 返回的是一个新的对象。
3、BigDecimal 舍入模式模式有 ROUNDING_UP 远离0, ROUNDING_DOWN 接近0, ROUNDING_CEILING 向上进位, ROUNDING_FLOOR 向下靠近, ROUNDING_HALF_UP 四舍五入, ROUNDING_HALF_DOWN 五舍六入, ROUNDING_HALF_EVEN 银行家舍入法,四舍六入,五后非零入,五后为零靠前位奇偶,前偶舍前奇入
/** * Rounding mode to round away from zero. Always increments the * digit prior to a nonzero discarded fraction. Note that this rounding * mode never decreases the magnitude of the calculated value. */ public final static int ROUND_UP = 0; /** * Rounding mode to round towards zero. Never increments the digit * prior to a discarded fraction (i.e., truncates). Note that this * rounding mode never increases the magnitude of the calculated value. */ public final static int ROUND_DOWN = 1; /** * Rounding mode to round towards positive infinity. If the * {@code BigDecimal} is positive, behaves as for * {@code ROUND_UP}; if negative, behaves as for * {@code ROUND_DOWN}. Note that this rounding mode never * decreases the calculated value. */ public final static int ROUND_CEILING = 2; /** * Rounding mode to round towards negative infinity. If the * {@code BigDecimal} is positive, behave as for * {@code ROUND_DOWN}; if negative, behave as for * {@code ROUND_UP}. Note that this rounding mode never * increases the calculated value. */ public final static int ROUND_FLOOR = 3; /** * Rounding mode to round towards {@literal "nearest neighbor"} * unless both neighbors are equidistant, in which case round up. * Behaves as for {@code ROUND_UP} if the discarded fraction is * ≥ 0.5; otherwise, behaves as for {@code ROUND_DOWN}. Note * that this is the rounding mode that most of us were taught in * grade school. */ public final static int ROUND_HALF_UP = 4; /** * Rounding mode to round towards {@literal "nearest neighbor"} * unless both neighbors are equidistant, in which case round * down. Behaves as for {@code ROUND_UP} if the discarded * fraction is {@literal >} 0.5; otherwise, behaves as for * {@code ROUND_DOWN}. */ public final static int ROUND_HALF_DOWN = 5; /** * Rounding mode to round towards the {@literal "nearest neighbor"} * unless both neighbors are equidistant, in which case, round * towards the even neighbor. Behaves as for * {@code ROUND_HALF_UP} if the digit to the left of the * discarded fraction is odd; behaves as for * {@code ROUND_HALF_DOWN} if it's even. Note that this is the * rounding mode that minimizes cumulative error when applied * repeatedly over a sequence of calculations. */ public final static int ROUND_HALF_EVEN = 6; /** * Rounding mode to assert that the requested operation has an exact * result, hence no rounding is necessary. If this rounding mode is * specified on an operation that yields an inexact result, an * {@code ArithmeticException} is thrown. */ public final static int ROUND_UNNECESSARY = 7;
4、 double 类型数据保存到 数据库的 decimal 类型字段时, mybatis是采用了 四舍五入的 舍入模式。
参考资料:
1、提高你的Java代码质量吧:不容忽视的四舍五入细节 http://blog.csdn.net/p106786860/article/details/9377471