1、概述
Java在java.math包中提供的API类BigDecimal,用来对超过16位有效位的数进行精确的运算。float和double只能用来做科学计算或者是工程计算,在商业计算中往往要求结果精确,此时就要用到java.math.BigDecimal。BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。
2、简介
BigDecimal 由任意精度的整数非标度值 和32 位的整数标度 (scale) 组成。如果为零或正数,则标度是小数点后的位数。如果为负数,则将该数的非标度值乘以 10 的负scale 次幂,即 BigDecimal表示的数值是(unscaledValue ×
10−scale
)。
3、方法
① BigDecimal一共有4个常用构造方法
BigDecimal(int) 创建一个具有参数所指定整数值的对象。
BigDecimal(double) 创建一个具有参数所指定双精度值的对象。
BigDecimal(long) 创建一个具有参数所指定长整数值的对象。
BigDecimal(String) 创建一个具有参数所指定以字符串表示的数值的对象。
注:
参数类型为double的构造方法的结果有一定的不可预知性
如:newBigDecimal(0.1)所创建的BigDecimal实际上等于0.1000000000000000055511151231257827021181583404541015625。
如果要进行精确转换:
1) Double.toString(double)–>BigDecimal(String)构造方法
2) BigDecimal.valueOf(double val)
② BigDecimal 的运算方式 不支持 + - * / 这类的运算 它有自己的运算方法
BigDecimal add(BigDecimal augend) 加法运算
BigDecimal subtract(BigDecimal subtrahend) 减法运算
BigDecimal multiply(BigDecimal multiplicand) 乘法运算
BigDecimal divide(BigDecimal divisor) 除法运算
BigDecimal 加减乘除运算并返回double类型数据范例
public class Arith {
/**
* 提供精确加法计算的add方法
* @param value1 被加数
* @param value2 加数
* @return 两个参数的和
*/
public static double add(double value1,double value2){
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确减法运算的sub方法
* @param value1 被减数
* @param value2 减数
* @return 两个参数的差
*/
public static double sub(double value1,double value2){
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确乘法运算的mul方法
* @param value1 被乘数
* @param value2 乘数
* @return 两个参数的积
*/
public static double mul(double value1,double value2){
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供精确的除法运算方法div
* @param value1 被除数
* @param value2 除数
* @param scale 精确范围
* @return 两个参数的商
* @throws IllegalAccessException
*/
public static double div(double value1,double value2,int scale) throws IllegalAccessException{
//如果精确范围小于0,抛出异常信息
if(scale<0){
throw new IllegalAccessException("精确度不能小于0");
}
BigDecimal b1 = new BigDecimal(Double.valueOf(value1));
BigDecimal b2 = new BigDecimal(Double.valueOf(value2));
return b1.divide(b2, scale).doubleValue();
}
}
③ doubleValue()
将BigDecimal 类型转换为double类型
同理可得:
floatValue(): 将BigDecimal 类型转换为float类型
longValue(): 将BigDecimal类型转换为long类型
intValue(): 将BigDecimal 类型转换为int类型
注:
以上方法在向小精度的类型转换时均存在精度丢失问题
intValue() 将丢弃此 BigDecimal 的所有小数部分,并且如果生成的 ” BigInteger” 太大而不适合用 int 表示,则仅返回 32 位低位字节。注意,此转换会丢失关于此 BigDecimal 值的总大小和精度的信息,并返回带有相反符号的结果。
④ setScale(int newScale, int roundingMode)
返回一个保留指定小数位的BigDecimal类型的值
- newScale 保留BigDecimal的位数
roundingMode 有以下几种处理模式:
1.setScale(1,BigDecimal.ROUND_UP):
进位处理,2.35变成2.4 。舍入远离零的舍入模式。在丢弃非零部分之前始终增加数字2.setScale(1,BigDecimal.ROUND_DOWN):
直接删除多余的小数位,如2.35会变成2.3 。接近零的舍入模式。3.setScale(1,BigDecimal.ROUND_CEILING)
接近正无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;如果为负,则舍入行为与 ROUND_DOWN 相同。接近负无穷大的舍入模式。如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同;如果为负,则舍入行为与 ROUND_UP 相同4.setScale(1,BigDecimal.ROUND_HALF_UP):
向上四舍五入,2.35变成2.45.setScale(1,BigDecimal.ROUND_HALF_DOWN):
向下四舍五入,2.35变成2.36.setScaler(1,BigDecimal.ROUND_HALF_EVEN)
向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。7.setScaler(1,BigDecimal.ROUND_UNNECESSARY)
断言请求的操作具有精确的结果,因此不需要舍入。如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。
BigDecimal b1 = new BigDecimal("23.4567");
System.out.println(b1.setScale(2,BigDecimal.ROUND_HALF_UP)); // 23.46
System.out.println(b1.setScale(0,BigDecimal.ROUND_HALF_DOWN)); // 23
⑤ BigDecimal 的比较
- max(BigDecimal val) : 返回此 BigDecimal 和 val 的最大值。
min(BigDecimal val) : 返回此 BigDecimal 和 val 的最小值。
BigDecimal b1 = new BigDecimal("23.7");
BigDecimal b2 = new BigDecimal("28.7");
System.out.println(b1.max(b2)); // 28.7
System.out.println(b1.min(b2)); // 23.7
- equals(Object x)
比较此 BigDecimal 与指定的 Object 的相等性。
BigDecimal b1 = new BigDecimal("23");
BigDecimal b2 = new BigDecimal("23.0");
BigDecimal b3 = new BigDecimal("23.000");
System.out.println(b1.equals(b2)); // false
System.out.println(b1.equals(b3)); // false
很明显equals无法很好的比较两个BigDecimal,所以要用到compareTo()方法
- compareTo(BigDecimal val)
将此 BigDecimal 与指定的 BigDecimal 比较。
当此 BigDecimal 在数字上小于、等于或大于 val 时,返回 -1、0 或 1。
BigDecimal b1 = new BigDecimal("23");
BigDecimal b2 = new BigDecimal("23.0");
BigDecimal b3 = new BigDecimal("23.000");
System.out.println(b1.compareTo(b2)); // true
System.out.println(b1.compareTo(b3)); // true
一般我们都是在if语句中使用,如:
if(b1.compareTo(b2) == -1) { // b1<b2情况
}
if(b1.compareTo(b2) == 0) { // b1=b2情况
}
if(b1.compareTo(b2) == 1) { // b1>b2情况
}
⑥ 常量
数字常量:
public static final BigDecimal ZERO
public static final BigDecimal ONE
public static final BigDecimal TEN
roundingMode 模式常量:
public final static int ROUND_UP = 0;
public final static int ROUND_DOWN = 1;
public final static int ROUND_CEILING = 2;
public final static int ROUND_FLOOR = 3;
public final static int ROUND_HALF_UP = 4;
public final static int ROUND_HALF_DOWN = 5;
public final static int ROUND_HALF_EVEN = 6;
public final static int ROUND_UNNECESSARY = 7;
更多方法参见 java API手册 java.math–>BigDecimal
4、总结
(1)商业计算使用BigDecimal。
(2)尽量使用参数类型为String的构造函数。
(3) BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。
(4)我们往往容易忽略JDK底层的一些实现细节,导致出现错误,需要多加注意。
参考:
http://blog.csdn.net/jackiehff/article/details/8582449
http://www.cnblogs.com/chenssy/archive/2012/09/09/2677279.html
另外可参考:http://zhangyinhu8680.iteye.com/blog/1536397