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 }