目录
2、BigDecimal(int val, MathContext mc)
4、BigDecimal(long val, MathContext mc)
6、BigDecimal(BigInteger unscaledVal, int scale)
7、BigDecimal(BigInteger unscaledVal, int scale, MathContext mc)
8、BigDecimal(BigInteger val, MathContext mc)
10、BigDecimal(double val, MathContext mc)
12、BigDecimal(char[] in, int offset, int len)
13、BigDecimal(char[] in, int offset, int len, MathContext mc)
14、BigDecimal(char[] in, MathContext mc)
16、BigDecimal(String val, MathContext mc)
一、BigDecimal概述
浮点数值不适用于无法接受舍入误差的计算当中,比如金融计算。
例如,System.out.println(2.0-1.1)打印的结果为0.8999999999999999,而不是人们想象当中的0.9,这是为什么呢?因为System.out.println()中的数字默认是double类型的,而浮点数值采用二进制系统表示,而在二进制系统中无法精确地表示小数0.1,因此产生了这种舍入误差。
就像在十进制当中,无法精确地表示分数1/3一样。
既然浮点数都不能精确的表示出来,那么通过浮点数进行的加减乘除运算自然都不是精确的值。
如果在数值计算中需要精确的结果,不允许有这种舍入误差,那么就需要使用BigDecima类。
BigDecima是java.math包中的一个类,这个类可以处理任意长度数字序列的数值,实现了任意精度的浮点数运算。类似BigDecima,还有一个Biglnteger,实现了任意精度的整数运算。
BigDecimal所创建的是对象,故我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。
二、构造函数详解
1、BigDecimal(int val)
将 int转换成 BigDecimal对象
。例如:
BigDecimal a = new BigDecimal(8);
这是通过构造函数的方式,将一个整型值转换成BigDecimal 对象,还可以通过静态的valueOf方法将普通的数值转换成BigDecimal。例如:
BigDecimal b = BigDecimal.valueOf(100);将long型数值转换成(小数位数为零的)BigDecimal。
BigDecimal c = BigDecimal.valueOf(3.14);将double型数值转换成BigDecimal。
2、BigDecimal(int val, MathContext mc)
将 int转换成 BigDecimal ,根据上下文设置进行舍入。
这里需要先讲述一下MathContext。
MathContext
MathContext是一个不可变对象,用于封装上下文设置,这些设置描述数字运算符的某些规则。它有三个构造函数:
(1)MathContext(int setPrecision)
构造具有指定精度的MathContext对象,默认的是HALF_UP舍入模式(关于舍入模式,本文后面会进行详细讲解)。
(2)MathContext(int setPrecision, RoundingMode setRoundingMode)
使用指定的精度和舍入模式构造新的MathContext对象。
(3)MathContext(String val)
使用字符串构造一个MathContext对象。
使用示例如下,
MathContext mathContext = new MathContext(4);
BigDecimal d = new BigDecimal(12345, mathContext);
System.out.println(d);
打印的结果为:1.235E+4
分析上述代码。第一行代码创建了一个MathContext 对象,指定的精度为4,也就是4位有效数字。第二行代码利用整型的“12345”和mathContext创建了一个BigDecimal对象,将12345保留4位有效数字转换成BigDecimal。12345保留四位有效数字的结果也就是1.235E+4(1.235乘于10的四次方)。
3、BigDecimal(long val)
将 long转换成 BigDecimal 。
4、BigDecimal(long val, MathContext mc)
将 long转换为 BigDecimal ,根据上下文设置进行舍入。
5、BigDecimal(BigInteger val)
将 BigInteger转换成 BigDecimal 。
6、BigDecimal(BigInteger unscaledVal, int scale)
将BigInteger的 BigInteger值和 int等级转换为 BigDecimal 。
7、BigDecimal(BigInteger unscaledVal, int scale, MathContext mc)
将 BigInteger未缩放值,根据scale和mc ,转换为 BigDecimal 。
8、BigDecimal(BigInteger val, MathContext mc)
根据上下文设置将 BigInteger转换为 BigDecimal,根据上下文设置进行舍入。
9、BigDecimal(double val)
将 double转换为 BigDecimal ,这是 double的二进制浮点值的精确十进制表示。
10、BigDecimal(double val, MathContext mc)
将 double转换为 BigDecimal ,根据上下文设置进行舍入。
11、BigDecimal(char[] in)
将字符数组转换成 BigDecimal 。
12、BigDecimal(char[] in, int offset, int len)
将字符数组转换成 BigDecimal,同时允许一个子阵列被指定。
13、BigDecimal(char[] in, int offset, int len, MathContext mc)
将字符数组转换成 BigDecimal,同时允许指定一个子阵列和用根据上下文设置进行舍入。
14、BigDecimal(char[] in, MathContext mc)
将字符数组转换成 BigDecimal ,根据上下文设置进行舍入。
15、BigDecimal(String val)
常用!!!将字符串表示的数字转换为 BigDecimal 。
16、BigDecimal(String val, MathContext mc)
常用!!!将字符串表示的数字转换为 BigDecimal ,根据上下文设置进行舍入。
可以看到,BigDecimal的构造函数很多,根据自己的需要,大家选择合适的构造函数创建对应的BigDecimal对象就行了。但是有一点需要注意,就是以数值型参数创建BigDecimal对象的时候,会产生精度问题。
例如,
System.out.println(new BigDecimal(0.1));
System.out.println(new BigDecimal("0.1"));
打印结果为:
0.1000000000000000055511151231257827021181583404541015625
0.1
可以看到,通过double类型的0.1创建BigDecimal对象的时候,实际打印的值并不是精确的0.1,通过String类型创建的BigDecimal对象,实际打印的值是0.1。这是为什么呢?
因为参数类型为double的构造方法的结果有一定的不可预知性。不熟悉BigDecimal的人可能会认为在Java中通过newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1,但是它实际上等于0.1000000000000000055511151231257827021181583404541015625。因为0.1无法用二进制精确地表示出来,本来开头亦对此进行了解释。这样,传入到构造方法的值并不是我们看到的0.1,实际上传的是0.1000000000000000055511151231257827021181583404541015625,因此产生了上述的打印结果。
String 构造方法是完全可预知的:通过 newBigDecimal(“0.1”) 将创建一个 BigDecimal对象,它正好等于预期的 0.1。因此, 通常建议优先使用String构造方法。
三、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
今天的文章详解BigDecimal及其加减乘除运算分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/8265.html