BigDecimal的基本使用

一、BigDecimal描述

BigDecimal类位于java.math.BigDecimal包下。是Java中用于表示任意精度数字的类,它可以表示无限长度的小数,BigDecimal 通常支持任意位数的小数部分,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。而且也可以使用此类进行精确的四舍五入,这一点在开发中经常使用。

二、BigDecimal常用构造函数

方法描述
BigDecimal(int val)将int类型转换成BigDecimal类型数据。
BigDecimal(double val)将double类型转换成BigDecimal类型数据。不推荐使用,存在丢失精度问题。
BigDecimal(long val)将long类型转换成BigDecimal类型数据。
BigDecimal(String val)将String类型转换成BigDecimal类型数据。推荐使用
BigDecimal bigDecimal1 = new BigDecimal(1);
BigDecimal bigDecimal2 = new BigDecimal(0.1); // 不推荐使用,对丢失精度。
BigDecimal bigDecimal3 = new BigDecimal(1L);
BigDecimal bigDecimal4 = new BigDecimal("0.1"); // 推荐使用

System.out.println("int转BigDecimal结果" + bigDecimal1);
System.out.println("double转BigDecimal结果" + bigDecimal2);
System.out.println("long转BigDecimal结果" + bigDecimal3);
System.out.println("String转BigDecimal结果" + bigDecimal4);

在这里插入图片描述

三、BIgDecimal常用方法

方法描述
add(BigDecimal)BigDecimal对象中的值相加,返回BigDecimal对象。
subtract(BigDecimal)BigDecimal对象中的值相减,返回BigDecimal对象。
multiply(BigDecimal)BigDecimal对象中的值相乘,返回BigDecimal对象。
divide(BigDecimal)BigDecimal对象中的值相除,返回BigDecimal对象。该方法可能会遇到无限精度问题,会抛出异常,使用时需注意。
compareTo(BigDecimal val)比较大小,返回int类型。0(相等) 1(大于) -1(小于)。a、b均不能为null,否则会报空指针。
max(BigDecimal)两值比较,返回最大值。
min(BigDecimal val)两值比较,返回最小值。
setScale(位数,舍弃规则)用于格式化小数点。
remainder(BigDecimal)求余数,求BigDecimal类型数据除以divisor的余数。
doubleValue()将BigDecimal对象中的值转换成双精度数double类型。
floatValue()将BigDecimal对象中的值转换成单精度数float类型。
longValue()将BigDecimal对象中的值转换成长整数long类型。
intValue()将BigDecimal对象中的值转换成整数int类型。
negate()求BigDecimal类型数据的相反数。
abs()将BigDecimal对象中的值转换成绝对值。

1、加法运算(add)

  • 使用 add 方法将两个 BigDecimal 对象相加。
BigDecimal bigDecimal1 = new BigDecimal("0.1");
BigDecimal bigDecimal2 = new BigDecimal("0.2");
BigDecimal add = bigDecimal1.add(bigDecimal2);

System.out.println("输出结果======================");
System.out.println("加法运算的结果:" + add);
System.out.println("输出结果======================");

在这里插入图片描述

2、减法运算(subtract)

  • 使用 subtract 方法将一个 BigDecimal 对象减去另一个。
BigDecimal bigDecimal1 = new BigDecimal("0.2");
BigDecimal bigDecimal2 = new BigDecimal("0.1");
BigDecimal subtract = bigDecimal1.subtract(bigDecimal2);

System.out.println("输出结果======================");
System.out.println("减法运算的结果:" + subtract);
System.out.println("输出结果======================");

在这里插入图片描述

3、乘法运算(multiply)

  • 使用 multiply 方法将两个 BigDecimal 对象相乘。
BigDecimal bigDecimal1 = new BigDecimal("0.2");
BigDecimal bigDecimal2 = new BigDecimal("0.1");
BigDecimal multiply = bigDecimal1.multiply(bigDecimal2);

System.out.println("输出结果======================");
System.out.println("乘法运算的结果:" + multiply);
System.out.println("输出结果======================");

在这里插入图片描述

4、除法运算(divide)

  • 使用 divide 方法将一个 BigDecimal 对象除以另一个。
  • 注意:该方法可能会遇到无限精度问题,会抛出异常。需要指定舍入规则。

