C 如何检测2';补码乘法溢出?
在我正在阅读的一本书中,下面的函数用于确定2的补码整数乘法溢出C 如何检测2';补码乘法溢出?,c,twos-complement,C,Twos Complement,在我正在阅读的一本书中,下面的函数用于确定2的补码整数乘法溢出 int tmult_ok(int x, int y) { int p = x*y; return !x || p/x == y; } 虽然这是可行的,但如何在所有情况下证明其正确性? 如何确保p!=当溢出时x*y 以下是我的理解: 当您将2个大小为“w位”的整数相乘时,结果可能是2w位 计算截断高阶w位。剩下的是低阶w位。 3.假设P=最低w位 然后,我们需要证明当存在溢出时(P/x!=y)和(P/y!=x) 我的困惑就在这里。当
int tmult_ok(int x, int y) {
int p = x*y;
return !x || p/x == y;
}
虽然这是可行的,但如何在所有情况下证明其正确性?
如何确保p!=当溢出时x*y
以下是我的理解:
溢出类下溢是未定义的行为,这意味着编译器可以认为它们永远不会发生,并简化依赖于它的代码。因此,无法检测溢出是否已经发生了
相反,您应该检查可能溢出的操作是否即将溢出int tmult_ok(int x, int y) {
if (MAX_INT / y >= x)
//throw somthing
return x*y;
}
tmult\u ok(x,y)
在int p=x*y中的x*y
任何时候都会失败代码>溢出,因为这是未定义的行为(UB)。
由于同样的原因,它也会使类似于tmultu\u ok(INT\u MIN,-1)
的拐角情况失败。
它不能便携“工作”
不依赖于2的补码的替代(and)。请注意,这将返回与tmult\u ok()
相反的结果
尝试所有组合都是一种蛮力方法——对于32位int
来说可能太长了
尝试所有组合的子集,对于每个x,y
,尝试INT\u MIN,INT\u MIN-1,INT\u MIN-2,-2,-1,0,1,2,INT\u MAX-1,INT\u MAX
。(10*10次测试)
也是所有组合的子集,对于sqrt(INT_MAX)
中2范围内的每个值+/-。(10*10次测试)
另外,int
范围内的几百万个随机值也是谨慎的
这可能是不够的,但是如果代码通过了这一点,剩下的角落案例就很少了——它们非常依赖于您的源代码
另请参见您可以检查溢出标志()
在计算机处理器中,溢出标志(有时称为V标志)通常是系统状态寄存器中的一个位,用于指示运算中何时发生算术溢出,表明带符号的2的补码结果不适合用于运算的位数(ALU宽度)
如何访问它的示例:仅适用于同一符号x
和y
。也应该是=
(但应检查某些角情况…)。的可能重复的可能重复即使程序在具有此标志的系统上执行,也不能保证编译器使用设置溢出标志的指令执行乘法。这个想法还有其他问题,比如确保溢出标志的读取紧跟在乘法指令之后(因为编译器可能会重新排序)。要扩展这个概念,@yoones应该意识到他链接的示例也使用assembly来添加数字。如果你开始混合编译后的代码和汇编后的代码,你永远不知道在没有严格测试的情况下你是否得到了你认为是的东西,即使这样,一个新的编译器可能会做出不同的决定,破坏你的旧解决方案。解决方法是使用编译器特定的函数,例如GCC。你可以用16位整数进行彻底的测试,这将是32位整数算法的合理运用。但我认为这样简单的算法容易受到形式证明的影响。
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
}
int tmult_ok_ll(int x, int y) {
long long prod = x;
prod *= y;
return (prod >= INT_MIN && prod <= INT_MAX);
}