package com.vstsoft.common.util;

import java.math.BigDecimal;
import java.text.DecimalFormat;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Comments : 由于Java的简单类型不能够精确的对浮点数进行运算, 这个工具类提供精确的浮点数运算,包括加减乘除和四舍五入。
 */
public class ArithUtil {
	// 默认除法运算精度
	private static final int DEFAULT_DIV_SCALE = 10;
	private final static Logger logger = LoggerFactory.getLogger(ArithUtil.class);

	/**
	 * 提供精确的加法运算。
	 * 
	 * @param v1
	 * @param v2
	 * @return 两个参数的和
	 */
	public static BigDecimal add(BigDecimal b1, BigDecimal b2) {
		return b1.add(b2);
	}

	public static BigDecimal add(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return add(b1, b2);
	}

	/**
	 * 提供精确的减法运算。
	 * 
	 * @param v1
	 * @param v2
	 * @return 两个参数的差
	 */

	public static BigDecimal subtract(BigDecimal b1, BigDecimal b2) {
		return b1.subtract(b2);
	}

	public static BigDecimal subtract(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.subtract(b2);
	}

	/**
	 * 提供精确的乘法运算。
	 * 
	 * @param v1
	 * @param v2
	 * @return 两个参数的积
	 */

	public static BigDecimal multiply(BigDecimal b1, BigDecimal b2) {
		return b1.multiply(b2);
	}

	public static BigDecimal multiply(double v1, double v2) {
		BigDecimal b1 = new BigDecimal(Double.toString(v1));
		BigDecimal b2 = new BigDecimal(Double.toString(v2));
		return b1.multiply(b2);
	}

	/**
	 * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入,舍入模式采用ROUND_HALF_EVEN
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @return 两个参数的商
	 */

	public static BigDecimal divide(BigDecimal b1, BigDecimal b2) {
		return divide(b1, b2, DEFAULT_DIV_SCALE);
	}

	public static BigDecimal divide(double v1, double v2) {
		return divide(v1, v2, DEFAULT_DIV_SCALE);
	}

	/**
	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用ROUND_HALF_EVEN
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @param scale
	 *            表示需要精确到小数点以后几位。
	 * @return 两个参数的商
	 */

	public static BigDecimal divide(BigDecimal b1, BigDecimal b2, int scale) {
		return divide(b1, b2, scale, BigDecimal.ROUND_HALF_EVEN);
	}

	public static BigDecimal divide(double v1, double v2, int scale) {
		return divide(v1, v2, scale, BigDecimal.ROUND_HALF_EVEN);
	}

	/**
	 * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。舍入模式采用用户指定舍入模式
	 * 
	 * @param v1
	 *            被除数
	 * @param v2
	 *            除数
	 * @param scale
	 *            表示需要精确到小数点以后几位
	 * @param round_mode
	 *            表示用户指定的舍入模式
	 * @return 两个参数的商
	 */

