一、先观察一下这句代码的打印结果是多少?
System.out.println(0.06+0.01);
正确答案:0.06999999999999999,有人会问为什么不是0.07呢?
解析:因为计算机是二进制处理数据的。浮点数没有办法是用二进制进行精确表示,会失去一定的精确度,有些浮点数运算也会产生一定的误差。
针对这种情况Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。float和double只能用来做科学计算或者是工程计算,在商业计算中要用java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
二、BigDecimal类的基本使用
1、使用步骤
①先用float或者double变量构建BigDecimal对象。
通常可以使用BigDecimal的构造方法或者静态方法的valueOf()方法把基本类型的变量构建成BigDecimal对象。
BigDecimal b1 = new BigDecimal(Double.toString(0.48)); //参数必须是是字符串类型
BigDecimal b2 = BigDecimal.valueOf(0.48);
②在通过调用BigDecimal的加,减,乘,除等相应的方法进行算术运算。
public BigDecimal add(BigDecimal value); //加法
public BigDecimal subtract(BigDecimal value); //减法
public BigDecimal multiply(BigDecimal value); //乘法
public BigDecimal divide(BigDecimal value); //除法
③最后把BigDecimal对象转换成float,double,int等类型。
可以使用floatValue(),doubleValue()等方法转换成基本类型
2、案例
//损失精度
Double d1 = 0.06;
Double d2 = 0.01;
System.out.println(d1+d2); // 0.06999999999999999
//精确计算
BigDecimal b1 = BigDecimal.valueOf(d1);
BigDecimal b2 = new BigDecimal(d2);
BigDecimal sum = b1.add(b2); // 加法
System.out.println(sum.doubleValue()); // 0.07
3、工具类
package Demo1;
import java.math.BigDecimal;
/**
* 浮点型数据精确运算的工具类 提供加,减,乘,除运算的方法
* */
public class ArithUtil {
/**
* 提供精确加法计算的add方法
*
* @param value1
* 被加数
* @param value2
* 加数
* @return 两个参数的和
*/
public static double add(double value1, double value2) {
BigDecimal b1 = new BigDecimal(Double.toString(value1));
BigDecimal b2 = new BigDecimal(Double.toString(value2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确减法运算的sub方法
*
* @param value1
* 被减数
* @param value2
* 减数
* @return 两个参数的差
*/
public static double sub(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确乘法运算的mul方法
*
* @param value1
* 被乘数
* @param value2
* 乘数
* @return 两个参数的积
*/
public static double mul(double value1, double value2) {
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.multiply(b2).doubleValue();
}
/**
* 提供精确的除法运算方法div
*
* @param value1
* 被除数
* @param value2
* 除数
* @param scale
* 精确范围
* @return 两个参数的商
* @throws IllegalAccessException
*/
public static double div(double value1, double value2, int scale)
throws IllegalAccessException {
// 如果精确范围小于0,抛出异常信息
if (scale < 0) {
throw new IllegalAccessException("精确度不能小于0");
}
BigDecimal b1 = BigDecimal.valueOf(value1);
BigDecimal b2 = BigDecimal.valueOf(value2);
return b1.divide(b2, scale).doubleValue();
}
}
scale 精确范围
negate 取反
b1.compareTo(b2) 比较大小(即左边比右边数大,返回1,相等返回0,比右边小返回-1。注意 不可用equals进行相等的判断,equals 比较是两个BigDecimal对象的地址。)
4、BigDecimal格式化
通常使用NumberFormat类的format()方法进行格式化,以利用BigDecimal对货币和百分比格式化为例。首先,创建BigDecimal对象,进行BigDecimal的算术运算后,分别建立对货币和百分比格式化的引用,最后利用BigDecimal对象作为format()方法的参数,输出其格式化的货币值和百分比。
public static void main(String[] args) {
NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用
percent.setMaximumFractionDigits(3); //百分比小数点最多3位
BigDecimal loanAmount = new BigDecimal("15000.48"); //贷款金额
BigDecimal interestRate = new BigDecimal("0.008"); //利率
BigDecimal interest = loanAmount.multiply(interestRate); //相乘
System.out.println("贷款金额:\t" + currency.format(loanAmount));
System.out.println("利率:\t" + percent.format(interestRate));
System.out.println("利息:\t" + currency.format(interest));
}
打印结果:贷款金额: ¥15,000.48
利率: 0.8%
利息: ¥120.00