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不变
posted @ 2021-12-06 20:58  HeiDaotu  阅读(339)  评论(0编辑  收藏  举报