BigDecimal的介绍及使⽤
⼀、BigDec im a l 的介绍
BigDecimal是Java在java.math包中提供的API类,⽤来对超过16位有效位的数进⾏精确的运算。虽然double类型的变量也可以处理16位有效数,但是在实际应⽤中,也会出现对更⼤或者更⼩的数进⾏运算和处理的。float和double只能⽤来做科学计算或者是⼯程计算。
BigDecimal创建的是对象,不能使⽤传统的+、-、*、/等算数运算符来对其进⾏数学运算,⽽是应该采⽤该类下⾯的对应的⽅法,⽅法中的参数也必须是BigDecimal对象。
⼀般情况下,对于那些不需要准确精度的数字,我们可以直接使⽤Float和Double处理,但是Double.valueOf(Stirng)和
Float.valueOf(String)会丢失精度,所以开发中,如果我们需要精确计算的结果,则必须使⽤BigDecimal类来操作。
⼆、BigDec im a l的构造⽅法
1、常⽤构造函数
BigDecimal(int):创建⼀个具有参数所指定整数值的对象;
BigDecimal(double):创建⼀个具有参数所指定双精度值的对象;
BigDecimal(long):创建⼀个具有参数所指定的长整数值的对象;
BigDecimal(String):创建⼀个具有参数所指定以字符串表⽰的数值的对象。
2、使⽤注意:
当使⽤double类型创建BigDecimal对象的时候,可能会出现精度误差,不建议使⽤。如果想要创建Double类型的,建议使⽤new String(bigDecimal))的形式。
代码详解:
public static void main(String[] args) {
double dou = 0.1;
BigDecimal big1 = new BigDecimal(dou);
System.out.println("big1 = " + big1);
BigDecimal big2 = new String(dou));
System.out.println("big2 = " + big2);
}
结果:
big1 = 0.1000000000000000055511151231257827021181583404541015625
big2 = 0.1
图⽚粘不上来,就将结果直接粘到上⾯了,有兴趣的同学⾃⼰去IDE⾥运⾏⼀下,看结果是否⼀致。
原因:
参数类型为double的构造⽅法的结果具有⼀定的不可预知性。就像上⾯的程序表⽰的那样,虽然new BigDecimal(0.1)所创建的Bigdecimal是0.1,但是实际上得到的值确是不同的,因为0.1⽆法准确地表⽰为double,所以传⼊构造⽅法的值也就出现了误差。
⽽String 类型的构造⽅法是完全可以预知到的,参数给的是“0.1”,得到的结果就是0.1.所以使⽤该类型
的构造⽅法更能符合⾃⼰预期中的值。
当double必须⽤作BigDecimal的源时,可以使⽤BigDecimal.valueOf(double)的⽅法来进⾏,因为该⽅法的底层,也是将double值转换为String类型,在进⾏运算的。
public static BigDecimal valueOf(double val) {
// Reminder: a zero double returns '0.0', so we cannot fastpath
// to use the constant ZERO. This might be important enough to
// justify a factory approach, a cache, or a few private
// constants, later.
return new String(val));
}
// ps:不知道今天怎么了,图⽚都贴不上来,只能将源码粘上来了。
// 这⾥可以看到,valueOf的底层还是 BigDecimal(String)实现的。
3、为什么使⽤double类型会出现精度误差
其实我们平时使⽤的语⾔⼤多数都有精度不准确的情况,不是Java独有的。
出现这种情况的原因就要从我们计算机的数据表现形式说起了,计算机底层所有数据的表现形式都为⼆进制形式,⼀般情况下,⼆进制转为⼗进制所使⽤的⽅法是按权相加法,⼗进制转⼆进制是除2取余的逆序排列法。
⼆进制到⼗进制:
10010: 0 * 2^0 + 1 * 2^1 + 0 * 2^2 + 0 * 2^3 + 1 * 2^4 = 18
⼗进制到⼆进制:
18 / 2 =9。 余0
9 / 2 = 4。 余1
4 / 2 = 2。 余0
2 / 2 = 1。 余0
1 /
2 = 0。 余1
得出的结果就是10010
进制数之间的转换相信⼤家已经很熟了,如果忘记的,看这个算法⼤概都能回想起来了吧,这⾥就不做详细解释了
上⾯是整数转换的⽅法,在⼩数部分,使⽤乘2取整数位,顺序排列。⼆进制⼩数到⼗进制⼩数还是按权相加法。
⼆进制到⼗进制:
10.01 = 1 * 2^-2 + 0 * 2^-1 + 0 * 2^0 + 1 * 2^1 = 2.25
⼗进制到⼆进制(⼩数部分):
0.25
0.25 * 2 = 0.5 0
0.5 * 2 = 1 (1)
结果等于0.01
那是进制之间的转换问题,那为什么浮点运算会有不准确的情况呢?下⾯进⼊正题,以2.1举例
// 整数部分
2 / 2 = 1 0
1 / 2 = 0 (1)
// ⼩数部门bigdecimal格式化两位小数
0.1 * 2 = 0.2 0
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 (1)
0.6 * 2 = 1.2 (1)
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 (1)
0.6 * 2 = 1.2 (1)
0.2 * 2 = 0.4 0
0.4 * 2 = 0.8 0
0.8 * 2 = 1.6 (1)
0.6 * 2 = 1.2 (1)
............
这就是⽆限循环,结果为 , 我们的计算机在存储⼩数时肯定是有长度限制的,
所以会进⾏截取部分⼩数进⾏存储,从⽽导致计算机存储的数值只能是个⼤概的值,⽽不是精确的值。从这⾥看出来我们的计算机根本就⽆法使⽤⼆进制来精确的表⽰ 2.1 这个⼗进制数字的值,连表⽰都⽆法精确表⽰出来,计算肯定是会出现问题的。
三、BigDec im a l常⽤⽅法详解
常⽤⽅法:
1、常⽤⽅法:
add(BigDecimal):BigDecimal对象中的值相加,返回BigDecimal对象
subtract(BigDecimal):BigDecimal对象中的值相减,返回BigDecimal对象
multiply(BigDecimal):BigDecimal对象中的值相乘,返回BigDecimal对象
divide(BigDecimal):BigDecimal对象中的值相除,返回BigDecimal对象
abs():将BigDecimal对象中的值转换成绝对值
doubleValue():将BigDecimal对象中的值转换成双精度数
floatValue():将BigDecimal对象中的值转换成单精度数
longValue():将BigDecimal对象中的值转换成长整数
intValue():将BigDecimal对象中的值转换成整数
toString():将BigDecimal对象中的值转换成字符串
2、⼤⼩⽐较
java中对BigDecimal⽐较⼤⼩⼀般⽤的是BigDemical的compareTo⽅法.
举例:new BigDemica("0.1")pareTo(new BigDemical(0.2))
关于这些⽅法的使⽤,⼤家可以去IDE中执⾏⼀下,⾮常好上⼿,这⾥不做详细展⽰了。
四、BigDec im a l的⼋种舍⼊模式
BigDecimal.setScale()⽅法⽤于格式化⼩数点
setScale(1)表⽰保留⼀位⼩数,默认⽤四舍五⼊⽅式
setScale(1,BigDecimal.ROUND_DOWN)直接删除多余的⼩数位,如2.35会变成2.3
setScale(1,BigDecimal.ROUND_UP)进位处理,2.35变成2.4
setScale(1,BigDecimal.ROUND_HALF_UP)四舍五⼊,2.35变成2.4
setScaler(1,BigDecimal.ROUND_HALF_DOWN)四舍五⼊,2.35变成2.3,如果是5则向下舍
setScaler(1,BigDecimal.ROUND_CEILING)接近正⽆穷⼤的舍⼊
setScaler(1,BigDecimal.ROUND_FLOOR)接近负⽆穷⼤的舍⼊,数字>0和ROUND_UP作⽤⼀样,数字<0和ROUND_DOWN作⽤⼀样
setScaler(1,BigDecimal.ROUND_HALF_EVEN)向最接近的数字舍⼊,如果与两个相邻数字的距离相等,则向相邻的偶数舍⼊。
1、ROUND_UP,向远离0的⽅向舍⼊,始终对⾮零舍弃部位前⾯的数字+1,该⽅式就是只增不减。
2、ROUND_DOWN,向0⽅向舍⼊,在丢弃某部分之前,始终不增加数据(即,截断),该⽅式是只减不加。
3、ROUND_CEILING,向正⽆穷⽅向舍⼊,如果数值为正,舍⼊⽅式与ROUND_UP⼀致,如果为负,舍⼊⽅式与ROUND_DOWN⼀致,该模式始终不会减少计算数值。
4、ROUND_FLOOR,向负⽆穷⽅向舍⼊,如果数值为正,舍⼊⾏为与 ROUND_DOWN 相同;如果为负,则舍⼊⾏为与 ROUND_UP 相同。该模式始终不会增加计算数值。
5、ROUND_HALF_UP,向“最接近的”数字摄⼊,也就是四舍五⼊。
6、ROUND_HALF_DOWN,向“最接近的”数字舍⼊,如果与两个相邻数字的距离相等,则为上舍⼊的舍⼊模式,也就是五舍六⼊。
7、ROUND_HALF_EVEN,向“最接近的”数字舍⼊,如果与两个相邻数字的距离相等,则向相邻的偶数舍⼊。如果舍弃部分左边的数字为奇数,则舍⼊⾏为与 ROUND_HALF_UP 相同;如果为偶数,则舍⼊⾏为与 ROUND_HALF_DOWN 相同。(向(距离)最近的⼀边舍⼊,除⾮两边(的距离)是相等,如果是这样,如果保留位数是奇数,使⽤四舍五⼊,如果是偶数,使⽤五舍六⼊),此模式也被称为“银⾏家舍⼊法”,主要在美国使⽤。
eg. 1.15->1.2, 1.25->1.2
8、ROUND_UNNECESSARY,计算结果是精确的,不需要舍⼊模式。如果对获得精确结果的操作指
定此舍⼊模式,则抛出ArithmeticException。
五、总结
BigDecimal的性能⽐double和float都差,在处理庞⼤、复杂的运算时尤为明显,所以⼀般精度的计算没必要使⽤BigDecimal,在需要精确的⼩数计算时再使⽤。
创建BigDecimal对象时,尽量使⽤参数类型为String的构造函数。
BigDecimal都是不可变的,在进⾏每⼀次四则运算时,都会产⽣⼀个新对象,所以在做加减乘除运算时要保存⼀下操作后的值哦。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论