
一、BigDecimal
的产生背景
首先我们先来看如下代码示例:
@Testpublic 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.069999999999999990.5800000000000001401.499999999999940.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个构造方法
1.BigDecimal(int)
创建一个具有参数所指定整数值的对象;2.BigDecimal(double)
创建一个具有参数所指定双精度值的对象;3.BigDecimal(long)
创建一个具有参数所指定长整数值的对象;4.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
类来进行计算的时候,主要三个步骤:
1.用float
或者double
变量构建BigDecimal
对象。2.通过调用BigDecimal
的加,减,乘,除等相应的方法进行算术运算。3.把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();}}
•测试
@Testpublic void bigDecimalUtilTest(){// 0.06System.out.println(BigDecimalUtil.add(0.05, 0.01));// 0.58System.out.println(BigDecimalUtil.subtract(1.0, 0.42));// 401.5System.out.println(BigDecimalUtil.multiply(4.015, 100));// 1.233System.out.println(BigDecimalUtil.div(123.3, 100));// 4.02System.out.println(BigDecimalUtil.divide(4.015, 2));}
•打印结果
0.060.58401.51.2334.02
2.5 比较BigDecimal
在比较两个BigDecimal
的值是否相等时,必须使用compareTo()
方法来比较,它根据两个值的大小分别返回负数、正数和0
,分别表示小于、大于和等于。
@Testpublic void compareDecimal() {BigDecimal v1 = new BigDecimal("1.2");BigDecimal v2 = new BigDecimal("1.20");// 0System.out.println(v1.compareTo(v2));// falseSystem.out.println(v1.equals(v2));}
三、BigDecimal
精度也丢失
仔细的你一定发现了,在工具类中使用BigDecimal
中,使用它的BigDecimal(String)
构造器创建对象。因为其他的如BigDecimal b = new BigDecimal(1)
这种,还是会发生精度丢失的问题。
示例
@Testpublic 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.0300000000000000266453525910037569701671600341796875System.out.println(a.add(b));// 2.03System.out.println(c.add(d));}
•结果
2.03000000000000002664535259100375697016716003417968752.03
四、总结
1.BigDecimal
用于表示精确的小数,常用于财务计算;2.比较BigDecimal
的值是否相等,必须使用compareTo()
而不能使用equals()
。
示例代码
Github 示例代码[1]
文中链接
[1]
Github 示例代码: https://github.com/vanDusty/JDK/tree/master/JDK-5/src/main/java/cn/van/jdk/five/generic/bigdecimal




