ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

整数溢出陷阱:用除法安全比较乘积

整数溢出陷阱:用除法安全比较乘积

整数溢出陷阱:用除法安全比较乘积

在金融、电商、游戏等涉及大数值的业务中,经常出现类似判断:

if(balance>=unitPrice*quantity){// 余额足够支付}

unitPrice * quantity超出整型上限时,计算结果发生环绕,条件失效。尤其在 32 位环境下(int32_t上限约 21 亿),9 亿单价乘以 1 万数量,乘积 9 万亿远超上限,直接溢出为负数或小正数,导致错误放行或误拒。

本文给出一种零成本、完全等价的防御写法。


1. 溢出问题演示

假设使用int64_t存储金额(单位为分),手上余额balance = 9000 0000 0000(900亿分),需判断能否支付单价unitPrice = 9 0000 0000(9亿分)的 1 万件商品。

乘法表达式:

9 0000 0000 * 10000 = 9 0000 0000 0000 (9万亿)

int64_t最大值约 922 亿亿,看似不会溢出,但若使用int32_tuint32_t则会直接溢出。即便使用 64 位,若单价和数量进一步放大,依然会溢出。核心问题是:无法预知乘积是否越界


2. 解决方案:除法比较

将原条件balance >= unitPrice * quantity等价转换为:

balance / quantity >= unitPrice

前提:quantity > 0。这是业务上的自然约束(数量不能为 0 或负)。

转换后,除法运算的上限是balance,不会超出整型范围,彻底规避乘法溢出。


3. 等价性证明

balance = q * quantity + r,其中0 ≤ r < quantity,则balance / quantity = q

原条件:

balance >= unitPrice * quantity ⇔ q * quantity + r >= unitPrice * quantity ⇔ q + r/quantity >= unitPrice

由于r/quantity < 1,且unitPrice为整数,因此q + r/quantity >= unitPrice等价于q >= unitPrice。即:

balance / quantity >= unitPrice

整除截断不会造成误判,比较结果严格等价


4. 决策流程

在实际编码中,可以先用乘法安全阈值判断是否走除法分支,以获得最佳性能:

判断 balance >= unitPrice * quantity

quantity == 0?

处理 quantity 为 0 的边界

unitPrice > MAX / quantity?

直接比较 balance >= unitPrice * quantity

除法比较 balance / quantity >= unitPrice

返回结果

其中MAX为当前整型的最大值。若unitPrice > MAX / quantity,乘积必溢出,走除法路径;否则乘法安全。


5. 注意事项

  • quantity 必须为正整数。如果业务中可能出现 0 或负值,提前做参数校验。
  • 整除不会影响正确性。比较运算不关心余数,截断后的商已经足以判定。
  • 无需引入高精度或浮点库。原地使用原有整型,指令开销与乘法几乎无差别。
  • 可用于无符号整数。所有讨论同样适用于uint32_t/uint64_t,且无需担心符号位。

6. 总结

遇到形如A >= B * C且乘积可能溢出时,直接改写为A / C >= BC > 0)。这是一种简洁、可移植、无性能损失的防御技术,无需扩展整数宽度,就能彻底消除隐性溢出 bug。

在数值范围不可预知的接口中,养成这种写法习惯,能从源头避免难以复现的边界错误。

返回列表