Double类型数据的精确计算

在Java中为什么会总是出现double值的乘积总是在一个正确的结果左右偏0.0000**1,这是因为当两个double数值相乘时,底层采用转换成二进制来进行乘法的运算,由于在乘完之后在包含小数的二进制中无法完全转换成十进制才会发生这种情况。Java中有一个类是BigDecimal,该类是专门计算一些要求精度很高的算法,常用于银行金融类的计算,BigDecimal一共有4个够造方法,我们不关心用BigInteger来够造的那两个,那么还有两个, 它们是:

BigDecimal(double val) 
          Translates a double into a BigDecimal. 
BigDecimal(String val) 
          Translates the String repre sentation of a BigDecimal into a BigDecimal.

但是在测试中直接将Double值传进去,也就是调用第一个构造方法来创建的BigDecimal也会损失精度,具体这里先不说了,此处只是利用第二个构造函数来进行对BigDecimal来创建对象的,下面是一个工具类:

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 功能描述: 用于对数值的精确计算工具类
 * author:Created by WangZhiQiang on 2018/5/18.
 */
public class CalcUtils {
    /**
     * 加法
     */
    public static final int TYPE_ADD = 0x00;
    /**
     * 乘法
     */
    public static final int TYPE_MULTIPLY = 0x01;
    /**
     * 除法
     */
    public static final int TYPE_DIVISION = 0x02;
    /**
     * 减法
     */
    public static final int TYPE_SUBTRACT = 0x03;

    /**
     *  加法
     * @param a
     * @param b
     * @return
     */
    public static Double add(Double a, Double b) {
        return calc(a, b, -1, TYPE_ADD, null);
    }

    /**
     * 减法
     * @param a
     * @param b
     * @return
     */
    public static Double subtract(Double a, Double b) {
        return calc(a, b, -1, TYPE_SUBTRACT, null);
    }

    /**
     * 乘法
     * @param a
     * @param b
     * @return
     */
    public static Double multiply(Double a, Double b) {
        return calc(a, b, -1, TYPE_MULTIPLY, null);
    }

    /**
     * 除法
     * @param a
     * @param b
     * @return
     */
    public static Double division(Double a, Double b) {
        return calc(a, b, -1, TYPE_DIVISION, null);
    }

    /**
     * Scale
     *      是用来对利用BigDecimal对数值进行运算后保留的位数。
     *
     * RouningMode
     *  该参数是BigDecimal是一个枚举类,包含有8个枚举类型,用来说明对经过计算后数值的取舍模式。
     *
     *      ROUND_UP:远离零方向舍入。向绝对值最大的方向舍入,只要舍弃位非0即进位。
     *      ROUND_DOWN:趋向零方向舍入。向绝对值最小的方向输入,所有的位都要舍弃,不存在进位情况。
     *      ROUND_CEILING:向正无穷方向舍入。向正最大方向靠拢。若是正数,舍入行为类似于ROUND_UP,若为负数,舍入行为类似于ROUND_DOWN。Math.round()方法就是使用的此模式。
     *      ROUND_FLOOR:向负无穷方向舍入。向负无穷方向靠拢。若是正数,舍入行为类似于ROUND_DOWN;若为负数,舍入行为类似于ROUND_UP。
     *      HALF_UP:最近数字舍入(5进)。这是我们最经典的四舍五入。
     *      HALF_DOWN:最近数字舍入(5舍)。在这里5是要舍弃的。
     *      HAIL_EVEN:银行家舍入法。
     */

    /**
     * 乘法
     * @param a
     * @param b
     * @param scale 小数点后保留的位数
     * @param mode 保留的模式
     * @return
     */
    public static Double multiply(Double a, Double b, int scale, RoundingMode mode) {
        return calc(a, b, scale, TYPE_MULTIPLY, mode);
    }
    /**
     * 除法
     * @param a
     * @param b
     * @param scale 小数点后保留的位数
     * @param mode 保留的模式
     * @return
     */
    public static Double division(Double a, Double b, int scale, RoundingMode mode) {
        return calc(a, b, scale, TYPE_DIVISION, mode);
    }
    /**
     *  计算
     * @param a
     * @param b
     * @param scale
     * @param type
     * @param mode
     * @return
     */
    private static Double calc(Double a, Double b, int scale, int type, RoundingMode mode) {
        BigDecimal result = null;

        BigDecimal bgA = new BigDecimal(String.valueOf(a));
        BigDecimal bgB = new BigDecimal(String.valueOf(b));
        switch (type) {
            case TYPE_ADD:
                result = bgA.add(bgB);
                break;
            case TYPE_MULTIPLY:
                result = bgA.multiply(bgB);
                break;
            case TYPE_DIVISION:
                try {
                    result = bgA.divide(bgB);
                } catch (ArithmeticException e) {// 防止无限循环而报错  采用四舍五入保留3位有效数字
                    result = bgA.divide(bgB, 3, RoundingMode.HALF_DOWN);
                }
                break;
            case TYPE_SUBTRACT:
                result = bgA.subtract(bgB);
                break;
            default:
        }
        if (mode == null) {
            if (scale != -1) {
                result = result.setScale(scale);
            }
        } else {
            if (scale != -1) {
                result = result.setScale(scale, mode);
            }
        }
        return result.doubleValue();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值