错误示范:因1/3 会无限循环小时,所以程序会抛出异常。需要指定舍入规则。

BigDecimal bigDecimal1 = new BigDecimal("1");
BigDecimal bigDecimal2 = new BigDecimal("3");
BigDecimal divide = bigDecimal1.divide(bigDecimal2);

在这里插入图片描述

正确使用方式:指定舍入规则

BigDecimal bigDecimal1 = new BigDecimal("1");
BigDecimal bigDecimal2 = new BigDecimal("3");
BigDecimal divide = bigDecimal1.divide(bigDecimal2,2,BigDecimal.ROUND_HALF_DOWN);

System.out.println("输出结果======================");
System.out.println("除法运算的结果-截取2位并四舍五入:" + divide);
System.out.println("输出结果======================");

在这里插入图片描述

5、比较大小(compareTo)

  • 使用 compareTo 方法比较两个 BigDecimal 对象的大小。
  • a、b均不能为null,否则会报空指针。
  • 返回值:0(相等) 1(大于) -1(小于)

方式一:

BigDecimal bigDecimal1 = new BigDecimal("1");
BigDecimal bigDecimal2 = new BigDecimal("3");
int i = bigDecimal1.compareTo(bigDecimal2);

// 0(相等) 1(大于) -1(小于)
System.out.println("输出结果======================");
System.out.println("比较大小的结果:" + i);
System.out.println("输出结果======================");

在这里插入图片描述
方式二:

  • a小于b:< 0
BigDecimal a = new BigDecimal("1");
BigDecimal b = new BigDecimal("3");
boolean bool = a.compareTo(b) < 0;

System.out.println("输出结果======================");
System.out.println("比较大小的结果 a小于b:" + bool);
System.out.println("输出结果======================");

在这里插入图片描述

  • a等于b:== 0
BigDecimal a = new BigDecimal("2");
BigDecimal b = new BigDecimal("2");
boolean bool = a.compareTo(b) == 0;

System.out.println("输出结果======================");
System.out.println("比较大小的结果 a等于b:" + bool);
System.out.println("输出结果======================");

在这里插入图片描述

  • a大于b:> 0
BigDecimal a = new BigDecimal("3");
BigDecimal b = new BigDecimal("1");
boolean bool = a.compareTo(b) > 0;

System.out.println("输出结果======================");
System.out.println("比较大小的结果 a大于b:" + bool);
System.out.println("输出结果======================");

在这里插入图片描述

  • a大于等于b:> -1
BigDecimal a = new BigDecimal("3");
BigDecimal b = new BigDecimal("1");
BigDecimal c = new BigDecimal("1");
boolean bool1 = a.compareTo(c) > -1;
boolean bool2 = b.compareTo(c) > -1;

System.out.println("输出结果======================");
System.out.println("比较大小的结果 a大于等于c:" + bool1);
System.out.println("比较大小的结果 b大于等于c:" + bool2);
System.out.println("输出结果======================");

在这里插入图片描述

  • a小于等于b:< 1
BigDecimal a = new BigDecimal("2");
BigDecimal b = new BigDecimal("5");
BigDecimal c = new BigDecimal("5");
boolean bool1 = a.compareTo(c) < 1;
boolean bool2 = b.compareTo(c) < 1;

System.out.println("输出结果======================");
System.out.println("比较大小的结果 a小于等于c:" + bool1);
System.out.println("比较大小的结果 b小于等于c:" + bool2);
System.out.println("输出结果======================");

在这里插入图片描述

6、返回最大值(max)

  • 从两个 BigDecimal 对象中选取最大的值。
BigDecimal bigDecimal1 = new BigDecimal("1");
BigDecimal bigDecimal2 = new BigDecimal("3");
BigDecimal max = bigDecimal1.max(bigDecimal2);

System.out.println("输出结果======================");
System.out.println("max比较的结果:" + max);
System.out.println("输出结果======================");

在这里插入图片描述

7、返回最小值(min)

  • 从两个 BigDecimal 对象中选取最小的值。
BigDecimal bigDecimal1 = new BigDecimal("1");
BigDecimal bigDecimal2 = new BigDecimal("3");
BigDecimal min = bigDecimal1.min(bigDecimal2);

System.out.println("输出结果======================");
System.out.println("min比较的结果:" + min);
System.out.println("输出结果======================");

