BigDecimal 浅析

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));

BigDecimal 浅析_第1张图片

比较方法

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)
            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).compareTo(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").compareTo(new BigDecimal("0.10")));	//String构造,compareTo

		compareTo返回值为0,表示true;返回值为1,

BigDecimal 浅析_第2张图片
[注意]

  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 底层源码及实现原理分析

你可能感兴趣的:(java,基础)