如何正确检查整数乘法中是否发生溢出?
int i = X(), j = Y();
i *= j;
如何检查溢出,给定i
,j
及其类型的值?请注意,对于有符号和无符号类型,检查必须正常工作。可以假设i
和j
属于同一类型。也可以假设在编写代码时已知类型,因此可以为签名/未签名的案例提供不同的解决方案(不需要模板杂耍,如果它适用于" C"它是奖励)。
修改: @pmg的答案是正确的。我暂时无法绕过它的简单,所以我会在这里与大家分享。假设我们要检查:
i * j > MAX
但是我们无法检查,因为i * j
会导致溢出并且结果不正确(并且总是小于或等于MAX
)。所以我们这样修改它:
i > MAX / j
但这不太正确,因为在分裂中,涉及到一些舍入。相反,我们想知道这个结果:
i > floor(MAX / j) + float(MAX % j) / j
所以我们有了除法本身,它被整数算术隐式向下舍入(floor
在那里是无操作,仅仅是为了说明),我们有剩下的除法先前的不等式(评估小于1)。
假设i
和j
是极限处的两个数字,如果它们中的任何一个增加1,则会发生溢出。假设它们都不为零(在这种情况下,无论如何都不会发生溢出),(i + 1) * j
和i * (j + 1)
都超过1 + (i * j)
。因此,我们可以安全地忽略除法的舍入误差,该误差小于1。
或者,我们可以重新组织:
i - floor(MAX / j) > float(MAX % j) / j
基本上,这告诉我们i - floor(MAX / j)
必须大于[0, 1)
间隔中的数字。这可以写成:
i - floor(MAX / j) >= 1
因为1
就在间隔之后。我们可以改写为:
i - floor(MAX / j) > 0
或者作为:
i > floor(MAX / j)
所以我们已经证明了简单测试和浮点版本的等价性。这是因为除法不会导致显着的舍入误差。我们现在可以使用简单的测试,并且从此过上幸福的生活。
答案 0 :(得分:7)
之后你无法测试。如果乘法溢出,则会触发未定义的行为,这会导致测试无法确定。
你需要在>>进行乘法
之前测试if (INT_MAX / x > y) /* multiplication of x and y will overflow */;
答案 1 :(得分:2)
如果您的编译器的类型至少是int
的两倍,那么您可以这样做:
long long r = 1LL * x * y;
if ( r > INT_MAX || r < INT_MIN )
// overflowed...
else
x = r;
为了便于携带,您应该STATIC_ASSERT( sizeof(long long) >= 2 * sizeof(int) );
或类似的东西,但如果您担心填充位,则应该更加极端!
答案 2 :(得分:1)
试试这个
bool willoverflow(uint32_t a, uint32_t b) {
size_t a_bits=highestOneBitPosition(a),
size_t b_bits=highestOneBitPosition(b);
return (a_bits+b_bits<=32);
}
答案 3 :(得分:0)
通过使用除法可以看出是否发生了溢出。在无符号值的情况下,如果y!= 0,则乘法z = x * y溢出:
bool overflow_occured = (y!=0)? z/y!=x : false;
(如果y确实等于零,则不会发生溢出)。对于签名值的情况,这有点棘手
if(y!=0){
bool overflow_occured = (y<0 && x=2^31) | (y!=0 && z/y != x);
}
我们需要表达式的第一部分,因为如果x = -2 ^ 31且y = -1,第一个测试将失败。在这种情况下,乘法溢出,但机器可能给出-2 ^ 31的结果。因此,我们单独测试它。
对于32位值,这是正确的。将代码扩展到64位的情况留给读者练习。