大于C中的函数
我知道这是一个由来已久的问题,您可能也遇到过这个问题,但我的解决方案中有一个bug,我不知道如何解决它。我需要写一个比较两个整数的函数。我只允许使用操作(!、~、&、^、|、+、>>,,首先让我们澄清一下,我们假设:大于C中的函数,c,bitwise-operators,C,Bitwise Operators,我知道这是一个由来已久的问题,您可能也遇到过这个问题,但我的解决方案中有一个bug,我不知道如何解决它。我需要写一个比较两个整数的函数。我只允许使用操作(!、~、&、^、|、+、>>,,首先让我们澄清一下,我们假设: 负整数用2的补码表示 int正好是32位宽,long-long正好是64位宽 右移负数是一种算术移位 解决方案中的(~x+1)部分有问题,它应该返回-x。问题是INT\u MIN的绝对值大于INT\u MAX的绝对值,因此当x是INT\u MIN时,(~x+1)产生INT\u
- 负整数用2的补码表示
正好是32位宽,int
正好是64位宽long-long
- 右移负数是一种算术移位
(~x+1)
部分有问题,它应该返回-x
。问题是INT\u MIN
的绝对值大于INT\u MAX
的绝对值,因此当x是INT\u MIN
时,(~x+1)
产生INT\u MIN
而不是您所期望的-INT\u MIN
解决方案的y+(-x)
部分也存在溢出问题(第二步)
现在,如果允许您使用除int
以外的其他类型,我们可以通过在转换之前将值强制转换为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\u MIN
,x==0
和x==INT\u 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));
}
}
int main(无效){
int x=int_MIN;
对于(long long y=INT_MIN;y y));
}
x=INT_MAX;
对于(long long y=INT_MIN;y y));
}
x=0;
对于(long long y=INT_MIN;y y));
}
}
这在我的特定机器上使用特定的编译器是成功的。测试耗时163秒
但同样,这取决于是否能够使用除int
以外的其他类型(但同样,通过更多的工作,您可以使用int
模拟long
)
如果您相应地使用int32\u t
和int64\u t
而不是int
和long
,那么整个程序可能更易于移植。但是,它仍然不是便携式的:
ISO/IEC 9899:2011§6.5.7位移位运算符
5 E1>>E2的结果是E1右移位E2位位置。如果E1具有无符号类型或E1具有有符号类型和非负值,则结果值为E1/2E2商的整数部分。如果E1具有有符号类型和负值,则结果值由实现定义
Hacker's Delight有一章比较谓词,这正是我们在这里需要的 它写的东西之一是:
x < y: (x - y) ^ ((x ^ y) & ((x - y) ^ x))
我有一个网站说它是有效的
如果允许使用局部变量,则可以通过分解子表达式将其简化一点~(~y+x)
:
假设您知道
int
的大小,就会产生不可移植的代码。@JohnColeman试图通过按位操作获得x>y
。我认为可以肯定的是,可移植性不是一个问题,我们假设32位和2位是互补的。我们现在不担心可移植性,因为这不是问题所在。@MarkWeston仍然存在右移是实现定义的问题,有些系统甚至可能没有定义int32\t
。实际上,这个问题必须伴随着一系列特定于实现的条件。可移植性不是本练习的问题。这段代码永远不会接近生产代码。这只是位运算的练习。我同意资源可以更好地用于学习其他东西,而不是这些无用的技巧,但这就是OP发布的练习和问题。我甚至同意应该教授可移植性。但让我们把注意力集中在手头的问题上。
x < y: (x - y) ^ ((x ^ y) & ((x - y) ^ x))
// 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
int diff = ~(~y + x);
return ((diff ^ ((y ^ x) & (diff ^ y))) >> 31) & 1;