- 如果
x == y 那么x - y == 0。
- 如果
x < y 那么x - y < 0
- 如果
x > y 那么x - 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,0th 位)设置为 @ 987654336@.
对于 32 位整数,这意味着以下表达式对所有负数都为真:
( anyNegativeNumber & 0x8000000 ) == 0x8000000
反之亦然:所有正非零整数的 MSB 为 0。最后,所有零值 (int zero == 0) 的所有位都设置为 0。
( anyPositiveNumber & 0x8000000 ) == 0
如果我们查看 MSB(第一位),除了检查是否有任何其他位是 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 是一种特殊情况,但我们可以处理它,如下所示:
int diff = x - y; // so only the 1st and last bits are set
如果设置了 diff 的第 1 位,则返回 -1。
如果差异值为0,则返回0
否则返回1
return ( diff & 0x80000000 == 0x80000000 ) ? 0xFFFFFFFF : ( diff != 0 );
这可以压缩:
int diff;
return ( ( ( diff = x - y ) & 0x80000000 ) == 0x80000000 ) ? 0xFFFFFFFF : ( diff != 0 );
这仍然使用== 和!= 运算符,但可以通过利用可以移动一位(nth 位)值的事实来消除n-bits将其转换为布尔值的权利:
( 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
>> 运算符具有符号值的“符号扩展”,这意味着:
1000 b >> 2 == 1110 b
0100 b >> 2 == 0001 b
如果0th 位为1,则diff >> 31 将为1111..1111,否则为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;
或者
int diff;
return diff = x - y, ( diff >> 31 ) | !!diff;
必须使用The comma operator,因为C不指定也不保证二元运算符操作数表达式的求值顺序,但逗号运算符的求值顺序是。
由于这是一个内联函数,并且假设我们可以使用可变参数,那么我们可以消除diff,因为我们只使用一次x 或y:
return x = x - y, ( x >> 31 ) | !!x;
这是我的测试程序和我使用 GCC 得到的输出:
#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 都是大数并且x 是负数,那么由于整数溢出的问题,现在这并不完美,例如(-4000000000) - (4000000000)。 Checking for this condition is possible 但是没有让代码尽可能简洁——您还需要添加处理错误情况的代码。在这种情况下,更好的方法是简单地检查用户提供的输入而不是检查函数参数值。
TL;DR:
int cmp(int x, int y) {
return x = x - y, ( x >> 31 ) | !!x;
}