	public static BigDecimal divide(BigDecimal b1, BigDecimal b2, int scale, int round_mode) {
		if (scale < 0) {
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		return b1.divide(b2, scale, round_mode);
	}

	public static BigDecimal divide(double v1, double v2, int scale, int round_mode) {
		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));
		return b1.divide(b2, scale, round_mode);
	}

	/**
	 * 提供保留两位小数的四舍五入处理,舍入模式采用ROUND_HALF_EVEN
	 * 
	 * @param v
	 *            需要四舍五入的数字
	 * @param scale
	 *            小数点后保留几位
	 * @return 四舍五入后的结果
	 */

	public static BigDecimal round(BigDecimal b) {
		return round(b, 2);
	}

	/**
	 * 提供精确的小数位四舍五入处理,舍入模式采用ROUND_HALF_EVEN
	 * 
	 * @param v
	 *            需要四舍五入的数字
	 * @param scale
	 *            小数点后保留几位
	 * @return 四舍五入后的结果
	 */

	public static BigDecimal round(BigDecimal b, int scale) {
		return round(b, scale, BigDecimal.ROUND_HALF_EVEN);
	}

	/**
	 * 提供精确的小数位四舍五入处理
	 * 
	 * @param v
	 *            需要四舍五入的数字
	 * @param scale
	 *            小数点后保留几位
	 * @param round_mode
	 *            指定的舍入模式
	 * @return 四舍五入后的结果
	 */

	public static BigDecimal round(BigDecimal b, int scale, int round_mode) {
		if (scale < 0) {
			logger.error(StaticUtil.UTILERROR, "scale:" + scale + ",the scale must be a positive integer or zero");
			throw new IllegalArgumentException("The scale must be a positive integer or zero");
		}
		return b.setScale(scale, round_mode);
	}

	public static void main(String[] args) {
		BigDecimal b1 = new BigDecimal("100");
		BigDecimal b2 = new BigDecimal("1.765");
		BigDecimal b3 = new BigDecimal("1.65");
		BigDecimal b4 = new BigDecimal("7");

		// 精确除法运算
		logger.info(divide(b1, b4).toString());
		logger.info(divide(b1, b4, 1).toString());

		logger.info(round(b2, 2, BigDecimal.ROUND_HALF_EVEN).toString());
		logger.info(round(b2, 2, BigDecimal.ROUND_HALF_UP).toString());
		logger.info(round(b2, 2, BigDecimal.ROUND_HALF_DOWN).toString());

		logger.info(round(b3, 1, BigDecimal.ROUND_HALF_EVEN).toString());
		logger.info(round(b3, 1, BigDecimal.ROUND_HALF_UP).toString());
		logger.info(round(b3, 1, BigDecimal.ROUND_HALF_DOWN).toString());

		// 数学运算在实际中存在的问题
		/**
		 * 0.060000000000000005 0.5800000000000001 401.49999999999994
		 * 1.2329999999999999
		 */
		logger.info("错误的结果:");
		logger.info(0.05 + 0.01 + "");
		logger.info(1.0 - 0.42 + "");
		logger.info(4.015 * 100 + "");
		logger.info(123.3 / 100 + "");
		logger.info("精确计算的结果:");
		logger.info(ArithUtil.add(new BigDecimal("0.056789123446"), new BigDecimal("0.01234566789")).toString());
		logger.info(ArithUtil.subtract(new BigDecimal("1.012345678912"), new BigDecimal("0.42345689127")).toString());
		logger.info(ArithUtil.multiply(new BigDecimal("4.0156789"), new BigDecimal("100.12345")).toString());
		logger.info(ArithUtil.divide(new BigDecimal("123.3"), new BigDecimal("100")).toString());
		/**
		 * 输入结果为 504.549999999999982946974341757595539093017578125 实际结果应为 504.55
		 */
		logger.info("BigDecimal 的不精确计算问题:");
		BigDecimal bg1 = new BigDecimal(100.91);
		BigDecimal bg2 = new BigDecimal(5);
		BigDecimal bg3 = bg1.multiply(bg2);
		logger.info(bg3 + "");
		DecimalFormat df = new DecimalFormat("¥0,000.00");
		logger.info("精确计算的结果:");

		BigDecimal bg4 = multiply(new BigDecimal("1000000000.91"), new BigDecimal("5"));
		logger.info(ArithUtil.round(bg4).toString());
		BigDecimal bg5 = multiply(new BigDecimal("10000000"), new BigDecimal("9999999"));
		logger.info(ArithUtil.round(bg5).toString());
		logger.info("***************************************************");

	}
}
最近下载更多
无名者  LV2 2022年5月7日
luohaipeng  LV23 2019年12月3日
低调人  LV38 2019年8月4日
2428902929  LV1 2019年6月11日
DawnWalker  LV19 2018年12月14日
银风落幻  LV2 2018年1月7日
865229936  LV6 2017年7月18日
cityhunter  LV5 2017年2月10日
司马文若  LV1 2016年12月4日
wq1234567pw  LV2 2016年6月26日
最近浏览更多
无名者  LV2 2022年5月7日
2196316269  LV10 2021年2月24日
小小123123  LV1 2020年11月17日
jachyn  LV6 2020年9月9日
kinggode  LV14 2020年7月28日
飞翔的天空 2020年6月28日
暂无贡献等级
低调人  LV38 2019年8月4日
2428902929  LV1 2019年6月11日
DawnWalker  LV19 2018年12月14日
xp9522  LV9 2018年8月13日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友