背景
涉及到比较大的数字之间的计算,使用float、double这样的浮点数就不那么准确了。因为不论是float 还是double都是浮点数,而计算机是二进制的,浮点数会失去一定的精确度。所以在商业计算中基本要用java.math.BigDecimal
一、初始化使用
public static void main(String[] args) {
// 可以使用
BigDecimal bigDecimal1 = new BigDecimal(10);
// 推荐使用
BigDecimal bigDecimal2 = new BigDecimal("10.123");
// 不推荐 会丢失精确度
BigDecimal bigDecimal3 = new BigDecimal(10.123);
System.out.println("输出bigDecimal1:"+bigDecimal1);
System.out.println("输出bigDecimal2:"+bigDecimal2);
System.out.println("输出bigDecimal3:"+bigDecimal3);
}
结果输出:
1.2、BigDecimal还提供默认值创建(0、1、10)
public static void main(String[] args) {
// 创建默认为0
BigDecimal bigDecimal1 = BigDecimal.ZERO;
// 创建默认为1
BigDecimal bigDecimal2 = BigDecimal.ONE;
// 创建默认为10
BigDecimal bigDecimal3 = BigDecimal.TEN;
System.out.println("输出bigDecimal1: "+bigDecimal1);
System.out.println("输出bigDecimal2: "+bigDecimal2);
System.out.println("输出bigDecimal3: "+bigDecimal3);
}
结果输出:
1.3、int、long、double等,也可以这么创建实例(不推荐)
public static void main(String[] args) {
// 创建默认为0
BigDecimal bigDecimal1 = BigDecimal.valueOf(10);
// 创建默认为1
BigDecimal bigDecimal2 = BigDecimal.valueOf(10L);
// 创建默认为10
BigDecimal bigDecimal3 = BigDecimal.valueOf(10.1);
System.out.println("输出bigDecimal1: "+bigDecimal1);
System.out.println("输出bigDecimal2: "+bigDecimal2);
System.out.println("输出bigDecimal3: "+bigDecimal3);
}
结果输出:
二、使用compareTo方法比较大小
flag = -1, 表示bigDecimal1小于 bigDecimal2;
flag = 0, 表示bigDecimal1等于bigDecimal2;
flag = 1, 表示bigDecimal1大于bigDecimal2;
实际应用中直接跟0比较就可以了,
大于0就是bigDecimal1 大于 bigDecimal2
等于0就是bigDecimal1 等于 bigDecimal2
小于0就是bigDecimal1 小于 bigDecimal2
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("10");
BigDecimal bigDecimal2 = new BigDecimal("20");
int result = bigDecimal1.compareTo(bigDecimal2);
System.out.println("bigDecimal1小于 bigDecimal2: " + result);
if (bigDecimal1.compareTo(bigDecimal2) < 0) {
System.out.println("实际使用 bigDecimal1小于 bigDecimal2");
}
BigDecimal bigDecimal3 = new BigDecimal("10");
int result1 = bigDecimal1.compareTo(bigDecimal3);
System.out.println("bigDecimal1等于bigDecimal3: " + result1);
if (bigDecimal1.compareTo(bigDecimal3) == 0) {
System.out.println("实际使用 bigDecimal1等于bigDecimal3 ");
}
BigDecimal bigDecimal4 = new BigDecimal("5");
int result2 = bigDecimal1.compareTo(bigDecimal4);
System.out.println("bigDecimal1大于bigDecimal4: " + result2);
if (bigDecimal1.compareTo(bigDecimal4) > 0) {
System.out.println("实际使用 bigDecimal1大于bigDecimal4");
}
}
输出结果:
三、BigDecimal设置精度 以及相应八种舍入模式
ROUND_UP | 向远离零的方向舍入。舍弃非零部分,并将非零舍弃部分相邻的一位数字加一 |
ROUND_DOWN | 向接近零的方向舍入。舍弃非零部分,同时不会非零舍弃部分相邻的一位数字加一,采取截取行为。 |
ROUND_CEILING | 向正无穷的方向舍入。如果为正数,舍入结果同ROUND_UP一致;如果为负数,舍入结果同ROUND_DOWN一致。注意:此模式不会减少数值大小。 |
ROUND_FLOOR | 向负无穷的方向舍入。如果为正数,舍入结果同ROUND_DOWN一致;如果为负数,舍入结果同ROUND_UP一致。注意:此模式不会增加数值大小。 |
ROUND_HALF_UP | 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。 如果舍弃部分>= 0.5,则舍入行为与ROUND_UP相同;否则舍入行为与ROUND_DOWN相同。 这种模式也就是我们常说的我们的“四舍五入”。 |
ROUND_HALF_DOWN | 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则为向下舍入的舍入模式。 如果舍弃部分> 0.5,则舍入行为与ROUND_UP相同;否则舍入行为与ROUND_DOWN相同。 这种模式也就是我们常说的我们的“五舍六入”。 |
ROUND_HALF_EVEN | 向“最接近”的数字舍入,如果与两个相邻数字的距离相等,则相邻的偶数舍入。 如果舍弃部分左边的数字奇数,则舍入行为与 ROUND_HALF_UP 相同; 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。 注意:在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。 此舍入模式也称为“银行家舍入法”,主要在美国使用。 四舍六入,被舍位为5时两种情况,如果前一位为奇数,则入位,否则舍去。 |
ROUND_UNNECESSARY | 断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。 |
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("20.1252122");
System.out.print("ROUND_UP:-->");
System.out.println(bigDecimal1.setScale(2,BigDecimal.ROUND_UP));
System.out.print("ROUND_DOWN:-->");
System.out.println(bigDecimal1.setScale(2,BigDecimal.ROUND_DOWN));
System.out.print("ROUND_CEILING:-->");
System.out.println(bigDecimal1.setScale(2,BigDecimal.ROUND_CEILING));
System.out.print("ROUND_FLOOR:-->");
System.out.println(bigDecimal1.setScale(2,BigDecimal.ROUND_FLOOR));
System.out.print("ROUND_HALF_UP:-->");
System.out.println(bigDecimal1.setScale(2,BigDecimal.ROUND_HALF_UP));
System.out.print("ROUND_HALF_DOWN:-->");
System.out.println(bigDecimal1.setScale(2,BigDecimal.ROUND_HALF_DOWN));
System.out.print("ROUND_HALF_EVEN:-->");
System.out.println(bigDecimal1.setScale(2,BigDecimal.ROUND_HALF_EVEN));
System.out.print("ROUND_UNNECESSARY:-->");
System.out.println(bigDecimal1.setScale(BigDecimal.ROUND_UNNECESSARY));
}
结果输出
注意点:
ROUND_UNNECESSARY断言请求的操作具有精确的结果,因此不需要舍入。
如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。
四、基本使用(加、减、乘、除)
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("20");
BigDecimal bigDecimal2 = new BigDecimal("10");
System.out.print("加-->" + bigDecimal1 + "+" + bigDecimal2 + "=");
System.out.println(bigDecimal1.add(bigDecimal2));
System.out.print("减-->" + bigDecimal1 + "-" + bigDecimal2 + "=");
System.out.println(bigDecimal1.subtract(bigDecimal2));
System.out.print("乘-->" + bigDecimal1 + "*" + bigDecimal2 + "=");
System.out.println(bigDecimal1.multiply(bigDecimal2));
System.out.print("除-->" + bigDecimal1 + "/" + bigDecimal2 + "=");
System.out.println(bigDecimal1.divide(bigDecimal2));
}
结果输出:
注意点:除法小数点过多会报错 示例:
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("20");
BigDecimal bigDecimal2 = new BigDecimal("10.00212");
System.out.println(bigDecimal1.divide(bigDecimal2));
}
结果输出:
出现当前异常的原因在于 BigDecimal 是不可变的、任意精度的有符号十进制数,所以可以做精确计算。但在除法中,准确的商可能是一个无限长的十进制扩展;例如,1 除以 3 所得的商。我们在做除法时,没有指定舍入模式,并且无法表示为准确的结果,因此抛出了这个异常(java.lang.ArithmeticException)。
了解问题所在我们只需要在 divide() 方法中传入 MathContext 对象或 RoundingMode 对象,指定精度和舍入模式就可以解决该问题
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("20");
BigDecimal bigDecimal2 = new BigDecimal("10.00212");
BigDecimal divide = bigDecimal1.divide(bigDecimal2, 2, BigDecimal.ROUND_HALF_DOWN);
System.out.println(divide);
}
结果输出:
五、BigDecimal其他方法
abs() | 求绝对值,不管正数还是负数,都得到正数 |
negate() | 求相反数,正变负,负变正 |
pow(int n) | 求乘方,如BigDecimal.valueOf(2).pow(3)的值为8 |
max(BigDecimal val) | 两值比较,返回最大值 |
min(BigDecimal val) | 两值比较,返回最小值 |
public static void main(String[] args) {
BigDecimal bigDecimal1 = new BigDecimal("-20");
System.out.println("求绝对值-->" + bigDecimal1.abs()); // 求绝对值
System.out.println("求相反数-->" + bigDecimal1.negate()); // 求相反数
System.out.println("求乘方-->" + bigDecimal1.pow(2));//求乘方
System.out.println("最大值-->" + bigDecimal1.max(new BigDecimal(10)));//两值比较,返回最大值
System.out.println("最小值-->" + bigDecimal1.min(new BigDecimal(15)));//两值比较,返回最小值
}