BigDecimal详解
float和double类型一般用于科学计算,BigDecimal由于拥有完全精确的结果,所以商业计算往往使用BigDecimal
在需要精确度的项目不适用float和double的原因
代码:
System.out.println(0.2 + 0.1);
System.out.println(0.3 - 0.1);
System.out.println(0.2 * 0.1);
System.out.println(0.3 / 0.1);
运行结果:
0.30000000000000004
0.19999999999999998
0.020000000000000004
2.9999999999999996
结论:由于我们的计算机是二进制的。浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。如:2.4的二进制表示并非就是精确的2.4。反而最为接近的二进制表示是 2.3999999999999999。浮点数的值实际上是由一个特定的数学公式计算得到的。
BigDecimal构造方法
- public BigDecimal(double val)
- public BigDecimal(int val)
- public BigDecimal(long val)
- public BigDecimal(String val)
- public BigDecimal(char[] val)
- public BigDecimal(BigInteger val)
在构造方法中不推荐使用double
-
当BigDecimal传参为
BigDecimal bDouble = new BigDecimal(2.3);
时,结果为bDouble=2.29999999999999982236431605997495353221893310546875
所以参数类型为double的构造方法的结果有一定的不可控性。实际上的值不一定等于你想要的值。 -
String 构造方法是完全可控的:在newBigDecimal("0.1") 中创建一个 BigDecimal,它的值为0.1。因此通常建议优先使用String构造方法。
BigDecimal加减乘除
- public BigDecimal add(BigDecimal value); 加法
- public BigDecimal subtract(BigDecimal value); 减法
- public BigDecimal multiply(BigDecimal value); 乘法
- public BigDecimal divide(BigDecimal value); 除法
示例代码:
BigDecimal a = new BigDecimal("3.2");
BigDecimal b = new BigDecimal("2.3");
BigDecimal add = a.add(b);
BigDecimal subtract = a.subtract(b);
BigDecimal multiply = a.multiply(b);
BigDecimal divide = a.divide(b,2,RoundingMode.HALF_UP);
System.out.println("a + b ="+add);
System.out.println("a - b ="+subtract);
System.out.println("a * b ="+multiply);
System.out.println("a / b +"+divide);
结果:
a + b =5.5
a - b =0.9
a * b =7.36
a / b +1.39
加减乘法
MathContext构造器
MathContext(int setPrecision, RoundingMode setRoundingMode)
- setPrecision:小数点几位
- setRoundingMode:舍入模式
可以通过mathContext来改变精度和舍入模式
BigDecimal a = new BigDecimal("3.2");
BigDecimal b = new BigDecimal("2.3");
MathContext mathContext = new MathContext(2,RoundingMode.HALF_UP);
BigDecimal multiply = a.multiply(b,mathContext);
除法divide
我们需要注意的是如果我们的参数只填写被除数,除后的结果不是整数就会报错
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1693)
at com.demos.date.date01.main(date01.java:30)
解决方法:
完善构造参数
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
参数解释:
- divisor:除数
- scale:表示小数点保留位数
- roundingMode:表示舍入模式
舍入模式roundingMode
我们一般舍入模式采取的是ROUND_HALF_UP
- ROUND_CEILING //向正无穷方向舍入
BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_CEILING); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_CEILING); // 0.10
BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_CEILING); // -0.09
BigDecimal f = new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_CEILING); // 0.09
- ROUND_UP //不管保留数字后面是大是小 (0 除外) 都会进1。
BigDecimal a = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.09
BigDecimal c = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.09
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.09
BigDecimal f = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.10
- ROUND_DOWN //舍去制,截断操作,后面所有数字直接去除。结果会向原点方向对齐
BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_DOWN); // 0.09
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_DOWN); // 0.09
BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_DOWN); // -0.09
BigDecimal f = new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_DOWN); // 0.09
- ROUND_FLOOR //向负无穷方向舍入
BigDecimal a = new BigDecimal("0.098").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09
BigDecimal c = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_FLOOR); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_FLOOR); // -0.10
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_FLOOR); // -0.09
BigDecimal f = new BigDecimal("0.090").setScale(2, BigDecimal.ROUND_FLOOR); // 0.09
- ROUND_HALF_EVEN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
BigDecimal a = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_HALF_EVEN); // 0.09
BigDecimal c = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.09
BigDecimal e = new BigDecimal("-0.085").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.08
BigDecimal f = new BigDecimal("-0.084").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.08
BigDecimal g = new BigDecimal("-0.086").setScale(2, BigDecimal.ROUND_HALF_EVEN); // -0.09
- ROUND_HALF_UP //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 1.55保留一位小数结果为1.6
BigDecimal a = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.10
BigDecimal b = new BigDecimal("0.094").setScale(2, BigDecimal.ROUND_HALF_UP); // 0.09
BigDecimal c = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.10
BigDecimal d = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.09
BigDecimal e = new BigDecimal("-0.090").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.09
BigDecimal f = new BigDecimal("-0.098").setScale(2, BigDecimal.ROUND_HALF_UP); // -0.10
- ROUND_HALF_DOWN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如1.55 保留一位小数结果为1.5
BigDecimal a = new BigDecimal("0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN); // 0.10
BigDecimal b = new BigDecimal("0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN); // 0.09
BigDecimal c = new BigDecimal("-0.096").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.10
BigDecimal d = new BigDecimal("-0.095").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.09
BigDecimal e = new BigDecimal("-0.094").setScale(2, BigDecimal.ROUND_HALF_DOWN); // -0.09
- ROUND_UNNECESSARY //计算结果是精确的,不需要舍入模式
BigDecimal g = new BigDecimal("-0.086").setScale(2, BigDecimal.ROUND_UNNECESSARY);
减乘除最终都返回的是一个新的BigDecimal对象,因为BigInteger与BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象
代码示例
BigDecimal a = new BigDecimal("4.5");
BigDecimal b = new BigDecimal("1.5");
a.add(b);
System.out.println(a); //输出4.5. 加减乘除方法会返回一个新的BigDecimal对象,原来的a不变