对于补码,最简单的理解是最高位的权值是负数,而不是取反加一。
最简理解
原码和补码其实都是使用最高位来凑出负数,但是方法不同,以8位的编码为例:
- 负数原码最高位:乘以-1
- 负数补码最高位:减去128
最简方法
故按照最高位是权值是负数的思路进行转换,代码非常简单:
- 补码转负数:只要提供的是正经表示负数的补码即可
- 负数转补码:除输入负数以外,还要指定补码的位数
def bu2fu(a):
b = bin(a).replace("0b1","0b0")
return int(b, 2)-pow(2,len(b)-3)
def fu2bu(a,b):
return pow(2,b)+a
print(hex(fu2bu(-1,32)))
print(hex(fu2bu(-10,64)))
print(bu2fu(0xff))
print(bu2fu(0xfffe))
print(bu2fu(0x80000000))
print(bu2fu(0xfffffffd))
测试:
➜ python3 bu.py
0xffffffff
0xfffffffffffffff6
-1
-2
-2147483648
-3
完整工具
打印详细信息以及相应位数下的最大最小值的完整脚本如下:
def bu2fu(a):
b = bin(a).replace("0b1","0b0")
e = int(b, 2)-pow(2,len(b)-3)
print("input : "+hex(a))
print("bin : "+b[2:])
print("signed : "+str(e))
print("signed hex : "+hex(e))
print("-"*(len(b)+14))
print("bit : "+str(len(b)-2))
print("min signed : "+str(hex(pow(2,len(b)-3)))+", -"+str(pow(2,len(b)-3)))
print("max signed : "+str(hex(pow(2,len(b)-3)-1))+", "+str(pow(2,len(b)-3)-1))
print("max unsigned : "+str(hex(pow(2,len(b)-2)-1))+", "+str(pow(2,len(b)-2)-1))
return e
def fu2bu(a,b):
c = pow(2,b)+a
print("input : "+str(a))
print("hex : "+hex(c))
print("bin : "+bin(c)[2:])
print("-"*(b+16))
print("bit : "+str(b))
print("min signed : "+str(hex(pow(2,b-1)))+", -"+str(pow(2,b-1)))
print("max signed : "+str(hex(pow(2,b-1)-1))+", "+str(pow(2,b-1)-1))
print("max unsigned : "+str(hex(pow(2,b)-1))+", "+str(pow(2,b)-1))
return c
fu2bu(-100,32)
print('\n\n')
bu2fu(0xffffffffffffffff)
➜ Desktop python3 bu.py
input : -100
hex : 0xffffff9c
bin : 11111111111111111111111110011100
------------------------------------------------
bit : 32
min signed : 0x80000000, -2147483648
max signed : 0x7fffffff, 2147483647
max unsigned : 0xffffffff, 4294967295
input : 0xffffffffffffffff
bin : 0111111111111111111111111111111111111111111111111111111111111111
signed : -1
signed hex : -0x1
--------------------------------------------------------------------------------
bit : 64
min signed : 0x8000000000000000, -9223372036854775808
max signed : 0x7fffffffffffffff, 9223372036854775807
max unsigned : 0xffffffffffffffff, 18446744073709551615
可以封装成命令行工具如下
bu2fu
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
a = int(sys.argv[1], 0)
b = bin(a)
c = b.replace("0b1","0b0")
e = int(c, 2)-pow(2,len(b)-3)
print("input : "+hex(a))
print("bin : "+b[2:])
print("signed : "+str(e))
print("signed hex : "+hex(e))
print("-"*(len(b)+14))
print("bit : "+str(len(b)-2))
print("min signed : "+str(hex(pow(2,len(b)-3)))+", -"+str(pow(2,len(b)-3)))
print("max signed : "+str(hex(pow(2,len(b)-3)-1))+", "+str(pow(2,len(b)-3)-1))
print("max unsigned : "+str(hex(pow(2,len(b)-2)-1))+", "+str(pow(2,len(b)-2)-1))
➜ bu2fu 0xff
input : 0xff
bin : 11111111
signed : -1
signed hex : -0x1
------------------------
bit : 8
min signed : 0x80, -128
max signed : 0x7f, 127
max unsigned : 0xff, 255
➜ bu2fu 0xffffffe0
input : 0xffffffe0
bin : 11111111111111111111111111100000
signed : -32
signed hex : -0x20
------------------------------------------------
bit : 32
min signed : 0x80000000, -2147483648
max signed : 0x7fffffff, 2147483647
max unsigned : 0xffffffff, 4294967295
fu2bu
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
a = int(sys.argv[1], 0)
b = int(sys.argv[2], 0)
c = pow(2,b)+a
print("input : "+str(a))
print("hex : "+hex(c))
print("bin : "+bin(c)[2:])
print("-"*(b+16))
print("bit : "+str(b))
print("min signed : "+str(hex(pow(2,b-1)))+", -"+str(pow(2,b-1)))
print("max signed : "+str(hex(pow(2,b-1)-1))+", "+str(pow(2,b-1)-1))
print("max unsigned : "+str(hex(pow(2,b)-1))+", "+str(pow(2,b)-1))
➜ fu2bu -1 32
input : -1
hex : 0xffffffff
bin : 11111111111111111111111111111111
------------------------------------------------
bit : 32
min signed : 0x80000000, -2147483648
max signed : 0x7fffffff, 2147483647
max unsigned : 0xffffffff, 4294967295
➜ fu2bu -10 64
input : -10
hex : 0xfffffffffffffff6
bin : 1111111111111111111111111111111111111111111111111111111111110110
--------------------------------------------------------------------------------
bit : 64
min signed : 0x8000000000000000, -9223372036854775808
max signed : 0x7fffffffffffffff, 9223372036854775807
max unsigned : 0xffffffffffffffff, 18446744073709551615
pwntools
后来有一次比赛,xkt问我怎么在python脚本里获得负数的十六进制表示,我说就是补码呗,然后给他看我写的这篇,xkt认为pwntools肯定有这个功能,果然查到:
from pwn import *
pack(-1, 16, endian='little', sign=True)
unpack(b'\xff\xff', 16, endian='little', sign=True)
不过从补码转负数时这里仍然需要给出数值长度。