大家都知道在基本数据类型中,操作运算(+、-、、/)多多少少都会存在精度丢失的问题,所以我们会用一种保存精度更高的类来替代,那就是BigDecamal*。
使用原因(以浮点数为例)
浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数
这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。如:2.4的二进制表示并非就是精确的2.4。反而最为接近的二进制表示是 2.3999999999999999。
浮点数的值实际上是由一个特定的数学公式计算得到的。正是因为如此,java的float只能用来进行科学计算或工程计算,在大多数的商业计算中,一般采用java.math.BigDecimal类来进行精确计算。常见构造及方法
BigDecimal extends Number implements Comparable
图上标记的就是现在用的就是比较多的构造了,其次就是普通运算操作符(+、-、*、/)方法
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构造方法上一段注释中有这么一段话:
这就可以很好的解释为什么不使用doubleg构造的原因了。当时要想使用也还是有办法的,添加一个setScale方法(保留小数点位数)即可。
b1.add(b2).setScale(2, RoundingMode.HALF_UP).doubleValue()
好了,文章到此结束了,其实内容很简单,主要是看自己是否细心,另外说一句网上的例子最好自己实践下,否则会有什么坑在那都不知道呢!