• 精度损失而引发的 bug


    精度损失而引发的 bug

    本周碰到因为 精度损失,导致 分段计算的结果之和 ≠ 整体计算的结果

    基本背景

    有一个佣金功能,需要计算销售人员每个月的佣金以及销售人员所有月份的总佣金。

    佣金金额 = 销售额 * 佣金比例
    

    其中 销售额 和 佣金 的精度都是当前货币的最小单位。目前货币单位为美元,最小单元为美分。

    最初的实现 (有 bug)
    单月佣金 = 单月销售额 * 佣金比例
    
    总佣金   = 总计销售额 * 佣金比例
    
    异常 case

    上面的实现在一般情况下的确是没有问题的。

    但是偶尔会出现: 总佣金 ≠ 单月佣金之和的情况

    举个例子,假设佣金比例为 0.3

    销售人员在一月的销售金额为 3333 美分,则他一月的佣金为 3333 * 0.3 = 999.9 -> 999 美分 (美分即为最小单位了, 直接取整)

    销售人员在二月的销售金额为 3333 美分,则他二月的佣金为 3333 * 0.3 = 999.9 -> 999 美分

    销售人员总计销售额为 6666 美分,则它总佣金为 6666 * 0.3 = 1999.8 -> 1999 美分

    此时 999 + 999 = 1998 ≠ 1999

    原因是将浮点数强行转换为整数而造成了精度损失。

    这种精度损失是无法避免的,我们能做的只是让结果看起来不那么怪。

    在这种情况下:用户发现 每个月的佣金之和 ≠ 总佣金,可能就会觉得非常奇怪。

    如何让 每个月的佣金之和 = 总佣金 呢?

    有两种解决方案

    资本家的做法: 优化总佣金的计算方法

    原来的佣金计算方法为

    单月佣金 = 单月销售额 * 佣金比例
    
    总佣金   = 总计销售额 * 佣金比例
    

    现改为

    单月佣金 = 单月销售额 * 佣金比例
    
    总佣金   = 单月佣金之和
    

    应用到上面的 case 里,总佣金就不在是 1999 了,而是 999 + 999 = 1998。

    足足少支付了一分钱,资本家心满意足的离开现场

    不贪小便宜的做法: 优化单月佣金的计算方法
    总佣金   = 总计销售额 * 佣金比例
    
    第 n 月佣金  = 第 (1 ~ n) 个月的总佣金 - 第 (1 ~ n - 1) 个月的总佣金
    

    应用到上面的例子中:

    一月的佣金 = 3333 * 0.3 - 0 = 999

    二月的佣金 = (3333 + 3333) * 0.3 - 3333 * 0.3 = 1999 - 999 = 1000

    总佣金 = 6666 * 0.3 = 1999 = 999 + 1000

    没有贪墨劳动人民的一分血汗钱!

    总结

    由于进度损失

    m * p + n * p ≠ (m + n) * p
    

    的现象是无法避免的

    可以使用加减法来替代乘法

    因为

    m * p + n * p = m * p + n * p
    
    m * p + ((m + n) * p - (m * p)) = (m + n) * p
    

    这两个等式是始终成立的

  • 相关阅读:
    比官方文档更易懂的Vue.js教程!包你学会!
    张腾:腾讯云融合通信应用场景及案例分享
    你大概走了假敏捷:《手绘敏捷宝典》在此,还不来收!
    这个PHP无解深坑,你能解出来吗?(听说能解出来的都很秀)
    从小数学就不及格的我,竟然用极坐标系表白了我的女神!(附代码)
    epoll
    hash_map
    BloomFilter
    Wrapper模式(Decorator模式)
    setvbuf
  • 原文地址:https://www.cnblogs.com/XiaoXiaoShuai-/p/15511360.html
Copyright © 2020-2023  润新知