再也不怕面试官问我Java浮点数运算精度问题了!BigDecimal如何使用?

在Java中使用浮点数进行运算时会发生精度丢失的问题。先看实例:

图片

我们想要的结果是:

v的值为6.2;

(d3-d2)==(d2-d1) 为true;

为什么Java中小数精度会丢失?

大家都应该都知道电脑的底层就是二进制吧,所有的操作最终都是在二进制中进行的,内存中只有0和1;

不知道的见 我的上一篇 彻底搞懂二进制的文章!

知道怎么转化和计算的,记住一个核心:二进制表示的小数是精确的而不是确切的

1、小数的二进制转化:

例:0.7的二进制为:

0.7 * 2 = 1.4 取整数部分1 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.7的二进制为(正向取整)101100…

上面的进制转化计算出现了循环也就是说乘2永远无法等于整数而不是小数

所以二进制表示的小数是精确的而不是确切的。

小数无法确切的储存在二进制中,而小数计算是通过二进制完成的,所以出现了精度丢失。

2、Java浮点计算精度丢失解决方案

我们如何解决这个问题呢?一种很常用的方法是:使用使用 BigDecimal 来定义浮点数的值,再进行浮点数的运算操作。

1、运算

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");

BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);

System.out.println(x); /* 0.1 */
System.out.println(y); /* 0.1 */
System.out.println(Objects.equals(x, y)); /* true */

2、数值比较

a.compareTo(b) : 返回 -1 表示 a 小于 b,0 表示 a 等于 b , 1表示 a 大于 b

BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("0.3");
System.out.println(a.compareTo(b));// 1

3、保留几位小数

通过 setScale方法设置保留几位小数以及保留规则。保留规则有挺多种,不需要记,IDEA会提示。

BigDecimal m = new BigDecimal("2.125433");
BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN);
System.out.println(n);// 2.125

4、注意事项:

注意:我们在使用BigDecimal时,为了防止精度丢失,推荐使用它的 BigDecimal(String) 构造方法来创建对象。《阿里巴巴Java开发手册》对这部分内容也有提到如下图所示。

图片

5、扩展:

BigDecimal 主要用来操作(大)浮点数,BigInteger 主要用来操作大整数(超过 long 类型)。

BigDecimal 的实现利用到了 BigInteger, 所不同的是 BigDecimal 加入了小数位的概念

6、自定义运算工具类

对BigDecimal包装后的基本运算,具体使用方法如下:

 /**
 * 加法
    * @param m1
    * @param m2
    * @return
    */
   public static double add(double m1, double m2) {
       BigDecimal p1 = new BigDecimal(Double.toString(m1));
       BigDecimal p2 = new BigDecimal(Double.toString(m2));
       return p1.add(p2).doubleValue();
  }

   /**
 * 减法
    * @param m1
    * @param m2
    * @return
    */
   public static double sub(double m1, double m2) {
       BigDecimal p1 = new BigDecimal(Double.toString(m1));
       BigDecimal p2 = new BigDecimal(Double.toString(m2));
       return p1.subtract(p2).doubleValue();
  }

   /**
 * 乘法
    * @param m1
    * @param m2
    * @return
    */
   public static double mul(double m1, double m2) {
       BigDecimal p1 = new BigDecimal(Double.toString(m1));
       BigDecimal p2 = new BigDecimal(Double.toString(m2));
       return p1.multiply(p2).doubleValue();
  }


    /**    
  *   除法
     *   @param   m1  
     *   @param   m2        
     *   @param   scale      
     *   @return    
     */    
   public static double div(double m1, double m2, int scale) {
       if (scale < 0) {
           throw new IllegalArgumentException("Parameter error");
      }
       BigDecimal p1 = new BigDecimal(Double.toString(m1));
       BigDecimal p2 = new BigDecimal(Double.toString(m2));
       return p1.divide(p2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
  }

关注公众号,回复 pdf 可以获得pdf电子书资源

发哥讲Java

图片                

● 扫码关注我们

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

航迹者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值