Python中整数在机器内部是按按二进制格存储的,每一位非0即1。因此,每一位都构成了布尔代数
(
B
,
∨
,
∧
,
¬
)
(B,\vee,\wedge,\neg)
(B,∨,∧,¬)以及域
(
B
,
⊕
,
∧
)
(B,\oplus,\wedge)
(B,⊕,∧),其中
B
=
{
0
,
1
}
B=\{0,1\}
B={0,1},
∨
,
∧
,
¬
,
⊕
\vee,\wedge,\neg,\oplus
∨,∧,¬,⊕分别表示或、与、非、异或运算。两个整数的
x
x
x与
y
y
y的位运算指的是:若
x
x
x与
y
y
y的二进制位数相同则对应位进行相应的运算,否则对齐最低位,位数少的高位补0,然后对应位进行相应运算。对应各运算的运算符如下表所列:
运算 | 运算结果 | 备注 | |
---|---|---|---|
(1) | x|y | x和y按位或 | ∨ \vee ∨ |
(2) | x^y | x和y按位异或 | ⊕ \oplus ⊕ |
(3) | x&y | x和y按位与 | ∧ \wedge ∧ |
(4) | ~x | 对x带符号位的补码按位取反后的补码 | |
(5) | x<<n | x左移n位 | n必须是非负整数 |
(6) | x>>y | x右移n位 | n必须是非负整数 |
需要说明的是,
- ~x并非对整数x的二进制原码逐位取反,要实现整数原码逐位取反只需对每一位与1作异或运算即可(参见例1-8);
- 左移运算表示将整数x向左每移动一个二进制位,右端添加一位0。即x<<n相当于x ⋅ 2 n \cdot 2^n ⋅2n。相仿地,x>>n相当于x / 2 n /2^n /2n。
int类对象的函数bit_length计算并返回整数的二进制表达式的长度(位数),例如设a中整数为286,则a.bit_length()将返回9。注意,
2
n
−
1
2^n-1
2n−1的二进制表达式为
11
⋯
1
⏟
n
\underbrace{11\cdots1}_{n}
n
11⋯1。Python的bin函数将整数转换成二进制表达式串,例如bin(2**8-1)将返回串’0b11111111’。
例1 遵从\emph{TCP/IP}协议的互联网Internet中,每一个节点都有一个IP地址,对IPV4而言,一个IP地址a就是一个32位2进制非负整数。为方便计,通常将此整数的二进制式分成4节,每节8位(1个字节),用点号“.”隔开。例如一个节点的IP地址为整数3232236047,其二进制式为
11000000.10101000.00000010.00001111
\text{11000000.10101000.00000010.00001111}
11000000.10101000.00000010.00001111
第1节的11000000对应十进制整数192,第2节的10101000对应168,第3节的00000010对应
2
2
2,第4节的00001111对应15。在文献中为简短计,常记为
192.168.2.15
\text{192.168.2.15}
192.168.2.15
TCP/IP规定每个IP地址分成网络号和主机号两部分,并将所有
2
32
=
4294967296
2^{32}=4294967296
232=4294967296个IP地址分成{A、B、C、D、E}五类。常用的{A、B、C}三类地址的定义如下:
类别 | 网络号 | 主机号 |
---|---|---|
A | 第1个字节,取值1~126 | 后3个字节 |
B | 前两个字节,第1字节取值128~191 | 后2个字节 |
C | 前3个字节,第1个字节取值192~223 | 最后一个字节 |
路由程序用“掩码”来分析IPV4地址a的类型、网络号和主机号。具体地说,
- 用掩码 255.0.0.0 255.0.0.0 255.0.0.0与a计算按位与,然后右移 24 24 24位得到地址的第一个字节取值,以此判断网络类型;
- 根据(1)算得的网络类型,设置A型网络掩码m为255.0.0.0,B型网络掩码m为255.255.0.0,C型网络掩码m为255.255.255.0;
- 计算a与掩码m的按位与并右移若干位(A类右移24位,B类右移16位,C类右移 8 8 8位)得网络号;
- 对由(3)算得的掩码m逐位取反得m1,计算a与m1的按位与算得主机号。
下列程序对给定的表示IPV4地址的整数a判断其类型并分析网络号及主机号。
def ipAnaly(a):
b32=2**32-1 #32位1
m=255<<24 #掩码255.0.0.0
t=(a&m)>>24 #地址的第1个字节
if 1<=t<=126: #A类地址
t1='A' #地址类型
n=t #网络号
m1=m^b32 #掩码255.0.0.0的反码
p=a&m1 #主机号
if 128<=t<=191: #B类地址
t1='B' #地址类型
m=(2**16-1)<<16 #掩码255.255.0.0
n=(a&m)>>16 #网络号
m1=m^b32 #掩码的反码
p=a&m1 #主机号
if 192<=t<=223: #C类地址
t1='C' #地址类型
m=(2**24-1)<<8 #掩码255.255.255.0
n=(a&m)>>8 #网络号
m1=m^b32 #掩码的反码
p=a&m1 #主机号
return t1, n, p
a=2005012608 #地址119.130.16.128
print(ipAnaly(a))
a=2282885253 #地址136.18.16.133
print(ipAnaly(a))
a=3321996052 #地址198.1.163.20
print(ipAnaly(a))
程序的第1~22行定义了用于IP地址分析的函数ipAnaly,该函数仅有一个表示待分析的IP地址的整型参数a。函数体中,第2行用
2
32
−
1
2^{32}-1
232−1设置用于原码求反的32位1的整数为b32;第3行用255<<24将掩码m设置为第1个字节全为1,其他全为0,即255.0.0.0,用于分析地址a的第1个字节以判断地址类型;第4行用按位与a&m算出a中第1字节后面三个字节全为0,然后a&m>>24右移24位得a的第1字节值,赋予t;第5~9行、第10~15行、第16~21行的if语句分别就算得的t判断而得的三种地址类型分析计算网络号n和主机号p。以第10~15行的if语句为例加以说明,另外两个情形读者可参考代码的解释信息参照阅读理解。第10行测得地址类型为B,第11行将字符’B’赋予t1;第12行将掩码m置为前两个字节为1,其余全为0,即255.255.0.0;第13行中按位与a&m计算a的前两个字节及后两个为0的字节构成整数,(a&m)>>16则将该整数右移16位,得到a的前两个字节的整数值,赋予网络号n;第14行中按位异或m^b32计算m的按位取反:前两个字节为0后两个字节为1,赋予m1;第15行中按位与a^m1算得a的后两个字节的值,赋予主机号p。
第23~24行、25~26和27~28分别对表示IP地址的整数2005012608(119.130.16.128)、2282885253(136.18.16.133)和3321996052(198.1.163.20)调用函数ipAnaly分析计算地址的类型、网络号及主机号并输出。运行程序,输出
('A', 119, 8523904)
('B', 34834, 4229)
('C', 12976547, 20)
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!
代码诚可贵,原理价更高。若为AI学,读正版书好。