今天我们来谈谈左移这件事。
❤️简单来说,对一个数左移就是在其的二进制表达末尾
添0。左移一位添一个0,结果就是乘以2
;左移两位添两个0,结果就乘以2 ^ 2
;左移n位添n个0,结果就是乘以2 ^ n
,小心溢出!
✨左移操作是一种位操作,用来将一个数的二进制表达的所有位向左移动指定的位数,并在右侧用0填充空位。
✨x<
如:1001 << 2 ----> 100100
✨左移操作相当于将一个数乘以2的n次方。
x<
如:
#include
int main(void)
{
int x = 3, y = 2;
printf("%d", x << y);//3*(2^2)
return 0;
}
执行结果如下:
如上图可见,3<<2 = 3 * (2^2)= 3*4 = 12;
对1左移时,得到的都是2的幂
,这是一个非常重要的知识点❗️1左移n位 | 值 | 2的幂 |
---|---|---|
1<<1 | 2 | 2^1 |
1<<2 | 4 | 2^2 |
1<<3 | 8 | 2^3 |
… | … | … |
1<2^n |
2^n |
|
如:-3<<2 是多少呢?相信你心中已经有答案了,请看下图!
#include
int main(void)
{
int x = -3, y = 2;
printf("%d", x << y);
return 0;
}
执行结果如下: 这其实也可以用补码来解释: 那么,3<<(-2)又是多少呢? 放过我吧! 警告2(点我) ✨(非溢出)移位可以分为逻辑移位和算术移位。 ✨溢出可以分为位溢出和值溢出(此为博主自己分的,可能不合理,但博主自己觉得合理),而我们又知道,整数分为无符号整数和有符号整数。所以通过组合我们大概了解到,此处的溢出一共有4种情况。 ✨对于位溢出来说,我们在上面已经讨论过了,int 类型一共32位,移位大于等于32位或小于0位即为位溢出。 注:在不同的编程语言中,对于超出操作数位数的移位操作,可能会有不同的行为。 ✨当变量的值超过了其所能表达的值的范围时,产生值溢出。但溢出的值并不会消失,而是以另一种形式存在着。 下面我们举例来说明: ❤️有符号整数: 执行结果如下: 1️⃣左移31位:(31<32)未溢出 注:0 和 -2 ^ 31 相对 ☝️如上图所示,对于int 类型(有符号整数)的x来说,其范围为: 2️⃣左移32位: 这就要考虑到 mod可以理解为%运算; ✨对于上面的9<<32,我们不能仅仅通过表面的移位去观察,而更应该计算一番。 3️⃣左移33位与左移32位类似,先对32取模再进行移位。此处不再赘述。 ❤️无符号整数: 注:无符号十进制整数用%u输出; ✨ ✨现将 ✨ 置1—>位或: ✨ 置0—>位与:x&(1< ✨ ✨我们可以通过左移来生成一个掩码,从而实现对一个数二进制表达的末k位执行一些操作。 从而可以进行以下操作: 好了,今天的讲解就到这里了,相信你也是收获满满吧! 这真的我是肝的最久的一篇文章了,没有之一,从早上肝到了晚上,,,
✨(-x)<
4️⃣ 左移负数位:
#include
看来左移负数位是未定义的行为啊,所以我们最好不要这样写,可能会带来不必要的麻烦,如果想要右移的话就乖乖使用右移操作。
5️⃣左移时溢出!
(1)位溢出(移位/mod的角度):
一些语言会将超出的位数进行取模操作
,即将移位的位数先对操作数的位数取模,然后再进行相应的位移
,比如C语言❗️❗️❗️(2) 值溢出(值的角度):
#include
如上面的代码所示,a原本是9,其二进制表达为:00000000 00000000 00000000 00001001,现在对其进行左移操作:
因为9<<31=9 * (2 ^ 31) ,下面我们结合下图来看
-2 ^ 31 ~ 2 ^ 31-1
,当x=2 ^ 31 -1
时,x+1
将会变为-2 ^ 31
,从而我们可以看出越界的本质就是转圈圈。此处1圈是2 ^ 32个数
,半圈是2 ^ 31 个数
。所以9<<31=9* (2 ^ 31)=4.5* 2 ^ 32=4.5圈,去除整圈,得到0.5圈,从而结果是-2 ^ 31。怎么样,是不是很神奇?
当然,也可以通过取模来理解(其实补码的本质就是模运算),将原值与2 ^ 32
取模(因为1圈是2 ^ 32
个数),得到2 ^ 31
, 但有符号整数上界为2 ^ 31-1
,再加一个数将会过渡到最小数 - (2 ^ 31)
.(此处是有向增加的)
位溢出
了!我们上面说过了,C语言会将超出的位数进行取模操作,即将移位的位数对操作数的位数取模,然后进行相应的位移。因为如果直接移位的话,当移位长度大于31时必定是0;因为32>=32,所以32=0(mod(32)),从而原式的值等于00000000 00000000 00000000 00001001左移0位,也就是9本身。
因为9<<32=9*(2^32) ,所以原式就等于9圈,刚好是0。
unsigned int
范围为 0~(2 ^ 32)-1
,总共也是2 ^ 32
个数,但都是非负数
。(2 ^ 32 -1) +1 = 0
,即结果要对2 ^ 32
取模。#include
计算方法根据上图转圈圈就好,此处不再赘述。
二、拓展应用:
1. 取模和位运算的转换:
x mod (2 ^ y) = x &((1<
2. 生成标记码:
1<
k
个标记位的标记码。(此处取从0开始)(1. 标记位置1:
对于二进制数x,将它的第k位置为1
.(从低位开始计位,即从右往左)x|(1<
(2. 标记位置0:
对于二进制数x,将它的第k位置为0
.(从低位开始计位,即从右往左)(3. 标记位取反:
对于二进制数x,将它的第k位取反
.(从低位开始计位,即从右往左)
取反—>异或:x^(1<
3. 生成掩码:
1<
(1<
将末k位变为1:x|((1<
将末k位变为0:x&(1<
x&(~((1<
将末k位都取反:x^((1<