在这里插入图片描述

8、格式化小数点(setScale)

  • 用于对BigDecimal对象进行格式化小数点。
BigDecimal bigDecimal1 = new BigDecimal("1.1423194");
BigDecimal scale = bigDecimal1.setScale(2,BigDecimal.ROUND_HALF_DOWN);

System.out.println("输出结果======================");
System.out.println("格式化的结果:" + scale);	// 格式化的结果:1.14
System.out.println("输出结果======================");

在这里插入图片描述

9、求余数(remainder)

  • 用于获取一个 BigDecimal 对象除以另一个的余数。
BigDecimal bigDecimal1 = new BigDecimal("10");
BigDecimal bigDecimal2 = new BigDecimal("3");
BigDecimal remainder = bigDecimal1.remainder(bigDecimal2);

System.out.println("输出结果======================");
System.out.println("取余数的结果:" + remainder); // 取余数的结果:1
System.out.println("输出结果======================");

在这里插入图片描述

10、取相反数(negate)

  • 返回一个 BigDecimal 对象的负数
BigDecimal bigDecimal1 = new BigDecimal("10");
BigDecimal negate = bigDecimal1.negate();

System.out.println("输出结果======================");
System.out.println("取负数的结果:" + negate);
System.out.println("输出结果======================");

在这里插入图片描述

11、取绝对值(abs)

  • 返回一个 BigDecimal 对象的绝对值。
BigDecimal bigDecimal1 = new BigDecimal("-10");
BigDecimal abs = bigDecimal1.abs();

System.out.println("输出结果======================");
System.out.println("取绝对值的结果:" + abs);
System.out.println("输出结果======================");

在这里插入图片描述

13、转换类型

  • doubleValue() 将BigDecimal对象转换为double类型。
  • floatValue() 将BigDecimal对象转换为float类型。
  • longValue() 将BigDecimal对象转换为long类型。
  • intValue() 将BigDecimal对象转换为int类型。
BigDecimal bigDecimal1 = new BigDecimal("12.325167");
double doubleValue = bigDecimal1.doubleValue();
float floatValue = bigDecimal1.floatValue();
long longValue = bigDecimal1.longValue();
int intValue = bigDecimal1.intValue();

System.out.println("输出结果======================");
System.out.println("转换double的结果:" + doubleValue);
System.out.println("转换float的结果:" + floatValue);
System.out.println("转换long的结果:" + longValue);
System.out.println("转换int的结果:" + intValue);
System.out.println("输出结果======================");

在这里插入图片描述

四、BigDecimal的舍入模式

方法描述
ROUND_UP远离0的方向舍入
ROUND_DOWN接近0的方向舍入
ROUND_CEILING无穷大的舍入方式
ROUND_FLOOR负无穷大的舍入方式
ROUND_HALF_UP四舍五入
ROUND_HALF_DOWN五舍六入
ROUND_HALF_EVEN银行家舍入法
ROUND_UNNECESSARY舍掉的位数不是0

ROUND_UP(远离0的方向舍入)

  • 向远离0的方向舍入
    在这里插入图片描述
BigDecimal bigDecimal1 = new BigDecimal("12.321167");
BigDecimal bigDecimal2 = new BigDecimal("-12.321167");

BigDecimal up1 = bigDecimal1.setScale(2, BigDecimal.ROUND_UP);
BigDecimal up2 = bigDecimal2.setScale(2, BigDecimal.ROUND_UP);
System.out.println("正数:" + up1); 	// 向上取舍-正数:12.33
System.out.println("负数:" + up2);	// 向上取舍-负数:-12.33

ROUND_DOWN (接近0的方向舍入)

  • 向接近0的方向舍入
    在这里插入图片描述
BigDecimal bigDecimal1 = new BigDecimal("12.321167");
BigDecimal bigDecimal2 = new BigDecimal("-12.321167");

BigDecimal DOWN1 = bigDecimal1.setScale(2, BigDecimal.ROUND_DOWN);
BigDecimal DOWN2 = bigDecimal2.setScale(2, BigDecimal.ROUND_DOWN);
System.out.println("正数:" + DOWN1);	// 正数:12.32
System.out.println("负数:" + DOWN2);	// 负数:-12.32

ROUND_CEILING(无穷大的舍入)

  • 无穷大的舍入方式
    在这里插入图片描述
