BigDecimal 是java小数操做的一个专有类,在电商、金融行业 存储跟金额有关的字段java
java里面明明已经有了,float,double这种精度的小数,为何还须要BigDecimal呢?
这难道不是多余吗?ide
接下来看一个例子:函数
1 @Test 2 public void testDoubleSimple() { 3 double a = 3; 4 double b = 10; 5 double c = a / b; 6 System.out.println(c); 7 }
控制台输出:0.3this
在小数操做中,咱们一般但愿能有多种自由的定义方式。spa
例如在不一样的场景可能须要返回: 0.3, 0.4, 0.333 不一样精度,在不一样的精度进位时但愿能自主控制.net
这个时候,就轮到BigDecimal出场了code
加减乘除
首先来一段最简单的加减乘除blog
1 @Test 2 public void testDecimalSimple() { 3 BigDecimal a = new BigDecimal(5); 4 BigDecimal b = new BigDecimal(40); 5 BigDecimal add = a.add(b); 6 BigDecimal subtract = a.subtract(b); 7 BigDecimal multiply = a.multiply(b); 8 BigDecimal divide = a.divide(b); 9 System.out.println("add:" + add); 10 System.out.println("subtract:" + subtract); 11 System.out.println("multiply:" + multiply); 12 System.out.println("divide:" + divide); 13 }
控制台输出内容以下:ip
add:45; subtract:-35; multiply:200; divide:0.125;
在了解了BigDecimal基本内容后,在去深刻的去使用它的精度内存
精度控制
精度有7种模式,举例以下
1 @Test 2 public void testRound() { 3 // 正无穷大方向取整 4 System.out.println("celling:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.CEILING))); 5 // 负无穷大方向取整 6 System.out.println("floor:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.FLOOR))); 7 //向 0 的方向取整 8 System.out.println("down a:" + new BigDecimal(0.121, new MathContext(2, RoundingMode.DOWN))); 9 System.out.println("down b:" + new BigDecimal(-0.129, new MathContext(2, RoundingMode.DOWN))); 10 // 正数向正无穷大取整,负数向负无穷大取整 11 System.out.println("up a:" + new BigDecimal(0.121, new MathContext(2, RoundingMode.UP))); 12 System.out.println("up b:" + new BigDecimal(-0.129, new MathContext(2, RoundingMode.UP))); 13 /** 14 * 5,6,7,8,9 向上取整 15 * 1,2,3,4 向下取整 16 * 17 * 经常使用的4舍5入 18 */ 19 System.out.println("half up:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.HALF_UP))); 20 /** 21 * 6,7,8,9 向上取整 22 * 1,2,3,4,5 向下取整 23 * 24 * 5 向下取整 25 */ 26 System.out.println("half down:" + new BigDecimal(0.125, new MathContext(2, RoundingMode.HALF_DOWN))); 27 28 /** 29 * 小数位是5时,判断整数部分是奇数就进位 30 * 1,2,3,4, 舍弃 31 * 6,7,8,9, 进位 32 */ 33 System.out.println("odd a:" + new BigDecimal(5.4, new MathContext(1, RoundingMode.HALF_EVEN))); 34 System.out.println("odd b:" + new BigDecimal(5.5, new MathContext(1, RoundingMode.HALF_EVEN))); 35 /** 36 * 小数位是5时,判断整数部分是偶数就舍弃 37 * 1,2,3,4, 舍弃 38 * 6,7,8,9, 进位 39 */ 40 System.out.println("even a:" + new BigDecimal(6.5, new MathContext(1, RoundingMode.HALF_EVEN))); 41 System.out.println("even b:" + new BigDecimal(6.6, new MathContext(1, RoundingMode.HALF_EVEN))); 42 }
控制台输出内容以下
celling:0.13; floor:0.12; down a:0.12; down b:-0.12; up a:0.13; up b:-0.13; half up:0.13; half down:0.12; odd a:5; odd b:6; even a:6; even b:7;
在 RoundingMode.XXXXX 类型的源码注释上面,有更加详细的例子,能够看到是怎么舍入的
我认为在电商,金融领域中,用BigDecimal最重要的缘由有两个:
1. 精度准确
2. 除法运算支持好
因此必定要对除法作深刻的了解,作项目的时候,才能不会对这些类型感到疑惑
1 @Test 2 public void testDecimalDivide() { 3 BigDecimal a = new BigDecimal(5.4); 4 BigDecimal b = new BigDecimal(3.1); 5 BigDecimal divide = a.divide(b); 6 System.out.println("divide:" + divide); 7 }
出现异常:
1 java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
明明刚刚还好好的,怎么如今出了事?
那是由于 5.四、3.1都是double类型转换的 BigDecimal。
实际上5.4在内存中多是 5.40000003321546546 的内容。致使BigDecimal内部精度计算的时候,发生错误
这个错误是由于没有指定精度致使的,咱们只要指定告终果的精度,就能够避免这个问题。
推荐作法
1 @Test 2 public void testDecimalStandDivide() { 3 BigDecimal a = new BigDecimal(5.4); 4 BigDecimal b = new BigDecimal(3.1); 5 // 保留几位小数 6 int scale = 2; 7 // 重点:务必是3个参数 8 BigDecimal divide = a.divide(b,scale,RoundingMode.HALF_UP); 9 System.out.println("divide:" + divide); 10 11 }
控制台输出:divide:1.74
咱们额外传入第二个参数:保留的小数,指定告终果的精度,就能够避免出现这种问题。
因此咱们平常用BigDecimal作除法运算的时候,务必写成推荐的形式。避免出现了异常,本身还莫名其妙
默认除法精度
在文章的开头的除法,是用整数转成BigDecimal, 保留的3为小数。 那默认状况下会精确到几位呢?
在跟进到divide函数内部时,发现了构造MathContext的部份内容:
1 MathContext mc = new MathContext( (int)Math.min(this.precision() + 2 (long)Math.ceil(10.0*divisor.precision()/3.0), 3 Integer.MAX_VALUE), 4 RoundingMode.UNNECESSARY);
整数 12345 的precision 是5
整数 332 的precision 是 3
小数5.4 的precision多是 5.40000065464698656565454454555 的长度。 值不固定
根据MathContext的第一个参数的计算方式获得默认除法精度:
1. 当被除数为:0x1 最低精度5
2. 当被除数为:0xFFFFFFFF 最高精度36
BigDecimal 精度描述:
模式 | 描述 |
CEILING | 正无穷大方向取整 |
FLOOR | 负无穷大方向取整 |
DOWN | 向 0 的方向取整 |
UP | 正数向正无穷大取整,负数向负无穷大取整 |
HALF_UP | 5,6,7,8,9 向上取整、 1,2,3,4 向下取整、 经常使用的4舍5入 |
HALF_DOWN | 6,7,8,9 向上取整 1,2,3,4,5 向下取整 |
HALF_EVEN | 小数位是5时,判断整数部分是奇数就进位、 小数位是5时,判断整数部分是偶数就舍弃、 1,2,3,4, 舍弃、 6,7,8,9, 进位 |
————————————————参考连接:https://blog.csdn.net/mz4138/article/details/82708815