目录
三、BigDecimal的加、减、乘、除、绝对值
1、加法
加法对应的方法有两个:
(1)public BigDecimal add(BigDecimal augend);
方法返回的是一个BigDecimal对象,值为当前对象的值加上被加数对象augend的值(this+augend)。保留的小数位数是max(this.scale(), augend.scale())
.
(2) public BigDecimal add(BigDecimal augend,MathContext mc);
方法返回的是一个BigDecimal对象,值为当前对象的值加上被加数对象augend的值(this+augend),并根据上下文设置进行舍入。(关于上下文MathContext,前文已解释过)
加法示例如下,
-
@Test
-
public
void
testAdd
() {
-
//用double类型初始化BigDecimal对象
-
BigDecimal
numA
=
new
BigDecimal(
0.05);
-
BigDecimal
numB
=
new
BigDecimal(
0.06);
-
System.out.println(
"numA + numB = " + numA.add(numB));
-
//用double类型和int类型初始化BigDecimal对象。(作加法运算时得到的只是一个近似值)
-
BigDecimal
numC
=
new
BigDecimal(
3.05);
-
BigDecimal
numD
=
new
BigDecimal(
100);
-
System.out.println(
"numC + numD = " + numC.add(numD));
-
//用字符串类型初始化BigDecimal对象。(作加法运算时得到的是精确值)
-
BigDecimal
strA
=
new
BigDecimal(
"3.05");
-
BigDecimal
strB
=
new
BigDecimal(
"100");
-
System.out.println(
"strA + strB = " + strA.add(strB));
-
}
打印的结果如下:
numA + numB = 0.11000000000000000055511151231257827021181583404541015625
numC + numD = 103.04999999999999982236431605997495353221893310546875
strA + strB = 103.05
可以看到,使用double类型初始化BigDecimal 对象,进行加法运算时就出现了精度问题。正如前文所说,并不是所有的浮点数都能够在二进制系统中被精确的表示,自然而然的在进行加减乘除运算时就会出错。
2、减法
对应的方法有两个:
(1)public BigDecimal subtract(BigDecimal subtrahend);
方法返回的是一个BigDecimal对象,值为当前对象的值减去被减数对象subtrahend的值(this-subtrahend)。保留的小数位数是max(this.scale(), subtrahend.scale())
.。
(2)public BigDecimal subtract(BigDecimal subtrahend,MathContext mc);
方法返回的是一个BigDecimal对象,值为当前对象的值减去被减数对象subtrahend的值(this-subtrahend),并根据上下文设置进行舍入。
减法示例如下,
-
@Test
-
public
void
testSubtract
() {
-
//用double类型初始化BigDecimal对象
-
BigDecimal
numA
=
new
BigDecimal(
0.05);
-
BigDecimal
numB
=
new
BigDecimal(
0.06);
-
System.out.println(
"numA + numB = " + numA.subtract(numB));
-
//用double类型和int类型初始化BigDecimal对象。(作减法运算时得到的只是一个近似值)
-
BigDecimal
numC
=
new
BigDecimal(
100);
-
BigDecimal
numD
=
new
BigDecimal(
0.05);
-
System.out.println(
"numC + numD = " + numC.subtract(numD));
-
//用字符串类型初始化BigDecimal对象。(作减法运算时得到的是精确值)
-
BigDecimal
strA
=
new
BigDecimal(
"100");
-
BigDecimal
strB
=
new
BigDecimal(
"0.05");
-
System.out.println(
"strA + strB = " + strA.subtract(strB));
-
}
打印结果如下:
numA + numB = -0.00999999999999999500399638918679556809365749359130859375
numC + numD = 99.94999999999999999722444243843710864894092082977294921875
strA + strB = 99.95
3、乘法
对应的方法有:
(1)public BigDecimal multiply(BigDecimal multiplicand);
方法返回的是一个BigDecimal对象,值为当前对象的值乘于被乘数对象multiplicand的值(this*multiplicand)。保留的小数位数是(this.scale() + multiplicand.scale())
.。
(2)public BigDecimal multiply(BigDecimal multiplicand,MathContext mc);
方法返回的是一个BigDecimal对象,值为当前对象的值乘于被乘数对象subtrahend的值(this*multiplicand),并根据上下文设置进行舍入。
乘法示例如下,
-
@Test
-
public
void
testMultiply
() {
-
//用double类型初始化BigDecimal对象
-
BigDecimal
numA
=
new
BigDecimal(
0.05);
-
BigDecimal
numB
=
new
BigDecimal(
0.06);
-
System.out.println(
"numA + numB = " + numA.multiply(numB));
-
//用double类型和int类型初始化BigDecimal对象。(作乘法运算时得到的只是一个近似值)
-
BigDecimal
numC
=
new
BigDecimal(
100);
-
BigDecimal
numD
=
new
BigDecimal(
0.05);
-
System.out.println(
"numC + numD = " + numC.multiply(numD));
-
//用字符串类型初始化BigDecimal对象。(作乘法运算时得到的是精确值)
-
BigDecimal
strA
=
new
BigDecimal(
"100");
-
BigDecimal
strB
=
new
BigDecimal(
"0.05");
-
System.out.println(
"strA + strB = " + strA.multiply(strB));
-
}
打印结果如下,
-
numA
+ numB
=
0.00300000000000000005551115123125782085820576136538628584587058372823258067807472571075777523219585418701171875
-
numC
+ numD
=
5.00000000000000027755575615628913510590791702270507812500
-
strA
+ strB
=
5.00
4、除法
对应的方法主要有:
(1) public BigDecimal divide(BigDecimal divisor);
返回一个BigDecimal,其值为(this/divisor),首选小数位数为(this.scale()-divisor.scale());如果无法表示精确的商(因为它具有非终止的十进制扩展),则会引发算术异常。
(2) public BigDecimal divide(BigDecimal divisor,MathContext mc);
返回值为(this/divisor)的BigDecimal,并根据上下文设置进行舍入。
(3) public BigDecimal divide(BigDecimal divisor,int scale,int roundingMode);
返回一个BigDecimal,其值为(this/divisor),其小数位数与指定的相同。如果必须执行舍入才能生成具有指定比例的结果,则应该用指定的舍入模式。
(4) public BigDecimal divide(BigDecimal divisor,int scale,RoundingMode roundingMode);
返回一个BigDecimal,其值为(this/divisor),其小数位数与指定的相同(由第二个参数scale指定)。如果必须执行舍入才能生成具有指定比例的结果,则应该用指定的舍入模式(由第三个参数roundingMode指定)。
(3)(4)两个方法其实是一样的,区别只在于第三个参数的指定方式。第一个方法是int roundingMode,即传入的是一个整型数字,而第二个方法是RoundingMode roundingMode,即传入的是一个RoundingMode对象。
这里需要对RoundingMode作一下介绍。
RoundingMode
RoundingMode是一个枚举类,内部有8个枚举常量,分别是:
UP:远离0的舍入模式,在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。放到一维坐标轴上就很容易理解,就是以0为分隔点,0右侧部分一直向右舍入,0左侧部分一直向左侧舍入。例如0.333保留两位小数,采用UP舍入模式的结果为0.34,-0.333保留两位小数,采用UP舍入模式的结果为-0.34。
DOWN:和UP相反,是接近零的舍入模式。理解了UP舍入模式,就很容易理解DOWN舍入模式。例如0.333保留两位小数,采用DOWN舍入模式的结果为0.33,-0.333保留两位小数,采用DOWN舍入模式的结果为-0.33。可以发现,在采用DOWN模式进行舍入的时候,直接把需要舍弃的位数丢掉就行了。
CEILING:CEILING英文是天花板的意思,可以理解为向”大“舍入。如果 BigDecimal 为正,则舍入行为与 UP 相同,如果为负,则舍入行为与 DOWN 相同。例如0.333保留两位小数,采用CEILING舍入模式的结果为0.34(0.34 >0.333),-0.333保留两位小数,采用CEILING舍入模式的结果为-0.33(-0.33>0.333 )。
FLOOR:与CEILING相反,FLOOR有地板的意思,可以理解为向”小“舍入。如果 BigDecimal 为正,则舍入行为与 DOWN 相同,如果为负,则舍入行为与 UP 相同。例如0.333保留两位小数,采用FLOOR舍入模式的结果为0.33,-0.333保留两位小数,采用FLOOR舍入模式的结果为-0.34。
HALF_UP:向”最接近的“数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分 >= 0.5,则舍入行为与 UP 相同,否则舍入行为与 DOWN 相同。其实就是四舍五入。
HALF_DOWN:向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则为向上舍入的舍入模式。如果舍弃部分 > 0.5,则舍入行为与UP 相同,否则舍入行为与 DOWN 相同。与四舍五入的思路是一样的,只不过这里是”五舍六入“。如果扣字面意思的话,也很好理解,HALF_UP,half是一半的意思,也就是5,UP是向上的意思,就可以理解为5向上入,即四舍五入。同理,HALF_DOWN,5向下舍,即五舍六入。
HALF_EVEN:向“最接近的”数字舍入。如果舍弃部分左边的数字为奇数,则舍入行为与 UP 相同,如果为偶数,则舍入行为与 DOWN 相同。如0.135,保留两位小数,舍弃5,因为3是奇数,所以与UP相同,结果为0.14;0.125,保留两位小数,舍弃5,因为2是偶数,所以与DOWN相同,结果为0.12.
UNNECESSARY:以断言请求的操作具有精确的结果,因此不需要舍入。如果有舍入,会报java.lang.ArithmeticException异常。
关于这几个舍入模式的结果,亦可参照下面的表进行快速学习。
原数 | UP | DOWN | CEILING | FLOOR | HALF_UP | HALF_DOWN | HALF_EVEN | UNNECESSARY |
---|---|---|---|---|---|---|---|---|
5.5 | 6 | 5 | 6 | 5 | 6 | 5 | 6 | throw ArithmeticException |
2.5 | 3 | 2 | 3 | 2 | 3 | 2 | 2 | throw ArithmeticException |
1.6 | 2 | 1 | 2 | 1 | 2 | 2 | 2 | throw ArithmeticException |
1.1 | 2 | 1 | 2 | 1 | 1 | 1 | 1 | throw ArithmeticException |
1.0 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
-1.0 | -1 | -1 | -1 | -1 | -1 | -1 | -1 | -1 |
-1.1 | -2 | -1 | -1 | -2 | -1 | -1 | -1 | throw ArithmeticException |
-1.6 | -2 | -1 | -1 | -2 | -2 | -2 | -2 | throw ArithmeticException |
-2.5 | -3 | -2 | -2 | -3 | -3 | -2 | -2 | throw ArithmeticException |
-5.5 | -6 | -5 | -5 | -6 | -6 | -5 | -6 | throw ArithmeticException |
代码示例如下,
-
@Test
-
public
void
testDivide
() {
-
BigDecimal
numA
=
new
BigDecimal(
"1");
-
BigDecimal
numB
=
new
BigDecimal(
"-1");
-
BigDecimal
numC
=
new
BigDecimal(
"3");
-
// 保留两位小数,舍入模式为UP
-
System.out.println(
"1/3保留两位小数(UP) = " + numA.divide(numC,
2, RoundingMode.UP));
-
System.out.println(
"-1/3保留两位小数(UP) = " + numB.divide(numC,
2, RoundingMode.UP));
-
// 保留两位小数,舍入模式为DOWN
-
System.out.println(
"1/3保留两位小数(DOWN) = " + numA.divide(numC,
2, RoundingMode.DOWN));
-
System.out.println(
"-1/3保留两位小数(DOWN) = " + numB.divide(numC,
2, RoundingMode.DOWN));
-
// 保留两位小数,舍入模式为CEILING
-
System.out.println(
"1/3保留两位小数(CEILING) = " + numA.divide(numC,
2, RoundingMode.CEILING));
-
System.out.println(
"-1/3保留两位小数(CEILING) = " + numB.divide(numC,
2, RoundingMode.CEILING));
-
// 保留两位小数,舍入模式为FLOOR
-
System.out.println(
"1/3保留两位小数(FLOOR) = " + numA.divide(numC,
2, RoundingMode.FLOOR));
-
System.out.println(
"-1/3保留两位小数(FLOOR) = " + numB.divide(numC,
2, RoundingMode.FLOOR));
-
-
BigDecimal
numD
=
new
BigDecimal(
"1");
-
BigDecimal
numE
=
new
BigDecimal(
"-1");
-
BigDecimal
numF
=
new
BigDecimal(
"8");
-
// 保留两位小数,舍入模式为HALF_UP
-
System.out.println(
"1/8(=0.125)保留两位小数(HALF_UP) = " + numD.divide(numF,
2, RoundingMode.HALF_UP));
-
System.out.println(
"-1/8(=0.125)保留两位小数(HALF_UP) = " + numE.divide(numF,
2, RoundingMode.HALF_UP));
-
// 保留两位小数,舍入模式为HALF_DOWN
-
System.out.println(
"1/8(=0.125)保留两位小数(HALF_DOWN) = " + numD.divide(numF,
2, RoundingMode.HALF_DOWN));
-
System.out.println(
"-1/8(=0.125)保留两位小数(HALF_DOWN) = " + numE.divide(numF,
2, RoundingMode.HALF_DOWN));
-
-
// 保留两位小数,舍入模式为HALF_EVEN
-
System.out.println(
"0.54/4(=0.135)保留两位小数(HALF_EVEN) = " +
new
BigDecimal(
"0.54").divide(
new
BigDecimal(
"4"),
2, RoundingMode.HALF_EVEN));
-
System.out.println(
"1/8(=0.125)保留两位小数(HALF_EVEN) = " + numE.divide(numF,
2, RoundingMode.HALF_EVEN));
-
-
//UNNECESSARY,会报异常
-
System.out.println(
"1/8(=0.125) = " + numE.divide(numF, RoundingMode.UNNECESSARY));
-
}
打印结果:
1/3保留两位小数(UP) = 0.34
-1/3保留两位小数(UP) = -0.34
1/3保留两位小数(DOWN) = 0.33
-1/3保留两位小数(DOWN) = -0.33
1/3保留两位小数(CEILING) = 0.34
-1/3保留两位小数(CEILING) = -0.33
1/3保留两位小数(FLOOR) = 0.33
-1/3保留两位小数(FLOOR) = -0.34
1/8(=0.125)保留两位小数(HALF_UP) = 0.13
-1/8(=0.125)保留两位小数(HALF_UP) = -0.13
1/8(=0.125)保留两位小数(HALF_DOWN) = 0.12
-1/8(=0.125)保留两位小数(HALF_DOWN) = -0.12
0.54/4(=0.135)保留两位小数(HALF_EVEN) = 0.14
1/8(=0.125)保留两位小数(HALF_EVEN) = -0.12
java.lang.ArithmeticException: Rounding necessary
5、绝对值
public BigDecimal abs();
返回一个BigDecimal,其值为该BigDecimal的绝对值,其小数位数为this.scale()。
public BigDecimal abs(MathContext mc);
返回一个BigDecimal,其值是此BigDecimal的绝对值,并根据上下文设置进行舍入。
代码示例,
-
@Test
-
public
void
testAbs
() {
-
BigDecimal
a
=
new
BigDecimal(
"1.234");
-
BigDecimal
b
=
new
BigDecimal(
"-1.234");
-
System.out.println(
"1.234的绝对值:" + a.abs());
-
System.out.println(
"-1.234的绝对值:" + b.abs());
-
System.out.println(
"-1.234的绝对值,保留两位有效数字:" + b.abs(
new
MathContext(
2)));
-
}
打印结果为:
1.234的绝对值:1.234
-1.234的绝对值:1.234
-1.234的绝对值,保留两位有效数字:1.2