笔记一下二进制在程序中的应用

运算符含义

运算符含义功能
&按位与如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。
l按位或两个相应的二进制位中只要有一个为1,该位的结果值为1。
按位异或若参加运算的两个二进制位同号则结果为0(假)异号则结果为1(真)
取反~是一个单目(元)运算符,用来对一个二进制数按位取反,即将0变1,将1变0。
<<左移左移运算符是用来将一个数的各二进制位全部左移N位,右补0。
>>右移表示将a的各二进制位右移N位,移到右端的低位被舍弃,对无符号数,高位补0。

运算符运算

运算符或 ‘l’ or与 “&”and非 “~” not异或 “^” xor
操作数101010101110101011010101010000001
操作数20010101010101010(无)01111111
也能算结果01111111100000000101010111111110

常用二进制数

二进制数二进制值用处
0xAAAAAAAA10101010101010101010101010101010偶数位为1,以1位为单位提取奇位
0x5555555501010101010101010101010101010101奇数位为1,以1位为单位提取偶位
0xCCCCCCCC11001100110011001100110011001100以“2位”为单位提取奇位
0x3333333300110011001100110011001100110011以“2位”为单位提取偶位
0xF0F0F0F011110000111100001111000011110000以“8位”为单位提取奇位
0x0F0F0F0F00001111000011110000111100001111以“8位”为单位提取偶位
0xFFFF000011111111111111110000000000000000以“16位”为单位提取奇位
0x0000FFFF00000000000000001111111111111111以“16位”为单位提取偶位

运算符的应用:

  • 判断奇偶性:
bool Parity(Type value)  
{  
    if(value & 0x0001 == 0)  
        return false;  
    else  
        return true;  
}  
//加以优化:   
template<class Type>  
inline bool Parity(Type value)  
{  
    return (value & 1 != 0);  
}  
  • 是否是2的二次方:
bool is_pow2(int x) //判断是否2的n次方 
{
    x &= x-1;
    if(!x) return true;
    return false;
}

可以写成 return ( (x&(x-1))==0) && (x!=0);  
  • 计算绝对值:
abs( x ) { 
      y=x>>31 ; 
      return(x^y)-y;//也可写作 (x+y)^y 
  }
  • 补码运算公式:
-x = ~x + 1 = ~(x-1)
~x = -x-1
-(~x) = x+1
~(-x) = x-1
x+y = x - ~y - 1 = (x|y)+(x&y)
x-y = x + ~y + 1 = (x|~y)-(~x&y)
x^y = (x|y)-(x&y)
x|y = (x&~y)+y
x&y = (~x|y)-~x
x==y:    ~(x-y|y-x)
x!=y:    x-y|y-x
x< y:    (x-y)^((x^y)&((x-y)^x))
x<=y:    (x|~y)&((x^y)|~(y-x))
x< y:    (~x&y)|((~x|y)&(x-y))//无符号x,y比较
x<=y:    (~x|y)&((x^y)|~(y-x))//无符号x,y比较
  • 计算平均值:
int average(int x, int y)   //返回X、Y的平均值  
{     
     return (x & y) + ( (x^y)>>1 );  
     //x&y  取出x和y二进制都为 1 的所有位,这是x、y都为 1 的部分,因为相同,所以直接加就行了  
     //x^y  x和y中有一个为 1 的所有位  
     //后者是x为 1,y为 0的部分,以及y为 1,x为 0 的部分,两部分加起来除以2,然后跟前面的相加就可以了  
}  
  • 不用临时变量交换两个数:
void swap(int x , int y)  
{  
    x ^= y;  
    y ^= x;  
    x ^= y;  
}  

如果x=7,y=8,
x^=y>>7^=8,x=15,y=8
y^=x>>8^=15,x=15,y=7
x^=y>> x^=y,x=8,y=7
  • 常用运算(在不产生溢出的情况下):
取模运算转化成位运算
a % (2^n) 等价于 a & (2^n - 1)

