C在减法期间检查溢出

bd1hkmkf  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(72)

我一直在尝试判断两个32位的数字相减时是否有溢出。给出的规则是:

Can only use: ! ~ & ^ | + << >>
 *   Max uses: 20
Example: subCheck(0x80000000,0x80000000) = 1,
 *       subCheck(0x80000000,0x70000000) = 0
No conditionals, loops, additional functions, or casting

字符串
到目前为止我已经

int dif = x - y; // dif is x - y
int sX = x >> 31; // get the sign of x
int sY = y >> 31; // get the sign of y
int sDif = dif >> 31; // get the sign of the difference
return (((!!sX) & (!!sY)) | (!sY)); // if the sign of x and the sign of y 
                                    // are the same, no overflow. If y is 
                                    // 0, no overflow.


我现在意识到我不能在实际的函数(-)中使用减法,所以我的整个函数无论如何都是无用的。我如何使用不同于减法的方法,并仅使用按位操作来确定是否存在溢出?

a14dhokn

a14dhokn1#

请购买和阅读黑客的喜悦为这些东西。这是一个非常好的书。

int overflow_subtraction(int a, int b, int overflow)
{
    unsigned int sum = (unsigned int)a - (unsigned int)b; // wrapround subtraction
    int ssum = (int)sum;
    // Hackers Delight: section Overflow Detection, subsection Signed Add/Subtract
    // Let sum = a -% b == a - b - carry == wraparound subtraction.
    // Overflow in a-b-carry occurs, iff a and b have opposite signs
    // and the sign of a-b-carry is opposite of a (or equivalently same as b).
    // Faster routine: res = (a ^ b) & (sum ^ a)
    // Slower routine: res = (sum^a) & ~(sum^b)
    // Overflow occured, iff (res < 0)
    if (((a ^ b) & (ssum ^ a)) < 0)
        panic();
    return ssum;
}

字符串

2ul0zpep

2ul0zpep2#

感谢大家的帮助!以下是我提出的解决问题的方法:

int ny = 1 + ~y; // -y
int dif = x + ny; // dif is x - y
int sX = x >> 31; // get the sign of x
int sY = y >> 31; // get the sign of -y
int sDif = dif >> 31; // get the sign of the difference
return (!(sX ^ sY) | !(sDif ^ sX));

字符串
我尝试的每一个案例都成功了。我改变了@HackerBoss的建议,得到了y的符号而不是ny,然后在return语句中颠倒了两个检查。这样,如果符号相同,或者如果结果的符号和x的符号相同,它就返回true。

tf7tbtn2

tf7tbtn23#

为了避免未定义的行为,我将假设整数以二进制补码表示,这是从sXsYsDif的计算中推断出来的。我还将假设sizeof(int)为4。如果您只处理32位整数,那么使用int32_t可能会更好,因为int的大小会因平台而异。
既然你可以使用加法,你可以把减法看作是一个数的负数的加法。一个存储在二进制补码中的数可以通过翻转所有的位并加一来求反。这给出了以下修改后的代码:

int ny = 1 + ~y; // -y
int dif = x + ny; // dif is x - y
int sX = x >> 31; // get the sign of x
int sNY = ny >> 31; // get the sign of -y
int sDif = dif >> 31; // get the sign of the difference
return ((sX ^ sNY) | (~sDif ^ sX)); // if the sign of x and the sign of y 
                                    // are the same, no overflow. If the
                                    // sign of dif is the same as the signs
                                    // of x and -y, no overflow.

字符串

相关问题