- 左移运算符 << :把一个数的所有位都向左移动若干位
int i = 1;
i = i << 2; //把i里的值左移2位
为了理解方便,假设 int 占1个字节(8位,实际32位机器占4字节,32位)
i = 0000 0100
i = i << 2 ;
//结果 i = 0001 0000
此时需要注意的是 int 类型 最左端的符号位
0 表示 正
1 表示 负
i = 0100 0000 //十进制为64 111 1111:127 1000 0000:128
i = i << 1 ; //变为i = 1000 0000 即十进制为-128 变成了最小数
这里是不是一脸问号???接下来解释为什么
计算机中以补码的形式存储数据
/*
正数补码 = 原码
负数补码 = 原码取反+1
*/
-1 原码为1000 0000 0000 0000 0000 0000 0000 0001
-1 反码为1111 1111 1111 1111 1111 1111 1111 1110
-1 补码为1111 1111 1111 1111 1111 1111 1111 1111
所以最小的负数是-2147483647吗?错,不是。
在二进制中,0有两种表方法。
+0 原码为0000 0000 0000 0000 0000 0000 0000 0000
-0 原码为1000 0000 0000 0000 0000 0000 0000 0000
因为0 只需要一个,所以把-0拿来当做一个最小的数-2147483648
这也就是为什么负数比正数多一个的原因(int 范围-2^31 ~ 2^31-1)
-2147483648 的补码表示为1000 0000 0000 0000 0000 0000 0000 0000 在32位没有原码
注意,这个补码并不是真正的补码,真正的补码是1 1000 0000 0000 0000 0000 0000 0000 0000 溢出
所以在这里只占1个字节带符号的int的最小值为-128 即 0100 0000 << 1 = 1000 0000 (即-128)
当左移的位数超过该数值类型的最大位数时,编译器会用左移的位数去模类型的最大位数,然后按余数进行移位,如:
int i = 1 //设int占1个字节,即8位 i = 1:0000 0001
i = i << 9;
// 9 % 8 = 1 即此时左移1位 i = 2 :0000 0010
int i = 64 //设int占1个字节,即8位 i = 1:0100 0000
i = i << 9;
// 9 % 8 = 1 即此时左移1位 i = 0 :1000 0000 最高位被丢弃
总结:丢弃高位,0补低位
- 右移运算符 >> :把一个数的所有位都向左移动若干位
符号位向右移动后,正数补0,负数补1,也就是汇编语言中的算术右移,同样当移动的位数超过类型的长度时,会取余数,余数是几,移动几位
整型 int 为4个字节32位,那么当我们右移32位会发生什么?35位呢?
原数: 2147483647 (整型最大值)
二进制: 01111111 11111111 11111111 11111111
结果不变
这是因为位移是一个取模的过程
当我们右移4位:4 % 32 = 4
当我们右移32位:32 % 32 = 0 也就是不动
那么当右移33位,那不就是右移1位吗!
- 无符号右移(没有无符号左移)
无符号右移在右移时,高位补0,也就是不会管你是不是负数
-10 >>> 1
二进制: 01111111 11111111 11111111 11111011 | 0
是这样吗?答案是对的,负数无符号右移,高位同样补0
无符号右移1位后最高位的1变为了0,负数变为了正数
总结
负数右移高位补1
无符号负数高位补0
eg 小测验:
原数: 75
二进制: 00000000 00000000 00000000 01001011
请问 左移25位结果是?
10010110 00000000 00000000 00000000
-1778384896
- 总结
码制 | 添补代码 | |
---|---|---|
正数 | 原码、补码、反码 | 0 |
负数 | 原码 | 0 |
补码 | 左移添0 | |
右移添1 | ||
反码 | 1 |