BigDecimal 简介
我们知道,双精度浮点型变量 double 最多可以处理 16 位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。于是,便诞生了 BigDecimal 类型。Java 在 java.math 包中提供的 API 类 BigDecimal,专门用来对超过 16 位有效位的数进行精确的运算。
一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用 float 或 double处理,但是Double.valueOf(String) 和 Float.valueOf(String) 会丢失精度。所以开发中,如果我们需要对超过16位有效位的数字计算的精确结果,则必须使用 BigDecimal 类来操作。
关于 Double.valueOf(String) 和 Float.valueOf(String) 会丢失精度的问题如下:
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
34
35
36
37
38
39
40
41
42package jx.BigDecimal; /** * @ClassName: jx.BigDecimal.BigDecimalTest * @Author: jiaoxian * @Date: 2022/4/24 09:58 * @Description: */ public class BigDecimalTest { public static void main(String[] args) { handle8(); handle16(); handle20(); } public static void handle8() { String str = "0.12345678"; Double doubleStr = Double.valueOf(str); System.out.println(doubleStr); Float floatStr = Float.valueOf(str); System.out.println(floatStr); } public static void handle16() { String str = "0.1234567898765432"; Double doubleStr = Double.valueOf(str); System.out.println(doubleStr); Float floatStr = Float.valueOf(str); System.out.println(floatStr); } public static void handle20() { String str = "0.12345678987654329999"; Double doubleStr = Double.valueOf(str); System.out.println(doubleStr); Float floatStr = Float.valueOf(str); System.out.println(floatStr); } }
可以看到,当有效位数小于等于 8 的时候,Double 和 Float 都不会丢失精度,大于 8 小于等于 16 时,Float 会丢失精度,大于 16 时,Double 和 Float 都会丢失精度,至于为什么 Float 只有 8 位有效位的情况,后期会出一期关于这方面的文章再讲一下。
因为 BigDecimal 所创建的是对象,所以我们不能使用传统的 +、-、*、/ 等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是 BigDecimal 的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。
BigDecimal 常用构造函数
常用构造函数
-
BigDecimal(int):创建一个具有参数所指定整数值的对象
-
BigDecimal(double):创建一个具有参数所指定双精度值的对象
-
BigDecimal(long):创建一个具有参数所指定长整数值的对象
-
BigDecimal(String):创建一个具有参数所指定以字符串表示的数值的对象
代码示例:
1
2
3
4
5
6
7
8
9
10public static void createBigDecimal() { BigDecimal intBigDecimal = new BigDecimal(1); BigDecimal longBigDecimal = new BigDecimal(1L); BigDecimal strBigDecimal = new BigDecimal("0.1"); BigDecimal doubleBigDecimal = new BigDecimal(0.1d); System.out.println(intBigDecimal); System.out.println(longBigDecimal); System.out.println(strBigDecimal); System.out.println(doubleBigDecimal); }
可以看到,使用 BigDecimal(double) 去创建双精度值对象时,实际值并不是预期的 0.1,这是因为:参数类型为 double 的构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 newBigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。
如阿里巴巴代码规范所言,禁止将 double 值转化为 BigDecimal 对象。所以日常使用中需要注意,如果确需将 double 转化为 BigDecimal,那么正确的做法应该是先将 double 类型转化为 String 类型,再使用 BigDecimal(String) 方法去创建 BigDecimal 对象。
BigDecimal 常用方法详解
常用方法
-
add(BigDecimal):BigDecimal 对象中的值相加,返回 BigDecimal 对象
-
subtract(BigDecimal):BigDecimal 对象中的值相减,返回 BigDecimal 对象
-
multiply(BigDecimal):BigDecimal 对象中的值相乘,返回 BigDecimal 对象
-
divide(BigDecimal):BigDecimal 对象中的值相除,返回 BigDecimal 对象
-
toString():将 BigDecimal 对象中的值转换成字符串
-
doubleValue():将 BigDecimal 对象中的值转换成双精度数
-
floatValue():将 BigDecimal 对象中的值转换成单精度数
-
longValue():将 BigDecimal 对象中的值转换成长整数
-
intValue():将 BigDecimal 对象中的值转换成整数
代码示例:
1
2
3
4
5
6
7
8
9
10
11
12private static void bigDecimalMethod() { BigDecimal bigDecimal1 = new BigDecimal("800"); BigDecimal bigDecimal2 = new BigDecimal("123"); BigDecimal addResult = bigDecimal1.add(bigDecimal2); BigDecimal subtractResult = bigDecimal1.subtract(bigDecimal2); BigDecimal multiplyResult = bigDecimal1.multiply(bigDecimal2); BigDecimal divideResult = bigDecimal1.divide(bigDecimal2); System.out.println(addResult); System.out.println(subtractResult); System.out.println(multiplyResult); System.out.println(divideResult); }
需要注意的是,使用 divide(BigDecimal) 方法时,如果最终的结果会是一个无限不循环小数,那么就需要设置精度。否则会报错:
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode);
该方法会有 3 个参数,divisor 表示除数,scale 表示最终结果保留几位小数,roundingMode 表示保留小数的原则,即四舍五入还是直接舍弃,一般情况下都是使用 BigDecimal.ROUND_HALF_UP 四舍五入的原则。如,我们对以上代码的 divide() 方法设置使用四舍五入的原则保留 6 位小数,那么最终计算结果就为:
BigDecimal 大小比较
Java 中对 BigDecimal 类型的值比较大小一般用的是 BigDecimal 的 compareTo 方法
1
2
3
4
5
6
7
8
9
10
11
12private static void bigDecimalCompare() { BigDecimal bigDecimal1 = new BigDecimal("800"); BigDecimal bigDecimal2 = new BigDecimal("123"); BigDecimal bigDecimal3 = new BigDecimal("1000"); BigDecimal bigDecimal4 = new BigDecimal("800"); int equalResult = bigDecimal1.compareTo(bigDecimal4); int bigResult = bigDecimal1.compareTo(bigDecimal2); int smallResult = bigDecimal1.compareTo(bigDecimal3); System.out.println(equalResult); System.out.println(bigResult); System.out.println(smallResult); }
BigDecimal格式化
由于 NumberFormat 类的 format() 方法可以使用 BigDecimal 对象作为其参数,可以利用 BigDecimal 对超出 16 位有效数字的货币值、百分值,以及一般数值进行格式化控制。
代码示例:
1
2
3
4
5
6
7
8
9
10
11private static void bigDecimalFormat() { NumberFormat numberFormat1 = NumberFormat.getCurrencyInstance(); NumberFormat numberFormat2 = NumberFormat.getPercentInstance(); numberFormat2.setMaximumFractionDigits(4); BigDecimal bigDecimal1 = new BigDecimal("12345.6789"); BigDecimal bigDecimal2 = new BigDecimal("0.0008"); BigDecimal bigDecimal3 = bigDecimal1.multiply(bigDecimal2); System.out.println(numberFormat1.format(bigDecimal1)); System.out.println(numberFormat2.format(bigDecimal2)); System.out.println(numberFormat1.format(bigDecimal3)); }
BigDecimal 常见异常
使用 String 类型对 BigDecimal 进行初始化
Java 在 java.math 包中提供的 API 类 BigDecimal,用来对超过16位有效位的数进行精确的运算。在 BigDecimal 初始化时,禁止使用 double 类型的值传入构造器,因为会出现精度问题,而在当使用 String 类型的数据创建 BigDecimal 对象时,就不会出现精度问题。
1
2
3
4
5
6
7public static BigDecimal valueOf(double val) { // Reminder: a zero double returns '0.0', so we cannot fastpath // to use the constant ZERO. This might be important enough to // justify a factory approach, a cache, or a few private // constants, later. return new BigDecimal(Double.toString(val)); }
valueOf(double val) 的源码展示其实也是使用 String 类型的字符串进行的初始化,所以一般情况下建议使用 String 类型来创建 BigDecimal 对象。
在进行 BigDecimal 数值比较时不要使用 equals 进行比较
使用 equals 进行比较会比较值的大小和精度的大小,即 0.00 和 0.000 是不相等的,要使用compareTo() 来进行比较。
1
2
3
4
5
6private static void bigDecimalCompare2() { BigDecimal bigDecimal1 = new BigDecimal("0.00"); BigDecimal bigDecimal2 = new BigDecimal("0.000"); System.out.println(bigDecimal1.equals(bigDecimal2)); System.out.println(bigDecimal1.compareTo(bigDecimal2)); }
进行计算时需要保证参与计算的值不能为 null
在使用 BigDecimal 类型进行计算时,进行加、减、乘、除、比较大小时,一定要保证参与计算的两个值不能为空,否则会抛出 java.lang.NullPointerException 空指针异常。
使用 divide
() 方法时小心无限循环小数以及除数不能为 0
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode);
该方法会有 3 个参数,divisor 表示除数,scale 表示最终结果保留几位小数,roundingMode 表示保留小数的原则,即四舍五入还是直接舍弃,一般情况下都是使用 BigDecimal.ROUND_HALF_UP 四舍五入的原则。
BigDecimal 转 String,科学计数法问题
将 BigDecimal 转换为 String,推荐使用 toPlainString(),而不是 toString()。
其实,BigDecimal 提供了 3 个转换为 String 的方法,分别为:
-
toString():某些场景下使用科学计数法
-
toPlainString():不使用任何计数法
-
toEngineeringString():某些场景下使用工程计数法
1
2
3
4
5
6
7
8
9private static void bigDecimalToString() { BigDecimal bigDecimal1 = new BigDecimal("0.0000000000"); String string1 = bigDecimal1.toString(); String string2 = bigDecimal1.toPlainString(); String string3 = bigDecimal1.toEngineeringString(); System.out.println(string1); System.out.println(string2); System.out.println(string3); }
还有个更为常用的场景:抹零,也容易踩坑,比如 98760.00,抹零之后预期的输出结果是98760:
1
2
3
4
5
6
7private static void bigDecimalToString2() { BigDecimal bigDecimal1 = new BigDecimal("98760.0000"); String string1 = bigDecimal1.stripTrailingZeros().toString(); String string2 = bigDecimal1.stripTrailingZeros().toPlainString(); System.out.println(string1); System.out.println(string2); }
使用规范
尽量不要在项目中使用 new BigDecimal("0"),而是使用 BigDecimal 提供的常量BigDecimal.ZERO。
1
2
3
4
5
6
7
8private static void bigDecimalConstant() { BigDecimal bigDecimal0 = BigDecimal.ZERO; BigDecimal bigDecimal1 = BigDecimal.ONE; BigDecimal bigDecimal10 = BigDecimal.TEN; System.out.println(bigDecimal0); System.out.println(bigDecimal1); System.out.println(bigDecimal10); }
本文中涉及到的所有代码如下:
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136package jx.BigDecimal; import java.math.BigDecimal; import java.text.NumberFormat; /** * @ClassName: jx.BigDecimal.BigDecimalTest * @Author: jiaoxian * @Date: 2022/4/24 09:58 * @Description: */ public class BigDecimalTest { public static void main(String[] args) { handle8(); handle16(); handle20(); createBigDecimal(); bigDecimalMethod(); bigDecimalCompare(); bigDecimalFormat(); bigDecimalCompare2(); bigDecimalToString(); bigDecimalToString2(); bigDecimalConstant(); } private static void bigDecimalConstant() { BigDecimal bigDecimal0 = BigDecimal.ZERO; BigDecimal bigDecimal1 = BigDecimal.ONE; BigDecimal bigDecimal10 = BigDecimal.TEN; System.out.println(bigDecimal0); System.out.println(bigDecimal1); System.out.println(bigDecimal10); } private static void bigDecimalToString2() { BigDecimal bigDecimal1 = new BigDecimal("98760.0000"); String string1 = bigDecimal1.stripTrailingZeros().toString(); String string2 = bigDecimal1.stripTrailingZeros().toPlainString(); System.out.println(string1); System.out.println(string2); } private static void bigDecimalToString() { BigDecimal bigDecimal1 = new BigDecimal("0.0000000000"); String string1 = bigDecimal1.toString(); String string2 = bigDecimal1.toPlainString(); String string3 = bigDecimal1.toEngineeringString(); System.out.println(string1); System.out.println(string2); System.out.println(string3); } private static void bigDecimalCompare2() { BigDecimal bigDecimal1 = new BigDecimal("0.00"); BigDecimal bigDecimal2 = new BigDecimal("0.000"); System.out.println(bigDecimal1.equals(bigDecimal2)); System.out.println(bigDecimal1.compareTo(bigDecimal2)); } private static void bigDecimalFormat() { NumberFormat numberFormat1 = NumberFormat.getCurrencyInstance(); NumberFormat numberFormat2 = NumberFormat.getPercentInstance(); numberFormat2.setMaximumFractionDigits(4); BigDecimal bigDecimal1 = new BigDecimal("12345.6789"); BigDecimal bigDecimal2 = new BigDecimal("0.0008"); BigDecimal bigDecimal3 = bigDecimal1.multiply(bigDecimal2); System.out.println(numberFormat1.format(bigDecimal1)); System.out.println(numberFormat2.format(bigDecimal2)); System.out.println(numberFormat1.format(bigDecimal3)); } private static void bigDecimalCompare() { BigDecimal bigDecimal1 = new BigDecimal("800"); BigDecimal bigDecimal2 = new BigDecimal("123"); BigDecimal bigDecimal3 = new BigDecimal("1000"); BigDecimal bigDecimal4 = new BigDecimal("800"); int equalResult = bigDecimal1.compareTo(bigDecimal4); int bigResult = bigDecimal1.compareTo(bigDecimal2); int smallResult = bigDecimal1.compareTo(bigDecimal3); System.out.println(equalResult); System.out.println(bigResult); System.out.println(smallResult); } private static void bigDecimalMethod() { BigDecimal bigDecimal1 = new BigDecimal("800"); BigDecimal bigDecimal2 = new BigDecimal("123"); BigDecimal addResult = bigDecimal1.add(bigDecimal2); BigDecimal subtractResult = bigDecimal1.subtract(bigDecimal2); BigDecimal multiplyResult = bigDecimal1.multiply(bigDecimal2); BigDecimal divideResult = bigDecimal1.divide(bigDecimal2, 6, BigDecimal.ROUND_HALF_UP); System.out.println(addResult); System.out.println(subtractResult); System.out.println(multiplyResult); System.out.println(divideResult); } public static void createBigDecimal() { BigDecimal intBigDecimal = new BigDecimal(1); BigDecimal longBigDecimal = new BigDecimal(1L); BigDecimal strBigDecimal = new BigDecimal("0.1"); BigDecimal doubleBigDecimal = new BigDecimal(0.1d); System.out.println(intBigDecimal); System.out.println(longBigDecimal); System.out.println(strBigDecimal); System.out.println(doubleBigDecimal); } public static void handle8() { String str = "0.12345678"; Double doubleStr = Double.valueOf(str); System.out.println(doubleStr); Float floatStr = Float.valueOf(str); System.out.println(floatStr); } public static void handle16() { String str = "0.1234567898765432"; Double doubleStr = Double.valueOf(str); System.out.println(doubleStr); Float floatStr = Float.valueOf(str); System.out.println(floatStr); } public static void handle20() { String str = "0.12345678987654329999"; Double doubleStr = Double.valueOf(str); System.out.println(doubleStr); Float floatStr = Float.valueOf(str); System.out.println(floatStr); } }
本文参考自:BigDecimal踩坑总结 - 掘金
最后
以上就是无辜苗条最近收集整理的关于BigDecimal 使用总结的全部内容,更多相关BigDecimal内容请搜索靠谱客的其他文章。
发表评论 取消回复