一、BigDecimal
的产生背景
首先我们先来看如下代码示例:
@Test
public void countDemo() {
System.out.println(0.06+0.01);
System.out.println(1.0-0.42);
System.out.println(4.015*100);
System.out.println(303.1/1000);
}
复制代码
结果如下
0.06999999999999999
0.5800000000000001
401.49999999999994
0.30310000000000004
复制代码
问题在哪里呢?原因在于我们的计算机是二进制的,浮点数是没有办法用二进制进行精确表示。
Java
的float
只能用来进行科学计算或工程计算,在大多数的商业计算中,一般采用java.math.BigDecimal
类来进行精确计算。
二、正确运用BigDecimal
2.1 BigDecimal
常量
BigDecimal
定义了几个常用的值,0
、1
、10
,静态的,可以通过类名直接引用如:BigDecimal.ZERO
。
/**
* The value 0, with a scale of 0.
*
* @since 1.5
*/
public static final BigDecimal ZERO =
zeroThroughTen[0];
/**
* The value 1, with a scale of 0.
*
* @since 1.5
*/
public static final BigDecimal ONE =
zeroThroughTen[1];
/**
* The value 10, with a scale of 0.
*
* @since 1.5
*/
public static final BigDecimal TEN =
zeroThroughTen[10];
复制代码
2.2 构造方法
BigDecimal a = new BigDecimal();
复制代码
BigDecimal一共有4个构造方法
-
BigDecimal(int)
创建一个具有参数所指定整数值的对象; -
BigDecimal(double)
创建一个具有参数所指定双精度值的对象; -
BigDecimal(long)
创建一个具有参数所指定长整数值的对象; -
BigDecimal(String)
创建一个具有参数所指定以字符串表示的数值的对象。
2.3 BigDecimal
加减乘除
BigDecimal
所创建的是对象,不能使用传统的+
、-
、*
、/
等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。
public BigDecimal add(BigDecimal value); //加法
public BigDecimal subtract(BigDecimal value); //减法
public BigDecimal multiply(BigDecimal value); //乘法
public BigDecimal divide(BigDecimal value); //除法
复制代码
2.4 BigDecimal
运算
在使用BigDecimal
类来进行计算的时候,主要三个步骤:
-
用
float
或者double
变量构建BigDecimal
对象。 -
通过调用
BigDecimal
的加,减,乘,除等相应的方法进行算术运算。 -
把
BigDecimal
对象转换成float
,double
,int
等类型。
在一般开发过程中,我们数据库中存储的数据都是float
和double
类型的。在进行拿来拿去运算的时候还需要不断的转化,这样十分的不方便。下面是一个工具类,该工具类提供加,减,乘,除运算。
public class BigDecimalUtil {
/**
* 默认除法运算精度
*/
private static final int DEF_DIV_SCALE = 10;
/**
* 提供精确的加法运算。
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static double add(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确的减法运算。
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static double subtract(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确的乘法运算。
* @param v1 被乘数
* @param v2 乘数
* @return 两个参数的积
*/
public static double multiply(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
* 小数点以后10位,以后的数字四舍五入。
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static double div(double v1,double v2){
return div(v1,v2,DEF_DIV_SCALE);
}
/**
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
* 定精度,以后的数字四舍五入。
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1,double v2,int scale){
if(scale<0){
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 提供精确的小数位四舍五入处理。
* @param v 需要四舍五入的数字
* @param scale 小数点后保留几位
* @return 四舍五入后的结果
*/
public static double divide(double v,int scale){
//如果精确范围小于0,抛出异常信息
if(scale<0){
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one,scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
复制代码
- 测试
@Test
public void bigDecimalUtilTest(){
// 0.06
System.out.println(BigDecimalUtil.add(0.05, 0.01));
// 0.58
System.out.println(BigDecimalUtil.subtract(1.0, 0.42));
// 401.5
System.out.println(BigDecimalUtil.multiply(4.015, 100));
// 1.233
System.out.println(BigDecimalUtil.div(123.3, 100));
// 4.02
System.out.println(BigDecimalUtil.divide(4.015, 2));
}
复制代码
- 打印结果
0.06
0.58
401.5
1.233
4.02
复制代码
2.5 比较BigDecimal
在比较两个BigDecimal
的值是否相等时,必须使用compareTo()
方法来比较,它根据两个值的大小分别返回负数、正数和0
,分别表示小于、大于和等于。
@Test
public void compareDecimal() {
BigDecimal v1 = new BigDecimal("1.2");
BigDecimal v2 = new BigDecimal("1.20");
// 0
System.out.println(v1.compareTo(v2));
// false
System.out.println(v1.equals(v2));
}
复制代码
三、BigDecimal
精度也丢失
仔细的你一定发现了,在工具类中使用BigDecimal
中,使用它的BigDecimal(String)
构造器创建对象。因为其他的如BigDecimal b = new BigDecimal(1)
这种,还是会发生精度丢失的问题。
示例
@Test
public void precisionLose() {
BigDecimal a = new BigDecimal(1.01);
BigDecimal b = new BigDecimal(1.02);
BigDecimal c = new BigDecimal("1.01");
BigDecimal d = new BigDecimal("1.02");
// 2.0300000000000000266453525910037569701671600341796875
System.out.println(a.add(b));
// 2.03
System.out.println(c.add(d));
}
复制代码
- 结果
2.0300000000000000266453525910037569701671600341796875
2.03
复制代码
四、总结
-
BigDecimal
用于表示精确的小数,常用于财务计算; -
比较
BigDecimal
的值是否相等,必须使用compareTo()
而不能使用equals()
。