简介
BigInteger 和 BigDecimal 都能实现大数字的运算,不同的是 BigDecimal 加入了小数的概念。Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,它们没有提供完全精确的结果,不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,在商业计算中要用BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。
标度
BigDecimal由非标度值和 32 位的整数标度 (scale) 组成,表示的数为: unscaledValue × 10的-scale 次幂。
如果scale为零或正数,最终的结果中,小数点后面的位数就等于scale标度。比如: scale为1,10的-1次方, 0.1 小数点后有1位;如果scale是负数,那最终的结果将会是乘以 10的|scale| 次方比如: scale为-3 最终的值就是非标度值乘以1000 (10的(- -3)次方)。
ulp
unit in the last place
两个数之间的距离,在数学中是无限的,比如1和2之间有无数个数,但是在计算机中是有限的,因为计算机需要用有限个字节来表示double或者float,计算机表示不了无限的数,因为没有无限内存。
假设两个数之间有10个数,那么ulp 就是1/10 ,1和2之间有一个数,距离为1;1.1和2.1之间有十个数,距离为0.1,这就是ulp。
非零 BigDecimal 值的 ulp 是此值与下一个具有相同位数的较大 BigDecimal 值之间的正距离,零值的 ulp 在数值上等于1 和 this.scale()之间的距离,所以可以说所有的数的ulp为[1, this.scale()]。
实例如下:
1 | System.out.println(BigDecimal.ZERO.ulp()); |
初始化数据
new BigDecimal() 传参支持 integer,long,double,float,BigInteger。
1 | BigDecimal.ZERO 初始化一个为0的BigDecimal对象 |
构造方法
1 | BigDecimal(int) |
如果是想将一个有小数的数字转换为BigDecimal的话,如“3.1”,那么最好用BigDecimal(String),即new BigDecimal(“3.1”),而不能用new BigDecimal(3.1),因为这样会导致精度缺失,实际得出的值不等于3.1,jdk中也明确说明了不推荐使用new BigDecimal(Double),并且new BigDecimal(String)的效率比new BigDecimal(Double)要高,还有一个方法就是BigDecimal(BigInteger,int),可以使用new BigDecimal(BigInteger.valueOf(31),1)来得到3.1,它的效率也很高。在jdk6.0中加入了BigDecimal(int)的构造函数,所以当被转换的数值是整数时,也可以用它。
其余构造方法
1 | BigDecimal(BigInteger val) |
方法
加减乘除
方法名称 | 说明 |
---|---|
BigDecimal add(BigDecimal augend) | 做加法运算,返回一个 BigDecimal,其值为 (this + augend),其标度为 max(this.scale(), augend.scale())。 |
BigDecimal subtract(BigDecimal subtrahend) | 做减法运算,返回一个 BigDecimal,其值为 (this - subtrahend),其标度为 max(this.scale(), subtrahend.scale())。 |
BigDecimal multiply(BigDecimal multiplieand) | 做乘法运算,返回一个 BigDecimal,其值为 (this × multiplicand),其标度为 (this.scale() + multiplicand.scale())。 |
BigDecimal divide(BigDecimal divisor,int scale,int roundingMode ) | 做除法运算,返回一个 BigDecimal,其值为 (this / divisor),其标度为指定标度。 |
其中,divide() 方法的 3 个参数分别表示除数、商的小数点后的位数和近似值处理模式(详见BigDecimal的舍入方式)
其它方法:
1 | BigDecimal abs() |
部分实例
加减乘除
1 | BigDecimal b1 = new BigDecimal("1"); |
使用divide()方法注意:相除的时候,被除数为0,会抛出异常:java.lang.ArithmeticException: Division by zero。
相除后为小数,则应该保留小数位,否则可能会抛出异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result。
乘方
1 | BigDecimal b1 = new BigDecimal("1.11"); |
比较大小
1 | BigDecimal b1 = new BigDecimal("1.11"); |
精度
1 | BigDecimal b1 = new BigDecimal("11.111"); |
格式化例子
由于NumberFormat类的format()方法可以使用BigDecimal对象作为其参数,可以利用BigDecimal对超出16位有效数字的货币值,百分值,以及一般数值进行格式化控制。以利用BigDecimal对货币和百分比格式化为例,首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。
1 | NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用 |
运行结果如下:
1 | 贷款金额: ¥15,000.48 |
代码详见GitHub:AccuracyCalculationTest
参考资料:
Ruthless BigDecimal用法详解
java入门教程 java BigInteger类和BigDecimal类
OpenFire_ 高精度操作数值 BigDecimal类和BinInteger类
noteless [十七]基础类型BigDecimal简介
来醉一场 BigDecimal 详解