Java中如何正确进行浮点数运算

1、浮点数不是精确存储

参考: 关于浮点数存储


  		System.out.println(0.1+0.2);
        System.out.println(1.0-0.8);
        System.out.println(4.015*100);
        System.out.println(123.3/100);
        double amount1 = 2.15;
        double amount2 = 1.10;
        System.out.println(amount1 - amount2);

可以看到数据在未计算前输出结果不是精确的:
在这里插入图片描述

2、正确使用BigDecimal

 System.out.println(new BigDecimal(0.1).add(new BigDecimal(0.2)));
        System.out.println(new BigDecimal(1.0).subtract(new BigDecimal(0.8)));
        System.out.println(new BigDecimal(4.015).multiply(new BigDecimal(100)));
        System.out.println(new BigDecimal(123.3).divide(new BigDecimal(100)));

和你想的可能不一样,以数值型赋值计算结果仍旧是不精确的:
在这里插入图片描述
我们以字符串方式计算,看看结果:

   		 // 使用 BigDecimal 表示和计算浮点数,且务必使用字符串的构造方法来初始化
        System.out.println(new BigDecimal("0.1").add(new BigDecimal("0.2")));
        System.out.println(new BigDecimal("1.0").subtract(new BigDecimal("0.8")));
        System.out.println(new BigDecimal("4.015").multiply(new BigDecimal("100")));
        System.out.println(new BigDecimal("123.3").divide(new BigDecimal("100")));
        // BigDecimal 有 scale 和 precision 的概念,scale 表示小数点右边的位数,而 precision 表示精度,也就是有效数字的长度。
        System.out.println(new BigDecimal(Double.toString(100)));
        System.out.println(BigDecimal.valueOf(100));

在这里插入图片描述

3、浮点数的精确度与格式化输出

浮点数格式化输出通过BigDecimal实现

  double num1 = 3.35;
        float num2 = 3.35f;
        System.out.println(String.format("%.1f", num1)); // 3.4
        System.out.println(String.format("%.1f", num2)); // 3.3

        // 默认舍入模式是 HALF_UP, 即四舍五入
        DecimalFormat format = new DecimalFormat("#.##");
        format.setRoundingMode(RoundingMode.DOWN);
        System.out.println(format.format(num1)); // 3.35
        format.setRoundingMode(RoundingMode.DOWN);
        System.out.println(format.format(num2)); // 3.34

        // 格式化float与double存在异常,
        // 浮点数的字符串格式化也要通过BigDecimal 进行
        BigDecimal k1 = new BigDecimal("3.35");
        BigDecimal k2 = k1.setScale(1, BigDecimal.ROUND_DOWN);
        System.out.println(k2); // 3.3
        BigDecimal k3 = k1.setScale(1, BigDecimal.ROUND_HALF_UP);
        System.out.println(k3); // 3.4

4、浮点数运算溢出

  • Math工具类运算溢出时抛出异常
		long l = Long.MAX_VALUE;
        System.out.println(l);
        System.out.println(l + 1);
        System.out.println(l + 1 == Long.MIN_VALUE);

        //方法一是,考虑使用 Math 类的 addExact、subtractExact 等
        // xxExact 方法进行数值运算,这些方法可以在数值溢出时主动抛出异常
        try {
            long l1 = Long.MAX_VALUE;
            System.out.println(Math.addExact(l1, 1));
        } catch (Exception ex) {
            ex.printStackTrace();
        }
  • 使用BigInteger处理, 正常计算但是不能转为long型
 long l = Long.MAX_VALUE;
        System.out.println(l);
        BigInteger i = new BigInteger(String.valueOf(Long.MAX_VALUE));
        System.out.println(i.add(BigInteger.ONE).toString());
        try {
            long l2 = i.add(BigInteger.ONE).longValueExact();
        } catch (Exception ex) {
            ex.printStackTrace();
        }

在这里插入图片描述

5、浮点数判断相等

通常我们采用是做差判断是否小于某个范围

 // equals 比较的是 BigDecimal 的 value 和 scale
        System.out.println(new BigDecimal("1.0").equals(new BigDecimal("1"))); // false
        // 只比较 BigDecimal 的 value,可以使用 compareTo 方法
        System.out.println(new BigDecimal("1.0").compareTo(new BigDecimal("1")) == 0); // true


        // 集合为数字类型问题
        Set<BigDecimal> hashSet1 = new HashSet<>();
        hashSet1.add(new BigDecimal("1.0"));
        System.out.println(hashSet1.contains(new BigDecimal("1"))); //返回false
        // 解决: 第一个方法是,使用 TreeSet 替换 HashSet。
        // TreeSet 不使用 hashCode 方法,也不使用 equals 比较元素,
        // 而是使用 compareTo 方法,所以不会有问题。
        Set<BigDecimal> treeSet = new TreeSet<>();
        treeSet.add(new BigDecimal("1.0"));
        System.out.println(treeSet.contains(new BigDecimal("1"))); //返回true
        // 第二个方法是,把 BigDecimal 存入 HashSet 或 HashMap 前,先使用stripTrailingZeros
        // 方法去掉尾部的零,比较的时候也去掉尾部的 0,确保 value 相同的BigDecimal,scale 也是一致的
        Set<BigDecimal> hashSet2 = new HashSet<>();
        hashSet2.add(new BigDecimal("1.0").stripTrailingZeros());
        System.out.println(hashSet2.contains(new BigDecimal("1.000").stripTrailingZeros())); // true
  • 8
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值