python虽然是在某种程度上脱离了底层硬件的高级语言,它还是可以对数值的二进制机器码表示进行各种运算和操作。本文介绍如何计算反码和补码。
首先,我们介绍一下反码和补码的知识:反码,就是各位取反,很简单!补码,就是在反码的基础上加1得到。
计算机存储有符号的数,采用的就是补码!
正数的补码就是它本身,负数的补码,就是其绝对值的反码加1。符号位是最高bit位,0为正数,1为负数。在C语言中,我们使用singed和unsinged来表示此数据是否有符号,这个关键词决定了此数据在计算机中的存储表示方式。
python跟C语言在计算反码的时候,一样都是使用~符号。不过,python有它自己的细节需要注意。
>>> ~6
-7
6取反后得到-7,这是怎么回事?我们需要通过查看二进制码来分析:
>>> bin(6)
'0b110'
>>> bin(-7)
'-0b111'
bin函数看起来不够给力,对于-7,它的输出其实就是7的二进制码,前面再带一个符号。这跟我们人类的直观感受一样,这也是python的特点。因此,我们需要换一种方式,来查看6和-7的二进制码,是否真的就是取反的关系:
>>> bin((6).to_bytes(1, 'big', signed=True)[0])
'0b110'
>>> bin((-7).to_bytes(1, 'big', signed=True)[0])
'0b11111001'
真的是取反(to_bytes函数请参考:int与bytes的相互转换)!bin函数输出的bytes对象,0b后面不会有多余的0,这个要注意。我们把上面代码简化一下,并且使用~取反符号:
>>> (6).to_bytes(1, 'big', signed=True)
b'\x06'
>>> (~6).to_bytes(1, 'big', signed=True)
b'\xf9'
看16进制,可能更简单一点点。
以上证明,~符号在python中,就是直接取反。而补码,就是在取反的基础上,加1得到:
>>> (6).to_bytes(1, 'big', signed=True)
b'\x06'
>>> (~6+1).to_bytes(1, 'big', signed=True)
b'\xfa'
>>> 6 + ~6+1 == 0
True
6和-6的和是0,0x06和0xfa的和,不考虑溢出,结果也是0。
还有一种方式查看反码和补码的二进制表示,请看下面的代码:
>>> bin(6)
'0b110'
>>> bin(~6 & 0xFF)
'0b11111001'
>>> bin(~6+1 & 0xFF)
'0b11111010'
& 在python中,按位进行与(AND)运算。在表达式中引入&,就强迫python解释器将负数按照补码的方式来进行计算,最后再使用bin函数将结果的二进制显示出来。
python中的整数,不受底层硬件字节数的限制,一般情况int都是4个字节。我们用4个字节来总结以上各种计算:
>>> (6).to_bytes(4, 'big', signed=True)
b'\x00\x00\x00\x06'
>>> (~6).to_bytes(4, 'big', signed=True)
b'\xff\xff\xff\xf9'
>>> (~6+1).to_bytes(4, 'big', signed=True)
b'\xff\xff\xff\xfa'
>>>
>>> bin(6)
'0b110' # '0b00000000000000000000000000000110'
>>> bin(~6 & 0xFFFFFFFF)
'0b11111111111111111111111111111001'
>>> bin(~6+1 & 0xFFFFFFFF)
'0b11111111111111111111111111111010'
-- EOF --