BigDecimal bigDecimal1 = new BigDecimal("12.321167");
BigDecimal bigDecimal2 = new BigDecimal("-12.321167");

BigDecimal CEILING1 = bigDecimal1.setScale(2, BigDecimal.ROUND_CEILING);
BigDecimal CEILING2 = bigDecimal2.setScale(2, BigDecimal.ROUND_CEILING);
System.out.println("正数:" + CEILING1);    // 正数:12.33
System.out.println("负数:" + CEILING2);    // 负数:-12.32

ROUND_FLOOR(负无穷大的舍入)

  • 负无穷大的舍入方式
    在这里插入图片描述
BigDecimal bigDecimal1 = new BigDecimal("12.321167");
BigDecimal bigDecimal2 = new BigDecimal("-12.321167");

BigDecimal FLOOR1 = bigDecimal1.setScale(2, BigDecimal.ROUND_FLOOR);
BigDecimal FLOOR2 = bigDecimal2.setScale(2, BigDecimal.ROUND_FLOOR);
System.out.println("正数:" + FLOOR1);	// 正数:12.32
System.out.println("负数:" + FLOOR2);	// 负数:-12.33

ROUND_HALF_UP(四舍五入)

  • 四舍五入
BigDecimal bigDecimal1 = new BigDecimal("12.321167");
BigDecimal bigDecimal2 = new BigDecimal("-12.321167");

BigDecimal HALF_UP1 = bigDecimal1.setScale(2, BigDecimal.ROUND_HALF_UP);
BigDecimal HALF_UP2 = bigDecimal2.setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println("正数:" + HALF_UP1);	// 正数:12.32
System.out.println("负数:" + HALF_UP2);	// 负数:-12.32

ROUND_HALF_DOWN(五舍六入)

  • 五舍六入
BigDecimal bigDecimal1 = new BigDecimal("12.325");
BigDecimal bigDecimal2 = new BigDecimal("-12.325");

BigDecimal HALF_DOWN1 = bigDecimal1.setScale(2, BigDecimal.ROUND_HALF_DOWN);
BigDecimal HALF_DOWN2 = bigDecimal2.setScale(2, BigDecimal.ROUND_HALF_DOWN);
System.out.println("正数:" + HALF_DOWN1);	// 正数:12.32
System.out.println("负数:" + HALF_DOWN2);	// 负数:-12.32

ROUND_HALF_EVEN(银行家舍入法)

  • 银行家舍入法,目标是偶数,可以使用四舍五入,也可以适用五舍六入,具体看哪边更接近一个偶数
BigDecimal bigDecimal1 = new BigDecimal("12.329");
BigDecimal bigDecimal2 = new BigDecimal("-12.322");

BigDecimal HALF_EVEN1 = bigDecimal1.setScale(2, BigDecimal.ROUND_HALF_EVEN);
BigDecimal HALF_EVEN2 = bigDecimal2.setScale(2, BigDecimal.ROUND_HALF_EVEN);
System.out.println("正数:" + HALF_EVEN1);	// 正数:12.33
System.out.println("负数:" + HALF_EVEN2);	// 负数:-12.32

ROUND_UNNECESSARY(舍掉的位数不是0)

  • 需要舍掉的位数不是0,就报异常 ArithmeticException。如果需要舍掉的位数是0,就返回已结舍掉的正常结果。
BigDecimal bigDecimal1 = new BigDecimal("12.329");
BigDecimal unnecessary = bigDecimal1.setScale(2, BigDecimal.ROUND_UNNECESSARY);
System.out.println(unnecessary);

在这里插入图片描述

五、BigDecimal常见问题

踩坑一:创建BigDecimal精度丢失

在BigDecimal中提供了很多创建方式,可以通过new 直接创建,也可以通过 BigDecimal#valueOf 创建。这两种方式使用不当,也会导致精度问题。如下:

BigDecimal bigDecimal1 = new BigDecimal(0.123912214890);
System.out.println(bigDecimal1);
BigDecimal bigDecimal2 = BigDecimal.valueOf(0.318283929989232910293889129273729932);
System.out.println(bigDecimal2);

执行结果:

0.1000000000000000055511151231257827021181583404541015625
0.3182839299892329

