C语言整数位运算中的一些技巧

1 与(&)

1. 与(&)运算实现求余%运算

在计算机里整数求余运算比较低效,在对2的整数幂进行求余时,可以使用&来优化

如: a对2 ^ n (n为自然数)求余:

a % (2 ^ n)  = a & ((2 ^ n) - 1)  // 如 a % 4 = a & 0x3

判断奇数偶数时,可以与1相与

if (n & 0x1 == 0x1) {
    // printf("n 是奇数");
}

2. 计算一个整数2进制中有多少个1

利用n = n & (n - 1) 用来将最低位的1变为0

count = 0
while (n)
{
   n &= (n - 1);
   count++;
}

3. 判断一个数是否是2的整数次幂

如:判断n是否是2的整数次幂,2的整数次幂即整数的二进制表示中只有一个1,如(2--> 010b 4-->0100b等)

if (! (n & (n - 1))) {
    // printf("n is 2 的整数次幂");
}

4. 获取一个整数2进制最低位的1(最右边的1)

n & -n        // 最低位的1被与运算保留下来, 同样可以使用 n & -n == n 来判断n是不是2的整数次幂
n & (~n + 1)  // ~n+1即对n取反再加1,这个即得到-n的补码 ~n+1=-n

有时候,我们希望得到整数二进制最低位1的位置:

/* Find the First bit in uint8_t data */
uint8_t FindFirstBit_u8(uint8_t n)
{
    n = (n-1) & ~n;
    n = ((n & 0xAA) >> 1) + (n & 0x55);
    n = ((n & 0xCC) >> 2) + (n & 0x33);
    n = ((n & 0xF0) >> 4) + (n & 0x0F);
    return n;
}
/* Find the First bit in uint16_t data */
uint16_t FindFirstBit_u16(uint16_t n)
{
    n = (n-1) & ~n;
    n = ((n & 0xAAAA) >> 1) + (n & 0x5555);
    n = ((n & 0xCCCC) >> 2) + (n & 0x3333);
    n = ((n & 0xF0F0) >> 4) + (n & 0x0F0F);
    n = ((n & 0xFF00) >> 8) + (n & 0x00FF);
    return n;
}

/* Find the First bit in uint32_t data */
uint32_t FindFirstBit_u32(uint32_t n)
{
    n = (n-1) & ~n;
    n = ((n & 0xAAAAAAAA) >> 1) + (n & 0x55555555);
    n = ((n & 0xCCCCCCCC) >> 2) + (n & 0x33333333);
    n = ((n & 0xF0F0F0F0) >> 4) + (n & 0x0F0F0F0F);
    n = ((n & 0xFF00FF00) >> 8) + (n & 0x00FF00FF);
    n = ((n & 0xFFFF0000) >> 16) + (n & 0x0000FFFF);
    return n;
}

2 或(|)

(待补充)

3 异或(^)

1. 异或的重要性质

  • 一个数与自身异或,总是为0

    x ^ x = 0
    

    可以用来判断两个变量是否相等,如c语言中判断,变量a与b是否相等

    if ((a ^ b) == 0)
    {
       // printf("a 与 b相等") 
    }
    
  • 一个数与0异或,总是为自身

    x ^ 0 = x
    

2. 不使用临时变量交换两个整数的值

交换两个整数的值,通常做法是借助临时变量,然后交换,如:

tmp = a
a = b
b = tmp

利用异或的性质,不用临时变量也能交换两个数的值,过程如下:

a ^= b
b ^= a
a ^= b

4 移位

移位分为:逻辑左移、逻辑右移、算术左移、算术右移。
其中:

  • 逻辑左移、算术左移其操作相同,左移是低位补0
  • 逻辑右移与算术右移有差别,逻辑右移时高位补0,算术右移时高位补符号位(也即正数补0,负数补1)

不同数据类型参与的运算:

  • 无符号数参与逻辑左移/右移
  • 有符号数参与算术左移/右移

移位运算与除法的关系:

  • 左移n位等于原数值以2的n次方,注意:

    • 逻辑左移要注意避免溢出,当高位移到寄存器外就会溢出
    • 算术左移要注意可能变负数(即符号位从0变为1),甚至触发未定义行为
  • 逻辑右移n位等于原数值(无符号数)以2的n次方

  • 算术右移n位等于原数值(有符号数)以2的n次方,注意:

    • 对于正数,算术右移相当于向下取整
    • 对于负数,算术右移相当于向负无穷取整
      x >> n = floor(x / (2 ^ n))
      // 例如: -5 >> 1 = -3 而非-2
      

注意: 不管左移右移,都要考虑如果移位位数超过寄存器位宽,都会触发未定义行为(如 32 位系统中需 0≤n<32)

参考:

  1. 位运算的妙用
  2. 计算二进制数中1的个数
posted @ 2023-05-14 17:18  sureZ_ok  阅读(442)  评论(0)    收藏  举报