乘法溢出测试

时间:2014-04-08 13:41:32

标签: c++ c integer

如何正确检查整数乘法中是否发生溢出?

int i = X(), j = Y();
i *= j;

如何检查溢出,给定ij及其类型的值?请注意,对于有符号和无符号类型,检查必须正常工作。可以假设ij属于同一类型。也可以假设在编写代码时已知类型,因此可以为签名/未签名的案例提供不同的解决方案(不需要模板杂耍,如果它适用于" C"它是奖励)。

修改: @pmg的答案是正确的。我暂时无法绕过它的简单,所以我会在这里与大家分享。假设我们要检查:

i * j > MAX

但是我们无法检查,因为i * j会导致溢出并且结果不正确(并且总是小于或等于MAX)。所以我们这样修改它:

i > MAX / j

但这不太正确,因为在分裂中,涉及到一些舍入。相反,我们想知道这个结果:

i > floor(MAX / j) + float(MAX % j) / j

所以我们有了除法本身,它被整数算术隐式向下舍入(floor在那里是无操作,仅仅是为了说明),我们有剩下的除法先前的不等式(评估小于1)。

假设ij是极限处的两个数字,如果它们中的任何一个增加1,则会发生溢出。假设它们都不为零(在这种情况下,无论如何都不会发生溢出),(i + 1) * ji * (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)

所以我们已经证明了简单测试和浮点版本的等价性。这是因为除法不会导致显着的舍入误差。我们现在可以使用简单的测试,并且从此过上幸福的生活。

4 个答案:

答案 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位的情况留给读者练习。