BigDecimal丟失精度的坑
阿新 • • 發佈:2018-12-17
問題:new BigDecimal(double d)的數值居然還是不精確的
double d = 0.09;
BigDecimal bigDecimal=new BigDecimal(d);
System.out.println(bigDecimal);
System.out.println(d);
輸出結果:
0.0899999999999999966693309261245303787291049957275390625
0.09
原因:BigDecimal將double資料轉換成為long bits,進行移位計算數值,而double的long bits移位計算是無法得到0.09的精確數值,所有造成資料精度丟失。
為了避免丟失double的資料精度,將double資料轉成String,使用BigDecimal(String s)構造方法。
public class BigDecimal extends Number implements Comparable<BigDecimal>{ public BigDecimal(double val) { this(val,MathContext.UNLIMITED); } public BigDecimal(double val, MathContext mc) { if (Double.isInfinite(val) || Double.isNaN(val)) throw new NumberFormatException("Infinite or NaN"); // Translate the double into sign, exponent and significand, according // to the formulae in JLS, Section 20.10.22. long valBits = Double.doubleToLongBits(val); int sign = ((valBits >> 63) == 0 ? 1 : -1); int exponent = (int) ((valBits >> 52) & 0x7ffL); long significand = (exponent == 0 ? (valBits & ((1L << 52) - 1)) << 1 : (valBits & ((1L << 52) - 1)) | (1L << 52)); exponent -= 1075; // At this point, val == sign * significand * 2**exponent. /* * Special case zero to supress nonterminating normalization and bogus * scale calculation. */ if (significand == 0) { this.intVal = BigInteger.ZERO; this.scale = 0; this.intCompact = 0; this.precision = 1; return; } // Normalize while ((significand & 1) == 0) { // i.e., significand is even significand >>= 1; exponent++; } int scale = 0; // Calculate intVal and scale BigInteger intVal; long compactVal = sign * significand; if (exponent == 0) { intVal = (compactVal == INFLATED) ? INFLATED_BIGINT : null; } else { if (exponent < 0) { intVal = BigInteger.valueOf(5).pow(-exponent).multiply(compactVal); scale = -exponent; } else { // (exponent > 0) intVal = BigInteger.valueOf(2).pow(exponent).multiply(compactVal); } compactVal = compactValFor(intVal); } int prec = 0; int mcp = mc.precision; if (mcp > 0) { // do rounding int mode = mc.roundingMode.oldMode; int drop; if (compactVal == INFLATED) { prec = bigDigitLength(intVal); drop = prec - mcp; while (drop > 0) { scale = checkScaleNonZero((long) scale - drop); intVal = divideAndRoundByTenPow(intVal, drop, mode); compactVal = compactValFor(intVal); if (compactVal != INFLATED) { break; } prec = bigDigitLength(intVal); drop = prec - mcp; } } if (compactVal != INFLATED) { prec = longDigitLength(compactVal); drop = prec - mcp; while (drop > 0) { scale = checkScaleNonZero((long) scale - drop); compactVal = divideAndRound(compactVal, LONG_TEN_POWERS_TABLE[drop], mc.roundingMode.oldMode); prec = longDigitLength(compactVal); drop = prec - mcp; } intVal = null; } } this.intVal = intVal; this.intCompact = compactVal; this.scale = scale; this.precision = prec; } }