文章目录
- 一、背景
- 二、事故案例
- 1、问题
- 2、问题复现
- 3、源码分析
- 4、原因分析
- 三、总结
- 四、工具类
一、背景
BigDecimal 平时主要用于计算金钱时,其自身提供了很多的构造方法,但是这些构造方法使用不当会造成精度丢失,从而引起事故。
二、事故案例
1、问题
收银台计算商品价格报错,导致订单无法支付
2、问题复现
复制代码
1
2
3
4
5
6
7
8
9public static void main(String[] args) { BigDecimal bigDecimal=new BigDecimal(88); System.out.println(bigDecimal); bigDecimal=new BigDecimal("8.8"); System.out.println(bigDecimal); bigDecimal=new BigDecimal(8.8); System.out.println(bigDecimal); }
3、源码分析
复制代码
1
2
3
4
5
6
7
8
9
10
11public static long doubleToLongBits(double value) { long result = doubleToRawLongBits(value); // Check for NaN based on values of bit fields, maximum // exponent and nonzero significand. if ( ((result & DoubleConsts.EXP_BIT_MASK) == DoubleConsts.EXP_BIT_MASK) && (result & DoubleConsts.SIGNIF_BIT_MASK) != 0L) result = 0x7ff8000000000000L; return result; }
问题就处在 doubleToRawLongBits 这个方法上,在 jdk 中 double 类(float 与 int 对应)中提供了 double 与 long 转换,doubleToRawLongBits 就是将 double 转换为 long,这个方法是原始方法(底层不是 java 实现,是 c++ 实现的)。
4、原因分析
在 java 中 BigDecimal 处理数据时把十进制小数扩大 N 倍让它在整数上进行计算,并保留相应的精度信息。
- float 和 double 类型,主要是为了科学计算和工程计算而设计的,之所以执行二进制浮点运算,是为了在广泛的数值范围上提供较为精确的快速近和计算。
- 并没有提供完全精确的结果,所以不应该被用于精确的结果的场合。
- 当浮点数达到一定大的数,就会自动使用科学计数法,这样的表示只是近似真实数而不等于真实数。
- 当十进制小数位转换二进制的时候也会出现无限循环或者超过浮点数尾数的长度。
三、总结
在设计到精度计算时,我们尽量使用 String 类型来进行转换,而且涉及到 BigDecimal 的计算,要使用其对应方法进行计算。
四、工具类
这里封装一个 BigDecimal 工具类
复制代码
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
136public class BigDecimalUtils { /** * double 加 * * @param v1 加数 * @param v2 加数 * @return 和 */ public static BigDecimal doubleAdd(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2); } /** * float 加 * * @param v1 加数 * @param v2 加数 * @return 和 */ public static BigDecimal floatAdd(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.add(b2); } /** * double 减 * * @param v1 被减数 * @param v2 减数 * @return 差 */ public static BigDecimal doubleSub(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2); } /** * float 减 * * @param v1 被减数 * @param v2 减数 * @return 差 */ public static BigDecimal floatSub(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.subtract(b2); } /** * double 乘 * * @param v1 因数 * @param v2 因数 * @return 积 */ public static BigDecimal doubleMul(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2); } /** * float 乘 * * @param v1 因数 * @param v2 因数 * @return 积 */ public static BigDecimal floatMul(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.multiply(b2); } /** * double 除 * * @param v1 被除数 * @param v2 除数 * @return 商 */ public static BigDecimal doubleDiv(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); // 保留小数点后两位 ROUND_HALF_UP = 四舍五入 return b1.divide(b2, 2, RoundingMode.HALF_UP); } /** * float 除 * * @param v1 被除数 * @param v2 除数 * @return 商 */ public static BigDecimal floatDiv(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); // 保留小数点后两位 ROUND_HALF_UP = 四舍五入 return b1.divide(b2, 2, RoundingMode.HALF_UP); } /** * double<br> * 比较v1 v2大小 * * @param v1 * @param v2 * @return v1>v2 return 1 v1=v2 return 0 v1<v2 return -1 */ public static int doubleCompareTo(double v1, double v2) { BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.compareTo(b2); } /** * float<br> * 比较v1 v2大小 * * @param v1 * @param v2 * @return v1>v2 return 1 v1=v2 return 0 v1<v2 return -1 */ public static int floatCompareTo(float v1, float v2) { BigDecimal b1 = new BigDecimal(Float.toString(v1)); BigDecimal b2 = new BigDecimal(Float.toString(v2)); return b1.compareTo(b2); } }
最后
以上就是迷你皮卡丘最近收集整理的关于BigDecimal 的正确使用方式的全部内容,更多相关BigDecimal内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复