1 ackage com.vstsoft.common.util;
2
3 import java.math.BigDecimal;
4 import java.text.DecimalFormat;
5
6 import org.slf4j.Logger;
7 import org.slf4j.LoggerFactory;
8
9 /**
10 * @Comments : 由于Java的简单类型不能够精确的对浮点数进行运算, 这个工具类提供精确的浮点数运算,包括加减乘除和四舍五入。
11 */
12 public class ArithUtil {
13 // 默认除法运算精度
14 private static final int DEFAULT_DIV_SCALE = 10;
15 private final static Logger logger = LoggerFactory.getLogger(ArithUtil.class);
16
17 /**
18 * 提供精确的加法运算。
19 *
20 * @param v1
21 * @param v2
22 * @return 两个参数的和
23 */
24 public static BigDecimal add(BigDecimal b1, BigDecimal b2) {
25 return b1.add(b2);
26 }
27
28 public static BigDecimal add(double v1, double v2) {
29 BigDecimal b1 = new BigDecimal(Double.toString(v1));
30 BigDecimal b2 = new BigDecimal(Double.toString(v2));
31 return add(b1, b2);
32 }
33
34 /**
35 * 提供精确的减法运算。
36 *
37 * @param v1
38 * @param v2
39 * @return 两个参数的差
40 */
41
42 public static BigDecimal subtract(BigDecimal b1, BigDecimal b2) {
43 return b1.subtract(b2);
44 }
45
46 public static BigDecimal subtract(double v1, double v2) {
47 BigDecimal b1 = new BigDecimal(Double.toString(v1));
48 BigDecimal b2 = new BigDecimal(Double.toString(v2));
49 return b1.subtract(b2);
50 }
51
52 /**
53 * 提供精确的乘法运算。
54 *
55 * @param v1
56 * @param v2
57 * @return 两个参数的积
58 */
59
60 public static BigDecimal multiply(BigDecimal b1, BigDecimal b2) {
61 return b1.multiply(b2);
62 }
63
64 public static BigDecimal multiply(double v1, double v2) {
65 BigDecimal b1 = new BigDecimal(Double.toString(v1));
66 BigDecimal b2 = new BigDecimal(Double.toString(v2));
67 return b1.multiply(b2);
68 }
69
70 /**
71 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入,舍入模式采用ROUND_HALF_EVEN
72 *
73 * @param v1
74 * 被除数
75 * @param v2
76 * 除数
77 * @return 两个参数的商
78 */
79
80 public static BigDecimal divide(BigDecimal b1, BigDecimal b2) {
81 return divide(b1, b2, DEFAULT_DIV_SCALE);
82 }
83
84 public static BigDecimal divide(double v1, double v2) {
85 return divide(v1, v2, DEFAULT_DIV_SCALE);
86 }
87
88 /**
89 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用ROUND_HALF_EVEN
90 *
91 * @param v1
92 * 被除数
93 * @param v2
94 * 除数
95 * @param scale
96 * 表示需要精确到小数点以后几位。
97 * @return 两个参数的商
98 */
99
100 public static BigDecimal divide(BigDecimal b1, BigDecimal b2, int scale) {
101 return divide(b1, b2, scale, BigDecimal.ROUND_HALF_EVEN);
102 }
103
104 public static BigDecimal divide(double v1, double v2, int scale) {
105 return divide(v1, v2, scale, BigDecimal.ROUND_HALF_EVEN);
106 }
107
108 /**
109 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用用户指定舍入模式
110 *
111 * @param v1
112 * 被除数
113 * @param v2
114 * 除数
115 * @param scale
116 * 表示需要精确到小数点以后几位
117 * @param round_mode
118 * 表示用户指定的舍入模式
119 * @return 两个参数的商
120 */
121
122 public static BigDecimal divide(BigDecimal b1, BigDecimal b2, int scale, int round_mode) {
123 if (scale < 0) {
124 throw new IllegalArgumentException("The scale must be a positive integer or zero");
125 }
126 return b1.divide(b2, scale, round_mode);
127 }
128
129 public static BigDecimal divide(double v1, double v2, int scale, int round_mode) {
130 if (scale < 0) {
131 throw new IllegalArgumentException("The scale must be a positive integer or zero");
132 }
133 BigDecimal b1 = new BigDecimal(Double.toString(v1));
134 BigDecimal b2 = new BigDecimal(Double.toString(v2));
135 return b1.divide(b2, scale, round_mode);
136 }
137
138 /**
139 * 提供保留两位小数的四舍五入处理,舍入模式采用ROUND_HALF_EVEN
140 *
141 * @param v
142 * 需要四舍五入的数字
143 * @param scale
144 * 小数点后保留几位
145 * @return 四舍五入后的结果
146 */
147
148 public static BigDecimal round(BigDecimal b) {
149 return round(b, 2);
150 }
151
152 /**
153 * 提供精确的小数位四舍五入处理,舍入模式采用ROUND_HALF_EVEN
154 *
155 * @param v
156 * 需要四舍五入的数字
157 * @param scale
158 * 小数点后保留几位
159 * @return 四舍五入后的结果
160 */
161
162 public static BigDecimal round(BigDecimal b, int scale) {
163 return round(b, scale, BigDecimal.ROUND_HALF_EVEN);
164 }
165
166 /**
167 * 提供精确的小数位四舍五入处理
168 *
169 * @param v
170 * 需要四舍五入的数字
171 * @param scale
172 * 小数点后保留几位
173 * @param round_mode
174 * 指定的舍入模式
175 * @return 四舍五入后的结果
176 */
177
178 public static BigDecimal round(BigDecimal b, int scale, int round_mode) {
179 if (scale < 0) {
180 logger.error(StaticUtil.UTILERROR, "scale:" + scale + ",the scale must be a positive integer or zero");
181 throw new IllegalArgumentException("The scale must be a positive integer or zero");
182 }
183 return b.setScale(scale, round_mode);
184 }
185
186 public static void main(String[] args) {
187 BigDecimal b1 = new BigDecimal("100");
188 BigDecimal b2 = new BigDecimal("1.765");
189 BigDecimal b3 = new BigDecimal("1.65");
190 BigDecimal b4 = new BigDecimal("7");
191
192 // 精确除法运算
193 logger.info(divide(b1, b4).toString());
194 logger.info(divide(b1, b4, 1).toString());
195
196 logger.info(round(b2, 2, BigDecimal.ROUND_HALF_EVEN).toString());
197 logger.info(round(b2, 2, BigDecimal.ROUND_HALF_UP).toString());
198 logger.info(round(b2, 2, BigDecimal.ROUND_HALF_DOWN).toString());
199
200 logger.info(round(b3, 1, BigDecimal.ROUND_HALF_EVEN).toString());
201 logger.info(round(b3, 1, BigDecimal.ROUND_HALF_UP).toString());
202 logger.info(round(b3, 1, BigDecimal.ROUND_HALF_DOWN).toString());
203
204 // 数学运算在实际中存在的问题
205 /**
206 * 0.060000000000000005 0.5800000000000001 401.49999999999994
207 * 1.2329999999999999
208 */
209 logger.info("错误的结果:");
210 logger.info(0.05 + 0.01 + "");
211 logger.info(1.0 - 0.42 + "");
212 logger.info(4.015 * 100 + "");
213 logger.info(123.3 / 100 + "");
214 logger.info("精确计算的结果:");
215 logger.info(ArithUtil.add(new BigDecimal("0.056789123446"), new BigDecimal("0.01234566789")).toString());
216 logger.info(ArithUtil.subtract(new BigDecimal("1.012345678912"), new BigDecimal("0.42345689127")).toString());
217 logger.info(ArithUtil.multiply(new BigDecimal("4.0156789"), new BigDecimal("100.12345")).toString());
218 logger.info(ArithUtil.divide(new BigDecimal("123.3"), new BigDecimal("100")).toString());
219 /**
220 * 输入结果为 504.549999999999982946974341757595539093017578125 实际结果应为 504.55
221 */
222 logger.info("BigDecimal 的不精确计算问题:");
223 BigDecimal bg1 = new BigDecimal(100.91);
224 BigDecimal bg2 = new BigDecimal(5);
225 BigDecimal bg3 = bg1.multiply(bg2);
226 logger.info(bg3 + "");
227 DecimalFormat df = new DecimalFormat("¥0,000.00");
228 logger.info("精确计算的结果:");
229
230 BigDecimal bg4 = multiply(new BigDecimal("1000000000.91"), new BigDecimal("5"));
231 logger.info(ArithUtil.round(bg4).toString());
232 BigDecimal bg5 = multiply(new BigDecimal("10000000"), new BigDecimal("9999999"));
233 logger.info(ArithUtil.round(bg5).toString());
234 logger.info("***************************************************");
235
236 }
237 }