Java 学习笔记---Java double类型相加问题

 多个double类型的数直接相加的时候,可能存在精度误差.( 由于计算机算法以及硬件环境决定只能识别 0 1。计算机默认的计算结果在都在一个指定精度范围之内,想往深的了解,可以学习数值分析等)
在金融方面是绝对不允许的,好在java开发者有这个先见之明。
java.math.*里面提供了BigDecimal类(提供高精度计算的方法)

这个时候就要采用BigDecimal函数进行运算
第一步、建立String类型的数据
第二步、创建BigDecimal对象BigDecimal(Double.toString(double))
以下两种不推荐:
BigDecimal(double)或者BigDecimal(Double.valueOf(double)))

建议: 涉及到精度问题的时候,整个计算过程都是用String类型或者BigDecimal类对象。最后结果根据需求 在转过来。

另外该文章提供了一个计算辅助类Java Double相加出现的怪事
急需的话,直接学习、创建该工具类,就可以完成项目了。以下是 加法算法的几个实现的方法。

new BigDecimal(Double.toString(double)).add(new BigDecimal(Double.toString(double));
    /**
     * @param b1
     *            BigDecimal
     * @param v2
     *            double
     * @return BigDecimal
     * */
    public BigDecimal add(BigDecimal b1, double v2) {
        // BigDecimal b1=new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }
    /**
     * @param b1
     *           double
     * @param v2
     *            double
     * @return BigDecimal
     * */
    public BigDecimal add(double v1, double v2) {
         BigDecimal b1=new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2);
    }
    /**
     * @param b1
     *           double
     * @param v2
     *            double
     * @return double
     * */
    public 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();
    }

二、double 三种加法比较

+,strictfp,BigDecimel

Strictfp —— Java 关键字。
可应用于类、接口或方法。
使用 strictfp 关键字声明一个方法时,该方法中所有的floatdouble表达式都严格遵守FP-strict的限制,符合IEEE-754规范。
严格约束意味着所有表达式的结果都必须是 IEEE 754 算法对操作数预期的结果,以单精度和双精度格式表示。
public class MathDemo {

    /**
     * @param args
     */
    public static void main(String[] args) {
        System.err.println("普通 "+ addNormal(12353.21,21334.24,154435.03));
        System.err.println("strictfp "+addDouble(12353.21,21334.24,154435.03));
        System.err.println("BigDEcimel: "+add(12353.21,21334.24,154435.03));

    }

    public static double addNormal(double... v1) {
        double res = 0;
        for (int i = 0; i < v1.length; i++) {
            res += v1[i];
        }
        return res;
    }

    public static strictfp double addDouble(double... v) {
        double res = 0;
        for (int i = 0; i < v.length; i++) {
            res += v[i];
        }
        return res;
    }

    /**
     * @param b1
     *            double
     * @param v2
     *            double
     * @return double
     */
    public static double add(double... v) {
        BigDecimal b  = new BigDecimal(Double.toString(v[0]));
        for (int i = 1; i < v.length; i++) {
            BigDecimal b2 = new BigDecimal(Double.toString(v[i]));
            b=b.add(b2);
        }
        return b.doubleValue();
    }
}
输入
    12353.21,21334.24,154435.03三个类型的数据时候
结果:
    普通 188122.47999999998
    strictfp 188122.47999999998
    BigDEcimel: 188122.48

输入
    3.21, 4.24,5.03
结果
    普通 12.48
    strictfp 12.48
    BigDEcimel: 12.48

输入:
    12353.21,21334.24
结果:
    普通 33687.45
    strictfp 33687.45
    BigDEcimel: 33687.45

结论是:
BigDecimal的算法精度比较好。 其余两种方法 都存在缺点。至于strictfp 这个关键字 是去平台化影响。比如32为机器和64位机器结果都一样。 对于精度计算结果影响不大。

附录:.

//使用double类型创建BigDecimal
  public BigDecimal(double val) {
        if (Double.isInfinite(val) || Double.isNaN(val))
            throw new NumberFormatException("Infinite or NaN");

        // Translate the double into sign, exponent and significand, according
        // to the formulae in JLS, Section 20.10.22.
        long valBits = Double.doubleToLongBits(val);
        int sign = ((valBits >> 63)==0 ? 1 : -1);
        int exponent = (int) ((valBits >> 52) & 0x7ffL);
        long significand = (exponent==0 ? (valBits & ((1L<<52) - 1)) << 1
                            : (valBits & ((1L<<52) - 1)) | (1L<<52));
        exponent -= 1075;
        // At this point, val == sign * significand * 2**exponent.

        /*
         * Special case zero to supress nonterminating normalization
         * and bogus scale calculation.
         */
        if (significand == 0) {
            intVal = BigInteger.ZERO;
            intCompact = 0;
            precision = 1;
            return;
        }

        // Normalize
        while((significand & 1) == 0) {    //  i.e., significand is even
            significand >>= 1;
            exponent++;
        }

        // Calculate intVal and scale
        long s = sign * significand;
        BigInteger b;
        if (exponent < 0) {
            b = BigInteger.valueOf(5).pow(-exponent).multiply(s);
            scale = -exponent;
        } else if (exponent > 0) {
            b = BigInteger.valueOf(2).pow(exponent).multiply(s);
        } else {
            b = BigInteger.valueOf(s);
        }
        intCompact = compactValFor(b);
        intVal = (intCompact != INFLATED) ? null : b;
    }
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页