C中的按位运算比较两个整数

时间:2017-09-09 23:53:17

标签: c bit-manipulation bitwise-operators

我最近在其中一门课上接受了测验。问题如下:

  

在C中编写一个函数(称为cmp),接受两个整数(xy)   如果-1&lt; x,则返回y如果0 = x y1 x,则y cmp。写int cmp(int x, int y) { return ((x < y) ? (-1) : ((x == y) ? (0) : (1))); } 尽可能简洁。

我能想到的最简洁的功能是:

&

但我觉得可能会有一些操作,我可以用它来更简洁地做到这一点。也许是^和{{1}}的组合?最近几天这一直困扰着我,我想知道实际上 是否有更好的方法来做到这一点?

3 个答案:

答案 0 :(得分:14)

“尽可能简洁”是对测验的极其模糊的要求。你有望打高尔夫吗?删除空格和括号是否使其更简洁?无论如何,这是一个使用算术比较结果的解决方案:

int cmp(int x, int y) {
    return (x > y) - (x < y);
}

答案 1 :(得分:8)

  • 如果x == yx - y == 0
  • 如果x < yx - y < 0
  • 如果x > yx - y > 0

因此,我们想知道是否可以将上述3个要点中描述的3个条件转换为cmp函数所需的3个单输出值:

int cmp( int x, int y ) {
    return -1 if x < y
    return  0 if x == y
    return  1 if x > y
}

这可以重新定义为:

int cmp( int x, int y ) return singleValue( x - y );

int singleValue( int diff ) {
    return -1 if diff < 0
    return  0 if diff == 0
    return  1 if diff > 0
}

现在考虑(并假设)计算机使用two's complement表示32位有符号整数,即int),然后所有负值都将具有最高有效位(MSB,{{1} } bit)设置为0th

对于32位整数,这意味着以下表达式适用于所有负数:

1

反之亦然:所有正非零整数的MSB均为( anyNegativeNumber & 0x8000000 ) == 0x8000000 。最后,所有零值(0)的所有位都设置为int zero == 0

0

如果我们查看MSB(第一位),除了检查是否有任何其他位( anyPositiveNumber & 0x8000000 ) == 0 ,还有上述1函数所需的输出:

singleValue

我们可以通过屏蔽位直接从输入值创建value | first bit | any other bits | desired output 0 | 0 | 0 | 0b ( 0) 122 | 0 | 1 | 1b ( 1) -128 | 1 | 0 | 11111...111b (-1) -123 | 1 | 1 | 11111...111b (-1) 0,但1是一个特例,但我们可以处理它,如下所示:

-1

如果设置了diff的第1位,则返回int diff = x - y; // so only the 1st and last bits are set 。 如果diff值为-1,则返回0 否则返回0

1

这可以压缩:

return ( diff & 0x80000000 == 0x80000000 ) ? 0xFFFFFFFF : ( diff != 0 );

这仍然使用int diff; return ( ( ( diff = x - y ) & 0x80000000 ) == 0x80000000 ) ? 0xFFFFFFFF : ( diff != 0 ); ==运算符...这可以通过利用单位(!=位置)值可以消除的事实来消除移位n - 位正确将其转换为布尔值:

n

( diff = x - y ) >> 31 // evaluates to 1 if x < y, 0 if x == y or x > y 位可以通过以下事实消除:diff != 0对于所有非零值为!!a,对于零值为1

0

我们可以将两者结合起来:

!diff  // evaluates to 1 if diff == 0, else 0
!!diff // evaluates to 0 if diff == 0, else 1

此操作在其中有一个分支(int diff; return ( ( diff = x - y ) >> 31 ) ? -1 : !!diff; )和一个临时变量(?:),但是有一个相同功能的无分支版本。

可以看出三种可能的输出是:

diff

0xFFFFFFFF == 1111_1111 1111_1111 1111_1111 1111_1111 b 0x00000000 == 0000_0000 0000_0000 0000_0000 0000_0000 b 0x00000001 == 0000_0000 0000_0000 0000_0000 0000_0001 b 运算符具有&#34;符号扩展名&#34;对于签名值,这意味着:

>>

如果1000 b >> 2 == 1110 b 0100 b >> 2 == 0001 b 位为diff >> 31,则1111..11110,否则为1

每个位的值可以表示为0000..0000

的函数
diff

或者只是:

a = ( diff >> 31 ) // due to sign-extension, `a` will only ever be either 1111..1111 or 0000..0000
b = !!diff         // `b` will only ever 1 or 0
c = a | b          // bitwise OR means that `1111..1111 | 0` is still `1111..1111` but `0000..0000 | 1` will be `0000..0001`.

将其替换为上述表达式:

c = ( diff >> 31 ) | ( !!diff );

或者

int diff = x - y;
return ( diff >> 31 ) | !!diff;
必须使用

The comma operator,因为C没有指定也不保证二元运算符操作数表达式的求值顺序,但是逗号运算符的求值顺序是。

由于这是一个内联函数,假设我们可以使用可变参数,那么我们可以消除int diff; return diff = x - y, ( diff >> 31 ) | !!diff; ,因为我们只使用diffx一次:

y

这是我的测试程序和我得到的输出,使用GCC:

return x = x - y, ( x >> 31 ) | !!x;

输出:

#include <stdio.h>

int cmp(int x, int y) {

    return x = x - y, ( x >> 31 ) | !!x;
}

int main() {

    printf( "cmp( 1, 2 ) == %d\n", cmp( 1,2 ) );
    printf( "cmp( 2, 2 ) == %d\n", cmp( 2,2 ) );
    printf( "cmp( 2, 1 ) == %d\n", cmp( 2,1 ) );
}

现在这不完美,因为如果cmp( 1, 2 ) == -1 cmp( 2, 2 ) == 0 cmp( 2, 1 ) == 1 x都是大数且y为负数,则整数溢出问题,例如xChecking for this condition is possible但是尽可能地使代码变得简洁 - 你还需要添加处理错误条件的代码。在这种情况下,更好的方法是简单地检查用户提供的输入,而不是检查函数参数值。

TL; DR:

(-4000000000) - (4000000000)

答案 2 :(得分:0)

我可以建议:

int cmp(int x, int y) {
    return (x < y) ? -1 : (x > y);
}

x > y较大时,x比较为1,而当$.ajax({ url:"admin.php", method:"POST", data: {user_id: user_id, deleteall: true}, ... }); 不大时,admin.php比较为0。 Ryan的回答较短,但我认为这个版本仍然清楚地显示了代码的意图。