BigDecimal勿用double构造

大家都知道在基本数据类型中,操作运算(+、-、、/)多多少少都会存在精度丢失的问题,所以我们会用一种保存精度更高的类来替代,那就是BigDecamal*。

  1. 使用原因(以浮点数为例)
    浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数
    这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。如:2.4的二进制表示并非就是精确的2.4。反而最为接近的二进制表示是 2.3999999999999999。
    浮点数的值实际上是由一个特定的数学公式计算得到的。正是因为如此,java的float只能用来进行科学计算或工程计算,在大多数的商业计算中,一般采用java.math.BigDecimal类来进行精确计算。

  2. 常见构造及方法

BigDecimal extends Number implements Comparable
BigDecimal勿用double构造_第1张图片
这里写图片描述

图上标记的就是现在用的就是比较多的构造了,其次就是普通运算操作符(+、-、*、/)方法

 public BigDecimal add(BigDecimal augend) {
        if (this.intCompact != INFLATED) {
            if ((augend.intCompact != INFLATED)) {
                return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
            } else {
                return add(this.intCompact, this.scale, augend.intVal, augend.scale);
            }
        } else {
            if ((augend.intCompact != INFLATED)) {
                return add(augend.intCompact, augend.scale, this.intVal, this.scale);
            } else {
                return add(this.intVal, this.scale, augend.intVal, augend.scale);
            }
        }
    }

    public BigDecimal subtract(BigDecimal subtrahend) {
        if (this.intCompact != INFLATED) {
            if ((subtrahend.intCompact != INFLATED)) {
                return add(this.intCompact, this.scale, -subtrahend.intCompact, subtrahend.scale);
            } else {
                return add(this.intCompact, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
            }
        } else {
            if ((subtrahend.intCompact != INFLATED)) {
                // Pair of subtrahend values given before pair of
                // values from this BigDecimal to avoid need for
                // method overloading on the specialized add method
                return add(-subtrahend.intCompact, subtrahend.scale, this.intVal, this.scale);
            } else {
                return add(this.intVal, this.scale, subtrahend.intVal.negate(), subtrahend.scale);
            }
        }
    }
  public BigDecimal multiply(BigDecimal multiplicand) {
        int productScale = checkScale((long) scale + multiplicand.scale);
        if (this.intCompact != INFLATED) {
            if ((multiplicand.intCompact != INFLATED)) {
                return multiply(this.intCompact, multiplicand.intCompact, productScale);
            } else {
                return multiply(this.intCompact, multiplicand.intVal, productScale);
            }
        } else {
            if ((multiplicand.intCompact != INFLATED)) {
                return multiply(multiplicand.intCompact, this.intVal, productScale);
            } else {
                return multiply(this.intVal, multiplicand.intVal, productScale);
            }
        }
    }
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
        if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
            throw new IllegalArgumentException("Invalid rounding mode");
        if (this.intCompact != INFLATED) {
            if ((divisor.intCompact != INFLATED)) {
                return divide(this.intCompact, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
            } else {
                return divide(this.intCompact, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
            }
        } else {
            if ((divisor.intCompact != INFLATED)) {
                return divide(this.intVal, this.scale, divisor.intCompact, divisor.scale, scale, roundingMode);
            } else {
                return divide(this.intVal, this.scale, divisor.intVal, divisor.scale, scale, roundingMode);
            }
        }
    }

以上就是源码中提供的运算操作方法,具体更多的读者可自行阅读源码。
说了这么多,下面就说下本文的标题吧,为什么希望大家尽量不要使用double的构造参数呢?
这个坑就要从我查阅网上的文章说起了,比较了几篇文章,其中发现他们在对Bigdecimal参数的使用方法都不同,如下:

 BigDecimal b1 = new BigDecimal(Double.toString(value1));
 BigDecimal b2 = new BigDecimal(Double.valueOf(value2));

_,细心的读者或许已经发现了,toString()和valueOf()这两者的区别,初一看,没问题嘛,前面不是说了吗?BigDecimal不是支持double或string的构造参数么。下面我们就来看看区别在哪里?

3.toString()和valueOf()的区别
从源码来看,返回参数的类型虽然不同,但毕竟是符合BigDecimal的构造的

 public static String toString(double d) {
        return FloatingDecimal.toJavaFormatString(d);
    }
 public static Double valueOf(double d) {
        return new Double(d);
    }

OK,下面通过一个例子来证明下:

public class BigDecimalTest {


    public static void main(String[] args) {
System.out.println("Double.valueOf===="+(Double.valueOf(0.06)+Double.valueOf(0.01)));      System.out.println("Double.toString=="+Double.toString(0.006));        System.out.println("add:=="+BigDecimalArith.add(0.06, 0.01));        System.out.println("sub:=="+BigDecimalArith.sub(0.06, 0.01));
        System.out.println("mul:=="+BigDecimalArith.mul(0.06, 0.01));
        try {
            System.out.println("div:=="+BigDecimalArith.div(0.06, 0.01,0));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    public static class BigDecimalArith {
        public static double add(double value1, double value2) {
            BigDecimal b1 = new BigDecimal(Double.toString(value1));
            BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
            return b1.add(b2).doubleValue();
        }

        public static double sub(double value1, double value2) {
            BigDecimal b1 = new BigDecimal(Double.toString(value1));
            BigDecimal b2 = new BigDecimal(Double.toString(value2));
            return b1.subtract(b2).doubleValue();
        }

        public static double mul(double value1, double value2) {
            BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
            BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
            return b1.multiply(b2).doubleValue();
        }

        public static double div(double value1, double value2, int scale) throws IllegalAccessException {
            if (scale < 0) {
                throw new IllegalAccessException("精度不能小于0");
            }
            BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
            BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
            return b1.divide(b2, scale).doubleValue();
        }

    }
}

运行的结果如下:

Double.valueOf====0.06999999999999999
Double.toString====0.006
add:==0.06999999999999999
sub:==0.05
mul:==6.0E-4
div:==6.0

为什么add的结果不是0.07呢?答案就是在BigDecimal的double构造方法上一段注释中有这么一段话:


BigDecimal勿用double构造_第2张图片
这里写图片描述

这就可以很好的解释为什么不使用doubleg构造的原因了。当时要想使用也还是有办法的,添加一个setScale方法(保留小数点位数)即可。

b1.add(b2).setScale(2, RoundingMode.HALF_UP).doubleValue()

好了,文章到此结束了,其实内容很简单,主要是看自己是否细心,另外说一句网上的例子最好自己实践下,否则会有什么坑在那都不知道呢!

你可能感兴趣的:(BigDecimal勿用double构造)