
假设x是一个无符号类型的变量。例如,在 C/C++ 中,它可以是size_t类型。
你有一个表达式,例如6 * x ,你想知道6 * x是否溢出。也就是说,你想知道6 * x是否超出了该类型可以表示的值范围。在大多数情况下, size_t类型的变量将能够表示[0, 2^64-1]范围内的所有值。为了更清晰地表达,我们用一个表示位数的变量来表示: [0, 2^L-1] 。
最简单的方法是将x与(2^L-1) // 6进行比较,其中我使用符号//表示整数除法(而不是/ )。
但你还能有其他选择吗?
如果该值不溢出,我们可以确定(6 * x)//6 == x 。有趣的问题是,当该值溢出时会发生什么。对于[1, 2^L-1]范围内的任意非零常数a我们可以直接回答这个问题。
令k = (a*x)//2^L为乘法循环次数。机器计算出的有效(循环)值为r = a*x - k*2^L ,其中0 <= r < 2^L 。当k >= 1时,恰好发生溢出。由于x<2^L ,所以k <= a − 1 。
将r = a*x - k*2^L除以a ,得到x + -k*2^L//a 。当k非零时,最后一个值 ( -k*2^L//a ) 是-2^L//a 、 -2* 2^L//a 、…、 -(a-1) * 2^L//a中的一个。
- 当
k = 0(无溢出):r // a = x。 - 当
k ≥ 1时:r // a = x + (negative integer) ≠ x。
因此,我们得到以下结果。
定理:如果x是无符号类型, a是非零常量,则a * x溢出当且仅当(a * x)//a != x 。
实际上,将x与(2^L-1) // a进行简单比较可能更有效。
一个悬而未决的问题是,是否存在更符合数学原理的优雅检验方法。
原文: https://lemire.me/blog/2026/05/06/checking-multiplication-overflow/