位运算(一)

位运算的一般应用

功能 例子 运算 去掉最后一位 1110101->111010 x>>1 在最后加0 1110101->11101010 x<

通过如下的例子,来看看位运算在底层的应用:

1.判断是否是2的整数次幂(来自Linux kernel)

uint32_t is_power_two(uint32_t x){
    return !(x&(x-1));
}
uint32_t is_power_two(uint32_t x){
    return x==x&~x+1;
}

方法一

对于无符号数的编码,可以做出如下解释:

[x=\sum _{i=0} ^{w-1} x_i *2^i ]

显然,若该数是2的整数次幂,显然二进制形式是[00…0100…0]的形式,设第 k位为1(从第0位计数),则有:

[x-1=2^k-1=\sum_{i=0}^{k-1}2^i ]

显然,该数的表达方式为 [00...00111...1],与原数进行按位与,结果为0.

方法二

x&~x+1x&-x,首先关于补码的非运算.任意非0的 x总可以找到最后边的1,即可以将 x表示为:

[[x_{w-1},x_{w-2},…,x_{k+1},1,0,0,…,0] ]

从而有:

[-x=[~ x_{w-1},~x_{w-2},…,~x_{k+1},1,0,0,…,0] ]

显然,若 x是2的整数次幂则有 x==x&-x为真.

2.对2的整数次幂取余(来自HashMap)

uint32_t mod_power_two(uint32_t x,uint32_t bucket){
    return x&(bucket-1);
}

对于一个无符号数 x, xmod2^w的作用等价于丢掉该数二进制表示的从w+1位开始的高位,即将该二进制数进行类似截断.例如:

[25\% 16=9—>11001\&01111=01001_2=9_{10} ]

16=2^4,因此取余会将第5位开始的位均设为0,只保留后4位,因此:

[x\ mod\ 2^n=x\&(2^n-1) ]

3.向上取整到2的整数次幂

uint32_t next_power_two(uint32_t x){
    --x;
    x|=x>>1;
    x|=x>>2;
    x|=x>>4;
    x|=x>>8;
    x|=x>>16;
    return ++x;
}

--x是为了保证,这个数不是2的整数次幂. 设这个数可以表示为 1.....,则右移1位得 01......或运算得 11......,继续右移2位,得 0011......或运算得 1111......,最后一次位运算后得 111...111最后返回时 +1遍得到了向上的一个2的整数次幂.

4.判断是否含有奇数个1

bool is_oddones(uint32_t x){
    x^=x>>1;
    x^=x>>2;
    x^=x>>4;
    x^=x>>8;
    x^=x>>16;
    return x&0x1;

}

总的思想: &#x5947;&#x6570;&#x4E2A;1&#x8FDB;&#x884C;&#x5F02;&#x6216;&#x7ED3;&#x679C;&#x4F4D;1,因为只需想方法,将这个数二进制位的所有数进行异或就知道是否有奇数个1了.举了例子,下面这个 数是某个数二进制的低几位.

第一次运算:

​ 1110 1101 1000

​ 0111 0110 1100

^——————————

​ 1001 1011 0100

最右边的0是第0位与第1位异或所得,依次可得 从右往左位 0^1 1^2 2^3 3^4.....(此处的数字为第位数,下同)

第二次运算:

​ 1001 1011 0100

​ 0010 0110 1101

^—————————–

​ 1011 1101 1001

第二排数字的结果分别是 2^3 3^4 4^5 5^6...,故最右边的0是 0^1^2^3,依次再有 1^2^3^4,…

第三次运算:

​ 1011 1101 1001

​ 1011 1101

^—————————–

​ 0110 0100

第二排的数字从最右边开始为 4^5^6^7,依次…….从而结果中,最右边的数代表 0^1^2^3^4^5^6^7

因而,若原数字在原始数据中是第 i位,在最终的结果中,该位代表的即是:

[i\oplus (i+1)\oplus (i+2)\oplus \cdots \oplus (i+30)\oplus (i+31) ]

因为,令 i=0,即最低位的值,即为结果,故最终 x&0x1

5. 1的个数

uint32_t count_one(uint32_t x){
    x=(x&0x55555555)+(x>>1&0x55555555);
    x=(x&0x33333333)+(x>>2&0x33333333);
    x=(x&0x0f0f0f0f)+(x>>4&0x0f0f0f0f);
    x=(x&0x00ff00ff)+(x>>8&0x00ff00ff);
    x=(x&0x0000ffff)+(x>>16&0x0000ffff);
    return x;
}

思想: &#x5206;&#x800C;&#x6CBB;&#x4E4B;

先将32位数分成16份,每份俩部分:

0x55555555= 0101......01

x&0x55555555得到每个小份的第二部分的1情况, x>>1&0x55555555得到每个小份中第一部分的1的情况,相加得到的2位二进制数即为,该小份中1的个数.现在的任务就是将这些数都加起来 .

​ 10 11 01 00 ……

第一次 01 10 01 00

0x33333333= 0011......0011

x&0x33333333将每个2位能单独拿出来,再加上 x>>2&0x33333333就是两个2位数的和(是一个用4位二进制表示的数),即将这16份,合并成8份,4位一组.

