为什么取模在除数等于2^n的时候可以用按位与替代?

大家在学习的过程中总是经常看到:x % 2^n = x & (2^n - 1);

比如java的ThreadLocal源码中:firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);

也就是:一个数取模(求余数),如果除数是2的n次方,那么等于这个 数 按位与 2的n次方-1。

还是不好理解,举一个具体的例子。

十进制

23 % 8 = 2....7

二进制形式

10111 % 1000

等价于:

10111

& 00111 (2^3-1)

= 00111 (7)

经过上面的案例,可以看到,23转换二进制是10111, 8的二进制是1000,转换成2^n-1就是0111。

最终异或的结果就是7。和我们取模计算的结果一样。

重点:为什么会这样?为什么取模会在除数是2的n次方的时候等价于按位与操作。

基本概念
什么是按位与

按位与是一种运算操作,可以理解成加减乘除。按位与的意思是,两个二进制数,只有都为1的时候结果才是1。按位:按照位置一对一对比,与:与操作,两者都为true则是true。

案例: 100001 & 000111 = 000001 。两个二进制数的最后一位都是1,所以按位与的结果是1。

2^n-1 和2^n 之间是什么关系?

2^n: 2^0 2^1 2^2 2^3 2^4

二进制表示: 0001 0010 0100 1000 10000

2^n-1: 0000 0001 0011 0111 011111

可以发现2的n次方实际就是1后面全是0,而2^n-1则是

对比十进制来学习:

118 % 10 = 18 ----118 % 10^1= 8

1231 % 100 = 31 ------1231 % 10^2 = 31

可以发现,求余数就是只保留x^n中的低位,n就是保留的位数,118%10中,n=1,保留的就是1位,也就是8。1231%100中n=2,所以保留2位就是余数,也就是31.

回到二进制中,2^n就相当于十进制中的10,100,1000。而二进制取模就是保留2^n中的n位,就是余数。

那么继续回到23 % 8的案例

23 % 8 = 2....7

二进制形式

10111 % 2^3 保留3位 就是111 =7,就是余数。

所以这就是为什么必须除数需要时2^n的时候才能用按位与替代 %。

那么为什么需要使用2^n-1呢?

上面我们知道了2^n-1就是2^n的1变成0,1后面的0全部变成1。而这个时候,1的个数刚好是n的值

比如 1000 ----> 0111 2^3 。1的个数也是3个。这就有一个特点,通过2^n-1之后的值,前面的全是0,后面的1的个数刚好是n的值。而按位或操作,必须有两个都是1才是1。那么就能保证,按位与得到的结果刚好是被除数的后n位。

举个例子:

23 % 8 = 7

10111

& 00111

------------------

00111 (7)

个人表达能力有限,感觉并没有讲清楚,但是能够弄明白十进制中的例子,代入二进制中就很容易明白了。按位与2^n-1只是为了得到后n位的操作。

相关推荐
空中海14 小时前
第二章:Maven进阶篇 — 依赖管理与构建生命周期
java·maven
xun-ming14 小时前
AI时代Java程序员自救手册
java·开发语言·人工智能
DavidSoCool14 小时前
GB28181 PTZCmd 完整指令对照表(8 位 16 进制)+ 详细注释 + 使用说明
java·sip·gb28181
张健115640964814 小时前
C++访问控制与友元
java·开发语言·c++
Sam_Deep_Thinking14 小时前
中小团队需要一个资源微服务
java·微服务·架构
Thanks_ks14 小时前
透过 Copy-On-Write 机制:理解并发编程中的性能与一致性权衡
java·多线程·并发编程·底层原理·写时复制·copyonwrite·性能优
一只幸运猫.14 小时前
JAVA后端面试题
java·开发语言
空中海15 小时前
第三章:Maven高级篇 — 插件开发与多模块工程
java·maven
秋915 小时前
TiDB 数据库全链路实战指南:从下载部署到 Java 高并发调优
java·数据库·tidb
JAVA面经实录91715 小时前
Java开发工程基础完整手册(企业实战完整版)
java·开发语言·git·ci/cd·svn·github·intellij idea