Java中的浮点数-科学计数法-加减乘除

  上次,提到“元转分”这个浮点数问题,boss倾向于手动把1.23元这种格式,转换成123分。
  
  但实际上,浮点数很容易遇到精度问题。
  
  比如,System.out.println(4.015 * 1000);结果就不会是4015。
  
  以前,总结的元转分的问题,没能考虑到所有的场景,今天补充点上次遗漏的。
  
  -5.09,如果金额是负数,应该是-500-9=-509,而不是-500+9=-441,这是上次的一个超级bug。
  
  另外,需要还有一个超级bug,“1045189788”转换成double类型的元时,结果变成了1.04。
  
  debug了好久,才发现以前代码的漏洞。
  
  1045189788这种比较大的浮点数,在传递过程中,是“科学计数法”表示的,类似“1.04E”,
  所以,最后转换出了问题。
  
  其实,我一直不建议boss采用这种人工截取计算的方式,需要考虑的场景太复杂,建议使用JDK内置的BigDecimal。
  
  经过实践,发现很不错。
  
  可以用double、string等多种原始类型,构造BigDecimal,再进行四则运算。
  
  使用BigDecimal的关键是,控制“标度”即“精度”,scale。
  
  算术运算结果的首选标度  运算 结果的首选标度 
加 max(addend.scale(), augend.scale()) 
减 max(minuend.scale(), subtrahend.scale()) 
乘 multiplier.scale() + multiplicand.scale() 
除 dividend.scale() - divisor.scale() 


  下面这个工具类,是从网上copy的,还是非常有价值的。
  
  import java.math.BigDecimal;


/**
 * 消除加减乘除的精度,解决Float与Double类型进度不准确的问题.
 */
public class DoubleUtil {
/**
* 加法运算

* @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();
}


/**
* 除法运算

* @param v1
*            被除数
* @param v2
*            除数
* @return 商
*/
public static double div(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2).doubleValue();
}


/**
* 除法运算

* @param v1
*            被除数
* @param v2
*            除数
* @return 商和余数
*/
public static BigDecimal[] divideAndRemainder(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
BigDecimal[] arr = b1.divideAndRemainder(b2);
return arr;
}


/**
* 求商(向下舍入)

* @param v1
* @param v2
* @return
*/
public static BigDecimal divideToIntegralValue(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
System.out.println("------------");
return b1.divideToIntegralValue(b2);
}


public static void main(String[] args) {
testUnPrecision();
System.out.println("----------使用BigDecimal消除精度影响------------ "
+ DoubleUtil.add(0.05, 0.01));
System.out.println(DoubleUtil.sub(1.0, 0.54));
System.out.println(DoubleUtil.mul(4.015, 1000));
System.out.println(DoubleUtil.div(12.3, 10));
// 得到商和余数
BigDecimal[] arr = DoubleUtil.divideAndRemainder(12.3, 10);
System.out.println("得到商和余数");
for (BigDecimal bigDecimal : arr) {
System.out.println(bigDecimal);
}
System.out.println(DoubleUtil.divideToIntegralValue(4.5, 2));
}


/**
* 不准确问题示例
*/
private static void testUnPrecision() {
System.out.println("--------Java自身的Double类型有精度损失----------");
System.out.println(0.05 + 0.01);
System.out.println(1.0 - 0.54);
System.out.println(4.015 * 1000);
System.out.println(12.3 / 100);
}
}

原文地址:https://www.cnblogs.com/qitian1/p/6462860.html