首先让我们澄清一下,我们假设:
- 负整数以2的补码表示
int
精确地为32位宽,long long
精确地为64位宽
- 对负数进行右移是算术移位
你的解决方案中(~x+1)
部分存在问题,它应该返回-x
。问题在于INT_MIN
的绝对值大于INT_MAX
的绝对值,因此当x为INT_MIN
时,(~x+1)
会产生INT_MIN
而不是你期望的-INT_MIN
。
你的解决方案(第二步)中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
以外的其他类型(但是如果进行更多工作,可以使用int
来模拟long long
)。
如果您使用int32_t
和int64_t
而不是int
和long long
,则整个过程可能更具可移植性。但是,它仍然不具备可移植性:
ISO/IEC 9899:2011 §6.5.7 位移运算符
5 E1 >> E2的结果是E1右移E2位。如果E1具有无符号类型或者E1具有带非负值的有符号类型,则结果的值是E1 / 2E2商的整数部分。如果E1具有带负值的有符号类型,则结果的值是实现定义的。
int
的大小会导致不可移植的代码。 - John Colemanx > y
。我认为可以安全地假设可移植性不是一个问题。 - bolovint32_t
。实际上,这个问题必须伴随着一堆特定于实现的条件。 - M.Mx = INT_MIN/2-10
和y = INT_MAX/2+20
,那么减法的结果很可能是一个正数(假设在溢出时进行了包装,这并不保证),这将无法得出正确的结果。在达到正限制时也存在类似的问题。 - Jonathan Leffler