按位运算等效于大于运算符

时间:2012-04-10 21:17:01

标签: c binary bit-manipulation

我正在研究一个基本上能看到两个整数中哪一个更大的函数。传递的参数是2 32位整数。诀窍是允许的唯一运算符是! ~ | & << >> ^(没有转换,除了signed int,*,/, - 等其他数据类型。)。

到目前为止,我的想法是将^两个二进制文件放在一起,以查看它们不共享的1值的所有位置。我想要做的就是获取该值并将最远的1隔离开来。然后看看它们中的哪一个具有该值。那个价值就会越大。 (假设我们使用8位整数而不是32位)。 如果传递的两个值是0101101101101001 我使用^来获取00100010。 然后我想用00100000换句话说01xxxxxx -> 01000000 然后&使用第一个数字 !!结果并将其返回。 如果是1,则第一个#会更大。

有关如何01xxxxxx -> 01000000或其他任何可以提供帮助的想法吗?

忘记注意:没有ifs,whiles,fors等...

8 个答案:

答案 0 :(得分:18)

这是一个无循环版本,用于比较O(lg b)操作中的无符号整数,其中b是机器的字大小。注意OP没有说明除signed int之外的其他数据类型,因此这个答案的顶部可能不符合OP的规范。 (扰流板版本在底部。)

请注意,我们要捕获的行为是1的最重要位不匹配a0的{​​{1}}。另一种思考方式是b中的任何位都大于a中的相应位,意味着b大于a,只要不存在b a中较早的位小于b中的相应位。

为此,我们计算ab中的所有位大于a中的相应位,同样计算b中的所有位小于1中的相应位。 1}}。我们现在想要掩盖低于任何'小于'位的所有'大于'位,所以我们取所有'小于'位并将它们全部涂抹到右边制作一个掩码:最重要的位设置全部从最低位开始的方式现在是a <= b

现在我们要做的就是删除使用简单位掩码逻辑设置的'大于'位。

如果a > b,则结果值为0,如果1,则结果为非零值。如果我们希望在后一种情况下它是#include <stdio.h> // Works for unsigned ints. // Scroll down to the "actual algorithm" to see the interesting code. // Utility function for displaying binary representation of an unsigned integer void printBin(unsigned int x) { for (int i = 31; i >= 0; i--) printf("%i", (x >> i) & 1); printf("\n"); } // Utility function to print out a separator void printSep() { for (int i = 31; i>= 0; i--) printf("-"); printf("\n"); } int main() { while (1) { unsigned int a, b; printf("Enter two unsigned integers separated by spaces: "); scanf("%u %u", &a, &b); getchar(); printBin(a); printBin(b); printSep(); /************ The actual algorithm starts here ************/ // These are all the bits in a that are less than their corresponding bits in b. unsigned int ltb = ~a & b; // These are all the bits in a that are greater than their corresponding bits in b. unsigned int gtb = a & ~b; ltb |= ltb >> 1; ltb |= ltb >> 2; ltb |= ltb >> 4; ltb |= ltb >> 8; ltb |= ltb >> 16; // Nonzero if a > b // Zero if a <= b unsigned int isGt = gtb & ~ltb; // If you want to make this exactly '1' when nonzero do this part: isGt |= isGt >> 1; isGt |= isGt >> 2; isGt |= isGt >> 4; isGt |= isGt >> 8; isGt |= isGt >> 16; isGt &= 1; /************ The actual algorithm ends here ************/ // Print out the results. printBin(ltb); // Debug info printBin(gtb); // Debug info printSep(); printBin(isGt); // The actual result } } ,我们可以做一个类似的拖尾技巧,只看一下最不重要的位。

a ^= 0x80000000

注意:如果你翻转输入的两个的顶部位,这应该适用于有符号整数。 int isGt(int a, int b) { int diff = a ^ b; diff |= diff >> 1; diff |= diff >> 2; diff |= diff >> 4; diff |= diff >> 8; diff |= diff >> 16; diff &= ~(diff >> 1) | 0x80000000; diff &= (a ^ 0x80000000) & (b ^ 0x7fffffff); return !!diff; }

扰流

如果您想要满足所有要求的答案(包括25名或更少的操作员):

{{1}}

