文章目录
1、为什么要用BigDecimal ?
工作中我们通过浮点数进行运算时,好像时不时的会出现一些小误差。例如:
public static void main(String[] args) {
System.out.println(1.9 - 1.2);
System.out.println(1.9 - 1.5);
System.out.println(100 - 99.8);
}
大致搜了一下原因,网上说我们的计算机是二进制的,而浮点数是没有办法通过二进制精准的表示出来。
也就导致在运算的时候,float类型和double类型很容易丢失精度。
所以在开发中,如果我们需要精确计算的结果,可以使用java.math包中提供的BigDecimal类来进行操作。
2、BigDecimal初始化赋值
方法 | 类型 | 描述 |
---|---|---|
public BigDecimal(int val) | 构造函数 | int类型的值生成BigDecimal对象 |
public BigDecimal(long val) | 构造函数 | long类型的值生成BigDecimal对象 |
public BigDecimal(String val) | 静态方法 | String类型的值转换为BigDecimal类型 |
public static BigDecimal valueOf(double val) | 静态方法 | double类型的值转换为BigDecimal类型 |
public static BigDecimal valueOf(long val) | 静态方法 | long类型(包含int类型)的值转换为BigDecimal类型 |
- 代码示例:
BigDecimal b = new BigDecimal("33");
BigDecimal c = BigDecimal.valueOf(4.7);
- 注意:不建议使用public BigDecimal(double val)方式初始化值,编码时idea提示禁止使用构造方法BigDecimal(double),描述如下:
使用了new BigDecimal(double)构造函数 less... (Ctrl+F1)
Inspection info:
禁止使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象 说明:反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。
Negative example(不建议使用):
BigDecimal good1 = new BigDecimal(0.1);
Positive example(建议使用):
BigDecimal good1 = new BigDecimal("0.1");
BigDecimal good2 = BigDecimal.valueOf(0.1);
3、BigDecimal的加减乘除运算
运算法则 | 对应方法 |
---|---|
加法 | public BigDecimal add(BigDecimal value) |
减法 | public BigDecimal subtract(BigDecimal value) |
乘法 | public BigDecimal multiply(BigDecimal value) |
除法 | public BigDecimal divide(BigDecimal value) |
- 代码示例:
public static void main(String[] args) {
System.out.println("计算加法: " + BigDecimal.valueOf(1.9).add(BigDecimal.valueOf(0.2)));
System.out.println("计算减法: " + BigDecimal.valueOf(1.9).subtract(BigDecimal.valueOf(1.5)));
System.out.println("计算乘法: " + BigDecimal.valueOf(1.9).multiply(BigDecimal.valueOf(0.2)));
System.out.println("计算除法: " + BigDecimal.valueOf(1.9).divide(BigDecimal.valueOf(0.2)));
}
- 注意1:BigDecimal的运算结果都是返回了一个新的BigDecimal对象,并不是在原有的对象上进行操作。
public static void main(String[] args) {
BigDecimal a = BigDecimal.valueOf(5);
System.out.println("a的地址:" + System.identityHashCode(a));
a = a.add(BigDecimal.valueOf(3));
System.out.println("计算后a的地址:" + System.identityHashCode(a));
}
- 注意2:使用divide除法函数除不尽,出现无线循环小数的时候,就需要使用另外精确的小数位数以及舍入模式,不然会出现报错。例如:
public static void main(String[] args) {
BigDecimal a = BigDecimal.valueOf(10), b = BigDecimal.valueOf(3);
System.out.println(a.divide(b));
}
// 该程序运行会出现以下错误
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide(BigDecimal.java:1690)
at com.fivesix._05_bigdecimal.Demo01.main(Demo01.java:31)
解决方法如下(此处舍入模式使用四舍五入的方式,其他模式在该文章后面有讲解):
public static void main(String[] args) {
BigDecimal a = BigDecimal.valueOf(10), b = BigDecimal.valueOf(3);
System.out.println(a.divide(b, 3, BigDecimal.ROUND_HALF_UP));
}
// 该程序运行后输出:
3.33
4、BigDecimal比较大小
public int compareTo(BigDecimal val)
BigDecimal类提供的比较值的方法,注意比较的两个值均不能为空。
a.compareTo(b)得到结果 1, 0, -1。
比较结果 | 描述 |
---|---|
1 | a 大于b |
0 | a 等于b |
-1 | a 小于b |
- 代码示例:
public static void main(String[] args) {
BigDecimal a = BigDecimal.valueOf(1);
BigDecimal b = BigDecimal.valueOf(2);
BigDecimal c = BigDecimal.valueOf(1);
BigDecimal d = BigDecimal.ZERO;
System.out.println("1和2比较结果:" + a.compareTo(b));
System.out.println("1和1比较结果:" + a.compareTo(c));
System.out.println("1和0比较判断:" + (a.compareTo(d) > 0) );
}
5、BigDecimal保留两位小数及舍入模式
public BigDecimal setScale(int newScale, int roundingMode)
用于格式化小数的方法,第一个值表示保留几位小数,第二个值表示格式化的类型。
8种类型如下:
格式化类型 | 描述 |
---|---|
ROUND_DOWN | 舍弃多余位数,如1.55会格式化为1.5,-1.55会格式化为-1.5 |
ROUND_UP | 进位处理,如1.52会格式化为1.6,-1.52会格式化为-1.6 |
ROUND_HALF_UP | 四舍五入,如果舍弃部分>= .5,则进位 |
ROUND_HALF_DOWN | 五舍六入,如果舍弃部分> .5,则进位 |
ROUND_CEILING | 正无穷大方向舍入模式。如果值为正数,则与ROUND_UP模式相同;如果值为负数,则与ROUND_DOWN模式相同 |
ROUND_FLOOR | 负无穷大方向舍入模式。如果值为正数,则与ROUND_DOWN模式相同;如果值为负数,则与ROUND_UP模式相同 |
ROUND_UNNECESSARY | 确认值的小数位数是否与传入第一个参数(保留小数的位数)相等,如果符合则返回值,如果不符抛出异常 |
ROUND_HALF_EVEN | 如果舍弃部门左边的数字为奇数,则与ROUND_HALF_UP模式相同,如果为偶数则与ROUND_HALF_DOWN模式相同 |
- 代码示例:
public static void main(String[] args) {
BigDecimal a = BigDecimal.valueOf(5.445);
System.out.println("5.445舍弃多余位数:" + a.setScale(2, BigDecimal.ROUND_DOWN));
System.out.println("5.445进位处理:" + a.setScale(2, BigDecimal.ROUND_UP));
System.out.println("5.445四舍五入(舍弃部分>= .5,进位):" + a.setScale(2, BigDecimal.ROUND_HALF_UP));
System.out.println("5.445四舍五入(舍弃部分未> .5,舍弃):" + a.setScale(2, BigDecimal.ROUND_HALF_DOWN));
System.out.println("5.446四舍五入(舍弃部分> .5,进位):" + BigDecimal.valueOf(5.446).setScale(2, BigDecimal.ROUND_HALF_DOWN));
}
6、BigDecimal其他方法及常量
代码 | 类型 | 描述 |
---|---|---|
BigDecimal.ZERO | 常量 | 初始化一个为0的BigDecimal对象 |
BigDecimal.ONE | 常量 | 初始化一个为1的BigDecimal对象 |
BigDecimal.TEN | 常量 | 初始化一个为10的BigDecimal对象 |
public BigDecimal abs() | 方法 | 求绝对值,不管正数还是负数,都得到正数 |
public BigDecimal negate() | 方法 | 求相反数,正变负,负变正 |
public BigDecimal pow(int n) | 方法 | 求乘方,如BigDecimal.valueOf(2).pow(3)的值为8 |
public BigDecimal max(BigDecimal val) | 方法 | 两值比较,返回最大值 |
public BigDecimal min(BigDecimal val) | 方法 | 两值比较,返回最小值 |