我知道这是一个古老的问题,你可能也遇到过这个问题,但我的解决方案中存在一个错误,我不知道如何解决它。我需要编写一个比较两个整数的函数。我只被允许使用操作(!,〜,&,^,|,+,>>,<<),也没有控制结构(if,else循环等)。
isGreater(int x, int y) {
//returns 1 if x > y.
return ((y+(~x+1))>>31)&1;
}
我的想法很简单,我们计算yx,我们移动31来得到符号位,如果它是负数,那么我们返回零,否则我们返回1.当x为负且falsly返回1时失败它应该返回零。我坚持这一点并且不知道如何继续。
我们假设整数是32位并使用两个补码表示。这个问题与可移植性无关。 一些帮助将非常感激。
提前致谢
答案 0 :(得分:4)
Hacker's Delight有一章比较谓词,这正是我们在这里所需要的。
它写的一件事是:
x < y: (x - y) ^ ((x ^ y) & ((x - y) ^ x))
我们几乎可以直接使用,除了应该交换x
和y
之外,减法必须用合法的东西替换,结果出现在最高位而不是最低位。幸运的是a - b == ~(~a + b)
所以这并不太难。首先应用这些转换:
// swap x <-> y
(y - x) ^ ((y ^ x) & ((y - x) ^ y))
// rewrite subtraction
~(~y + x) ^ ((y ^ x) & (~(~y + x) ^ y))
// get answer in lsb
((~(~y + x) ^ ((y ^ x) & (~(~y + x) ^ y))) >> 31) & 1
我有一个网站here,说它有效。
如果允许局部变量,可以通过分解子表达式来简化一点
~(~y + x)
:
int diff = ~(~y + x);
return ((diff ^ ((y ^ x) & (diff ^ y))) >> 31) & 1;
答案 1 :(得分:2)
首先让我们澄清一下我们假设:
int
正好是32位宽,long long
正好是64位宽您的解决方案中的(~x+1)
部分存在问题,该部分应返回-x
。问题是INT_MIN
的绝对值大于INT_MAX
的绝对值,因此当x为INT_MIN
时,(~x+1)
会产生INT_MIN
而不是-INT_MIN
{1}}正如您所料。
解决方案y+(-x)
部分的溢出也存在问题(第二步)。
现在,如果您允许使用除int
以外的其他类型,我们可以通过在转换前将值转换为long long
来解决这两个问题,假设它是64位类型,以便(~x+1)
返回预期结果-x
,而y+(-x)
不会导致任何溢出。然后,显然,我们必须将>>31
位更改为>>63
。
最终解决方案如下:
static bool isGreater(int x, int y) {
long long llx = x;
long long lly = y;
long long result = ((lly+(~llx+1))>>63)&1;
return result;
}
使用x == INT_MIN
,x == 0
和x == INT_MAX
等一些案例进行测试是可行的:
int main(void) {
int x = INT_MIN;
for (long long y = INT_MIN; y <= INT_MAX; ++y) {
assert(isGreater(x, y) == (x > y));
}
x = INT_MAX;
for (long long y = INT_MIN; y <= INT_MAX; ++y) {
assert(isGreater(x, y) == (x > y));
}
x = 0;
for (long long y = INT_MIN; y <= INT_MAX; ++y) {
assert(isGreater(x, y) == (x > y));
}
}
使用我的特定编译器在我的特定机器上取得了成功。测试花了163秒。
但同样,这取决于能够使用除int
以外的其他类型(但是再次使用更多的工作,您可以使用long long
模仿int
。
如果您使用int32_t
和int64_t
代替int
和long long
,则整个过程可能更具可移植性。但是,它仍然不可移植:
ISO / IEC 9899:2011§6.5.7按位移位运算符
5 E1&gt;的结果&gt; E2是E1右移E2位位置。如果E1具有无符号类型或者E1具有带符号类型和非负值,则结果的值是E1 / 2E2的商的整数部分。如果E1具有有符号类型和负值,则结果值是实现定义的。