我会解释为什么它对您有用。

答案 1 :(得分:5)

无符号变体,因为可以使用逻辑(&amp;&amp;,||)和比较(!=,==)。

int u_isgt(unsigned int a, unsigned int b)
{
    return a != b && (    /* If a == b then a !> b and a !< b.             */
               b == 0 ||  /* Else if b == 0 a has to be > b (as a != 0).   */
               (a / b)    /* Else divide; integer division always truncate */
           );             /*              towards zero. Giving 0 if a < b. */
}

!===很容易消除,即:

int u_isgt(unsigned int a, unsigned int b)
{
    return a ^ b && (
               !(b ^ 0) ||
               (a / b)
           );
}

对于签名,可以扩展为:

int isgt(int a, int b)
{
    return
    (a != b) &&
    (
        (!(0x80000000 & a) && 0x80000000 & b) ||  /* if a >= 0 && b < 0  */
        (!(0x80000000 & a) && b == 0) ||
        /* Two more lines, can add them if you like, but as it is homework
         * I'll leave it up to you to decide. 
         * Hint: check on "both negative" and "both not negative". */
    )
    ;
}

可以更紧凑/消除操作。 (至少有一个),但为了清楚起见,请这样说。

而不是0x80000000可以说ie:

#include <limits.h>
static const int INT_NEG = (1 << ((sizeof(int) * CHAR_BIT) - 1));

用它来测试:

void test_isgt(int a, int b)
{
    fprintf(stdout,
        "%11d > %11d = %d : %d %s\n",
        a, b,
        isgt(a, b), (a > b),
        isgt(a, b) != (a>b) ? "BAD!" : "OK!");
}

结果:

         33 >           0 = 1 : 1 OK!
        -33 >           0 = 0 : 0 OK!
          0 >          33 = 0 : 0 OK!
          0 >         -33 = 1 : 1 OK!
          0 >           0 = 0 : 0 OK!
         33 >          33 = 0 : 0 OK!
        -33 >         -33 = 0 : 0 OK!
         -5 >         -33 = 1 : 1 OK!
        -33 >          -5 = 0 : 0 OK!
-2147483647 >  2147483647 = 0 : 0 OK!
 2147483647 > -2147483647 = 1 : 1 OK!
 2147483647 >  2147483647 = 0 : 0 OK!
 2147483647 >           0 = 1 : 1 OK!
          0 >  2147483647 = 0 : 0 OK!

答案 2 :(得分:4)

要将001xxxxx转换为00100000,请先执行:

x |= x >> 4;
x |= x >> 2;
x |= x >> 1;

(这是8位;将其扩展为32,在序列开始时将移位加8和16)。

这给我们留下00111111(这种技术有时被称为“比特涂抹”)。然后我们可以切掉除前1位以外的所有内容:

x ^= x >> 1;

给我们留下00100000

答案 3 :(得分:2)

Kaganar's smaller isGt function的完全无分支版本可能如下所示:

int isGt(int a, int b)
{
    int diff = a ^ b;
    diff |= diff >> 1;
    diff |= diff >> 2;
    diff |= diff >> 4;
    diff |= diff >> 8;
    diff |= diff >> 16;

    //1+ on GT, 0 otherwise.
    diff &= ~(diff >> 1) | 0x80000000;
    diff &= (a ^ 0x80000000) & (b ^ 0x7fffffff);

    //flatten back to range of 0 or 1.
    diff |= diff >> 1;
    diff |= diff >> 2;
    diff |= diff >> 4;
    diff |= diff >> 8;
    diff |= diff >> 16;
    diff &= 1;

    return diff;
}

这为实际计算提供了大约60条指令(MSVC 2010编译器,在x86拱门上),加上函数的prolog / epilog额外的10个堆栈操作。

答案 4 :(得分:0)

编辑:

好的,代码存在一些问题,但我修改了以下内容。

此辅助功能比较数字的第n位有效数字:

int compare ( int a, int b, int n )
{
    int digit = (0x1 << n-1);
    if ( (a & digit) && (b & digit) )
       return 0; //the digit is the same

    if ( (a & digit) && !(b & digit) )
       return 1; //a is greater than b

    if ( !(a & digit) && (b & digit) )
       return -1; //b is greater than a
}