结论:

  • 第一,在使用BigDecimal构造函数时,尽量传递字符串而非浮点类型,不然会出现精度问题;
  • 第二,如果无法满足第一条,则可采用BigDecimal#valueOf方法来构造初始化值。但是valueOf受double类型精度影响,当传入参数小数点后的位数超过double允许的16位精度还是可能会出现问题的

踩坑二:无限精度问题

BigDecimal并不代表无限精度,当在两个数除不尽的时候,就会出现无限精度的坑。

BigDecimal bigDecimal1 = new BigDecimal("0.1");
BigDecimal bigDecimal2 = new BigDecimal("0.3");
System.out.println(bigDecimal1.divide(bigDecimal2));

执行结果:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.math.BigDecimal.divide(BigDecimal.java:1693)
	at com.kss.ewaterapi.TestCon.main(TestCon.java:131)

因为在除法(divide)运算过程中,商是一个无限小数,那么就会抛出ArithmeticException异常。这种情况可以在使用divide方式的时候指定舍入方式即可,如下:

BigDecimal bigDecimal1 = new BigDecimal("0.1");
BigDecimal bigDecimal2 = new BigDecimal("0.3");
// 执行结果:0.33
System.out.println(bigDecimal1.divide(bigDecimal2,2,BigDecimal.ROUND_HALF_UP));

踩坑三:使用BigDecimal进行计算的时参数不能为null

在使用BigDecimal类型进行计算时,进行加、减、乘、除、比较大小时,一定要保证参与计算的两个值不能为空,否则会抛出java.lang.NullPointerException异常。如下:

BigDecimal bigDecimal1 = new BigDecimal("0.1");
BigDecimal bigDecimal2 = null ;
System.out.println(bigDecimal1.divide(bigDecimal2,2,BigDecimal.ROUND_HALF_UP));

执行结果:

Exception in thread "main" java.lang.NullPointerException
	at java.math.BigDecimal.divide(BigDecimal.java:1563)
	at com.kss.ewaterapi.TestCon.main(TestCon.java:131)

踩坑四:数值比较

一般在比较两个值是否相等时,都是用equals 方法,但是,在BigDecimal 中使用equals可能会导致结果错误,BigDecimal 中提供了 compareTo 方法,在很多时候需要使用compareTo 比较两个值。如下所示:

BigDecimal bigDecimal1 = new BigDecimal("1.00");
BigDecimal bigDecimal2 = new BigDecimal("1");

System.out.println(bigDecimal1.equals(bigDecimal2));
System.out.println(bigDecimal1.compareTo(bigDecimal2));

执行结果:

false
0

结论:

由于equals不仅比较了值是否相等,还比较了精度是否相同。因为两个值的精度不同,所有结果也就不相同。而 compareTo 是只比较值的大小。返回的值为-1(小于),0(等于),1(大于)。

踩坑五:除法计算时被除数不能为0

代码实例:

BigDecimal bigDecimal1 = new BigDecimal("1.00");
BigDecimal bigDecimal2 = new BigDecimal("0.00");
System.out.println(bigDecimal1.divide(bigDecimal2, 2, BigDecimal.ROUND_HALF_UP));

执行结果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at java.math.BigDecimal.divideAndRound(BigDecimal.java:4137)
	at java.math.BigDecimal.divide(BigDecimal.java:5214)
	at java.math.BigDecimal.divide(BigDecimal.java:1564)
	at com.kss.ewaterapi.TestCon.main(TestCon.java:143)

踩坑六:执行顺序不能调换(乘法交换律失效)

乘法满足交换律是一个常识,但是在计算机的世界里,会出现不满足乘法交换律的情况。如下:

BigDecimal bigDecimal1 = new BigDecimal("1.00");
BigDecimal bigDecimal2 = new BigDecimal("3.00");
BigDecimal bigDecimal3 = new BigDecimal("3.0");

System.out.println(bigDecimal1.multiply(bigDecimal2).divide(bigDecimal3,2,BigDecimal.ROUND_HALF_UP));
System.out.println(bigDecimal1.divide(bigDecimal3,2,BigDecimal.ROUND_HALF_UP).multiply(bigDecimal2));

执行结果:

1.00
0.9900

结论:

执行顺序交换后,产生的结果可能不同,会导致一定的问题,使用顺序建议先乘后除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值