BigDecimal 使用

1,412 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情

在项目开发中,我们经常会涉及数字计算,尤其是金钱方面会涉及到浮点数的计算,但是由于计算机对于浮点数的存储规则的限制,使得float和double在计算过程中往往不能提供精确的计算结果。

println(1-(9 * 0.1))

上面这个运算结果本应该是0.1,但是最后输出的结果却并不精确,而是一个无限接近正确值得数据。

0.09999999999999998

一般在金钱计算的时候,我们会把运算的金额扩大100倍转化为int型,计算完成之后再缩小100倍,这样处理简单快捷。

除此之外,Java还提供了BigDecimal来做数据运算。

BigDecimal

BigDecimal是专门为了弥补浮点数无法精确计算而设计的类,它提供了常用的加减乘除等计算方法。

构造BigDecimal

BigDecimal提供了很多构造方法,支持int,long,double等数据类型直接转化为BigDecimal对象。

println(BigDecimal("0.01"))//String
println(BigDecimal(0.01))//double

不过虽然BigDecimal提供了double的构造函数,但是并不推荐使用,先看上面的输出结果

0.01
0.01000000000000000020816681711721685132943093776702880859375

可以看到BigDecimal(double val)构造器创建的结果数据并不精确,它具有一定的不可预知性,所以当我们需要将double转化为BigDecimal对象的时候,可以优先考虑BigDecimal(Stringg val)构造器,除此之外,BigDecimal还提供了BigDecimal.valueOf(0.01)方法,它也可以精确地转换。

println(BigDecimal.valueOf(0.01))//输出0.01

运算

public BigDecimal add(BigDecimal augend)//加        
public BigDecimal subtract(BigDecimal subtrahend)//减        
public BigDecimal multiply(BigDecimal multiplicand)//乘        
public BigDecimal divide(BigDecimal divisor)//除        
public BigDecimal divide(BigDecimal divisor,int scale, int roundingMode)//除,保留几位小数和舍取模式

加,减 ,乘 运算比较简单,需要注意的是除法运算,一般情况下不推荐使用divide(BigDecimal divisor),因为除法可能出现除不尽的情况。

println(BigDecimal(1).divide(BigDecimal(3)))

这种情况下就会计算失败,抛出异常(Non-terminating decimal expansion; no exact representable decimal result.),这时候就需要使用带有指定保留几位小数和舍取模式的除法方法。

println(BigDecimal(1).divide(BigDecimal(3), 2, RoundingMode.DOWN))//输出0.33

舍取模式是RoundingMode枚举类,常见的有ROUND_UP(向远离零的方向舍入),ROUND_DOWN(向接近零的方向舍入)

BigDecimal设计的目的是为了精确地表示大数和小数,它是不可变的,在每一步运算过程中都会生成新的对象,所以需要防止频繁使用它进行大量的数学运算,防止内存抖动。