二进制和位运算
原码、反码、补码
对于有符号的数而言:
- 二进制最高位是符号位:0表正数,1表负数
1 -> 0000 0001
、-1 -> 1000 0001
- 0和正数的原码、反码、补码全一样
1 -> 原码[0000 0001]
1 -> 反码[0000 0001]
1 -> 补码[0000 0001]
- 负数的反码 = 原码的符号为不变,其它位取反(1->0、0->1)
-1 ->原码[1000 0001]
-1 ->反码[1111 1110]
- 负数的补码 = 它的反码加1
-1 ->反码[1111 1110]
-1 ->补码[1111 1111]
- 计算机运算时,都取补码来运算,运算的结果也是补码
1+1
、1-1 = 1+(-1)
、3 & 2
等运算将先取1、-1、3、2的补码,再进行对应的运算,示例见后文
位运算
均先转为二进制,然后对两个二进制数进行位运算。
- 位与
&
:两位均为1时得1,否则得0 - 位或
|
:任一位为1得1,否则得0 - 位异或
^
:双目运算符,位不同时得1,相同时得0。例如3 ^ -2
- 位取反
^
(某些语言中为~
):单目运算符,0得1、1得0。运算结果中负数的绝对值比正数大1,即^a = b
,则a + b = -1
。例如^3 = -4
、^-3 = 2
注意,在Golang中,^
即可以是位异或运算符,也可以是位取反运算符,单目时是位取反,双目时是位异或。
例如:3 & -2
和3 | -2
和3 ^ -2
的值分别为?
1 | 3的补码 :0000 0011 |
再例如,减法运算3 - 4 = ?
,它等价于3 + (-4)
1 | 3的补码 :0000 0011 |
移位运算
- 右移运算
>>
:符号位固定,其它位向右移动,低位溢出,符号位和中间缺失的位补0 - 左移运算
<<
:符号位固定,低位补0
技巧:左移一位相当于乘2,右移一位相当于除2(但1右移1位后为0)。
位操作常见用途
- 位与
&
:常用来与对应位为1的值位与,取出值中的部分存在位,特别是存在于中间的某几位 - 位或
|
常用来合并设置。例如open()文件时,使用|
符号合并多个打开文件的模式 - 异或
^
常用来找出只有单边设置了的属性,即找不同。例如找出两个数据中的不同字符(如Ruby和ruby哪里不同) - 移位通常用于取边缘位、按2的倍数扩大或缩小
例如,进程退出状态信息为2字节16个位,**高8位是退出状态码,低8位中的低7位是导致进程退出的信号(如果是信号导致子进程退出的话),最高位是coredump的flag(即表示这个退出的进程是否进行了coredump)**。即:
假设该退出状态信息保存在变量exit_info
中。
如果想取出退出状态码部分,由于它在高位最边缘,所以右移位最方便:
1 | exit_code = exit_info >> 8 |
如果想取出信号部分,由于在中间的7个位上,用这些位的全1二进制做与运算最方面,与运算时,只有信号某位上为1时,与的结果才为1,这样就取出了信号为上的全部1信息位:
1 | sig_info = exit_info & 127 # 127 = 0111 1111 |
同理,如果想取出coredump flag,则与128位与即可:
1 | sig_info = exit_info & 128 # 128 = 1000 0000 |
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 骏马金龙!
评论