1.绝对值
给出两种算法
a).n < 0时,~(n - 1);n >= 0 时,等于本身。
b).(n ^ (n >> 31)) - (n >> 31) ,设一个 int 为 32 位。
解释如下:
a).由于负数的补码等于除符号位之外的所有位取反后再加1,由于计算机内存中参与运算的负数都是补码形式,因此,为了求负数 n 的绝对值,需要先减1,再按位取反刚好是负数的绝对值(符号位1刚好也取反,变成0,表示正数,因此刚好是所求)。
b).
首先. n < 0时,设 n 的补码是 N,在计算机内存里原式为 (N ^ (N >> 31) - (N >> 31)) ,N >> 31 等于 -1,因为 >> 是带符号的位移,即移位时,符号位一直不变。而 -1 的补码是 2 ^ 31 - 1 = 0xFFFF,所以 N ^ ( -1 ) 相当于按位取反。后面的减负 1 即是加1.所以,当 n < 0 时,n 的绝对值为 ~(n) + 1。
与 a) 式联立有 :~(N - 1) = ~(N) + 1,那么,这个式子是不是成立的呢?
证明:不论 N 是否小于 0,总有 ~(N - 1) = ~(N) + 1。
证:
故一个负数 n 的绝对值是 ~(n) + 1 是正确的由于 ~(x) + x = ~(0)对于所有 x 是成立的,因此 ~(x) = ~(0) - x,~(x - 1) = ~(0) - x + 1,故 ~(N - 1) = ~(0) - N + 1 = (~(0) - N) + 1 = ~(N) + 1
即原式得证
再者. n >= 0 时,n >> 31 为 0,所以上式的结果还是 n。一个正数的绝对值是本身,是正确的。
因此,b) 命题得证。
2.补码本质
想像一个钟表有 N 格,上面每格依次标记为 0 ~ N - 1,顺时间方向走 x 格,等价于逆时钟走 N - x 格。即 x 的补码是 N - x。
可参照我的另一篇文章《补码的实质》。
3.判断 n 是不是2的幂
0 == n & (n - 1)
4.相反数
我们在第一个命题中已经证明 :当 n < 0 时,n 的绝对值为 ~(n) + 1,也即 n < 0 时,n 的相反数是 ~(n) + 1;
当 n > 0 时,求其相反数就是求 - n 的补码,要把 -n 的各位除符号位外取反,再加 1,也即 ~(-n除符号位外) + 1,而此式刚好就等于 ~(n) + 1,刚好把符号位变为 1,所以刚好是所求。
即 n 的相反数是 ~(n) + 1
5.最大值,最小值
max(a,b) = a & ((b - a) >> 31) | (b & (~(b - a)) >> 31) //注意 | 后面的一项不能写为 b & ((a - b) >> 31),考虑到 a == b 的情况,这样写是错误的。
min(a, b) = a & ((a - b) >> 31) | (b & (~(a - b)) >> 31)
或者
min(a,b) = a ^ ((a ^ b) & -(a < b))
解释:当 b >= a 时,-(a - b) 为 0 ,整个结果为 a;当 a > b 时,-(a < b) 为 -1 ,任何数与 -1 相与结果不变,所以,整个结果为 a ^ a ^ b 为 b.
6. n & (n - 1) 令 n 中的 1 减少一个
如二进制 1010,进行运算 1010 & (1010 - 1) = 1 000,原来的二进制数中有两个 1,现在只有一个 1了。
证明:
设
N - 1 可以理解为 N 的最低位的一个 1 拿来减 1,那么这个位前面所有的位都保持不变,设最后一个 1 位为第 m 位,设这个位前面所有位构成的数字为 M,则
作图 1 并给出证明如下:
图 1 n & (n-1) 比 n 少一个 1
有了这个结论之后,就可以用来计算一个整数中,有多少个 1.给出代码如下:
int count1(int n)
{
int ncount = 0;
for(; n ;++ ncount)
{
n &= (n - 1);//每次都减少一个1,直到 n 中没有1
}
return ncount;
}
本文详细解析了如何通过补码计算负数的绝对值和正数的相反数,包括补码的本质、负数绝对值的计算公式、正数相反数的计算方法以及相关数学证明。
2269

被折叠的 条评论
为什么被折叠?



