JAVA计算结果精度处理

  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 }

 

posted @ 2020-12-15 14:56  骇客黑界  阅读(248)  评论(0编辑  收藏  举报