机器码与位运算
基础概念
机器码
一般书写表示的数叫做真值,真值在计算机中的表示方式叫做机器码。
计算机内存中整数是按照二进制补码进行存储的,浮点数在内存中按照科学计数法存储。
正整数的原码、反码、补码三种标识完全一样,而负整数用原码、反码、补码表示时,符号位都为1,用二进制表示的数值位却各不相同:原码符号位为1不变,数值位按位取反得到反码,反码符号位不变,最低位加1得到补码。
关于机器码的更多介绍详见int_32的最大值与最小值(C/C++)第3节。
位运算
所有的位运算都是针对整数范围,浮点数因为其在内存中特殊的存储方式无法进行位运算。
符号位是参与位运算的!
按位与 &
参加运算的两个数,换算为二进制(0、1)后,进行与 & 运算。只有当相应位上的数都是1时,该位才取1,否则该位为0。
按位或 |
参加运算的两个数,换算为二进制(0、1)后,进行或 | 运算。只要相应位上存在1,那么该位就取1,均不为1,即取0。
按位异或 ^
参加运算的两个数,换算为二进制(0、1)后,进行异或 ^ 运算。只有当相应位上的数字不相同时,该位才取1,若相同,即取0。
可以看出,任何数与0异或,结果都是其本身。利用异或还可以实现一个很好的交换算法,用于交换两个数,算法如下:
取反 ~
参加运算的两个数,换算为二进制(0、1)后,进行取反 ~ 运算。每个位上都取相反值,1变成0,0变成1。
左移 <<
参加运算的两个数,换算为二进制(0、1)后,进行左移 << 运算,用来将一个数各二进制位全部向左移动若干位。右边空出的位置补0,左移一位相当于乘以2:
右移 >>
参加运算的两个数,换算为二进制(0、1)后,进行右移 >> 运算,用来将一个数各二进制位全部向右移动若干位。左边空出的位,如果是正数则补0,若为负数则补0或1,取决于所用的计算机系统,其值相当于除以2。
可以发现,右移一位的结果就是原值除2,左移两位的结果就是原值除4。
位运算小技巧
判断奇偶
int a; a&1 == 0 //偶数 a&1 == 1 //奇数
取第k位
int a; a >> k & 1; //(k = 0,1,2……sizeof(int))
第k位清0 / 置1
a = a & ~(1 << k); //清0 a = a | (1 << k); //置1
循环左移 / 右移k次
// 假设sizeof(int) = 16 a = a << k | a >> 16 - k; //循环左移 a = a >> k | a << 16 - k; //循环右移
整数的平均值
对于两个整数$x$和$y$,如果用$(x + y) / 2$求平均值,会产生溢出,因为$x + y$可能会大于 INT_MAX ,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法:
#define AVE(x,y) ((x) & (y)) + (((x) ^ (y)) >> 1)
判断2的幂
((x & (x - 1)) == 0) && (x != 0);
不用temp交换两个整数
void swap(int x, int y) { x ^= y; y ^= x; x ^= y; }
计算绝对值
int abs(int x) { int y ; y = x >> 31 ; return (x ^ y) - y; //or: (x +y) ^ y }
四则运算转化成位运算 (在不产生溢出的情况下)
取余
a % (2^n) 等价于 a & (2^n - 1)
- a % 2 等价于 a & 1 ( a & log2(2))
- a % 4 等价于 a & 2 ( a & log2(4))
- ...
- a % 32 等价于 a & 5
乘法
a * (2^n) 等价于 a << n
除法
a / (2^n) 等价于 a >> n
if else条件分支
if (x == a) x = b; else x = a; // 等价于 x = a ^ b ^ x;
相反数
x的相反数表示为 (~x + 1)。
右移 vs. 除法
如果你做过右移一位和除以2的结果对比实验,就会发现:负奇数的两个结果不一致。
也和编译器有关,具体可查看汇编代码:
int F, G, X = -5;
00DF39F0 mov dword ptr [ebp-58h],0FFFFFFFBh ;X赋值为-5
F = X / 2;
00DF39F7 mov eax,dword ptr [ebp-58h] ;将X的值移到寄存器eax 00DF39FA mov ecx,2 ;将值2移到ecx 00DF39FF cdq ;将eax高位扩展到edx 00DF3A00 idiv eax,ecx ;做除法运算 00DF3A02 mov dword ptr [ebp-50h],eax ;移动到内存
G = x >> 1;
00DF3A05 mov eax,dword ptr [ebp-58h] ;将X的值移到寄存器eax 00DF3A08 sar eax,1 ;eax的值算术右移1位 00DF3A0A mov dword ptr [ebp-54h],eax ;结果移动到内存
究其原因,总结一句话:
除法运算,结果都是向0取整(即,正数,向下取整;负数,向上取整)。
而位运算,都是向下取整。
理论证明
xkh运用数学公式的方法证明了计算机中整数右移与除2的差别,点击原文可查看。这里附上文稿图片:
(整理自网络)
参考资料:
https://blog.csdn.net/tinyjian/article/details/50429660
https://blog.csdn.net/EnjoySilence/article/details/8827921
https://www.cnblogs.com/javaXRG/p/11135953.html
https://www.cnblogs.com/yonglin1998/p/11780856.html