java中new bigdecimal_Java BigDecimal使用详细说明(最细!最全!)

目录

一、为什么使用BigDecimal

二、BigDecimal的构造方法

三、BigDecimal中加减乘除运算

四、BigDecimal比较大小

五、BigDecimal始终返回新的对象

一、为什么使用BigDecimal

先来看一段代码:

public static void main(String[] args) {

System.out.println(10-9.8);

}

运行结果:

e2ca07e7207339582d553be58b456262.png

期望返回的结果是0.2,但实际返回却是0.1999999999999993。

在计算机中浮点数有可能是不准确的,它只能无限接近准确值,不能完全精确。为什么会这样?因为浮点数的存储规则,使用“乘2取正,顺序排列1”法将0.2十进制小数转换成二进制小数,会发现0.2在二进制中无法精确的表示,也就是说在二进制的世界中,0.2是一个无限循环的小数,所以连展示都无法精确展示,更别说在内存中存储了。

在项目中(特别是金融类项目),计算单位一般都是小数点后的4位小数,但是在汇总、展示、报表中只记录小数点后的2位小数,在大批量的加减乘除后得到的结果会有很大的差距(精度损失),所以这里就可以使用BigDecimal

BigDecimal是专门为弥补浮点数无法精确计算的缺憾而设计的类,并且它本身也提供了加减乘除的常用数学算法。特别是与数据库Decimal类型的字段映射时,BigDecimal是最优的解决方案。

乘2取正,顺序排列法:

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 // -> 0.2 二进制 0.00110011...0011

二、BigDecimal的构造方法

BigDecimal类常用的几个构造方法

public BigDecimal(int val)   将int表示形式转换成BigDecimal

public BigDecimal(double val) 将double表示形式转换为BigDecimal //不建议使用

public BigDecimal(String val)   将String表示形式转换成BigDecimal //推荐使用

public BigDecimal(long) 将long表示形式转换成BigDecimal

为什么不推荐使用Double构造BigDecial类?

BigDecimal bigDecimal = new BigDecimal(2);

BigDecimal bDouble = new BigDecimal(0.2);

BigDecimal bString = new BigDecimal("0.2");

System.out.println("bigDecimal=" + bigDecimal);

System.out.println("bDouble=" + bDouble);

System.out.println("bString=" + bString);

运行结果:

cc359b17f99f77777fcd66a6a0dd2d7b.png

从结果可以看到由Double构造得到的BigDecimal对象结果有一定的不可预知性,传入到构造方法的值不会正好等于 0.2(虽然表面上等于该值),所以不建议使用Double构造BigDecimal

再来看String 构造方法是完全可预知的,传入“0.2”其结果正好是预期的0.2,所以推荐使用String构造BigDecimal

如果在不得已必须将double作为入参进行BigDecimal进行构造时,可以先将double转成String,再使用BigDecimal(String val)进行构造:

BigDecimal bigDecimal1 = BigDecimal.valueOf(0.2);

BigDecimal bigDecimal2 = new BigDecimal(Double.toString(2.3));

BigDecimal bigDecimal3 = new BigDecimal(Convert.toStr(2.3));

System.out.println("bigDecimal1:"+bigDecimal1);

System.out.println("bigDecimal2:"+bigDecimal2);

System.out.println("bigDecimal3:"+bigDecimal3);

运行结果:

c5dfbddc97a94b63d9d2f89bd4beb29d.png

三、BigDecimal中加减乘除运算

BigDecimal中提供了常用的加减乘除运算:

public BigDecimal add(BigDecimal augend); //加法

public BigDecimal subtract(BigDecimal subtrahend); //减法

public BigDecimal multiply(BigDecimal multiplicand); //乘法

public BigDecimal divide(BigDecimal divisor); //除法

代码演示:

BigDecimal a = new BigDecimal("1");

BigDecimal b = new BigDecimal("2");

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));

运行结果:

94feba3b819c18c08f4df8f4f9967cf9.png

当divide做除法运算时,如果除数结果是无限循环小数,就会抛出异常java.lang.ArithmeticException:

f6168ee1bcee309bb1c4802638411184.png

解决办法,可以设置返回保留小数位数以及取舍模式:

BigDecimal a = new BigDecimal("2");

BigDecimal b = new BigDecimal("3");

BigDecimal c = new BigDecimal("0.857345");

BigDecimal d = new BigDecimal("0.2343253");

