你如何检测2的补码乘法溢出?

时间:2018-06-04 15:37:13

标签: c twos-complement

在我正在阅读的一本书中,以下函数用于确定2的补码整数乘法溢出。

int tmult_ok(int x, int y) {
int p = x*y;
return !x || p/x == y;
}

虽然这有效,但如何在所有情况下证明其正确性? 当出现溢出时,如何确保p!= x * y?

以下是我的理解:

  1. 当你将2个整数大小" w-位"相乘时,结果可能是2w位。
  2. 计算截断高阶w比特。所以我们留下了低阶w位。 3.让我们说P =最低w位
  3. 然后,当有溢出时,我们需要证明(P / x!= y)和(P / y!= x)。
  4. 我的困惑在于此。当没有溢出时,你怎么能说(P / x!= y)?除非有可能P除以x时的位模式即使出现溢出也会产生y?

3 个答案:

答案 0 :(得分:4)

tmult_ok(x, y)x*y溢出时随时int p = x*y;失败,因为未定义的行为(UB)。
出于同样的原因,它也失败了像tmult_ok(INT_MIN, -1)这样的角落案例 它不适合工作"。

替代(和others for /,-,+)不依赖于2的补码。请注意,这会返回tmult_ok()的反面。

int is_undefined_mult1(int a, int b) {
  if (a > 0) {
    if (b > 0) {
      return a > INT_MAX / b;       // a positive, b positive
    }
    return b < INT_MIN / a;         // a positive, b not positive
  }
  if (b > 0) {
    return a < INT_MIN / b;         // a not positive, b positive
  }
  return a != 0 && b < INT_MAX / a; // a not positive, b not positive
}
  

如果发生溢出,如何确保p!= x * y?

便携式代码不能。使用C中的有符号整数数学,溢出是UB。代码应该在没有先执行乘法的情况下检测潜在的溢出。 @Quentin @Eugene Sh.

  

如何在所有情况下证明其正确性?

使用2倍宽的数学形成参考测试。如果int为32位,则使用64位数学运算将tmult_ok()与乘法进行比较,并查看产品是否在范围内。 @rici

int tmult_ok_ll(int x, int y) {
  long long prod = x;
  prod *= y;
  return (prod >= INT_MIN && prod <= INT_MAX);
}

尝试所有组合是一种蛮力方法 - 对于32位int来说可能太长了。

尝试所有组合的子集,对于每个x,y,尝试INT_MIN, INT_MIN-1, INT_MIN-2, -2,-1, 0, 1, 2, , INT_MAX-1, INT_MAX。 (10 * 10测试)

也是sqrt(INT_MAX)中每个值+/-的所有组合的子集。 (10 * 10测试)

int范围内的几百万随机值也是谨慎的。

这可能还不够,但是如果代码通过了这个,那么剩下很少的角落情况 - 这非常依赖于您的源代码。

另见@Eric Postpischil

答案 1 :(得分:0)

像下溢这样的溢出是未定义的行为,这意味着编译器可以认为它们永远不会发生并简化依赖于它的代码。因此,您无法检测是否已经发生溢出

应该检查可能溢出的操作是否会溢出

int tmult_ok(int x, int y) {
  if (MAX_INT / y >= x)
    //throw somthing
  return x*y;
}

答案 2 :(得分:0)

您可以查看溢出标记(https://en.wikipedia.org/wiki/Overflow_flag

  

在计算机处理器中,溢出标志(有时称为V标志)通常是系统状态寄存器中的单个位,用于指示何时在操作中发生算术溢出,表明带符号的二进制补码结果不适合用于操作的位数(ALU宽度)。

如何访问它的示例:https://www.linuxquestions.org/questions/programming-9/c-how-to-check-the-overflow-flag-930420/#post4612830