关于BigDecimal 和 double 类型保存金钱,以及精度问题,银行家舍入法

时间:2021-07-23 09:27:33

1、 BigDecimal 类型数据 的创建,构造函数 有

 public BigDecimal(BigInteger intVal, long val, int scale, int prec);

 public BigDecimal(char[] in, int offset, int len) ;

 public BigDecimal(String val);

 public BigDecimal(double val);

 public BigDecimal(BigInteger val);

 public BigDecimal(int val);

 public BigDecimal(long val);

其中 构造方法

public BigDecimal(double val)

可能是不可预测的,因为double类型数据 有精度问题, 比如 double a = 0.1, new BigDecimal(a); 构造出来的BigDecimal 是不等于 0.1的, 解决方法是 将其转为String 或者用 。

         double a = 5.5D;
double b = 4.5D;
double c = 1.465D;
double d = 1.455D;
BigDecimal decimal = new BigDecimal(c).setScale(2, RoundingMode.HALF_EVEN); //构造的BigDecimal 不等于 1.465, 不精确 :1.4650000000000000799360577730112709105014801025390625
BigDecimal decimal2 = BigDecimal.valueOf(c).setScale(2, RoundingMode.HALF_EVEN); //这样构造的BigDecimal 才精确
BigDecimal decimal3 = new BigDecimal(c+"").setScale(2, RoundingMode.HALF_EVEN); //这样构造的BigDecimal 才精确 System.out.println(new BigDecimal(a).setScale(0, RoundingMode.HALF_EVEN).doubleValue());
System.out.println(new BigDecimal(b).setScale(0, RoundingMode.HALF_EVEN).doubleValue());
System.out.println(new BigDecimal(c+"").setScale(2, RoundingMode.HALF_EVEN).doubleValue());
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