BigDecimal工具类

来源:互联网 发布:手机酷狗4g连不上网络 编辑:程序博客网 时间:2024/05/01 13:52

问题:为什么要用BigDecimal?

在java 中
1.11+0.9 = 1.2 ? NO result=1.2000000000000002
1.2/3=0.4? NO result=0.39999999999999997

这就是为什么要用BigDecimal的原因,对,因为直接用浮点数进行运算是不准确的,这和计算机对浮点数的存储有关系。

BigDecimal使用过程中的注意事项

这里主要是 BigDecimal 工具类的一个封装,对于其原理不做深究。使用中我觉得BigDecimal(double val) 是个需要注意的点。
其实java 的API 已经介绍的很详细了。首先看一下BigDecimal(double val)的API 介绍。

public BigDecimal(double val)

将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。

注:

  1. 此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。

  2. 另一方面,String 构造方法是完全可预知的:写入 new BigDecimal(“0.1”) 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。

  3. 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。

参数:
val - 要转换为 BigDecimal 的 double 值。
抛出:
NumberFormatException - 如果 val 为无穷大或 NaN。

我的理解:

  1. 通过官文的注释的第1,2点,我们应该能知道大概是什么情况。因为 new BigDecimal(double val)有不可预知性,所以直接用new BigDecimal(double val) 进行表示或者运算同样不能达到我们预期的结果。但是官方文档建议我们优先用可预知的new BigDecimal(String val)构造方法。

  2. 关于注释中的第三点。我觉得翻译的有偏差。我是这样理解的:当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换。BigDecimal (double val)不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String,而要获取这种分步骤转换的结果,建议使用 static valueOf(double) 方法。而这个方法是java api中提供的。

static valueOf(double) API介绍和源码

既然这里官方推荐使用 static valueOf(double) 方法,我们看下static valueOf(double)这个方法的api介绍和源码。

public static BigDecimal valueOf(double val)

使用 Double.toString(double) 方法提供的 double 规范的字符串表示形式将 double 转换为 BigDecimal。
注:这通常是将 double(或 float)转化为 BigDecimal 的首选方法,因为返回的值等于从构造 BigDecimal(使用 Double.toString(double) 得到的结果)得到的值。

参数:
val - 要转换为 BigDecimal 的 double。
返回:
其值等于或约等于 val 值的 BigDecimal。
抛出:
NumberFormatException - 如果 val 为无穷大或 NaN。

源码:

    /**     * Returns a new {@code BigDecimal} instance whose value is equal to {@code     * val}. The new decimal is constructed as if the {@code BigDecimal(String)}     * constructor is called with an argument which is equal to {@code     * Double.toString(val)}. For example, {@code valueOf("0.1")} is converted to     * (unscaled=1, scale=1), although the double {@code 0.1} cannot be     * represented exactly as a double value. In contrast to that, a new {@code     * BigDecimal(0.1)} instance has the value {@code     * 0.1000000000000000055511151231257827021181583404541015625} with an     * unscaled value {@code 1000000000000000055511151231257827021181583404541015625}     * and the scale {@code 55}.     *     * @param val     *            double value to be converted to a {@code BigDecimal}.     * @return {@code BigDecimal} instance with the value {@code val}.     * @throws NumberFormatException     *             if {@code val} is infinite or {@code val} is not a number     */    public static BigDecimal valueOf(double val) {        if (Double.isInfinite(val) || Double.isNaN(val)) {            throw new NumberFormatException("Infinity or NaN: " + val);        }        return new BigDecimal(Double.toString(val));    }

这样就很清楚了。 因此看到网上有 new BigDecimal(Double.toString(double val)) 这种写法的 何不直接替换成 valueOf(double val)呢?

BigDecimal工具类

目前有的功能包括,“字符串转double类型”,“double四舍五入”,“BigDecimal四舍五入”,“double保留两位小数”,“BigDecimal保留两位小数”,“double的加、减、乘、除运算”,“BigDecimal 的加、减、乘、除运算”。

以后遇到需要的功能还会慢慢添加

