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
远离零方向舍入的舍入模式。