转载请注明源出处:http://www.cnblogs.com/lighten/p/6963836.html
1.前言
编程人员都应该知道计算机计算浮点数是不精确的,结果是近似数值,当然具体值还是和计算机操作的实现有关。因此,如果对于计算结果不需要过于精准的时候,使用浮点数类型是没什么问题的,但是如果要求精确值的话(比如计算利息),就不能使用这种不精确的结果了,而要选择java提供的用于计算精确浮点数的类BigDecimal。本文基于JDK1.8介绍此类的一般用法和源码实现。
BigDecimal类注解的第一段就介绍了一些基本信息:不可变的、任意精度的十进制数。一个BigDecimal由任意精度的非刻度值整数和32位整数的刻度组成。如果值大于等于0,标度就是小数点后的位数,如果值小于0,值就是:非标度值*10^(-标度值)。这样说不好理解,后面会说明白构成。
注意:上述的不可变指的并不是这个类被final修饰,而是指BigDecimal每次计算都是产生了一个新的对象,这点要注意。
2.用例
@Test public void test() { BigDecimal b = new BigDecimal(1.22); System.out.println(b.unscaledValue()); System.out.println(b.scale()); System.out.println(b); String a = "2199999999999999733546474089962430298328399658203125"; System.out.println(a.length()); }
上述例子运行的结果是:
由此可以看出以下几点:1.double类型是不精确的;2.上面所说的非刻度值就是指去掉了小数点的浮点数;3.刻度值指的就是小数点后面的位数了。
正数的时候计算值是非刻度值+小数点位数构成,如12345 4就是1.2345,四位小数;负数的时候则是-12345*10^(-4)结果是-1.2345。
@Test public void testMathContext() { BigDecimal add = new BigDecimal("1.17"); BigDecimal a = new BigDecimal("12.324", new MathContext(3, RoundingMode.UP)); BigDecimal b = new BigDecimal("12.324", new MathContext(2, RoundingMode.UP)); BigDecimal d = new BigDecimal("12.324", new MathContext(1, RoundingMode.UP)); BigDecimal c = new BigDecimal("12.324", new MathContext(0, RoundingMode.UP)); System.out.println(a); System.out.println(a.add(add)); // a变成12.4 System.out.println(b.add(add)); // a变成14 System.out.println(d.add(add)); // a变成20 System.out.println(c.add(add)); }
上面一个例子展示了MathContext的用法,补充介绍了具体含义,这里说下第一个参数的作用,可以看出是设置了MathContext的那个BigDecimal在运算之前发生了变化。
3.源码解读
关键字段:
private final BigInteger intVal; // 无刻度整数(无小数点) private final int scale; // 刻度值 private transient int precision; // 无小数点的整数的位数 private final transient long intCompact; //intVal如果其值小于Long.MAX_VALUE就可以紧凑的存储在这个字段方便计算
BigDecimal有很多种构造方法:
实际上也就是相互调用,主要的也就几个实现:首先所有具有BigInteger参数的构造方法都十分简单,大部分都是简单的赋值罢了。最多就是计算了一下intCompact的值:
上述就是就是一个压缩过程,如果不能压缩就存一个标志位。
入参为char和String的构造函数,最终调的都是方法:public BigDecimal(char[] in, int offset, int len, MathContext mc)。其它的int、long、double也都比较简单,int类型的时候intValue的值是null。总而言之,所有的构造方法,经过一系列运算,最终是要得到上面所列的四个关键字段的值。这些计算基本是位运算。值得一提的是可以通过静态valueof()方法来创建BigDecimal,过程大同小异。
BigDecimal的运算函数:
add(); // 加
subtract() // 减
multiply() // 乘
divide() // 除
reminder() //取余
pow() // 幂次方
abs() // 绝对值
negate() // 相反值(-this)
等等,这里就不一一介绍了。
4.补充
之前构造函数中会看见MathContext这个参数,这里补充介绍一下相关内容。MathContext:制定BigDecimal运算操作的运算规则。其主要是通过两个属性来控制的:
final int precision; // 被用于计算的位数,0就是不限制位数
final RoundingMode roundingMode; // 运算时的舍入规则,有很多,可以看源码上的注释,有举例说明。
默认使用的是无限制,ROUND_HALF_UP(无符号形式的四舍五入):1.1->1; 1.5->2.0; -1.1->-1.0; -1.5->-2.0