真正的java的四舍五入

来源:互联网 发布:中国空运交通运输网络 编辑:程序博客网 时间:2024/05/21 19:24

下面列举让你惊讶的现象,或许你还一直这么用:

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.49999999999Math.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.00*0.015,结果保留2位有效数字
理论上 199999.00*0.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;    }}
1 0