我试图确定是否可以计算两个32位整数的总和而不会溢出,同时仅使用某些按位运算符和其他运算符。因此,如果可以在不溢出的情况下添加整数x和y,则以下代码应返回1,否则返回0。
(((((x >> 31) + (y >> 31)) & 2) >> 1))
但是,它应该为1时返回0,反之亦然。当我使用逻辑NOT(!)运算符或按位XOR(^)和0x1时,它不能解决问题。
!(((((x >> 31) + (y >> 31)) & 2) >> 1))
(((((x >> 31) + (y >> 31)) & 2) >> 1) ^ 0x1)
^这些不起作用。
提前致谢。
答案 0 :(得分:8)
这有点清洁:
~(x & y) >> 31
<强>更新强>
kriss的评论是正确的。所有这些代码都检查两个MSB是否都已设置。
我只是看着kriss的答案,我发现只使用一个加法,加上按位运算符,假设无符号整数,就可以完成同样的事情。
((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) & 0x80000000 & (x | y)
第一个带括号的部分将MSB都设置为0然后添加结果。任何进位都将以结果的MSB结束。随身携带的下一个bitmask隔离区。最后一项检查x或y上的集合MSB,这导致整体进位。要满足问题中的规范,请执行以下操作:
~(((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) & 0x80000000 & (x | y)) >> 31
答案 1 :(得分:2)
假设两个数都是无符号整数。如果你使用有符号整数,那将有点棘手,因为有两种方法可以获得溢出,或者添加两个大的正数来添加两个大的负数。无论如何检查最重要的位是不够的,因为加法传播携带位,你必须考虑它。
对于无符号整数,如果你不打算欺骗一个简单的方法是:
(x+y < x) || (x+y < y)
这将起作用,因为大多数编译器在溢出发生时都不会做任何事情,只是让它成为。
您还可以注意到,对于溢出发生,至少有两个数字中的一个必须将其最重要的位设置为1.因此类似的东西应该起作用(小心,未经测试),但它比其他版本更加复杂
/* both Most Significant bits are 1 */
(x&y&0x80000000)
/* x MSb is 1 and carry propagate */
||((x&0x80000000)&&(((x&0x7FFFFFFF)+y)&0x80000000))
/* y MSb is 1 and carry propagate */
||((y&0x80000000)&&(((y&0x7FFFFFFF)+x)&0x80000000))
答案 2 :(得分:1)
合乎逻辑!对我来说工作正常。
me@desktop:~$ cat > so.c
#include <stdio.h>
void main() {
int y = 5;
int x = 3;
int t;
t = (((((x >> 31) + (y >> 31)) & 2) >> 1));
printf("%d\n", t);
t = !(((((x >> 31) + (y >> 31)) & 2) >> 1));
printf("%d\n", t);
}
^D
me@desktop:~$ gcc -o so so.c
me@desktop:~$ ./so
0
1
me@desktop:~$ uname -a
Linux desktop 2.6.32-23-generic #37-Ubuntu SMP Fri Jun 11 07:54:58 UTC 2010 i686 GNU/Linux
答案 3 :(得分:1)
没有简单的基于位运算的溢出测试,因为加法涉及进位。但是有一些简单的溢出测试,不涉及调用溢出或无符号整数包装,它们甚至比添加然后检查溢出更简单(这当然是有符号整数的未定义行为):
对于无符号整数x
和y
:(x<=UINT_MAX-y)
对于有符号整数,首先检查它们是否有相反的符号。如果是这样,添加是自动安全的。如果它们都是正面的,请使用(x<=INT_MAX-y)
。如果它们都是否定的,请使用(x>=INT_MIN-y)
。
答案 4 :(得分:0)
那些是否签署了整数?你的逻辑看起来对于无符号整数(unsigned int)应该没问题,但对于常规整数则不行,因为在这种情况下,移位将保留符号位。