Java在 java.math 包中提供的API类 BigDecimal,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。float和double只能用来做科学计算或者是工程计算,在商业计算中要用 java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
1、构造方法
BigDecimal 一共有4个构造方法:
BigDecimal(int): 创建一个具有参数所指定整数值的对象。
BigDecimal(double) :创建一个具有参数所指定双精度值的对象(不建议)。
BigDecimal(long): 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) :创建一个具有参数所指定以字符串表示的数值的对象 。
为什么不建议采用第二种构造方法?如下代码:
/**
* BigDecimal的构造方法
* @author pan_junbiao
*/
@Test
public void createBigDecimal()
{
BigDecimal bigDecimal1 = new BigDecimal(12); //推荐
BigDecimal bigDecimal2 = new BigDecimal("12.83"); //推荐
BigDecimal bigDecimal3 = new BigDecimal(12.83); //不推荐
System.out.println("bigDecimal1的值:" + bigDecimal1);
System.out.println("bigDecimal2的值:" + bigDecimal2);
System.out.println("bigDecimal3的值:" + bigDecimal3);
}
执行结果:
问题原因:JDK中的描述,参数类型为double的构造方法的结果有一定的不可预知性。
2、加减乘除
BigDecimal的加减乘除运算。
加法:使用 add() 方法。
减法:使用 subtract() 方法。
乘法:使用 multiply() 方法。
除法:使用 divide() 方法。
/**
* BigDecimal的加减乘除运算
* @author pan_junbiao
*/
@Test
public void operationTest()
{
BigDecimal bigDecimal1 = new BigDecimal("10");
BigDecimal bigDecimal2 = new BigDecimal("2");
//加法
BigDecimal addResult = bigDecimal1.add(bigDecimal2);
System.out.println("加法运算结果:" + addResult);
//减法
BigDecimal subResult = bigDecimal1.subtract(bigDecimal2);
System.out.println("减法运算结果:" + subResult);
//乘法
BigDecimal mulResult = bigDecimal1.multiply(bigDecimal2);
System.out.println("乘法运算结果:" + mulResult);
//除法
BigDecimal divResult = bigDecimal1.divide(bigDecimal2);
System.out.println("除法运算结果:" + divResult);
}
执行结果:
使用Hutool工具类库运算BigDecimal的加减乘除、保留两位小数。
/**
* BigDecimal的加减乘除运算
* @author pan_junbiao
*/
@Test
public void operationTest()
{
BigDecimal bigDecimal1 = new BigDecimal("10.2567");
BigDecimal bigDecimal2 = new BigDecimal("2.236");
//加法
BigDecimal addResult = NumberUtil.add(bigDecimal1,bigDecimal2);
System.out.println("加法运算结果:" + addResult);
//减法
BigDecimal subResult = NumberUtil.sub(bigDecimal1,bigDecimal2);
System.out.println("减法运算结果:" + subResult);
//乘法
BigDecimal mulResult = NumberUtil.mul(bigDecimal1,bigDecimal2);
System.out.println("乘法运算结果:" + mulResult);
//除法
BigDecimal divResult = NumberUtil.div(bigDecimal1,bigDecimal2);
System.out.println("除法运算结果:" + divResult);
//保留两位小数
BigDecimal roundResult = NumberUtil.round(bigDecimal1,2);
System.out.println("保留两位小数:" + roundResult);
}
执行结果:
3、四舍五入
(1)BigDecimal的四舍五入,并保留两位小数。
/**
* BigDecimal的四舍五入,并保留两位小数
* @author pan_junbiao
*/
@Test
public void roundTest()
{
BigDecimal bigDecimal1 = new BigDecimal("12.233");
BigDecimal bigDecimal2 = new BigDecimal("12.288");
//四舍五入,并保留两位小数
BigDecimal round1 = bigDecimal1.setScale(2, BigDecimal.ROUND_HALF_UP);
BigDecimal round2 = bigDecimal2.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println("数值1:" + bigDecimal1);
System.out.println("四舍五入:" + round1);
System.out.println("-----------------");
System.out.println("数值2:" + bigDecimal2);
System.out.println("四舍五入:" + round2);
}
注意:如果是互联网金融行业,所有在进行计算的时候尽量使用 ROUND_HALF_EVEN(银行家舍入法)。
执行结果:
(2)BigDecimal的四舍五入与格式化输出。
/**
* BigDecimal的四舍五入与格式化输出
* @author pan_junbiao
*/
@Test
public void formatTest()
{
// 保留两位小数,个位无数字填充0
NumberFormat nformat = NumberFormat.getNumberInstance();
nformat.setMaximumFractionDigits(2);
System.out.println(nformat.format(0.2));// 0.2
System.out.println(nformat.format(0.235));// 0.23
System.out.println(nformat.format(0.2351));// 0.24
System.out.println(nformat.format(42));// 42
// DecimalFormat,是NumberFormat的具体实现子类
// 保留两位小数,对应位上无数字填充0
DecimalFormat df = new DecimalFormat("#0.00");
System.out.println(df.format(0.2));// 0.20
System.out.println(df.format(0.235));// 0.23
System.out.println(df.format(0.2351));// 0.2, 因为0.2351在0.23-0.24之间,距离0.24更近,所以输出0.24
System.out.println(df.format(42));// 42.00
DecimalFormat df4 = new DecimalFormat();
// #:位置上无数字不显示
df4.applyPattern("#.##");
System.out.println(df4.format(345235.0));// 345235
// 0:位置上无数字显示0
df4.applyPattern("0.00");
System.out.println(df4.format(345235.0));// 345235.00
// 加负数显示
df4.applyPattern("-0.00");
System.out.println(df4.format(345235.34567));// -345235.35
// 逗号分隔
df4.applyPattern("-0,000.00");
System.out.println(df4.format(345235.34567));// -345,235.35
// 百分位
df4.applyPattern("0.00%");
System.out.println(df4.format(0.34567));// 34.57%
// 千分位
df4.applyPattern("0.00\u2030");
System.out.println(df4.format(0.34567));// 345.67‰
// 科学计数法,E之前是底数的格式,E之后的是指数的格式
df4.applyPattern("0.00E00");
System.out.println(df4.format(2342.444));// 2.34E03
// 格式后面加单位符号
df4.applyPattern("0.00 KG");
System.out.println(df4.format(2342.444));// 2342.44 KG
df4.applyPattern("0.00 QA");
System.out.println(df4.format(2342.444));// 2342.44 QA
System.out.println(df4.format(2342.435));// 2342.43 QA
System.out.println(df4.format(2342.445));// 2342.45 QA
// String.format
// 保留两位小数,个位数及小数点后两位无数字填充0,四舍五入
System.out.println(String.format("%.2f", 0.2));// 0.20
System.out.println(String.format("%.2f", 0.235));// 0.24
System.out.println(String.format("%.2f", 0.236));// 0.24
System.out.println(String.format("%.2f", 42.0));// 42.00
}
4、比较大小
BigDecimal的比较大小可以使用 compareTo(BigDecimal val) 方法,该方法返回结果如下:
- A大于B时返回:1
- A等于B时返回:0
- A小于B时返回:-1
/**
* BigDecimal的比较大小
* @author pan_junbiao
*/
@Test
public void compareTest()
{
BigDecimal bigDecimal1 = new BigDecimal("12.01");
BigDecimal bigDecimal2 = new BigDecimal("12.88");
int result = bigDecimal1.compareTo(bigDecimal2);
System.out.println("数值1:" + bigDecimal1);
System.out.println("数值2:" + bigDecimal2);
System.out.println("数值1与数值2的比较值:" + result);
System.out.print("数值1与数值2的比较结果:");
if (result == 1)
{
System.out.print("大于");
}
else if (result == 0)
{
System.out.print("等于");
}
else if (result == -1)
{
System.out.print("小于");
}
}
执行结果:
5、资金统计
对于资金相关的字段,通常会使用BigDecimal数据类型。
【示例】统计用户薪资信息。
/**
* BigDecimal类型的统计
* @author pan_junbiao
*/
@Test
public void BigDecimalTest()
{
//获取用户列表
List<User> userList = UserService.getUserList();
//最高薪资
BigDecimal maxSalary = userList.stream().map(User::getSalary).max((x1, x2) -> x1.compareTo(x2)).get();
//最低薪资
BigDecimal minSalary = userList.stream().map(User::getSalary).min((x1, x2) -> x1.compareTo(x2)).get();
//薪资总和
BigDecimal sumSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add);
//平均薪资
BigDecimal avgSalary = userList.stream().map(User::getSalary).reduce(BigDecimal.ZERO, BigDecimal::add).divide(BigDecimal.valueOf(userList.size()), 2, BigDecimal.ROUND_HALF_UP);
//打印统计结果
System.out.println("最高薪资:" + maxSalary + "元");
System.out.println("最低薪资:" + minSalary + "元");
System.out.println("薪资总和:" + sumSalary + "元");
System.out.println("平均薪资:" + avgSalary + "元");
}
执行结果:
6、类型转换
(1)将BigDecimal类型转换成其他数据类型。
/**
* 将BigDecimal类型转换成其他数据类型
* @author pan_junbiao
*/
@Test
public void convertTest()
{
BigDecimal bigDecimal = new BigDecimal("12.88");
//字符串
String toString = bigDecimal.toString();
System.out.println("字符串:" + toString);
//double值
double toDouble =bigDecimal.doubleValue();
System.out.println("double值:" + toDouble);
//float值
float toFloat = bigDecimal.floatValue();
System.out.println("float值:" + toFloat);
//long值
long toLong = bigDecimal.longValue();
System.out.println("long值:" + toLong);
//int值
int toInt = bigDecimal.intValue();
System.out.println("int值:" + toInt);
}
执行结果:
(2)将Object对象转换成BigDecimal类型。
/**
* 将Object对象转换成BigDecimal类型
*/
public static BigDecimal toBigDecimal(Object obj)
{
BigDecimal result = new BigDecimal(0);
if (obj != null)
{
if (obj instanceof BigDecimal)
{
result = (BigDecimal) obj;
}
else if (obj instanceof String)
{
result = new BigDecimal((String) obj);
}
else if (obj instanceof BigInteger)
{
result = new BigDecimal((BigInteger) obj);
}
else if (obj instanceof Number)
{
result = new BigDecimal(((Number) obj).doubleValue());
}
else
{
throw new ClassCastException("Not possible to coerce [" + obj + "] from class " + obj.getClass() + " into a BigDecimal.");
}
}
return result.setScale(2, BigDecimal.ROUND_HALF_UP);
}