我最近在其中一门课上接受了测验。问题如下:
在C中编写一个函数(称为
cmp
),接受两个整数(x
和y
) 如果-1
<x
,则返回y
如果0
=x
y
,1
x
,则y
cmp
。写int cmp(int x, int y) { return ((x < y) ? (-1) : ((x == y) ? (0) : (1))); }
尽可能简洁。
我能想到的最简洁的功能是:
&
但我觉得可能会有一些操作,我可以用它来更简洁地做到这一点。也许是^
和{{1}}的组合?最近几天这一直困扰着我,我想知道实际上 是 是否有更好的方法来做到这一点?
答案 0 :(得分:14)
“尽可能简洁”是对测验的极其模糊的要求。你有望打高尔夫吗?删除空格和括号是否使其更简洁?无论如何,这是一个使用算术比较结果的解决方案:
int cmp(int x, int y) {
return (x > y) - (x < y);
}
答案 1 :(得分:8)
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,{{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..1111
为0
,否则为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;
,因为我们只使用diff
或x
一次:
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
为负数,则整数溢出问题,例如x
。 Checking for this condition is possible但是尽可能地使代码变得简洁 - 你还需要添加处理错误条件的代码。在这种情况下,更好的方法是简单地检查用户提供的输入,而不是检查函数参数值。
(-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的回答较短,但我认为这个版本仍然清楚地显示了代码的意图。