以下应递归返回较大的数字:

int larger ( int a, int b )
{
    for ( int i = 8*sizeof(a) - 1 ; i >= 0 ; i-- )
    {
       if ( int k = compare ( a, b, i ) )
       {
           return (k == 1) ? a : b;
       }
    }
    return 0; //equal
}

答案 5 :(得分:0)

尽管我不想做别人的作业我无法抗拒这一个.. :)我相信其他人可以想到一个更紧凑的...但这里是我的..工作得很好,包括负数..

编辑:虽然有几个错误。我将把它留给OP来找到并修复它。

    #include<unistd.h>
    #include<stdio.h>
    int a, b, i, ma, mb, a_neg, b_neg, stop;

    int flipnum(int *num, int *is_neg) {
        *num = ~(*num) + 1;
        *is_neg = 1;

        return 0;
    }

    int print_num1() {
        return ((a_neg && printf("bigger number %d\n", mb)) ||
             printf("bigger number %d\n", ma));
    }

    int print_num2() {
        return ((b_neg && printf("bigger number %d\n", ma)) ||
             printf("bigger number %d\n", mb));
    }

    int check_num1(int j) {
        return ((a & j) && print_num1());
    }

    int check_num2(int j) {
        return ((b & j) && print_num2());
    }

    int recursive_check (int j) {
        ((a & j) ^ (b & j)) && (check_num1(j) || check_num2(j))  && (stop = 1, j = 0);

        return(!stop && (j = j >> 1) && recursive_check(j));
    }

    int main() {
        int j;
        scanf("%d%d", &a, &b);
        ma = a; mb = b;

        i = (sizeof (int) * 8) - 1;
        j = 1 << i;

        ((a & j) && flipnum(&a, &a_neg));

        ((b & j) && flipnum(&b, &b_neg));

        j = 1 << (i - 1);

        recursive_check(j);

        (!stop && printf("numbers are same..\n"));
    }

答案 6 :(得分:0)

我认为我有3个操作的解决方案:

在第一个数字中加一个,从可以表示的最大数字中减去它(全部为1&#39; s)。将该号码添加到第二个号码。如果它溢出,那么第一个数字小于第二个。

我不能100%确定这是否正确。也就是说你可能不需要添加1,我也不知道是否可以检查溢出(如果不是,那么只需保留最后一位并测试它最后是否为1) 。)

答案 7 :(得分:-1)

编辑:约束使得底部的简单方法无效。我正在添加二进制搜索功能和最终比较以检测更大的值:

unsigned long greater(unsigned long a, unsigned long b) {
    unsigned long x = a;
    unsigned long y = b;
    unsigned long t = a ^ b;
    if (t & 0xFFFF0000) {
        x >>= 16;
        y >>= 16;
        t >>= 16;
    }
    if (t & 0xFF00) {
        x >>= 8;
        y >>= 8;
        t >>= 8;
    }
    if (t & 0xf0) {
        x >>= 4;
        y >>= 4;
        t >>= 4;
    }
    if ( t & 0xc) {
        x >>= 2;
        y >>= 2;
        t >>= 2;
    }
    if ( t & 0x2) {
        x >>= 1;
        y >>= 1;
        t >>= 1;
    }
    return (x & 1) ? a : b;
}

我们的想法是从我们感兴趣的单词中最重要的一半开始,看看是否有任何设置位。如果有,那么我们不需要最不重要的一半,所以我们将不需要的位移走。如果没有,我们什么都不做(无论如何,一半是零,所以它不会妨碍)。由于我们无法跟踪移位量(它需要添加),我们还会移动原始值,以便我们可以执行最终and来确定更大的数字。我们用前一个掩码的一半大小重复这个过程,直到我们将有趣的位折叠到位0。

我没有故意在这里添加相同的案例。


旧答案:

最简单的方法可能是家庭作业的最佳方法。一旦你得到了不匹配的位值,你就可以从0x80000000处的另一个掩码(或者你的字大小的任何合适的最大位位置)开始,并保持右移,直到你达到你的不匹配值中设置的位。如果右移最终为0,则不匹配值为0.

我假设您已经知道确定较大数字所需的最后一步。