Java_BigDecimal(12)

BigDecimal

浮点数不能精确地计算,可以通过同样定义在 java.math 包下的 BigDecimal 来进行精确的计算

 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
public class Test {
  public static void main(String[] args) {
    System.out.println(0.05 + 0.01); //0.060000000000000005
    System.out.println(1.0 - 0.42);  //0.5800000000000001
    System.out.println(4.015 * 100); //401.49999999999994
    System.out.println(123.3 / 100); //1.2329999999999999
    BigDecimal b1 = new BigDecimal(0.05);
    BigDecimal b2 = new BigDecimal(0.01);
    //0.06000000000000000298372437868010820238851010799407958984375
    System.out.println(b1.add(b2));
   
    //可见一定要使用BigDecimal的String构造器
    BigDecimal b3 = new BigDecimal("0.05");
    BigDecimal b4 = new BigDecimal("0.01");
    System.out.println(b3.add(b4)); //0.06
    //Float.toString()同理
    BigDecimal bd5 = new BigDecimal(Double.toString(0.05));
    BigDecimal bd6 = new BigDecimal(Double.toString(0.01));
    System.out.println(bd5.add(bd6)); //0.06
    //或者使用静态方法valueOf,其实就是Double.toString()的封装
    BigDecimal bd7 = BigDecimal.valueOf(0.05);
    BigDecimal bd8 = BigDecimal.valueOf(0.01);
    System.out.println(bd7.add(bd8)); //0.06
  }
}

可以封装一个工具类来转换数据类型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class BigDecimalUtil {
  private BigDecimalUtil() {}
  public static BigDecimal add(double d1, double d2) {
    return BigDecimal.valueOf(d1).add(BigDecimal.valueOf(d2));
  }
  public static BigDecimal subtract(double d1, double d2) {
    return BigDecimal.valueOf(d1).subtract(BigDecimal.valueOf(d2));
  }
  public static BigDecimal multiply(double d1, double d2) {
    return BigDecimal.valueOf(d1).multiply(BigDecimal.valueOf(d2));
  }
  public static BigDecimal divide(double d1, double d2) {
    //保留两位小数,四舍五入
    return BigDecimal.valueOf(d1).divide(BigDecimal.valueOf(d2),                     2, BigDecimal.ROUND_HALF_UP);
  }
}

BigDecimal 的 equals 方法除了比较数值还会比较精度,而 compareTo 只会比较值的大小忽略精度

1
2
3
4
5
6
7
8
public class Test {
  public static void main(String[] args) {
    BigDecimal bd1 = new BigDecimal("1.00");
    BigDecimal bd2 = new BigDecimal("1.0");
    System.out.println(bd1.equals(bd2));    //false
    System.out.println(bd1.compareTo(bd2)); //0
  }
}

查看 BigDecimal 类的定义

1
public class BigDecimal extends Number implements Comparable<BigDecimal> {}

跟 BigInteger 类似,BigDecimal 也继承了 Number 类,所以可以通过 xxxValue 方法转换为基本类型,如果超出范围了,则转换后会丢弃高位的数据,对于精准地转换为整数的基本类型,提供了 xxxValueExact 方法,对于超出范围和丢失精度的情况会抛出异常 ArithmeticException

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class Test {
  public static void main(String[] args) {
    BigDecimal bd1 = new BigDecimal("9223372036854775808.0");
    System.out.println(bd1.doubleValue()); //9.223372036854776E18
    System.out.println(bd1.floatValue());  //9.223372E18
    System.out.println(bd1.longValue());   //-9223372036854775808
    //System.out.println(bd1.longValueExact());
    //ArithmeticException: Overflow
    BigDecimal bd2 = new BigDecimal("3.14");
    System.out.println(bd2.intValue());  //3
    //System.out.println(bd2.intValueExact());
    //ArithmeticException: Rounding necessary
    BigDecimal bd3 = new BigDecimal("3.00");
    System.out.println(bd3.intValueExact()); //3
  }
}

关键的类成员

1
2
3
4
private final int scale; //小数位数
private transient int precision; //整个浮点数总位数
private final transient long intCompact; //将字符串去掉小数点后转为long的值
private final BigInteger intVal; //传入的字符串长度大于18就用BigInteger,不算小数点

例如 new BigDecimal("-1.00”);

scale = 2、 precision = 3、 intCompact = -100、 intVal = null

构造方法

 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
public BigDecimal(String val) {
  this(val.toCharArray(), 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) {
  //省略检查char数组长度的代码...
  int prec = 0;                 // record precision value
  int scl = 0;                  // record scale value
  long rs = 0;                  // the compact value in long
  BigInteger rb = null;         // the inflated value in BigInteger

  try {
    //首先处理前导符号,代码省略...
    //然后再处理有效数字部分
    boolean dot = false;        // true when there is a '.'
    long exp = 0;               // exponent
    char c;                     // current character
    //MAX_COMPACT_DIGITS常量值为18
    boolean isCompact = (len <= MAX_COMPACT_DIGITS);
    int idx = 0;
    //如果有效数字长度小于18则转换为long值
    if (isCompact) {
      //太多了以后在看吧...
    //如果有效数字长度大于18了,则采用BigInteger
    } else {
      //太多了以后在看吧...
    }
  } catch (ArrayIndexOutOfBoundsException e) {
    throw new NumberFormatException();
  } catch (NegativeArraySizeException e) {
    throw new NumberFormatException();
  }
  this.scale = scl;
  this.precision = prec;
  this.intCompact = rs;
  this.intVal = rb;
}

运算方法

以后再说吧 ……


以上内容是玉山整理的笔记,如有错误还请指出