Double类型丢失精度的两种解决方案

1、继续使用Double类型

其实只要在计算时使用封装的工具类,继续使用Double类型精度也不会丢失;转换为BigDecimal时,构造函数采用String那一个。

package com.sugarppig.general.utils;

import com.sugarppig.common.exception.GeneralException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @author SugarPPig
 * @date 2020-12-09 16:05
 * @decription Double 精确计算工具
 */
public class DoubleArithUtil {
    /**
     * 提供精确加法计算的add方法
     *
     * @param value1
     *            被加数
     * @param value2
     *            加数
     * @return 两个参数的和
     */
    public static double add(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(String.valueOf(value1));
        BigDecimal b2 = new BigDecimal(String.valueOf(value2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精确减法运算的sub方法
     *
     * @param value1
     *            被减数
     * @param value2
     *            减数
     * @return 两个参数的差
     */
    public static double sub(double value1, double value2) {
        BigDecimal b1 = new BigDecimal(String.valueOf(value1));
        BigDecimal b2 = new BigDecimal(String.valueOf(value2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确乘法运算的mul方法
     *
     * @param value1
     *            被乘数
     * @param value2
     *            乘数
     * @return 两个参数的积
     */
    public static Double mul(Double value1, Double value2) {
        if (value1 == null || value2 == null) {
            return 0d;
        }

        BigDecimal b1 = new BigDecimal(String.valueOf(value1));
        BigDecimal b2 = new BigDecimal(String.valueOf(value2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1
     *            被除数
     * @param value2
     *            除数
     * @param scale
     *            精确范围
     * @return 两个参数的商,默认四舍五入
     * @throws GeneralException
     */
    public static Double div(double value1, double value2, int scale) {
        // 如果精确范围小于0,抛出异常信息
        if (scale < 0) {
            throw new GeneralException("精确度不能小于0");
        }
        if (value2 == 0) {
            return 0d;
        }
        BigDecimal b1 = new BigDecimal(String.valueOf(value1));
        BigDecimal b2 = new BigDecimal(String.valueOf(value2));
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 计算多个值的乘积
     */
    public static Double muls(Double... args) {
        if (args.length < 2) {
            throw new GeneralException("参数至少为2个");
        }
        List<Double> doubles = Arrays.asList(args);
        if (doubles.stream().anyMatch(Objects::isNull)) {
            return 0d;
        }
        return doubles.parallelStream().reduce(1d, DoubleArithUtil::mul);
    }

    /**
     * 计算多个值的和
     */
    public static Double adds(Double... args) {
        if (args.length < 2) {
            throw new GeneralException("参数至少为2个");
        }
        List<Double> doubles = Arrays.asList(args);
        if (doubles.stream().anyMatch(Objects::isNull)) {
            return 0d;
        }
        return doubles.parallelStream().reduce(0d, DoubleArithUtil::add);
    }

    /**
     * 计算多个值的和,元素为null时记为0d参与运算
     */
    public static Double addsNull(Double... args) {
        if (args.length < 2) {
            throw new GeneralException("参数至少为2个");
        }
        List<Double> doubles = Arrays.asList(args);
        return doubles.parallelStream().map(e -> e == null ? 0d : e).reduce(0d, DoubleArithUtil::add);
    }
}

2、将Double类型改为BigDecimal类型

BigDecimal工具类,将Double的工具类稍微改动了下。

package com.sugarppig.general.utils;

import com.sugarppig.common.exception.GeneralException;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * @author SugarPPig
 * @date 2020-12-10 10:03
 * @decription BigDecimal 精确计算工具
 */
public class BigDecimalArithUtil {
    /**
     * 提供精确加法计算的add方法
     *
     * @param value1
     *            被加数
     * @param value2
     *            加数
     * @return 两个参数的和
     */
    public static BigDecimal add(BigDecimal value1, BigDecimal value2) {
        return value1.add(value2);
    }

    /**
     * 提供精确减法运算的sub方法
     *
     * @param value1
     *            被减数
     * @param value2
     *            减数
     * @return 两个参数的差
     */
    public static BigDecimal sub(BigDecimal value1, BigDecimal value2) {
        return value1.subtract(value2);
    }

    /**
     * 提供精确乘法运算的mul方法
     *
     * @param value1
     *            被乘数
     * @param value2
     *            乘数
     * @return 两个参数的积
     */
    public static BigDecimal mul(BigDecimal value1, BigDecimal value2) {
        if (value1 == null || value2 == null) {
            return BigDecimal.ZERO;
        }

        return value1.multiply(value2);
    }

    /**
     * 提供精确的除法运算方法div
     *
     * @param value1
     *            被除数
     * @param value2
     *            除数
     * @param scale
     *            精确范围
     * @return 两个参数的商,默认四舍五入
     * @throws GeneralException
     */
    public static BigDecimal div(BigDecimal value1, BigDecimal value2, int scale) {
        // 如果精确范围小于0,抛出异常信息
        if (scale < 0) {
            throw new GeneralException("精确度不能小于0");
        }
        if (value2.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        return value1.divide(value2, scale, RoundingMode.HALF_UP);
    }

    /**
     * 计算多个值的乘积
     */
    public static BigDecimal muls(BigDecimal... args) {
        if (args.length < 2) {
            throw new GeneralException("参数至少为2个");
        }
        List<BigDecimal> bigDecimals = Arrays.asList(args);
        if (bigDecimals.stream().anyMatch(Objects::isNull)) {
            return BigDecimal.ZERO;
        }
        return bigDecimals.parallelStream().reduce(BigDecimal.ONE, ArithUtil::mul);
    }

    /**
     * 计算多个值的和
     */
    public static BigDecimal adds(BigDecimal... args) {
        if (args.length < 2) {
            throw new GeneralException("参数至少为2个");
        }
        List<BigDecimal> BigDecimals = Arrays.asList(args);
        if (BigDecimals.stream().anyMatch(Objects::isNull)) {
            return BigDecimal.ZERO;
        }
        return BigDecimals.parallelStream().reduce(BigDecimal.ZERO, ArithUtil::add);
    }

    /**
     * 计算多个值的和,元素为null时记为0参与运算
     */
    public static BigDecimal addsNull(BigDecimal... args) {
        if (args.length < 2) {
            throw new GeneralException("参数至少为2个");
        }
        List<BigDecimal> BigDecimals = Arrays.asList(args);
        return BigDecimals.parallelStream().map(e -> e == null ? BigDecimal.ZERO : e).reduce(BigDecimal.ZERO,
            ArithUtil::add);
    }
}

注意!!!使用BigDecimal比较两个数,值的大小时,一定要使用compareTo,不要用equals,源码显示它会检查小数位数是否相等。
在这里插入图片描述
测试两种比较方式的区别
测试

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值