BigDecimal的scale和precision

2,427 阅读2分钟

背景

在工作中发现BigDecimal的toString()会时不时出现科学计数法,为了了解其中的玄机,对BigDecimal的scale和precision进行了解释和总结

scale和precision

  • scale,理解成比例尺,也代表小数点左移或右移的位数,scale<0代表左移, scale>0代表右移
  • precision,理解成有效数字位数,即从左往右第一个不为0的数字开始计数,直到最后一位,其中所跨越的数字个数.特殊地,对于零,其有效位数是1

BigDecimal内部表示

在BigDecimal里,用integer,scale和precision来表示一个十进制数字
例如100表示为integer=100, scale=0, precision=2
同样100也可表示为integer=1, scale=-2, precision=1

再说scale

scale对数字的表示较为关键,以下是常见运算符对scale的影响,摘自BigDecimal的Javadoc

OperationPreferred Scale of Result
Addmax(addend.scale(), augend.scale())
Subtractmax(minuend.scale(), subtrahend.scale())
Multiplymultiplier.scale() + multiplicand.scale()
Dividedividend.scale() - divisor.scale()
Square rootradicand.scale()/2

特别地,对于除法(divide)如果除不尽会抛出ArithmeticException异常, 因为无论scale多大也无法准确表示一个无限位的十进制数,此时需要去除尾数策略(RoundingMode).其有以下几种策略

RoundingMode说明
CEILING向正无限大方向舍入
DOWN向零方向舍入
FLOOR向负无限大方向舍入
HALF_DOWN向最接近数字方向舍入,如果与两个相邻数字的距离相等,则向下舍入
HALF_EVEN向最接近数字方向舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入
HALF_UP向最接近数字方向舍入,如果与两个相邻数字的距离相等,则向上舍入
UNNECESSARY断言具有精确结果

带有rounding mode的除法,商的scale等同于被除数

  • 例如21/110,没有指定rounding, 会抛出rithmeticException
  • 还是21/110,被除数的scale=0,指定rounding=CEILING, 商的scale=0,结果为1
  • 还是21/110,被除数的scale=10,指定rounding=CEILING,商的scale=10,结果为0.1909090910

回到toString()

toString()会在必要时采用科学计数法表示,规则如下

  • scale=0, 即整数, 按实际表示,例如1230000,1111111
  • scale<0, 即比例较大的数, 用科学计数法
    • 例如1/0.1=10, scale=-1, precision=1, 表示为1E+1
  • scale>0, 且scale-Precision > 5, 使用科学计数法. 其他则直接表示
    • 例如1.000000, scale=7 Precision=8, 表示为1.0000000
    • 0.0000000, scale=7 Precision=1, 表示为0E-7
    • 0.0000001, scale=7 Precision=1, 表示为1E-7