C#中如何避免分数丢失,正确分配折扣匹配目标订单总额?
折扣分配问题解决方案
问题拆解
先明确核心数据:
- 原始订单总金额:
10000*1 + 5673*2 + 5000*1 = 26346(分) - 目标订单总金额:21565分
- 需分配的总折扣:
26346 - 21565 = 4781(分)
你的原有实现是将折扣平均分摊给每个商品,再按数量拆分到单价,但因为小数取整时丢失精度,导致最终总和差1分。要解决这个问题,需要先按合理比例分配折扣,再处理剩余的零头金额。
可行实现代码
// 计算原始总金额 var originalTotal = data.Sum(x => (long)x.PriceCents * x.Qty); // 总折扣额 var totalDiscount = originalTotal - 21565; // 存储每个商品的原始总价、应分配折扣等信息 var itemDiscountDetails = data.Select(item => new { OrderItem = item, OriginalItemTotal = (long)item.PriceCents * item.Qty, CalculatedDiscount = (decimal)item.PriceCents * item.Qty * totalDiscount / originalTotal }).ToList(); long allocatedDiscount = 0; // 先按截断整数分配折扣,记录已分配总额 foreach (var detail in itemDiscountDetails) { long discount = (long)detail.CalculatedDiscount; // 把折扣分摊到单价(确保单价为整数) detail.OrderItem.PriceCents -= (int)(discount / detail.OrderItem.Qty); allocatedDiscount += discount; } // 处理剩余未分配的折扣零头(此案例中剩余1分) long remainingDiscount = totalDiscount - allocatedDiscount; // 优先给数量为1的商品分配零头(避免单价出现小数),按原始总价从高到低排序 foreach (var detail in itemDiscountDetails.OrderByDescending(d => d.OriginalItemTotal)) { if (remainingDiscount <= 0) break; if (detail.OrderItem.Qty == 1) { detail.OrderItem.PriceCents -= 1; remainingDiscount--; } }
代码逻辑说明
- 比例分配:按每个商品的原始总价占总金额的比例计算折扣,比平均分配更合理(高价商品承担对应比例的折扣)。
- 截断取整:先将每个商品的折扣取整为整数,记录已分配的总折扣。
- 零头补全:计算剩余未分配的折扣,优先分配给数量为1的商品(保证单价为整数),确保最终所有商品的
PriceCents * Qty总和刚好等于目标值21565分。
验证结果
调整后各商品总价:
- 第一个商品(原10000分):总价变为
10000 - (1813 + 1) = 8186分 - 第二个商品(原11346分):总价变为
11346 - 2037 = 9309分 - 第三个商品(原5000分):总价变为
5000 - 930 = 4070分
总和:8186 + 9309 + 4070 = 21565分,完全符合目标。
内容的提问来源于stack exchange,提问作者Akshay Rajpaul
相关产品推荐
相关产品推荐