乘法运算转化成位运算
a * (2^n) 等价于 a<< n

除法运算转化成位运算 
 a / (2^n) 等价于 a>> n
 例: 12/8 == 12>>3

 a % 2 等价于 a & 1       

 if (x == a)
     x= b;
 else 
     x= a;
等价于 x= a ^ b ^ x;
  • 统计数字二进制中1的个数:
    朴素的统计办法是:先判断n的奇偶性,为奇数时计数器增加1,然后将n右移一位,重复上面步骤,直到移位完毕。
    博客出处
template<class Type>  
inline bool Parity(Type value)  
{  
    return (value % 2 != 0);  
}  

template<class Type>  
inline int CountOne(Type value)  
{  
    if(value != 0)  
    {  
        return Parity(value) + CountOne(value >> 1);  
    }  
    return 0;  
}  

另一种统计方法:32位无符 号数的1的个数可以这样数:

int CountOne(unsigned long n)  
{  
    //0xAAAAAAAA,0x55555555分别是以“1位”为单位提取奇偶位  
    n = ((n & 0xAAAAAAAA) >> 1) + (n & 0x55555555);  
    //0xCCCCCCCC,0x33333333分别是以“2位”为单位提取奇偶位  
    n = ((n & 0xCCCCCCCC) >> 2) + (n & 0x33333333);  
    //0xF0F0F0F0,0x0F0F0F0F分别是以“4位”为单位提取奇偶位  
    n = ((n & 0xF0F0F0F0) >> 4) + (n & 0x0F0F0F0F);  
    //0xFF00FF00,0x00FF00FF分别是以“8位”为单位提取奇偶位  
    n = ((n & 0xFF00FF00) >> 8) + (n & 0x00FF00FF);  
    //0xFFFF0000,0x0000FFFF分别是以“16位”为单位提取奇偶位  
    n = ((n & 0xFFFF0000) >> 16) + (n & 0x0000FFFF);  

    return n;  
}  

再来一种:
int bitCount(int x) {
  // 把32位划分为8部分,每部分4位。使用掩码0x11111111来获得每部分的最后一位。
  int mask = 0x11 + (0x11 << 8) + (0x11 << 16) + (0x11 << 24);
  // 通过右移、相加,来计数每一部分中1的数量。
  int seg8Count = (x & mask) + ((x >> 1) & mask) + ((x >> 2) & mask) + ((x >> 3) & mask);
  int seg4Count1 = seg8Count + (seg8Count >> 8);
  int seg4Count2 = (seg8Count >> 16) + (seg8Count >> 24);
  // 为了处理相加后的进位问题,我们需要与0x0F进行位与运算,只保留每部分最后4位。
  int result = (seg4Count1 & 0x0F) + ((seg4Count1 >> 4) & 0x0F) + (seg4Count2 & 0x0F) + ((seg4Count2 >> 4) & 0x0F);
  return result;
}
  • 取非:
int negate(int x) {
  return ~x + 1;
}
  • 果所有偶数位都是1,则返回1:
int allEvenBits(int x) {
  // 把32位划分为4部分,每部分8位。每部分都互相进行位与运算,再和0x55进行位与运算,取偶数位。
  // 当且仅当所有偶数位都是1,位与运算的结果才是0x55。然后加上0xAB,将进位变为0x100。
  // 如果有偶数位不是1,那么结果必然小于0x55,加上0xAB后,也会小于0x100。
  // 因此,这两种情况的区别就在于从右往左数第9位. 右移该位得到最后结果。 
  int y;
  int result = 0;
  y = x & (x >> 16);
  y = y & (y >> 8);
  result = ((y & 0x55) + 0xAB) >> 8;
  return result;
}
  • x是否大于y:
