Blablabla~
今天重温了《深入了解计算机系统》关于符号数的bianary表示,弄懂了补码的原理。并写了python脚本验证。当初对这块有疑惑是一上来就告诉你,xxx取反加1就是补码,完全不知道为什么这么做,今天抛开这个说法,从我们想要解决问题入手去思考。
目标
我们想要解的问题是:我们知道计算机都是binary的存储,那么这些binary怎么解释或者对应到 带符号的整数呢?
这是我们想要的解决的问题
解决方案(general)
提供了一种翻译机制,就是一个函数,输入是binary,输出是 符号数
def B2S(binary):
reutrn sign_integer
具体方案(detail)
- 一个很正常是思路就是一个bit位符号位,0为正,1为负,其他的n-1 bit照旧。这个方案有一个问题是0有两个表示10000和00000 (这套方案叫 singned magnitude 原码),这个方案正负数相加会有问题哦(不是0)
- 另外一个思路是负数:保留符号位,其余的数 取反,有一个问题就是+0 = 0000, -0 = 11111,有两个方式(这套方案叫 ones' complement), 两数 x+(-x)会得到1111 (就是-0)
- 最后看我们计算机中的最终选择的方案吧 (two's complement)
书里的2.2.3章节的这个图比较形象地画出方案细节,具体可以看下1011的例子。-8在左边是灰色的,蓝色的是正的,累加起来。
def B2U(b):
#binary to unsigned
x=0
b_str = b[::-1]
for i,v in enumerate(b_str):
if int(v)!=0:
x+= np.power(2,i)
return x
def test_B2U():
assert B2U('0001') == 1
assert B2U('0101') == 5
assert B2U('1011') == 11
assert B2U('1111') == 15
def B2T(b):
#bianry to two's component
x = 0
b_str = b[::-1]
idx = len(b_str)-1
for i,v in enumerate(b_str):
if i<idx:
if int(v)!=0:
x+= np.power(2,i)
else:
x += -int(v)*np.power(2, i)
return x
def test_B2U():
assert B2U('0001') == 1
assert B2U('0101') == 5
assert B2U('1011') == -5 # -8+2+1
assert B2U('1111') == -1 # -8+4+2+1
本质上,我们知道符号数的第一位来表示符号,0表示正,1表示负,那么实际的数值就是:
- 最大的值: 0111, 2^n-1
- 最小的值: 1000, - 2^n
- 一般的值:(-v)*2^n+ 其他项
明白了最小的值是1000,结合上面的图,那么任意bit的整数,-1的表示都是0xfffff。。。
补码:对于任意x, 我们用 来表示 -x的w位表示
另一个思路是,x+(-x)=0 那么,-x = 0-x = 2^w - x = 10000 -(x) 这个思路很好(但是我们不应该记住结论/公式:负数的binary就是其整数取反再加1,记住过程,我们希望x+(-x)=0,所以-x = 0000- x
总结
这套方案(函数),在计算机里叫做 "补码编码". 一般都使用补码的形式来表示有符号整数。弄明白了计算机使用补码(two's component)的编码方式来表示负数(即符号数)。本质就是制定一个规则,一个bianry怎么serialze 到有符号数,这个规则就是一个函数,就是我上面写的B2T,这个函数叫做补码编码。
unsigned char x=0;
printf("%u", x-1); #255