BigDecimal multiply = c.multiply(d);

BigDecimal bigDecimal1 = a.divide(b,2, BigDecimal.ROUND_HALF_UP);

System.out.println("设置精度后 -> a / b = " + bigDecimal1);

System.out.println("设置精度前 -> c * d = " + multiply);

System.out.println("设置精度后 -> c * d = " + multiply.setScale(2, BigDecimal.ROUND_HALF_UP));

运行结果:

62d71a5fde15e9b0b735e1329393011b.png

取舍模式介绍:

名称

作用

备注

ROUND_UP

(舍入远离零的舍入模式)

在丢弃非零部分之前始终增加数字(始终对非零舍弃部分前面的数字加1)。

注意,此舍入模式始终不会减少计算值的大小。

ROUND_DOWN

(接近零的舍入模式)

在丢弃某部分之前始终不增加数字(从不对舍弃部分前面的数字加1,即截短)。

注意,此舍入模式始终不会增加计算值的大小。

ROUND_CEILING

(接近正无穷大的舍入模式)

如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同;

如果为负,则舍入行为与 ROUND_DOWN 相同。

注意,此舍入模式始终不会减少计算值。

ROUND_FLOOR

(接近负无穷大的舍入模式)

如果 BigDecimal 为正,则舍入行为与 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

(银行家舍入法)2

向“最接近的”数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入。

如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同;

如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同。

注意,在重复进行一系列计算时,此舍入模式可以将累加错误减到最小。

ROUND_UNNECESSARY

断言请求的操作具有精确的结果,因此不需要舍入。

如果对获得精确结果的操作指定此舍入模式,则抛出ArithmeticException。

-

代码演示:

BigDecimal a = new BigDecimal("0.31414926");

System.out.println("ROUND_UP -> "+a.setScale(5,BigDecimal.ROUND_UP));

System.out.println("ROUND_DOWN -> "+a.setScale(5,BigDecimal.ROUND_DOWN));

System.out.println("ROUND_CEILING -> "+a.setScale(5,BigDecimal.ROUND_CEILING));

System.out.println("ROUND_FLOOR -> "+a.setScale(5,BigDecimal.ROUND_FLOOR));

System.out.println("ROUND_HALF_UP -> "+a.setScale(5,BigDecimal.ROUND_HALF_UP));

System.out.println("ROUND_HALF_DOWN -> "+a.setScale(5,BigDecimal.ROUND_HALF_DOWN));

System.out.println("ROUND_HALF_EVEN -> "+a.setScale(5,BigDecimal.ROUND_HALF_EVEN));

System.out.println("ROUND_UNNECESSARY -> "+a.setScale(5,BigDecimal.ROUND_UNNECESSARY));

运行结果:

aa7b54a3fac463ae2891c875c83bbf6e.png

四、BigDecimal比较大小

代码演示:

BigDecimal a = new BigDecimal("2");

BigDecimal b = new BigDecimal("3");

System.out.println(a.compareTo(b));

//判断BigDecimal对象是否等于0

BigDecimal c = new BigDecimal("0");

System.out.println(c.compareTo(BigDecimal.ZERO));

运行结果:

0d0c3a2586398cad2cc808963db72dd2.png

返回值说明:

返回值

说明

0

表示 a = b

1

表示 a > b

-1

表示 a < b

使用时注意BigDecimal对象不能为null,否则会报空指针异常;

五、BigDecimal始终返回新的对象

代码演示:

BigDecimal a = new BigDecimal("2");

BigDecimal b = new BigDecimal("3");

a.compareTo(b);

System.out.println(a);

运行结果:

7ab608b5bb35872355b5677f79f44f3f.png

a加b后,a值的仍然是2,相加后的结果是一个新的BigDecimal对象,BigDecimal在进行运算时都会产生一个新的对象,所以BigDecimal在运算操作后,一定要保存操作后的值。

参考博客:

https://www.cnblogs.com/Jason-Xiang/p/10220231.html

小数位乘2,取整数部分,剩下的小数部分继续乘以2,一直取到小数部分全都为零为止。 ↩︎

四舍六入五考虑,五后非零就进一,五后为零看奇偶,五前为偶应舍去,五前为奇要进一。举例说明,取2位精度:round(10.5551) = 10.56 round(10.555) = 10.56 round(10.545) = 10.54 ↩︎

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值