int isGreater(int x, int y) {
  // 如果x是非负数,y是负数,则flag的标志位为1。在此情况下,x一定大于y。
  int flag1 = (~x) & y;
  // 如果x,y的标志位相同,则flag2的标志位为0。需要进一步比较。
  // 如果x,y相等,则flag==0。
  int flag2 = x ^ y;
  // 如果x,y的标志位相同,则不可能发生溢出,除非x,y都是0x80000000。
  // 对于x,y都是0x80000000的特殊情况,可以使用x!=y来判断。
  // 如果x>y,他们差的标志位将会是0。
  int difference = x + (~y + 1);
  // 只有以下2种情况,result会是1:
  //   1. flag1是1 (x是非负数,y是负数)
  //   2. x, y同正负, 并且x-y>=0, 并且x!=y.
  int result = ((flag1 | ~(flag2 | difference)) >> 31) & 0x01 & !!flag2;
  return result;
}
  • 系统中权限应用:

设各权限值分别如下(2倍等比递增的关系):
1. 列表/查看 = 2
2. 新增 = 4
2. 修改 = 8
4. 删除 = 16

若某个(Role + Module)拥有查看、新增、修改、删除的权限,则其Permission=2+4+8+16=30。
判断Permission是否包含“查看”的权限值,就用 if((Permission & 2) == 2)
判断Permission是否包含“新增”的权限值,就用 if((Permission & 4) == 4)
判断Permission是否包含“修改”的权限值,就用 if((Permission & 8) == 8)
判断Permission是否包含“删除”的权限值,就用 if((Permission & 16) == 16)

  • 图像中颜色应用:

颜色RGB值,我们在这里谈24位颜色。也就是RGB中的R(红)、G(绿)、 B(蓝)分别占8位

a << 24
    1111 1111 0000 0000 0000 0000 0000 0000
r << 16
    0000 0000 1111 1111 0000 0000 0000 0000
g << 8
    0000 0000 0000 0000 1111 1111 0000 0000
b
    0000 0000 0000 0000 0000 0000 1111 1111

32位颜色除了RGB,还有一个A,即透明度
获取ARGB值:

  var color:uint = 0xff342388;

  var a:uint = color >>> 24 //注意这里是>>>,无符号右移位操作,右移24位,把342388移出,得到0xff
  //也可表示为:((color >> 24) & 255);
  var r:uint = color >> 16 & 0xff;//右移16位,把2388移出,取0x34

  var g:uint = color >> 8 & 0xff;//右移8位,把88移出,得0x3423,与0xff按位与操作,得0x23

  var b:uint = color & 0xff;//得到0x88

生成颜色值:

public static int ToArgb(this Color color) {
    int argb = color.A << 24;
    argb += color.R << 16;
    argb += color.G << 8;
    argb += color.B;
    return argb;
}

博客出处

  • 游戏中角色状态应用:

假设游戏状态如下:

enum EPLAYER_STATE
    {
        EPST_NONE    = 0x00000000,     // 没有状态
        EPST_ADDHP  = 0x00000001 ,    // 加血
        EPST_ADDMP  = 0x00000002,     // 加蓝
        EPST_ADDSP   = 0x00000004,    // 加体力
        EPST_JMDAM   = 0x00000008,    // 经脉受伤
        EPST_DIANX    = 0x00000010,    // 被点穴
        EPST_XUANY    = 0x00000020,    // 被眩晕
        EPST_ATTCK    = 0x00000040,    // 被攻击
        // ... ...
        // 最多可以写32个状态,已经足够了。为什么是32,因为一个32位整数来存放的。
    };

初始状态:

UINT dwPlayerState = EPST_NONE;

假如我要同时加上几个状态的话。那么:

dwPlayerState |= ( EPST_ADDMP| EPST_ADDSP| EPST_JMDAM );

如果我要清掉状态:

// 清除蓝药状态
dwPlayerState &= ~EPST_ADDMP;   // 这样便清掉了。
// 清除多个状态
 dwPlayerState &= ~( EPST_ADDMP| EPST_ADDSP| EPST_JMDAM );

参考,参考,参考,参考,参考,参考,参考,参考,参考,参考,参考,参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值