机器码与位运算

基础概念

机器码

一般书写表示的数叫做真值真值在计算机中的表示方式叫做机器码

计算机内存中整数是按照二进制补码进行存储的,浮点数在内存中按照科学计数法存储

正整数的原码、反码、补码三种标识完全一样,而负整数用原码、反码、补码表示时,符号位都为1,用二进制表示的数值位却各不相同:原码符号位为1不变,数值位按位取反得到反码,反码符号位不变,最低位加1得到补码。

关于机器码的更多介绍详见int_32的最大值与最小值(C/C++)第3节。

位运算

所有的位运算都是针对整数范围,浮点数因为其在内存中特殊的存储方式无法进行位运算。

符号位是参与位运算的

按位与 & 

参加运算的两个数,换算为二进制(0、1)后,进行与 & 运算。只有当相应位上的数都是1时,该位才取1,否则该位为0。

123456

按位或 | 

参加运算的两个数,换算为二进制(0、1)后,进行或 | 运算。只要相应位上存在1,那么该位就取1,均不为1,即取0。

123456

按位异或 ^ 

参加运算的两个数,换算为二进制(0、1)后,进行异或 ^ 运算。只有当相应位上的数字不相同时,该位才取1,若相同,即取0。

在这里插入图片描述

可以看出,任何数与0异或,结果都是其本身。利用异或还可以实现一个很好的交换算法,用于交换两个数,算法如下:

123a = a ^ b;b = b ^ a;a = a ^ b;

取反 ~ 

参加运算的两个数,换算为二进制(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

posted @ 2020-12-31 23:33  箐茗  阅读(751)  评论(0编辑  收藏  举报