​ 01 10 01 00

第二次 0011 0001

0x0f0f0f0f=00001111...00001111

x&0x0f0f0f0f将4位单独拿出来,再加上 x>>4&0x0f0f0f0f就是两个4位数的和.按照这种思想,最后会得到两个16位数的和(按32位表示)这个数的大小就是1的个数.

Original: https://www.cnblogs.com/oasisyang/p/13218500.html
Author: OasisYang
Title: 位运算(一)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/585699/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • uWSGI服务实现优雅重启(graceful reload)的方式

    服务端当前使用方式 直接通过svc发送SIGINT/SIGKILL信号 直接触发real_run脚本中的相关信号通知 使用简单 每次重启所有进程(包括master),重启完成为全新…

    Linux 2023年6月6日
    0109
  • Debian 9.4 安装教程

    我们这里选择install安装,不装桌面,因为是做服务器,装桌面没意义。 我们这里选择装英文版,你也可以装中文版本。 手动配置网络-manually 设置IP 设置 子网掩码 设置…

    Linux 2023年6月13日
    0107
  • Kafka部署安装及简单使用

    一、环境准备 1、jdk 8+ 2、zookeeper 3、kafka 说明:在kafka较新版本中已经集成了zookeeper,所以不用单独安装zookeeper,只需要在kaf…

    Linux 2023年6月13日
    0124
  • Redis数据类型

    该文章是对Redis官方文档的翻译 字符串(Strings) 字符串是Redis值的最基础的类型。Redis字符串是二进制安全的,这意味着一个Redis字符串可以包含任何种类的数据…

    Linux 2023年5月28日
    092
  • WSL系统安装与使用

    WSL是适用于 Linux 的 Windows 子系统,可让开发人员按原样运行 GNU/Linux 环境 – 包括大多数命令行工具、实用工具和应用程序 – …

    Linux 2023年5月27日
    0151
  • Paxos 协议简单介绍

    一、简介 Paxos 协议是少数在工程实践中证实的强一致性、高可用的去中心化分布式协议。Google 的很多大型分布式系统都采用了 Paxos 算法来解决分布式一致性问题,如 Ch…

    Linux 2023年6月16日
    0146
  • 剑指offer计划18( 搜索与回溯算法中等)—java

    1.1、题目1 剑指 Offer 55 – II. 平衡二叉树 1.2、解法 递归和下一面一题的结合版,abs去绝对值判断两边的差,然后递归isBalanced来遍历二…

    Linux 2023年6月11日
    072
  • JVM 运行时数据区 堆和方法区

    2、运行时数据区 哔哩哔哩 尚硅谷视频 宋红康老师 2.5、堆 堆的核心概述 一个JVM实例只存在一个堆内存,堆也是Java管理内存的核心区域 Java 堆区在JVM启动的时候即被…

    Linux 2023年6月7日
    0123
  • Redis:redis常用操作命令

    redis登录 #登录命令 -h 登录地址 -p 端口 ./redis-cli -h 127.0.0.1 -p 6379 查看缓存大小 #查看缓存大小 dbsize 查看所有Key…

    Linux 2023年5月28日
    0139
  • API 的 Authorization 头里为啥有个 Bearer

    在我们设计和使用 API 授权的时候,经常会接触到如下内容: Authorization : Bearer Tokenxxxxxx 为什么前面会有个 Bearer,直接弄成这样不是…

    Linux 2023年6月7日
    0115
  • Dockerfile 使用 SSH docker build

    如果在书写 Dockerfile 时,有些命令需要使用到 SSH 连接,比如从私有仓库下载文件等,那么我们应该怎么做呢? Dockerfile 文件配置 为了使得 Dockerfi…

    Linux 2023年6月7日
    099
  • C++ inline

    inline的坏处:若在一台内存有限的机器上,过度热衷inlining会造成程序体积太大,即使拥有虚拟内存,inline造成的代码膨胀也会导致额外的换页行为,降低指令高速缓存装置的…

    Linux 2023年6月7日
    0115
  • Centos7最小化安装报错There are no enabled repos. Run “yum repolist all” to see the repos you have.解决办法

    原因是缺少CentOS-Base.repo文件,因为我这台机器wget也不能用,所以我是下载到本地sftp上去的,传输的时候一定要在root用户下,否则会无法启动传输 这是报错的完…

    Linux 2023年6月7日
    0113
  • Linux目录标签概览

    根目录是整个系统最重要的一个目录,因为不但所有的目录都是由根目录衍生出来的,同时根目录也与开机/还原/系统修复等动作有关。由于系统开机时需要特定的开机软件、核心文件、开机所需程序、…

    Linux 2023年6月8日
    088
  • springboot redis key乱码

    原写法: 写入redis后,查看key值 解决方式: 调整后查看redis key值: Original: https://www.cnblogs.com/janes/p/8796…

    Linux 2023年5月28日
    0104
  • 手把手教你设置MongoDB密码

    mongodb密码和传统数据如mysql等有些区别: mongodb的用户名和密码是基于特定数据库的,而不是基于整个系统的。所有所有数据库db都需要设置密码。 1. 查看所有数据库…

    Linux 2023年6月14日
    0109
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球