【线上故障】float精度问题导致的一个bug

问题说明

业务背景:
下单的时候校验商品金额、使用抵扣比例是否与抵扣金额是否相等的业务。
开发同学给出一个简单的方法:

    /**
     * 校验金额、抵扣比例、折扣比例是否相等
     *
     * @param amount    金额(分)
     * @param ratio     抵扣比例(比例*100)
     * @param deductFee 抵扣比例
     * @return 是否相等
     */
    private static boolean division(long amount, float ratio, long deductFee) {
        if (ratio <= 0) return false;
        return amount / ratio == deductFee;
    }

看看这个方法有什么问题吗?乍一看,方法没什么问题。

测试结果:

    public static void main(String[] args) {
        System.out.println(division(49969950,50.0f,999399));
    }

输出:

false

我们想要的结果是49969950是50的整数倍,相除的话,正好是999399,但是实际上运行的结果确是false,与预期结果不一致。

来看下实际运行效果

        public static void main(String[] args) {
        System.out.println(49969950/50.0f);
        System.out.println((float)49969950);

    }

输出结果为:

999399.06
4.9969952E7

我们发现数值变了,实际上49969950/50.0f 得到的结果是999399.06
实际上想得到的结果是49969950除以50,是一个整数,但是经过运算得出的却是个小数。

但是这个方法不是一定是失败的,例如:

    public static void main(String[] args) {
        System.out.println(division(100L,10.0f,10));
    }

输出结果:

true

所以类似于这样的数值,运算结果是符合之前的预期的,这样就导致如果真正在下单业务中运用,如果商品价格、抵扣比例正好是这样的数值的话,结果完全是正确的。如果测试不充分的话,上线就是故障。

解决方案之一就是采用BigDecimal做精度运算,但是也有坑,后续分析。

针对以上问题,我抛出了几个疑问:
== 1.49969950转float的时候为什么会变成4.9969952E7?多个2出来。 ==
== 2.long型为什么可以转float? ==
== 3.long型转float为什么值会变? ==
== 4.float精度比较问题 ==

搞明白这几个问题,需要重新回顾一下java基本类型的基础知识。真正的搞明白这里面的原理。

未完待续~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值