Java浮点数精确计算

本文探讨了Java中浮点数(float和double)在处理精度问题上的不足,特别是在货币计算中的误差。为了解决这个问题,文章推荐使用BigDecimal类来进行精确的十进制计算,并展示了BigDecimal的基本加减乘除操作。最后,强调了在需要精确计算时避免使用float和double,以及在不同场景下选择合适的数据类型的重要性。
摘要由CSDN通过智能技术生成

一、出现精度失误的原因

        float和double类型主要是为了科学计算和工程计算而设计的。他们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合。float和double类型尤其不是和用于货币计算,因为要让一个float或者double精确地标识0.1(或者10的任何其他负数次方值)是不能的。

public static void main(String[] args) {
//所有金额
double funds = 1;
//购买次数
int itemsBought = 0;
//初次购买是0.1,后续每次购买都会涨价0.1
for (double price = 0.1; funds >= price; price += 0.1) {
funds -= price;
itemsBought++;
}
System.out.println(String.format("一共能买%d件物品", itemsBought));
System.out.println("余额:" + funds);
}

        这就是丢失精度的问题。

        修改代码如下,

public static void main(String[] args) {
//所有金额
        BigDecimal funds = new BigDecimal(1);
        //购买次数
        int itemsBought = 0;
        //初次购买是0.1,后续每次购买都会涨价0.1
        for (BigDecimal price = new BigDecimal("0.1"); funds.compareTo(price) >= 0;price = price.add(new BigDecimal("0.1"))) {
            funds =funds.subtract(price);
            itemsBought++;
        }
        System.out.println(String.format("一共能买%d件物品", itemsBought));
        System.out.println("余额:" + funds);
    }

 

二、BigDecimal用法

        常见加减乘除如下:

/**
     * 相加
     * @param doubleValA
     * @param doubleValB
     * @return
     */
    public static double add(String doubleValA, String doubleValB) {
        BigDecimal a2 = new BigDecimal(doubleValA);
        BigDecimal b2 = new BigDecimal(doubleValB);
        return a2.add(b2).doubleValue();
    }

    /**
     * 相减
     * @param doubleValA
     * @param doubleValB
     * @return
     */
    public static double sub(String doubleValA, String doubleValB) {
        BigDecimal a2 = new BigDecimal(doubleValA);
        BigDecimal b2 = new BigDecimal(doubleValB);
        return a2.subtract(b2).doubleValue();
    }

    /**
     * 相乘
     * @param doubleValA
     * @param doubleValB
     * @return
     */
    public static double mul(String doubleValA, String doubleValB) {
        BigDecimal a2 = new BigDecimal(doubleValA);
        BigDecimal b2 = new BigDecimal(doubleValB);
        return a2.multiply(b2).doubleValue();
    }

    /**
     * 相除
     * @param doubleValA
     * @param doubleValB
     * @param scale 除不尽时指定精度
     * @return
     */
    public static double div(String doubleValA, String doubleValB, int scale) {
        BigDecimal a2 = new BigDecimal(doubleValA);
        BigDecimal b2 = new BigDecimal(doubleValB);
        return a2.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

//主函数调用
public static void main(String[] args) {
        String doubleValA = "3.14159267";
        String doubleValB = "2.358";
        System.out.println("add:" + add(doubleValA, doubleValB));
        System.out.println("sub:" + sub(doubleValA, doubleValB));
        System.out.println("mul:" + mul(doubleValA, doubleValB));
        System.out.println("div:" + div(doubleValA, doubleValB, 8));
}

三、总结

  1.  对于任何需要精确答案的计算任务,请不要使用float或者double。
  2. 如果你想让系统来记录十进制小数点,并且不介意因为不使用基本类型而带来的不便,就请使用BigDecimal。使用BigDecimal还有一些额外的好处,他允许你完全控制舍入,每当一个操作涉及舍入的时候,他允许你从8种舍入模式中选择其一。如果你正通过法定要求的舍入行为进行业务计算,使用BigDecimal是非常方便的。
  3. 如果性能非常关键,并且你不介意自己记录十进制小数点,而且所涉及的数值又不太大,就可以使用int或者long。如果数值范围内没有超过9位十进制数字,就可以使用int;如果不超过18位数字,就可以使用long。如果数值可能超过18位数字,就必须使用BigDecimal。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值