BigDecimal实现精确计算

1、计算遇到的问题

如果你在java里面运行一下一段代码,你发现会出现意想不到的事情:

public class Test001 {
	public static void main(String[] args) {
		double d1 = 100.0234;
		double d2 = 12.0652;
		System.out.println(d1 - d2);

	}
}

输出的结果可能是这样的:

87.95819999999999


这个结果绝对不是我们所需要的结果!如果你想要得到正确的结果,那么必须用到java.math.BigDecimal里面的方法。

                 //还比如遇到如下计算问题
			double a = 25.9;
			int b = 10;
			// ===15.899999999999999
			System.out.println("===" + (a - b));

			double a2 = 25.9;
			double b2 = 10;
			// ===15.899999999999999
			System.out.println("===" + (a2 - b2));

			double a5 = 14.9;
			double b5 = 8.94;
			// ===5.960000000000001
			System.out.println("===" + (a5 - b5));


2、解决计算问题,用BigDecimal的工具类


下面的这个工具类可以实现小数的精确计算,包括加减乘除。

import java.math.BigDecimal;

/**
 * @Description: 工程计算,精确计算,由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精
 *               确的浮点数运算,包括加减乘除和四舍五入。
 * 
 * @date: 2019年8月23日 下午4:20:31
 */
public class EngineeringComputingUtil {

	// 除法运算精度
	public static final int DEF_DIV_SCALE0 = 0;
	public static final int DEF_DIV_SCALE1 = 1;
	public static final int DEF_DIV_SCALE2 = 2;
	public static final int DEF_DIV_SCALE3 = 3;
	public static final int DEF_DIV_SCALE4 = 4;
	public static final int DEF_DIV_SCALE5 = 5;

	public static final int DEF_DIV_SCALE6 = 6;
	public static final int DEF_DIV_SCALE7 = 7;
	public static final int DEF_DIV_SCALE8 = 8;
	public static final int DEF_DIV_SCALE9 = 9;
	public static final int DEF_DIV_SCALE10 = 10;

	// 这个类不能实例化
	private EngineeringComputingUtil() {
	}

	/**
	 * 提供精确的加法运算。
	 * 
	 * @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();
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后0位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div0(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE0);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后1位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div1(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE1);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后2位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div2(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE2);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后3位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div3(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE3);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后4位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div4(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE4);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后5位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div5(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE5);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后6位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div6(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE6);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后7位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div7(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE7);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后8位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div8(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE8);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后9位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div9(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE9);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */
	public static double div10(double v1, double v2) {
		return div(v1, v2, DEF_DIV_SCALE10);
	}

	/**
	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @param scale
	 *            表示表示需要精确到小数点以后几位。
	 * @return 两个参数的商
	 */
	public static double div(double v1, double v2, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException(
					"The scale must be a positive integer or zero");
		}
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		// BigDecimal.ROUND_HALF_UP,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。

		// 如有必要可以换成 BigDecimal.ROUND_HALF_EVEN 银行家舍入法 ,
		// 在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
		// 即四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
		return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	/**
	 * 提供精确的小数位四舍五入处理。
	 * 
	 * @param v
	 *            需要四舍五入的数字
	 * @param scale
	 *            小数点后保留几位
	 * @return 四舍五入后的结果
	 */
	public static double round(double v, int scale) {
		if (scale < 0) {
			throw new IllegalArgumentException(
					"The scale must be a positive integer or zero");
		}
		BigDecimal b = new BigDecimal(Double.toString(v));
		BigDecimal one = new BigDecimal("1");
		// BigDecimal.ROUND_HALF_UP,这是我们大多数人在小学时就学过的舍入模式(四舍五入)。

		// 如有必要可以换成 BigDecimal.ROUND_HALF_EVEN 银行家舍入法 ,
		// 在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。
		// 即四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。
		return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
	}

	public static void main(String[] args) {
		double a = 25.9;
		double b = 10;
		// Java的简单类型不能够精确的对浮点数进行运算 减 错误 ===15.899999999999999
		System.out.println("===" + (a - b));

		// 精确计算 减 正确 ===15.9
		System.out.println("===" + sub(a, b));

		// 保留三位小数的除法 ===0.333
		System.out.println("===" + div3(1.0D, 3.0D));

	}
}

在《Effective Java》这本书中提到,float和double只能用来做科学计算或者是工程计算。如果需要精确计算,例如在商业计算中,非得用String来够造java.math.BigDecimal不可!



BigDecimal枚举常量用法摘要  :
 

CEILING   
          向正无限大方向舍入的舍入模式。 
DOWN   
          向零方向舍入的舍入模式。 
FLOOR   
          向负无限大方向舍入的舍入模式。 
HALF_DOWN   
          向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向下舍入。 
HALF_EVEN   
          向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。 
HALF_UP   
          向最接近数字方向舍入的舍入模式,如果与两个相邻数字的距离相等,则向上舍入。 
UNNECESSARY   
          用于断言请求的操作具有精确结果的舍入模式,因此不需要舍入。 
UP   
          远离零方向舍入的舍入模式。

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值