BigDecimal类详解
# Java 中的 BigDecimal 类详解
在 Java 编程中,处理浮点数时经常会遇到精度问题。为了解决这个问题,Java 提供了一个 BigDecimal
类,它提供了精确的浮点数运算。本文将详细介绍 BigDecimal
类的使用方法和一些常见场景。
# 为什么使用 BigDecimal?
在 Java 中,float
和 double
类型的变量是基于 IEEE 754 标准的浮点数表示,这可能会导致精度损失。例如,0.1 无法在二进制浮点数中精确表示,这可能会导致计算结果与预期不符。而 BigDecimal
类则提供了任意精度的浮点数运算,非常适合处理金融数据。
# 创建 BigDecimal 对象
BigDecimal
对象可以通过多种方式创建:
- BigDecimal(int) 创建一个具有参数所指定整数值的对象。
- BigDecimal(double) 创建一个具有参数所指定双精度值的对象。 【不推荐使用】
- BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
- BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。【推荐使用】
@Test
void testConstructor() {
System.out.println("==================构造器==================");
// 通过 int 值构造
BigDecimal intBig = new BigDecimal(10);
System.out.println("intBig = " + intBig);
// 通过 double 值构造,会有精度问题(不推荐)
BigDecimal doubleBig = new BigDecimal(20.45);
System.out.println("doubleBig = " + doubleBig);
// 通过 long 值构造
BigDecimal longBig = new BigDecimal(30L);
System.out.println("longBig = " + longBig);
// 通过 String 值构造(推荐)
BigDecimal stringBig = new BigDecimal("123.45");
System.out.println("stringBig = " + stringBig);
// 将 double 值转换成 String 类型再构造
Double doubleValue = new Double(20.45);
BigDecimal doubleBig2 = new BigDecimal(doubleValue.toString());
System.out.println("doubleBig2 = " + doubleBig2);
// intBig = 10
// doubleBig = 20.449999999999999289457264239899814128875732421875
// longBig = 30
// stringBig = 123.45
// doubleBig2 = 20.45
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
也可以用 BigDecimal.valueOf()
方法:
BigDecimal multiplier = BigDecimal.valueOf(100);
1
# 获取小数位数
// 获取xia
int scale = amountDecimal.scale();
1
2
2
# 基本运算
BigDecimal
提供了一系列的方法来进行基本的数学运算,包括加法、减法、乘法和除法:
@Test
void testAsmd() {
System.out.println("==================加减乘除==================");
BigDecimal value1 = new BigDecimal("100.00");
BigDecimal value2 = new BigDecimal("12.34");
// 加法
BigDecimal sum = value1.add(value2); // 112.34
System.out.println("sum = " + sum);
// 减法
BigDecimal difference = value1.subtract(value2); // 87.66
System.out.println("difference = " + difference);
// 乘法
BigDecimal product = value1.multiply(value2); // 1234.0000
System.out.println("product = " + product);
// 除法(四舍五入,保留两位小数)
BigDecimal quotient = value1.divide(value2, 2, RoundingMode.HALF_UP); // 8.10
System.out.println("quotient = " + quotient);
// sum = 112.34
// difference = 87.66
// product = 1234.0000
// quotient = 8.10
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
在上面的例子中,divide
方法的第二个参数是小数位数,第三个参数是舍入模式。RoundingMode
是 BigDecimal
中的一个枚举,用于定义舍入行为。
# 舍入模式
- ROUND_UP :向远离 0 的方向舍入
- ROUND_DOWN :向零方向舍入
- ROUND_CEILING :向正无穷方向舍入
- ROUND_FLOOR :向负无穷方向舍入
- ROUND_HALF_UP :向(距离)最近的一边舍入,如果两边(的距离)是相等时,向上舍入, 1.55 保留一位小数结果为 1.6,也就是我们常说的“
四舍五入
” - ROUND_HALF_DOWN :向(距离)最近的一边舍入,如果两边(的距离)是相等时,向下舍入, 例如1.55 保留一位小数结果为1.5
- ROUND_HALF_EVEN :向(距离)最近的一边舍入,如果两边(的距离)是相等时,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
- ROUND_UNNECESSARY :计算结果是精确的,不需要舍入模式
@Test
void testRoundingMode() {
System.out.println("==================舍入模式==================");
// 注意:断言请求的操作具有精确的结果,因此【不需要舍入】。
// 如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。 -- 如果这里小数选择保留 1 位就会抛异常
BigDecimal bigDecimal = new BigDecimal("1.55").setScale(2, RoundingMode.UNNECESSARY);
System.out.println("Rounding mode: " + RoundingMode.UNNECESSARY + ", Value: " + bigDecimal);
testRoundingMode(new BigDecimal("1.55"), RoundingMode.values());
/* Rounding mode: UNNECESSARY, Value: 1.55
Rounding mode: UP, Value: 1.6
Rounding mode: DOWN, Value: 1.5
Rounding mode: CEILING, Value: 1.6
Rounding mode: FLOOR, Value: 1.5
Rounding mode: HALF_UP, Value: 1.6
Rounding mode: HALF_DOWN, Value: 1.5
Rounding mode: HALF_EVEN, Value: 1.6 */
}
private static void testRoundingMode(BigDecimal value, RoundingMode[] modes) {
for (RoundingMode mode : modes) {
BigDecimal roundedValue = value.setScale(1, mode);
System.out.printf("Rounding mode: %s, Value: %s%n", mode, roundedValue);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 比较操作
BigDecimal
类提供了比较两个对象的方法,这些方法返回一个整数,它表示调用对象与参数对象的相对大小:
@Test
void testCompare() {
System.out.println("==================比较操作==================");
BigDecimal value = new BigDecimal("100.00");
BigDecimal value1 = new BigDecimal("99.00");
BigDecimal value2 = new BigDecimal("100.00");
BigDecimal value3 = new BigDecimal("101.00");
// 比较值
int cmp1 = value.compareTo(value1);
int cmp2 = value.compareTo(value2);
int cmp3 = value.compareTo(value3);
System.out.println("cmp1 = " + cmp1); // 1
System.out.println("cmp2 = " + cmp2); // 0
System.out.println("cmp3 = " + cmp3); // -1
// 检查是否相等
boolean isEqual1 = value.equals(value1);
boolean isEqual2 = value.equals(value2);
boolean isEqual3 = value.equals(value3);
System.out.println("isEqual1 = " + isEqual1); // false
System.out.println("isEqual2 = " + isEqual2); // true
System.out.println("isEqual3 = " + isEqual3); // false
// 检查是否大于
boolean isGreaterThan1 = value.compareTo(value1) > 0;
boolean isGreaterThan2 = value.compareTo(value2) > 0;
boolean isGreaterThan3 = value.compareTo(value3) > 0;
System.out.println("isGreaterThan1 = " + isGreaterThan1); // true
System.out.println("isGreaterThan2 = " + isGreaterThan2); // false
System.out.println("isGreaterThan3 = " + isGreaterThan3); // false
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 格式化输出
DecimalFormat
类允许您自定义数字格式,包括小数点后的位数、分组分隔符、甚至前缀和后缀。
- 它是
java.text
包中的一个类,用于格式化十进制数字。
@Test
void testFormat() {
System.out.println("==================格式化输出==================");
DecimalFormat df = new DecimalFormat("#,##0.00");
double number1 = 123456.789;
double number2 = 1233123456.783;
String formattedNumber1 = df.format(number1); // 123,456.79
String formattedNumber2 = df.format(number2); // 1,233,123,456.78
System.out.println("Formatted Number1: " + formattedNumber1);
System.out.println("Formatted Number2: " + formattedNumber2);
}
// 这个例子展示了如何使用 DecimalFormat 对象来格式化一个 double 类型的数字,并添加了【千位分隔符】和【小数点后两位】的格式。
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
解析 "#,##0.00"
这个模式字符串:
#
:表示如果存在数字则显示数字。如果位数不足,它不会填充领先符号。在分组分隔符前的#
表示尽可能多地使用分组分隔符。,
:表示分组分隔符,通常用于分隔千位、百万位等。在这个模式中,它会在每三位数字后添加一个逗号。##0
:表示至少有一个有效数字(非零数字),如果数字不足,它将用零填充。##
表示如果存在数字则显示数字,如果不存在则不显示。这里的0
表示如果数字不足,将用零填充。.00
:表示小数点后有两位固定位数,即使它们是零。
综上所述,"#,##0.00"
这个模式会将数字格式化为带有千位分隔符的格式,并且小数点后总是有两位小数,如果小数部分不足两位,则用零填充。
# 处理精度问题
在使用 BigDecimal
时,需要注意精度问题。例如,直接使用 double
构造器可能会引入初始的精度问题:
// 错误的示例,可能会有精度问题
BigDecimal bd = new BigDecimal(0.1);
1
2
2
正确的做法是使用字符串构造器:
// 正确的示例
BigDecimal bd = new BigDecimal("0.1");
1
2
2
# 结论
BigDecimal
类是 Java 中处理精确数值计算的重要工具。
- 它提供了丰富的 API 来执行各种数学运算,并能够避免
float
和double
类型的精度问题。 - 在处理金融数据或需要精确计算的场景中,
BigDecimal
是首选的数据类型。 - 推荐使用
字符串
构造器来创建BigDecimal
对象,以确保精度不受损失。
# 学习参考
上次更新: 2024/9/12 21:38:27