/** * Created by skx * <p/> * Double 数据类型处理工具类。目前基本包括“字符串转double 类型”,“四舍五入”,“保留两位小数”,“double类型的加、减、乘、除运算” */public class DoubleFormatTool {    /**     * 字符串转Double     *     * @param doubleString double类型的字符串     * @return     */    public static double strToDouble(String doubleString) {        try {            return Double.parseDouble(doubleString);        } catch (Exception e) {            return 0.0;        }    }    /**     * 得到一个Double 类型的数 四舍五入后保留小数点后两位.     * <p/>     * 这里调用的是  getRoundHalfUpDouble(String doubleString) 方法,     * 而不是用  BigDecimal b = new BigDecimal(d);return b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); 这样的方式来。     * 主要原因是 BigDecimal(double) 的构造参数在使用的时候会存在精度不准确的情况,而在java中浮点运算本身就是不精确的,是用IEEE标准来表示的,当然这并不是所有的浮点数都不正确,而是有一部分不准确。     * <p/>     * API 中是这样解释的。public BigDecimal(double val)     * 将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。返回的 BigDecimal 的标度是使 (10scale × val) 为整数的最小值。     * <p/>     * 注:     * 1.此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。     * 2.另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。3.     * 当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。     * <p/>     * eg:  1.115  用BigDecimal(double) 的构造方法四舍五入,保留2位小数得到的值位 1.11  而BigDecimal(String) 的构造方法四舍五入,保留2位小数得到的值位 1.12     * 而且官方的API 也是推荐使用BigDecimal(String)的构造函数。     *     * @param d double类型的字符串     * @return 四舍五入后保留2位小数的值     */    public static double getRoundHalfUpDouble(double d) {        return getRoundHalfUpDouble(String.valueOf(d));    }    /**     * 对于一个double 型的字符串进行四舍五入和保留两位小数处理,如果最后一位小数位为0,则默认不显示     *     * @param doubleString double类型的字符串     * @return 四舍五入后保留2位小数的值     */    public static double getRoundHalfUpDouble(String doubleString) {        try {            BigDecimal b = new BigDecimal(doubleString);            return b.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();        } catch (Exception e) {            return 0.0;        }    }    /**     * 对于一个double 型的字符串进行四舍五入和保留两位小数处理,如果最后一位小数位为为0,则仍然显示。     *     * @param doubleString double类型的字符串     * @return     */    public static String getRoundHalfUpDoubleStr(String doubleString) {        try {            double tempDoubleString = getRoundHalfUpDouble(doubleString);            return String.format("%.2f", tempDoubleString);        } catch (Exception e) {            return doubleString;        }    }    /**     * 精确的double类型的字符串  加法运算     *     * @param doubleStr1 被加数     * @param doubleStr2 加数     * @return 两个参数的和     */    public static double add(String doubleStr1, String doubleStr2) {        if (TextUtils.isEmpty(doubleStr1) || TextUtils.isEmpty(doubleStr2)) {            return 0;        }        try {            BigDecimal b1 = new BigDecimal(doubleStr1);            BigDecimal b2 = new BigDecimal(doubleStr2);            return b1.add(b2).doubleValue();        } catch (Exception e) {            e.printStackTrace();        }        return 0;    }    /**     * 对double 类型的浮点数进行加法运算     *     * @param v1 被加数     * @param v2 加数     * @return 两个参数的和     */    public static double add(double v1, double v2) {        BigDecimal b1 = BigDecimal.valueOf(v1);        BigDecimal b2 = BigDecimal.valueOf(v2);        return b1.add(b2).doubleValue();    }    /**     * 对double 类型的浮点数进行减法运算。     *     * @param v1 被减数     * @param v2 减数     * @return 两个参数的差     */    public static double subtract(double v1, double v2) {        BigDecimal b1 = BigDecimal.valueOf(v1);        BigDecimal b2 = BigDecimal.valueOf(v2);        return b1.subtract(b2).doubleValue();    }    /**     * 对double 类型的浮点数进行乘法运算。     *     * @param v1 被乘数     * @param v2 乘数     * @return 两个参数的积     */    public static double multiply(double v1, double v2) {        BigDecimal b1 = new BigDecimal(Double.toString(v1));        BigDecimal b2 = new BigDecimal(Double.toString(v2));        return b1.multiply(b2).doubleValue();    }    /**     * 对double 类型的浮点数进行除法运算。当发生除不尽的情况时,由scale参数指     * 定精度,以后的数字四舍五入。     *     * @param v1    被除数     * @param v2    除数     * @param scale 表示表示需要精确到小数点以后几位。     * @return 两个参数的商     */    public static double divide(double v1, double v2, int scale) {        if (scale < 0) {            throw new IllegalArgumentException("The scale must be a positive integer or zero");        }        BigDecimal b1 = BigDecimal.valueOf(v1);        BigDecimal b2 = BigDecimal.valueOf(v2);        return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();    }}
1 0