精度损失而引发的 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

这两个等式是始终成立的

posted @ 2021-11-06 14:37  机智的小小帅  阅读(71)  评论(0编辑  收藏  举报