BigDecimal
是 Java 提供的一个关于小数精确计算的类,其位于
java.math
包下。不同于基本数据类型,
BigDecimal
是调用相关方法来进行运算。因为其拥有非常精确的小数计算能力,所以比较适合用于财务相关的计算等等。但是,其运行效率是不及
Float
与
Double
的,要按照实际情况,运用在合理的地方。
对象初始化
BigDecimal bigDec = new BigDecimal("99.6");
或
BigDecimal bigDec = new BigDecimal(99.6);
强烈建议使用 String 类型进行初始化对象,强烈不推荐使用 Double 类型。原因何在?我们来看一个小测试。 执行下方代码
BigDecimal bigDecimal=new BigDecimal("99.6");
System.out.println(bigDecimal.toString());
运行结果
99.6
而执行下方代码
BigDecimal bigDecimal=new BigDecimal(99.6);
System.out.println(bigDecimal.toString());
运行结果
99.599999999999994315658113919198513031005859375
问题立刻显现出来,使用 Double 型数据初始化 Bigdecimal 是不精确的,会损失精度。这不是 BigDecimal 的问题,而属于 Double 本身。在 IDEA 中,使用 Double 初始化时,则会“报黄”警告。
所以请务必使用 String 初始化 BigDecimal !
建议的数据库解决方案
基于上一部分得到的结论,建议在数据库中使用
VARCHAR
类型或者
DECIMAL
,这里以 MySQL + MyBatis 为例。
数据库数据类型 | MyBatis 实体类数据类型 |
---|
VARCHAR | String |
DECIMAL | BigDecimal |
加减运算
加减运算比较简单,使用 add()
与 subtract()
方法进行处理。
减法
示例代码
BigDecimal bigDecimal = new BigDecimal("99.6");
BigDecimal subtract = bigDecimal.subtract(new BigDecimal("66.9"));
System.out.println(subtract.toString());
System.out.println(bigDecimal.toString());
运行结果
32.7
99.6
此处需要注意的是,
所有的运算方法在执行后并不会影响参与计算的数,他只会将计算结果返回! 你可以使用新的对象来接受,或者覆盖旧的对象。 示例代码
BigDecimal bigDecimal = new BigDecimal("99.6");
bigDecimal = bigDecimal.subtract(new BigDecimal("66.9"));
System.out.println(bigDecimal.toString());
加法
示例代码
BigDecimal bigDecimal = new BigDecimal("99.6");
bigDecimal = bigDecimal.add(new BigDecimal("66.9"));
System.out.println(bigDecimal.toString());
运行结果
166.5
乘除运算
乘法
使用
multiply()
方法进行处理。 示例代码
BigDecimal bigDecimal = new BigDecimal("99.6");
bigDecimal = bigDecimal.multiply(new BigDecimal("66.9"));
System.out.println(bigDecimal.toString());
运行结果
6663.24
除法
除法是比较特殊的一个运算,因为除数与被除数的关系,可能会造成结果为无限循环小数
或无限不循环小数
,为了解决这一问题你需要指定小数保留方案
。
示例代码
BigDecimal bigDecimal = new BigDecimal("99.6");
bigDecimal = bigDecimal.divide(new BigDecimal("3"));
System.out.println(bigDecimal.toString());
运行结果
33.2
因为该运算结果不是
无限循环小数
,所以一切看起来都很 Nice ,但是当你稍稍改动一下,它就会“原形毕露”。
BigDecimal bigDecimal = new BigDecimal("99.6");
bigDecimal = bigDecimal.divide(new BigDecimal("66.9"));
System.out.println(bigDecimal.toString());
运行结果 Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. at java.math.BigDecimal.divide(BigDecimal.java:1690) at ... 我是谁?我在哪?我写的是什么BUG?我们来看关键字
Non-terminating decimal expansion
,翻译过来就是
无穷小数扩张
。看到这里,你是否已经明白问题所在。也就是上边所提到的,需要指定
小数保留方案
。 改动一下
BigDecimal bigDecimal = new BigDecimal("99.6");
bigDecimal = bigDecimal.divide(new BigDecimal("66.9"),2,BigDecimal.ROUND_DOWN);
System.out.println(bigDecimal.toString());
运行结果
1.48
此时一切都已恢复往日的平静,那么到底是什么力量遏制住了可恶的 Exception 呢?细心的同学会发现在
divide()
方法中多了两个参数,一个是
2
,一个是
BigDecimal.ROUND_DOWN
。其中,2是保留小数的位数,这个很容易理解。而
BigDecimal.ROUND_DOWN
又是什么东西呢?他是 BigDecimal 类中的小数保留方案常量,算上这一个,一共有八个方案常量,请看解析。
常量 | 描述 |
---|
BigDecimal.ROUND_UP | 舍入远离零的舍入模式。在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。 |
BigDecimal.ROUND_DOWN | 接近零的舍入模式。在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。 |
BigDecimal.ROUND_CEILING | 接近正无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;如果为负,则舍入行为与 ROUND_DOWN 相同。 |
BigDecimal.ROUND_FLOOR | 接近负无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;如果为负,则舍入行为与 ROUND_UP 相同。 |
BigDecimal.ROUND_HALF_UP | 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分 >= 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同。注意,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。 |
BigDecimal.ROUND_HALF_DOWN | 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为上舍入的舍入模式。如果舍弃部分 > 0.5,则舍入行为与 ROUND_UP 相同;否则舍入行为与 ROUND_DOWN 相同(五舍六入)。 |
BigDecimal.ROUND_HALF_EVE | 向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。此舍入模式也称为“银行家舍入法”,主要在美国使用。 |
BigDecimal.ROUND_UNNECESSARY | 断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。 |
这么多的小数保留方案,其实常用的也不多,感兴趣的话,可以自己写个测试类玩玩。
比较大小
比较大小也是需要调用方法的,使用compareTo()
方法即可处理。
示例代码
BigDecimal a = new BigDecimal("99.6");
BigDecimal b = new BigDecimal("66.9");
int i = a.compareTo(b);
System.out.println(i);
运行结果
1
compareTo()
方法的返回值是一个
int
型的数据,以上方示例为例,请看解析
情况 | compareTo() 的返回值 |
---|
a>b | 1 |
a=b | 0 |
a<b | -1 |
当你要判断一个数的与0的关系(正负)时可以这样写
BigDecimal a = new BigDecimal("-99.6");
if (a.compareTo(BigDecimal.ZERO) > 0) {
}else if (a.compareTo(BigDecimal.ZERO) == 0){
}else {
}
其他
去除无用的零
示例代码
BigDecimal a = new BigDecimal("99.12300000");
BigDecimal b = a.stripTrailingZeros();
System.out.println(b.toString());
运行结果
99.123
更多技术文章请访问我的个人网站:
zyan1226.cn