BigDecimal 用于精确的数值计算,无需担心小数精度所带来的问题。
继承与实现
继承Number抽象类
强制实现6种基本类型(除了boolean和char)的返回。
实现java.io.Serializable接口
实现Comparable接口
关键属性 BigDecimal 内部由long类型+小数点位数来表示数值的。
1 2 3 4 private final BigInteger intVal;private final transient long intCompact;private final int scale ;private transient int precision;
初始化,构造 由于folat和double无法精确表示小数,所以在用BigDecimal初始化的时候,也不选择用folat和double。 一般正常用的都是String。 MathContext是用于精确位数和入舍操作。 这里给出用String初始化的方法,如想看其他方法,可以自行翻看源码,处理方式大同小异。
public BigDecimal(String val) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 public BigDecimal(String val ) { this(val .to CharArray() , 0 , val .length() ); } public BigDecimal(char [] in , int offset , int len ) { this(in ,offset,len,MathContext.UNLIMITED); } public BigDecimal(char [] in , int offset , int len , MathContext mc ) { if (offset + len > in .length || offset < 0 ) throw new NumberFormatException("Bad offset or len arguments for char[] input." ) ; int prec = 0 ; int scl = 0 ; long rs = 0 ; BigInteger rb = null; try { boolean isneg = false ; if (in [offset ] == '-' ) { isneg = true ; offset++; len--; } else if (in [offset ] == '+' ) { offset++; len--; } boolean dot = false ; long exp = 0 ; char c; boolean isCompact = (len <= MAX_COMPACT_DIGITS); int idx = 0 ; if (isCompact) { for (; len > 0 ; offset++, len--) { c = in [offset ] ; if ((c == '0' )) { if (prec == 0 ) prec = 1 ; else if (rs != 0 ) { rs *= 10 ; ++prec; } if (dot) ++scl; } else if ((c >= '1' && c <= '9' )) { int digit = c - '0' ; if (prec != 1 || rs != 0 ) ++prec; rs = rs * 10 + digit; if (dot) ++scl; } else if (c == '.' ) { if (dot) throw new NumberFormatException() ; dot = true ; } else if (Character . isDigit(c ) ) { int digit = Character . digit(c, 10 ); if (digit == 0 ) { if (prec == 0 ) prec = 1 ; else if (rs != 0 ) { rs *= 10 ; ++prec; } } else { if (prec != 1 || rs != 0 ) ++prec; rs = rs * 10 + digit; } if (dot) ++scl; } else if ((c == 'e' ) || (c == 'E' )) { exp = parseExp(in , offset , len ) ; if ((int ) exp != exp) throw new NumberFormatException() ; break; } else { throw new NumberFormatException() ; } } if (prec == 0 ) throw new NumberFormatException() ; if (exp != 0 ) { scl = adjustScale(scl , exp ) ; } rs = isneg ? -rs : rs; int mcp = mc.precision; int drop = prec - mcp; if (mcp > 0 && drop > 0 ) { while (drop > 0 ) { scl = checkScaleNonZero((long ) scl - drop); rs = divideAndRound(rs , LONG_TEN_POWERS_TABLE[drop ], mc .roundingMode .oldMode ) ; prec = longDigitLength(rs ) ; drop = prec - mcp; } } } else { char coeff[] = new char [len ] ; for (; len > 0 ; offset++, len--) { c = in [offset ] ; if ((c >= '0' && c <= '9' ) || Character . isDigit(c ) ) { if (c == '0' || Character . digit(c, 10 ) == 0 ) { if (prec == 0 ) { coeff[idx ] = c; prec = 1 ; } else if (idx != 0 ) { coeff[idx ++ ] = c; ++prec; } } else { if (prec != 1 || idx != 0 ) ++prec; coeff[idx ++ ] = c; } if (dot) ++scl; continue; } if (c == '.' ) { if (dot) throw new NumberFormatException() ; dot = true ; continue; } if ((c != 'e' ) && (c != 'E' )) throw new NumberFormatException() ; exp = parseExp(in , offset , len ) ; if ((int ) exp != exp) throw new NumberFormatException() ; break; } if (prec == 0 ) throw new NumberFormatException() ; if (exp != 0 ) { scl = adjustScale(scl , exp ) ; } rb = new BigInteger(coeff , isneg ? -1 : 1, prec ) ; rs = compactValFor(rb ) ; int mcp = mc.precision; if (mcp > 0 && (prec > mcp)) { if (rs == INFLATED) { int drop = prec - mcp; while (drop > 0 ) { scl = checkScaleNonZero((long ) scl - drop); rb = divideAndRoundByTenPow(rb , drop , mc .roundingMode .oldMode ) ; rs = compactValFor(rb ) ; if (rs != INFLATED) { prec = longDigitLength(rs ) ; break; } prec = bigDigitLength(rb ) ; drop = prec - mcp; } } if (rs != INFLATED) { int drop = prec - mcp; while (drop > 0 ) { scl = checkScaleNonZero((long ) scl - drop); rs = divideAndRound(rs , LONG_TEN_POWERS_TABLE[drop ], mc .roundingMode .oldMode ) ; prec = longDigitLength(rs ) ; drop = prec - mcp; } rb = null; } } } } catch (ArrayIndexOutOfBoundsException e) { throw new NumberFormatException() ; } catch (NegativeArraySizeException e) { throw new NumberFormatException() ; } this.scale = scl; this.precision = prec; this.intCompact = rs; this.intVal = rb; }
常用方法 内部方法太多了,判断处理也很多,还有对最终值的位数和18做比较,所以这里就不详细展开分析了。有想看的同学,请自行翻阅源码。
add 加法可以当减法用,所以内部逻辑有越位和回位处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public BigDecimal add(BigDecimal augend) { if (this .intCompact != INFLATED) { if ((augend.intCompact != INFLATED)) { return add(this .intCompact, this .scale, augend.intCompact, augend.scale); } else { return add(this .intCompact, this .scale, augend.intVal, augend.scale); } } else { if ((augend.intCompact != INFLATED)) { return add(augend.intCompact, augend.scale, this .intVal, this .scale); } else { return add(this .intVal, this .scale, augend.intVal, augend.scale); } } }
subtract 看到了吧,内部是add
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public BigDecimal subtract(BigDecimal subtrahend) { if (this .intCompact != INFLATED) { if ((subtrahend.intCompact != INFLATED)) { return add(this .intCompact, this .scale, -subtrahend.intCompact, subtrahend.scale); } else { return add(this .intCompact, this .scale, subtrahend.intVal.negate(), subtrahend.scale); } } else { if ((subtrahend.intCompact != INFLATED)) { return add(-subtrahend.intCompact, subtrahend.scale, this .intVal, this .scale); } else { return add(this .intVal, this .scale, subtrahend.intVal.negate(), subtrahend.scale); } } }
multiply 乘法可以不设置位数和入舍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public BigDecimal multiply(BigDecimal multiplicand) { int productScale = checkScale((long) scale + multiplicand.scale); if (this .int Compact != INFLATED) { if ((multiplicand.int Compact != INFLATED)) { return multiply(this .int Compact, multiplicand.int Compact, productScale); } else { return multiply(this .int Compact, multiplicand.int Val, productScale); } } else { if ((multiplicand.int Compact != INFLATED)) { return multiply(multiplicand.int Compact, this .int Val, productScale); } else { return multiply(this .int Val, multiplicand.int Val, productScale); } } }
divide 除法也有直接除的方法,但是入舍和位数都是默认的,在实际应用中,不符合数据的精度。 一般而言,会使用divide(BigDecimal divisor, int scale, int roundingMode),手动设置入舍和小数点的精度。 大体流程先根据小数点的位数,除数和被除数进行扩容,还是有18位限制,然后直接做除法(/),之后在做小数点位数的处理和入舍。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public BigDecimal divide(BigDecimal divisor) { if (divisor.signum() == 0 ) { if (this .signum() == 0 ) throw new ArithmeticException("Division undefined" ); throw new ArithmeticException("Division by zero" ); } int preferredScale = saturateLong((long ) this .scale - divisor.scale ); if (this .signum() == 0 ) return zeroValueOf(preferredScale); else { MathContext mc = new MathContext( (int )Math.min (this .precision() + (long )Math.ceil (10.0 *divisor.precision()/3.0 ), Integer.MAX_VALUE), RoundingMode.UNNECESSARY); BigDecimal quotient; try { quotient = this .divide(divisor, mc); } catch (ArithmeticException e) { throw new ArithmeticException("Non-terminating decimal expansion; " + "no exact representable decimal result." ); } int quotientScale = quotient.scale (); if (preferredScale > quotientScale) return quotient.setScale(preferredScale, ROUND_UNNECESSARY); return quotient; } }
1 2 3 public BigDecimal divide (BigDecimal divisor, RoundingMode roundingMode) { return this .divide (divisor, scale, roundingMode.oldMode) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public BigDecimal divide(BigDecimal divisor, int scale , int roundingMode) { if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY) throw new IllegalArgumentException("Invalid rounding mode" ); if (this .intCompact != INFLATED) { if ((divisor.intCompact != INFLATED)) { return divide(this .intCompact, this .scale , divisor.intCompact, divisor.scale , scale , roundingMode); } else { return divide(this .intCompact, this .scale , divisor.intVal, divisor.scale , scale , roundingMode); } } else { if ((divisor.intCompact != INFLATED)) { return divide(this .intVal, this .scale , divisor.intCompact, divisor.scale , scale , roundingMode); } else { return divide(this .intVal, this .scale , divisor.intVal, divisor.scale , scale , roundingMode); } } }
获取值 有unscaledValue(),intValue(),longValue(),floatValue(),doubleValue()。位数超长的,会变成科学计数法。toString()也会变,可以用toPlainString()。