1、概述
计算机中数在内存中以二进制形式进行存储
位bit:计算机中处理数据的最小单位,其取值只能是0或1
字节Byte:计算机处理数据的基本单位,通常系统中一个字节为8位,即1Byte=8bit
=>位运算:直接对整数在内存中的二进制进行操作
=>执行效率高(在程序中尽量使用位运算进行操作)
序号 | 符号 | 描述 | 运算规则 | 实例 |
---|---|---|---|---|
1 | & | 与 | 两位数都为1时,结果才为1 | 1&1=1,1&0=0,0&0=0 |
2 | | | 或 | 两位都是0时,结果才是0 | 1|1=1,1|0=1,0|0=0 |
3 | ^ | 异或 | 两个位相异为1,相同为0 | 11=0,10=1,0^0=0 |
4 | ~ | 取反 | 0变1,1变0 | 0=1,1=0 |
5 | << | 左移 | 各二进制全部左移若干位,高位丢弃,低位补0 | 11二进制=0b1011;左移:44,二进制:0b101100 |
6 | >> | 右移 | 各二进制全部右移若干位:正数左补0,负数左补1;右边移出的丢弃 | >>1即/2 |
注意:计算机中位运算操作,均是以二进制补码
形式进行
2、原码、反码、补码
原码:用最高位表示符号位,其余表示数值位的编码称为原码。(正数的符号位为0,负数的符号位是1)
反码:
- 正数的反码:自身
- 负数的反码:符号位保持不变,数值位逐位取反
补码:
- 正数的补码:自身
- 负数的补码:负数的反码+1
注:补码再求一次补码的操作,可得到原码
1+(-1)=0
bin(1)=0000 0001
bin(0)=0000 0000
-1的二进制只有是:1111 1111 ;依次叠加进一,导致超出8位类似溢出,才能成立
==>负数num的二进制数(有符号数),则可视为无符号数的(2**n+num)的二进制数
a = -4
print(bin(2**8+a))
# a=-1,0b11111111
# a=-4,0b11111100
3、应用
3.1 按位与&
应用1:与1按位与,可获取对应二进制数的最低位
应用2:与按位与,可使该数低位的一个字节清零
# 按位与&
a=1 # 补码 01
# b=-1 # 补码 11
b = 9 # 101
print(a&b) # 1
a=0 # 补码0
print(a&b) # 0
3.2 按位或|
def get_huo():
# 按位或|
a=9 # 补码 0000 1001
b=20 # 补码 0001 0100
print(a|b) #0001 0101
print(bin(a|b))
# 1+4+16
# 引申
def get_bin(num:int):
# 求num的二进制数
res_bin=""
while num: # 辗转相除
res_bin=str(num%2)+res_bin #
num//=2 # // 整除
print(res_bin,num)
return res_bin
print(get_bin(10)) # 1010
3.3 按位异或^
不同为1,相同为0
# 按位异或
# 相同则为0,不同则为1
# 110 # 6
# 100 # 4
# 异或 010
a=6
b=4
# print(bin(6),bin(4))
print(a^b," ",bin(a^b))
特点:
1、a^0=a.即0与任意数按位异或都得该数本身
1^0=0;0^0=0
2、1与任意二进制位按位异或都得到该位取反(0变1,1变0)
1^1=0, 0^1=1
3、a^a=0.即任意数与自身按位异或都得0
4、ab=ba
5、(ab)c=a(bc)
6、abb=a(bb)=a^0=a
3.4 左移<<
高位丢弃(不包括1),低位补0。左移时舍弃的高位不包括1,则每左移一位,相当于该数乘以2。
<<=
移动后赋值
a=-10
# 1000 1010
# 反码(符号位不变,其他取反) 1111 0101
# +1 得补码 1111 0110
# 左移n位(n=2): 11 1101 1000
# -1: 11 1101 0111
# 取反(符号位不变,其他取反):10 0010 1000 即1000 1010 末尾加n个0
print(bin(a))
# print(a<<2) # -40 ;但是a本身不变仍是-10
a<<=2
print(a) # -40
a<<=2
print(a) # -160
3.5 右移>>
将运算符各个二进制全部右移若干位,正数左补0,负数左补1,右边移出的位丢弃。
>>=
右移后赋值
a=10 # 0000 1010
b=-10 # 1111 0110
print(a>>2)
# 左移2位,高位补0:0000 0010
print(b>>2) # -3
# 左移2位,高位补1,得补码 1111 1101;-1:1111 1100 ;反码(符号位不变,其他取反) 1000 0011
3.6 按位取反~
~0=1 、~1=0
应用:~a+1=-a 即对任意数按位取反后加1,得该数的相反数
a=10 # 0000 1010
# =>取反 1111 0101(负数的补码)
# =>-1:1111 0100 =>反码(符号位不变,其他取反):1000 1011(其为负数=-11)
b = -10 # 1111 0110 # 存储的补码=>取反:0000 1001(正数,=9)
print(~a) # -11
print(~b) # 9
4、二进制数的正负
- 看是否无符号数/有符号数
若是有符号数,开头第一位是符号位,1表示负数,0表示正数
- 看是如何存储在计算机中
本身是没有正负概念的;需要知道是原码还是补码,才能进而推断它的原始的值。如10101010 ,若是无符号,则值=170;若是有符号数,则值=-89???==>存储前是有符号数,则按有符号数处理,存储前是无符号数,则按无符号数处理
- 最后,看操作系统的位数
若是8位,1111 1111是-1;若是16位的FFFF 是-1;若是32位的FFFF FFFF 是-1
5、小数的二进制数
小数部分,将其乘以进制n,取出整数部分作为二进制表示第1位;依次类推,直到小数部分为0
特殊情况,小数部分出现循环,无法停止,则用有限的二进制位无法准确表示一个小数,这也是在编程语言中表示小数会出现误差的原因
引申:Py3浮点数取整
# print(bin(0.1)) # TypeError: 'float' object cannot be interpreted as an integer
def get_float_bin(f:float,n:int)->str:
"""
求浮点数的二进制数
:param f:
:param n: 二进制小数的结果保留n位
:return:
"""
f_int=int(f) # 整数部分
f_bin=bin(f_int)[2:]
# 小数部分math.modf也是ok的
f_float=f-f_int
bin_a=""
while f_float and n:
f_float*=2 # 2进制
bin_a+=str(int(f_float)) # 取整数
f_float-=int(f_float)# 取小数位
n-=1
while n: # 不足则补0
bin_a+="0"
return f"{f_bin}.{bin_a}"
print(get_float_bin(10.6,12)) # 1010.100110011001
引申:二进制小数转为十进制小数
def get_float_shi(s:str,n:int)->float:
"""
二进制小数转为十进制小数
:param s: 二进制小数
:param n: 十进制小数的精确度
:return: 十进制小数,小数点后n位
"""
i,f=s.split(".")
i_shi = int(i,2)
length=len(f)
f_int=0
flag=0.5
for i in range(length):
f_int+=int(f[i])*flag
flag*=0.5
# return round(i_shi+f_int,n) # 10.6,保留了1位而不是10.60
# return "%.2f"%(i_shi+f_int) # 10.60
return eval("{:.2f}".format(i_shi+f_int)) # float()
a=get_float_shi("1010.100110011001",2)
# print(type(a)) # <class 'str'>=> <class 'float'>变为10.6
print(a)
参考:
1、位运算
2、负数的二进制表示方法
3、原码、反码、补码
4、小数的二进制表示法