javabigdecimal百亿_JavaBigDecimal详解
1.引⾔
float和double类型的主要设计⽬标是为了科学计算和⼯程计算。他们执⾏⼆进制浮点运算,这是为了在⼴域数值范围上提供较为精确的快速近似计算⽽精⼼设计的。然⽽,它们没有提供完全精确的结果,所以不应该被⽤于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候BigDecimal就派上⼤⽤场啦。
先看下⾯代码
public static voidmain(String[] args) {
System.out.println(0.2 + 0.1);
System.out.println(0.3 - 0.1);
System.out.println(0.2 * 0.1);
System.out.println(0.3 / 0.1);
}
运⾏结果如下
你认为你看错了,但结果却是是这样的。问题在哪⾥呢?原因在于我们的计算机是⼆进制的。浮点数没有办法是⽤⼆进制进⾏精确表⽰。我们的CPU表⽰浮点数由两个部分组成:指数和尾数,这样的表⽰⽅法⼀般都会失去⼀定的精确度,有些浮点数运算也会产⽣⼀定的误差。如:2.4的⼆进制表⽰并⾮就是精确的2.4。反⽽最为接近的⼆进制表⽰是 2.3999999999999999。浮点数的值实际上是由⼀个特定的数学公式计算得到的。
其实java的float只能⽤来进⾏科学计算或⼯程计算,在⼤多数的商业计算中,⼀般采⽤java.math.BigDecimal类来进⾏精确计算。
2.BigDecimal构造⽅法
public BigDecimal(double val) 将double表⽰形式转换为BigDecimal *不建议使⽤
public BigDecimal(int val) 将int表⽰形式转换成BigDecimal
public BigDecimal(String val) 将String表⽰形式转换成BigDecimal
为什么不建议采⽤第⼀种构造⽅法呢?来看例⼦
public static voidmain(String[] args) {
BigDecimal bigDecimal= new BigDecimal(2);
BigDecimal bDouble= new BigDecimal(2.3);
BigDecimal bString= new BigDecimal("2.3");
System.out.println("bigDecimal=" +bigDecimal);
System.out.println("bDouble=" +bDouble);
System.out.println("bString=" +bString);
}
运⾏结果如下
为什么会出现这种情况呢?
JDK的描述:
1、参数类型为double的构造⽅法的结果有⼀定的不可预知性。有⼈可能认为在Java中写⼊newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(⾮标度值 1,其标度为 1),但是它实际上等于
0.1000000000000000055511151231257827021181583404541015625。这是因为0.1⽆法准确地表⽰为 double(或者说对于该情况,不能表⽰为任何有限长度的⼆进制⼩数)。这样,传⼊到构造⽅法的值不会正好等于 0.1(虽然表⾯上等于该值)。
2、另⼀⽅⾯,String 构造⽅法是完全可预知的:写⼊ newBigDecimal("0.1") 将创建⼀个 BigDecimal,它正好等于预期的 0.1。因此,⽐较⽽⾔,通常建议优先使⽤String构造⽅法。
当double必须⽤作BigDecimal的源时,请使⽤String(double)转成String,然后使⽤String构
造⽅法,或使⽤BigDecimal的静态⽅法valueOf,如下
public static voidmain(String[] args) {
BigDecimal bDouble1= BigDecimal.valueOf(2.3);
BigDecimal bDouble2= new String(2.3));
System.out.println("bDouble1=" +bDouble1);
System.out.println("bDouble2=" +bDouble2);
}
3.BigDecimal加减乘除运算
对于常⽤的加,减,乘,除,BigDecimal类提供了相应的成员⽅法。
public BigDecimal add(BigDecimal value); //加法
public BigDecimal subtract(BigDecimal value); //减法
public BigDecimal multiply(BigDecimal value); //乘法
public BigDecimal divide(BigDecimal value); //除法
⼤概的⽤法如下
public static voidmain(String[] args) {
BigDecimal a= new BigDecimal("4.5");
BigDecimal b= new BigDecimal("1.5");
System.out.println("a + b =" +a.add(b));
System.out.println("a - b =" +a.subtract(b));
System.out.println("a * b =" +a.multiply(b));
System.out.println("a / b =" +a.divide(b));
}
运⾏结果
这⾥有⼀点需要注意的是除法运算divide.
BigDecimal除法可能出现不能整除的情况,⽐如 4.5/1.3,这时会报错java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
其实divide⽅法有可以传三个参数
public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
第⼀参数表⽰除数, ⼆个参数表⽰⼩数点后保留位数,第三个参数表⽰舍⼊模式。
只有在作除法运算或四舍五⼊时才⽤到舍⼊模式,有下⾯这⼏种:
ROUND_UP :向远离零的⽅向舍⼊。舍弃⾮零部分,并将⾮零舍弃部分相邻的⼀位数字加⼀。
ROUND_DOWN :向接近零的⽅向舍⼊。舍弃⾮零部分,同时不会⾮零舍弃部分相邻的⼀位数字加⼀,采取截取⾏为。
ROUND_CEILING :向正⽆穷的⽅向舍⼊。如果为正数,舍⼊结果同ROUND_UP⼀致;如果为负数,舍⼊结果同ROUND_DOWN⼀致。注意:此模式不会减少数值⼤⼩。
ROUND_FLOOR :向负⽆穷的⽅向舍⼊。如果为正数,舍⼊结果同ROUND_DOWN⼀致;如果为负数,舍⼊结果同ROUND_UP⼀致。注意:此模式不会增加数值⼤⼩。
ROUND_HALF_UP :向“最接近”的数字舍⼊,如果与两个相邻数字的距离相等,则为向上舍⼊的舍⼊模式。如果舍弃部分>= 0.5,则舍⼊⾏为与ROUND_UP相同;否则舍⼊⾏为与ROUND_DOWN相同。这种模式也就是我们常说的我们的“四舍五⼊”。
ROUND_HALF_DOWN :向“最接近”的数字舍⼊,如果与两个相邻数字的距离相等,则为向下舍⼊的舍⼊模式。如果舍弃部分> 0.5,则舍⼊⾏为与ROUND_UP相同;否则舍⼊⾏为与ROUND_DOWN相同。这种模式也就是我们常说的我们的“五舍六⼊”。
ROUND_HALF_EVEN :向“最接近”的数字舍⼊,如果与两个相邻数字的距离相等,则相邻的偶数舍⼊。如果舍弃部分左边的数字奇数,则舍⼊⾏为与 ROUND_HALF_UP 相同;如果为偶数,则舍⼊⾏
为与 ROUND_HALF_DOWN 相同。注意:在重复进⾏⼀系列计算时,此舍⼊模式可以将累加错误减到最⼩。此舍⼊模式也称为“银⾏家舍⼊法”,主要在美国使⽤。四舍六⼊,五分两种情况,如果前⼀位为奇数,则⼊位,否则舍去。
ROUND_UNNECESSARY :断⾔请求的操作具有精确的结果,因此不需要舍⼊。如果对获得精确结果的操作指定此舍⼊模式,则抛出ArithmeticException。
按照各⾃的需要,可传⼊合适的第三个参数。四舍五⼊采⽤ ROUND_HALF_UP
需要对BigDecimal进⾏截断和四舍五⼊可⽤setScale⽅法,例:
public static voidmain(String[] args) {
BigDecimal a= new BigDecimal("4.5635");
a= a.setScale(3, RoundingMode.HALF_UP); //保留3位⼩数,且四舍五⼊
System.out.println(a);
}
减乘除其实最终都返回的是⼀个新的BigDecimal对象,因为BigInteger与BigDecimal都是不可变的(immutable)的,在进⾏每⼀步运算时,都会产⽣⼀个新的对象
public static voidmain(String[] args) {
BigDecimal a= new BigDecimal("4.5");
BigDecimal b= new BigDecimal("1.5");
a.add(b);
System.out.println(a);//输出4.5. 加减乘除⽅法会返回⼀个新的BigDecimal对象,原来的a不变
}
4.BigDecimal⽐较⼤⼩
public static voidmain(String[] args) {
BigDecimal a= new BigDecimal(101);
bigdecimal除法保留小数
BigDecimal b= new BigDecimal(111);//使⽤compareTo⽅法⽐较//注意:a、b均不能为null,否则会报空指针if (apareTo(b) == -1) {
System.out.println("a⼩于b");
}if (apareTo(b) == 0) {
System.out.println("a等于b");
}if (apareTo(b) == 1) {
System.out.println("a⼤于b");
}if (apareTo(b) > -1) {
System.out.println("a⼤于等于b");
}if (apareTo(b) < 1) {
System.out.println("a⼩于等于b");
}
}
5.BigDecimal转String
public static voidmain(String[] args) {//浮点数的打印
System.out.println(new BigDecimal("10000000000").toString());//普通的数字字符串
System.out.println(new BigDecimal("100.000").toString());//去除末尾多余的0
System.out.println(new BigDecimal("100.000").stripTrailingZeros().toString());//避免输出科学计数法System.out.println(new BigDecimal("100.000").stripTrailingZeros().toPlainString());
}
打印结果
⽤toString()⽅法输出的就是普通的数字字符串。
stripTrailingZeros()函数就是⽤于去除末尾多余的0的,
⽤toPlainString()函数代替toString(),避免输出科学计数法的字符串。
6.总结
商业计算使⽤BigDecimal。
尽量使⽤参数类型为String的构造函数。
BigDecimal都是不可变的(immutable)的,在进⾏每⼀步运算时,都会产⽣⼀个新的对象,所以在做加减乘除运算时千万要保存操作后的值。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论