BigDecimal浅析
是专门为弥补浮点数⽆法精确计算的遗憾⽽设计的类,并且提供了加减乘除的常⽤数学算法。特别是与数据库Decimal类型的字段映射时,BigDecimal是最佳的解决⽅案。
浮点数计算的缺陷
⼗进制的浮点数转化为⼆进制⼩数,在计算机中进⾏表达存储,采⽤的⽅法为“除2取整,顺序排列”。由于计算机浮点数的存储规则限制,因此计算机中的浮点数有可能不准确,只能⽆限接近于准确值,⽽不能精确。
public class BigDecimalTest {
public static void main(String[] args) {
System.out.println(10.00 - 9.60);
}
}
浮点数计算误差解决⽅式
由于浮点数的存储规则限制,浮点数的计算存在着误差,⽽在⾦融会计领域,预算和核算需要精确的结果,这样⼀来就不能使⽤直接截断进⾏结果输出了。对于这种情况有两种解决⽅式:
使⽤整形
1. 使⽤整型 把参与运算的值扩⼤100倍,并转变为整型,然后在计算完成后再缩⼩100倍,这样处理的好处就是简单、准确。在⾮⾦融
⾏业(⽐如零售业)应⽤较多。
2. 使⽤BigDecimal 在⼀些Java⾯试时或是进⾏财务类型的开发过程中,经常使⽤到BigDecimal类作为数据运算的载体,因为这样就可
以避免因为计算机浮点数误差⽽带来的种种问题。特别是与数据库Decimal类型的字段映射时,BigDecimal是最佳的解决⽅案。BigDecimal使⽤⽅法
构造⽅法
double d = 10.0 - 9.6;
BigDecimal bigDecimal = new BigDecimal(d);  // 不允许使⽤
BigDecimal bigDecimal1 = BigDecimal.valueOf(d); //valueof
BigDecimal bigDecimal2 = new BigDecimal("9.6"); //推荐使⽤
System.out.println(bigDecimal + "\n" + bigDecimal1 + "\n" + bigDecimal2);
[分析]
1. 不推荐使⽤BigDecimal(double val)构造器,因为使⽤该构造器时有⼀定的不可预知性,当程序使⽤new BigDecimal(0.1)创建⼀个
BigDecimal对象时,它的值并不是0.1,实际上是⼀个近似0.1的数。
2. 如果必须使⽤double浮点数作为BigDecimal构造器的参数时,不要使⽤double作为参数,⽽应该通过BigDecimal.valueOf(double
value)静态⽅法来创建对象。
3. 建议优先使⽤基于String的构造器,使⽤BigDecimal(String val)构造器时可以预知的,写⼊new BigDecimal(“0.1”)将创建⼀个
恰好等于0.1的BigDecimal。
类成员⽅法
加减乘除
public BigDecimal add(BigDecimal augend):加
public BigDecimal subtract(BigDecimal subtrahend):减
public BigDecimal multiply(BigDecimal multiplicand):乘
public BigDecimal divide(BigDecimal divisor):除
public BigDecimal divide(BigDecimal divisor,int scale, int roundingMode):商,⼏位⼩数,舍取模式
BigDecimal a = new BigDecimal("10.00");
BigDecimal b = new BigDecimal("9.60");
System.out.println("a + b = " + a.add(b));
System.out.println("a - b = " + a.subtract(b));
System.out.println("a * b = " + a.multiply(b));
System.out.println("a / b = " + a.divide(b,2,BigDecimal.ROUND_DOWN));
⽐较⽅法
equals()代码
@Override
public boolean equals(Object x) {
if (!(x instanceof BigDecimal))
return false;
BigDecimal xDec = (BigDecimal) x;
if (x == this)
return true;
if (scale != xDec.scale)  //⽐较精度,不等返回false
return false;
long s = this.intCompact;
long xs = xDec.intCompact;
if (s != INFLATED) {
if (xs == INFLATED)
xs = compactValFor(xDec.intVal);
return xs == s;
} else if (xs != INFLATED)bigdecimal除法保留小数
return xs == compactValFor(this.intVal);
return this.inflated().equals(xDec.inflated());
}
compareTo()代码
@Override
public int compareTo(BigDecimal val) {
// Quick path for equal scale and non-inflated case.
if (scale == val.scale) {
long xs = intCompact;
long ys = val.intCompact;
if (xs != INFLATED && ys != INFLATED)
return xs != ys ? ((xs > ys) ? 1 : -1) : 0;
}
int xsign = this.signum();
int ysign = val.signum();
if (xsign != ysign)
return (xsign > ysign) ? 1 : -1;
if (xsign == 0)
return 0;
int cmp = compareMagnitude(val);
return (xsign > 0) ? cmp : -cmp;
}
== 分析
== 判断的是类对象的引⽤是否相等,⽽不是值⽐较,即不同对象⼀定不相等。
测试代码
double A = 0.1;
double B = 0.10;
System.out.println(BigDecimal.valueOf(A).equals(BigDecimal.valueOf(B))); // valueof构造,equals
System.out.println(BigDecimal.valueOf(A)pareTo(BigDecimal.valueOf(B))); // valueof构造,compareTo
System.out.println(new BigDecimal("0.1").equals(new BigDecimal("0.10"))); //String构造,equals
System.out.println(new BigDecimal("0.1")pareTo(new BigDecimal("0.10"))); //String构造,compareTo
compareTo返回值为0,表⽰true;返回值为1,
[注意]
1. BigInteger和BigDecimal都是不可变(immutable)的,在进⾏每⼀步运算时,都会产⽣⼀个新的对象,由于创建对象会引起开销,它
们不适合于⼤量的数学计算,应尽量⽤long,float,double等基本类型做科学计算或者⼯程计算。设计BigInteger和BigDecimal的⽬的是⽤来精确地表⽰⼤整数和⼩数,使⽤于在商业计算中使⽤。
2. 应该避免使⽤double构造BigDecimal,因为:有些数字⽤double根本⽆法精确表⽰,传给BigDecimal构造⽅法时就已经不精确了。
3. equals()⽅法认为0.1和0.1是相等的,返回true,⽽认为0.10和0.1是不等的,结果返回false。 ⽅法compareTo()则认为0.1与0.1
相等,0.10与0.1也相等。即:equals⽅法会⽐较值和精确度,⽽compareTo则会忽略精度。所以在从数值上⽐较两个BigDecimal 值时,应该使⽤compareTo()⽽不是 equals()。 尤其是处理⽀付⾦额校验的时候,⽤BigDecimal的equals⽅法来⽐较两个⾦额是否相等,可能导致⾦额⽐较出现错误(⽐如3.0与3.00的⽐较等)。
4. 另外还有⼀些情形,任意精度的⼩数运算仍不能表⽰精确结果。例如,1除以9会产⽣⽆限循环的⼩数 .111111…。 出于这个原因,
在进⾏除法运算时,BigDecimal可以让您显式地控制舍⼊。
TODO
BigDecimal 除法等详细使⽤⽅法
BigDecimal 底层源码及实现原理分析

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。