zoukankan      html  css  js  c++  java
  • 精度损失而引发的 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
    

    这两个等式是始终成立的

  • 相关阅读:
    c# 泛型委托
    c# 用户自定义转换
    c# 溢出上下文检测
    c# 接口相同方法申明使用
    c# 事件的订阅发布Demo
    c# 匿名方法几种表现形式
    c# 数组协变
    c# 交错数组
    C# 位运算详解
    ABAP 7.4 新语法-内嵌生命和内表操作(转)
  • 原文地址:https://www.cnblogs.com/XiaoXiaoShuai-/p/15511360.html
Copyright © 2011-2022 走看看