package cn.util.math;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;  
import java.math.RoundingMode;
/** 
 * 精确的浮点数运算
 * 
 * JAVA中关于数字取几位有效位数，和小数点后保留几位小数的小示例。 
 * 浮点数的打印   System.out.println(new BigDecimal("10000000000").toString());
 * 普通的数字字符串  System.out.println(new BigDecimal("100.000").toString());
 * 去除末尾多余的0 System.out.println(new BigDecimal("100.000").stripTrailingZeros().toString());
 * 避免输出科学计数法  System.out.println(new BigDecimal("100.000").stripTrailingZeros().toPlainString());
 */  
public class ArithKit 
{  
	/** 默认除法运算精度,即保留小数点多少位  */
	private static final int DEF_DIV_SCALE = 10;

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

	/**
	 * 提供精确的加法运算。
	 * @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();
	}
	/**
	 * 提供（相对）精确的除法运算，当发生除不尽的情况时，精确到
	 * 小数点以后10位，以后的数字四舍五入。
	 * @param v1 被除数
	 * @param v2 除数
	 * @return 两个参数的商
	 */
	public static double div(double v1, double v2)
	{
		return div(v1, v2, DEF_DIV_SCALE);
	}

	/**
	 * 提供（相对）精确的除法运算。当发生除不尽的情况时，由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));
		if (b1.compareTo(BigDecimal.ZERO) == 0)
		{
			return BigDecimal.ZERO.doubleValue();
		}
		return b1.divide(b2, scale, RoundingMode.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");
		return b.divide(one, scale, RoundingMode.HALF_UP).doubleValue();
	}

	/**
	 * divide(除):/
	 * @param v1
	 * @param v2
	 * @return
	 */
	public static int divide(int v1,int v2)    {
		BigInteger x1 = new BigInteger(Integer.toString(v1));
		BigInteger x2 = new BigInteger(Integer.toString(v2));
		return x1.divide(x2).intValue();
	}

	public static int divide(String v1,String v2)    {
		BigInteger x1 = new BigInteger(v1);
		BigInteger x2 = new BigInteger(v2);
		return x1.divide(x2).intValue();
	}

	/**
	 * 计算Factorial阶乘！
	 * @param n   任意大于等于0的int
	 * @return     n!的值
	 */
	public static BigInteger getFactorial(int n) {
		if (n < 0) {
			System.err.println("n必须大于等于0！");
			return new BigInteger("-1");
		} else if (n == 0) {
			return new BigInteger("0");
		}
		//将数组换成字符串后构造BigInteger
		BigInteger result = new BigInteger("1");
		for (; n > 0; n--) {
			//将数字n转换成字符串后，再构造一个BigInteger对象，与现有结果做乘法
			result = result.multiply(new BigInteger(Integer.toString(n)));
		}
		return result;
	}
	/**保留几位有效数字 
	 * @param oldDouble 
	 * @param scale 
	 * @return 
	 */  
	public static double significand(double oldDouble, int scale)
	{  
		if (scale < 0) {  
			throw new IllegalArgumentException("scale指定的精度为非负值"); 
		}  
		/** 
		 * RoundingMode：舍入模式 
		 * UP：远离零方向舍入的舍入模式； 
		 * DOWN：向零方向舍入的舍入模式； 
		 * CEILING： 向正无限大方向舍入的舍入模式； 
		 * FLOOR：向负无限大方向舍入的舍入模式； 
		 * HALF_DOWN：向最接近数字方向舍入的舍入模式，如果与两个相邻数字的距离相等，则向下舍入； 
		 * HALF_UP：向最接近数字方向舍入的舍入模式，如果与两个相邻数字的距离相等，则向上舍入； 
		 * HALF_EVEN：向最接近数字方向舍入的舍入模式，如果与两个相邻数字的距离相等，则向相邻的偶数舍入;(在重复进行一系列计算时,此舍入模式可以将累加错误减到最小) 
		 * UNNECESSARY：用于断言请求的操作具有精确结果的舍入模式，因此不需要舍入。 
		 */  
		RoundingMode rMode =null;  
		//rMode=RoundingMode.FLOOR;  
		//下面这种情况，其实和FLOOR一样的。  
		if(oldDouble>0){  
			rMode=RoundingMode.DOWN;  
		}else{  
			rMode=RoundingMode.UP;  
		}  
		//此处的scale表示的是，几位有效位数  
		BigDecimal b = new BigDecimal(Double.toString(oldDouble),new MathContext(scale,rMode));  
		return b.doubleValue();  
	}  
	/**小数点之后保留几位小数(此处，我们用BigDecimal提供的（除以div）方法实现) 
	 * RoundingMode.CEILING：取右边最近的整数
	 * RoundingMode.DOWN：去掉小数部分取整，也就是正数取左边，负数取右边，相当于向原点靠近的方向取整
	 * RoundingMode.FLOOR：取左边最近的正数
	 * RoundingMode.HALF_DOWN:五舍六入，负数先取绝对值再五舍六入再负数
	 * RoundingMode.HALF_UP:四舍五入，负数原理同上
	 * RoundingMode.HALF_EVEN:这个比较绕，整数位若是奇数则四舍五入，若是偶数则五舍六入
	 * 
	 * @param oldDouble 
	 * @param scale 
	 * @return 
	 */  
	public static double decimal(double oldDouble, int scale) {  
		if (scale < 0) {  
			throw new IllegalArgumentException("The scale must be a positive integer or zero"); 
		}  
		BigDecimal b = new BigDecimal(Double.toString(oldDouble));  
		BigDecimal one = new BigDecimal("1");  
		//return b.divide(one, scale, BigDecimal.ROUND_FLOOR).doubleValue();  
		if(oldDouble>0){ //此处的scale表示的是小数点之后的精度。  
			return b.divide(one, scale, RoundingMode.DOWN).doubleValue();  
		}else{  
			return b.divide(one, scale, RoundingMode.UP).doubleValue();  
		}
	}  
}  