BigDecimal的使用方法和注意事项

BigDecimal的使用方法和注意事项


做超市管理系统纯后端用的JDBC然后数据库里面的金额字段是BigDecimal的

做的时候遇到了好多坑,总结下来希望和我一样的萌新看到少走弯路

float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,原因在于我们的计算机是二进制的。浮点数没有办法是用二进制进行精确表示。我们的CPU表示浮点数由两个部分组成:指数和尾数,这样的表示方法一般都会失去一定的精确度,有些浮点数运算也会产生一定的误差。如:2.4的二进制表示并非就是精确的2.4。反而最为接近的二进制表示是 2.3999999999999999。浮点数的值实际上是由一个特定的数学公式计算得到的。它们没有提供完全精确的结果,所以不应该被用于要求精确结果的场合。但是,商业计算往往要求结果精确,这时候BigDecimal就派上大用场啦。

探讨一:java已经有基本的数据类型float和double了为什么还要用DigDecimal呢接下来我们看一个案例

        System.out.println(0.1 + 0.2);     //0.30000000000000004
        System.out.println(0.3 - 0.1);     //0.19999999999999998
        System.out.println(0.1 * 0.2);	   //0.020000000000000004
        System.out.println(0.3 / 0.2);     //1.4999999999999998
--------------------------------------------------------------------------------
--------------------------------------对比---------------------------------------
--------------------------------------------------------------------------------
		BigDecimal big1 = new BigDecimal("0.3");    
        System.out.println(big1);
        BigDecimal big2 = new BigDecimal("0.2");
        System.out.println(big1.divide(big2));       //1.5

看一下0.3/0.2的结果通过基本类型计算得出来的是1.4999999999999998一个近似值而BigDecimal是一个准确的数值所以在进行金钱计算的时候一定要用准确是数字BigDcimal的作用就体现出来了

探讨二:在小数操作中,我们通常希望能有多种自由的定义方式。例如在不同的场景可能需要返回: 0.3, 0.4, 0.334等不同精度,在不同的精度进位时希望能自主控制。这个时候BigDecimal也可以帮忙

 情况一   double a = 3;
        double b = 10;
        double c = b / a;		  //3.3333333333333335
        System.out.println(c);   //大家都知道3/10的结果是一个无线循环小数,怎么擦能拿到结果0.334呢
--------------------------------------------------------------------------------
 解决方法  BigDecimal big1 = new BigDecimal("10");
         BigDecimal big2 = new BigDecimal("3");
         BigDecimal result = big1.divide(big2,3,BigDecimal.ROUND_UP);//制定精度
         System.out.println(result);
--------------------------------------------------------------------------------    
 情况二  BigDecimal a = new BigDecimal(5.4);
        BigDecimal b = new BigDecimal(3.1);
        BigDecimal divide = a.divide(b);
        System.out.println("divide:" + divide);  // 出现异常:
//`ava.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
//这个错误是因为没有指定精度导致的,我们只要指定了结果的精度,就可以避免这个问题。
--------------------------------------------------------------------------------
解决方法  BigDecimal d = new BigDecimal(5.4);
        BigDecimal f = new BigDecimal(3.1);
        BigDecimal divide = d.divide(f,2,BigDecimal.ROUND_UP);//制定精度
        System.out.println("divide:" + divide);				  //1.74

我们额外传入第二个参数:保留的小数,指定了结果的精度,就可以避免出现这种问题。

所以我们日常用BigDecimal做除法运算的时候,务必写成推荐的形式。避免出现了异常。

1.构造方法
构造描述
BigDecimal(double val)double转换为 BigDecimal ,这是 double的二进制浮点值的精确十进制表示
BigDecimal(int val)intBigDecimal
BigDecimal(String val)将BigDecimal的字符串表示 BigDecimal转换为 BigDecimal
 		BigDecimal bigdouble = new BigDecimal(5.4);    //5.4000000000000003552713678800500929355621337890625
        BigDecimal bigInt = new BigDecimal(3);		   //3
        BigDecimal bigString = new BigDecimal("3.2");  //3.2

不知道大家有没有看出什么端倪通过参数是double构造方法生成的bigdouble的数值我们不太认识。为什么会出现这种状况呢?

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的源时,请使用Double.toString(double)转成String,然后使用BigDecimal的参数为String类型的构造方法,或使用BigDecimal的静态方法valueOf,如下

        BigDecimal bDouble1 = BigDecimal.valueOf(5.4);                //2.3
        BigDecimal bDouble2 = new BigDecimal(Double.toString(5.4));   //2.3    

2.加减乘除(绝对值)
	    BigDecimal big1 = new BigDecimal("5");
        BigDecimal big2 = new BigDecimal("40");
        BigDecimal big3 = new BigDecimal("-20");
        //加法
        BigDecimal add = big1.add(big2);
        //减法
        BigDecimal subtract = big1.subtract(big2);
        //乘法
        BigDecimal multiply = big1.multiply(big2);
        //除法
        BigDecimal divide = big1.divide(big2);
        //绝对值
        BigDecimal abs  = big3.abs();

        System.out.println("  add    :" + add);             //45
        System.out.println("subtract :" + subtract);		//-35      
        System.out.println("multiply :" + multiply);		//200
        System.out.println(" divide  :" + divide);			//0.125
        System.out.println("  abs    :"+abs);  				//20
3.注意事项

1)System.out.println()中的数字默认是double类型的,double类型小数计算不精准。

2)使用BigDecimal类构造方法传入double类型时,计算的结果也是不精确的!

  1. BigDecimal都是不可变的(immutable)的,在进行每一步运算时,都会产生一个新的对象,所以在做加减乘除运算时千万要保存操作后的值。

4)使用除法函数在divide的时候要设置各种参数,要精确的小数位数和舍入模式,不然会出现报错

我们可以看到divide函数配置的参数如下

    • divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
    • 返回一个 BigDecimal ,其值为 (this / divisor) ,其比例为指定。
即为 (BigDecimal divisor 除数, int scale 精确小数位,  int roundingMode 舍入模式)

可以看到舍入模式有很多种BigDecimal.ROUND_XXXX_XXX, 具体都是什么意思呢

模式描述
CEILING正无穷大方向取整
FLOOR负无穷大方向取整
DOWN向 0 的方向取整
UP正数向正无穷大取整,负数向负无穷大取整(常用)
HALF_UP5,6,7,8,9 向上取整、 1,2,3,4 向下取整、 常用的4舍5入
HALF_DOWN6,7,8,9 向上取整 1,2,3,4,5 向下取整
HALF_EVEN小数位是5时,判断整数部分是奇数就进位、 小数位是5时,判断整数部分是偶数就舍弃、 1,2,3,4, 舍弃、 6,7,8,9, 进位
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵相机-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值