下面列举让你惊讶的现象,或许你还一直这么用:
1、使用Math.round()
double d = 1041.735; d = Math.round(d*100)/100.0; //除以100.0 而不是100 System.out.println(d); //输出结果是1041.73
2、使用 BigDecima
double d = 9.655; BigDecimal b = new BigDecimal(d); d = b.setScale(2,RoundingMode.HALF_UP).doubleValue(); System.out.println(d); //输出结果是9.65
3、使用DecimalFormat
double d = 11.225; DecimalFormat df = new DecimalFormat("0.00"); String s = df.format(d); System.out.println(s);//输出结果是11.22
所有的根源都是double的浮点运算机制
例1
1041.735*100=104173.49999999999 Math.round(1041.735*100)=104173
例2
在进行使用double的构造函数之后,发生了变化,导致后续的四舍五入错误
这里写图片描述
例3
和例1、例2有点区别,在内部实现时,也是进行了相关的浮点预算
综上所述:只要进行浮点运算,就可能产生精度问题
上述例2和例3几乎接近真实,只需要进行一点调整即可
例2
double d = 9.655; //通过改变构造方法,就可以解决精度丢失问题 BigDecimal b = new BigDecimal(Double.toString(d)); d = b.setScale(2,RoundingMode.HALF_UP).doubleValue(); System.out.println(d); //输出结果是9.66
例3
double d = 11.225; DecimalFormat df = new DecimalFormat("0.00"); //指定RoundingMode df.setRoundingMode(RoundingMode.HALF_UP); String s = df.format(d); System.out.println(s);//输出结果是11.23
下面我们再来看一个经典的例子
计算199999.000.015,结果保留2位有效数字
理论上 199999.000.015=2999.985
实际上java的bouble运算是 199999.00*0.015=2999.984999….
下面提供了一个double运算的工具类,最后的format解决了科学计数法显示问题(1.53E10)
public class DoubleUtil { private static final int DIV_SCALE=10;//默认除法运算精度 private DoubleUtil(){ } /** * 提供精确的加法运算 * @param v1 被加数 * @param v2 加数 * @return 两个参数的和 */ public static double add(double v1,double v2){ BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.add(b2).doubleValue(); } /** * 提供精确的减法运算 * @param v1 被减数 * @param v2 减数 * @return 两个参数的差 */ public static double sub(double v1,double v2){ BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.subtract(b2).doubleValue(); } /** * 提供精确的乘法运算 * @param v1 被乘数 * @param v2 乘数 * @return 两个参数的积 */ public static double mul(double v1,double v2){ BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.multiply(b2).doubleValue(); } /** * 提供(相对)精确的除法运算 * 当除不尽时,使用默认精度:DIV_SCALE * @param v1 被除数 * @param v2 除数 * @return 两个参数的商 */ public static double div(double v1,double v2){ return div(v1,v2,DIV_SCALE); } /** * 提供(相对)精确的除法运算 * 当除不尽时,使用参数scale指定精度,进行四舍五入 * @param v1 被除数 * @param v2 除数 * @param scale 表示精确到小数点后几位 * @return 两个参数的商 */ public static double div(double v1,double v2,int scale){ if(scale<0){ throw new IllegalArgumentException("scale不能小于0"); } BigDecimal b1 = new BigDecimal(Double.toString(v1)); BigDecimal b2 = new BigDecimal(Double.toString(v2)); return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); } /** * 提供精确的小数位四舍五入处理 * @param v 需要四舍五入的数字 * @param scale 小数点后保留几位 * @return 四舍五入后的结果 */ public static double round(double v1,int scale){ if(scale<0){ throw new IllegalArgumentException("scale不能小于0"); } BigDecimal b = new BigDecimal(Double.toString(v1)); BigDecimal one = new BigDecimal("1"); return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue(); } public static String format(double v1,int scale){ String s = String.valueOf(round(v1,scale)); s = new BigDecimal(s).toPlainString();//解决科学计算法转换问题 int k = s.indexOf("."); if(k<0 && scale>0){ s = s+".0"; k=s.indexOf("."); } if(k>0){ int d_scale = (s.length()-(k+1)); if(scale>d_scale){ for(int i=0;i<scale-d_scale;i++){ s+="0"; } }else if(scale<d_scale){//当scale=0时才会出现这种情况 s = s.substring(0,k); } } return s; } }