有符号二进制数表示方法Signed binary number representation

Made by Mike_Zhang


Computer System 相关文章:
有符号二进制数表示方法 Signed binary number representation
浮点数二进制数表示方法 Floating point numbers representation
UltraFish Plus - 有符号二进制数转换器 Signed binary number convertor
UltraFish Plus - 浮点数表示方法转换器 Floating Point Numbers Representation Convertor
UltraFish Plus - 多进制整数转换器 Multiple Bases Unsigned Integer Convertor
Y86-64学习1-State & Instruction & Basic Encoding
Y86-64学习2-Y86-64 SEQ Stages
x86-64学习1-Introduction & Data Formats & Information Accessing & Arithmetic Logical Operation
x86-64学习2-Control


(2020-09-24更新: 新增Excess)
(2020-09-24更新: 新增转换代码)

我在之前的文章 byte数据类型在显式类型转换时超出其取值范围的转换过程中提到里了计算机中原码,反码,补码等概念, 但是并没有仔细展开. 最近在学校的课程内容中又碰到了, 所以接下来具体来说一下.
数据在计算机中都是以二进制(binary)0和1的形式储存的, 但是在表示负数的时候, 并不能直接把“-”加在数字前面, 必须要用一些特定的方法来表示.
一般来说,有以下4种常用的表示负数的方法:

Signed Magnitude (原码);
One’s Complement (1的补码)(中文又称反码);
Two’s Complement (2的补码)(中文又称补码);
Excess (Biased)

(以下均以 8-bit 为例 )

Signed Magnitude

Signed Magnitude又称原码, 是用二进制数最高位(MSB)来表示符号, 0表示正号“+”, 1表示负号“-”, 剩下的位表示数值的绝对值
例如: (等号前为十进制数, 后为二进制数)

+20 = 00010100
-20 = 10010100

有意思的一点是, +0 = 00000000, -0 = 10000000.
对于8-bit来说, 用Signed Magnitude来表示, 其取值范围是-127 ~ +127.
如下表:

binary of: Signed Magnitude
00000000 0
00000001 1
00000010 2
01111111 127
10000000 -0
10000001 -1
11111111 -127

以下是Python代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
def signedMagnitude(numd):
outnum = "" #储存输出结果
absnumd = abs(int(numd)) #取得绝对值
numb = bin(absnumd) #把绝对值转成二进制
bnumb = numb[2:] #去除二进制数前两位0b
fixed = '{:0>7}'.format(bnumb) #用0填充空位至7位
if numd[0:1] == "-": #判断是否为负数
outnum = "1" + fixed #若是则在最高位加上1
else:
outnum = "0" + fixed #若不是则在最高位加上0
print(outnum) #输出
signedMagnitude(str(input("Enter(-127to127):")))

上面提到, 对于8-bit来说, 用Signed Magnitude来表示, 其取值范围是-127 ~ +127, 说明只能表示255个数, 但是8-bit应该是可以总共表示256个数的, 说明有一个数被浪费了.(0被表示了两次,+0和-0)


One’s Complement (反码)

One’s Complement (1的补码), 中文又称反码。对于 n-bit 的表示法, 数$x$的one’s complement(反码)即为:

随后把$x$转化成相应的二进制数。

举例来说:

for $x= -60$
$|x|=20$ = 00111100
one’s complement of x = 2^8 - 1 -$|x|_2$ = 100000000 - 1 - 00111100 = 11000011

可以发现, 00111100的one’s complement(反码)即为11000011, 简单来说就是把原来的1变成0, 0变成1, 这也是为说明把one’s complement叫做反码.

再举一个例子:

+20 = 00010100
-20 = 11101011

有意思的一点是, +0 = 00000000, -0 = 11111111
对于8-bit来说, 用one’s complement(反码)来表示, 其取值范围是-127 ~ +127.
如下表:

binary of: Signed Magnitude One’s Complement (反码 )
00000000 0 0
00000001 1 1
00000010 2 2
01111111 127 127
10000000 -0 -127
10000001 -1 -126
11111111 -127 -0

以下是Python实现:

1
2
3
4
5
6
7
8
9
10
11
def onesComplement(numd):
outnum = ""
if numd[0:1] == "-":
numd = 256 - 1 - abs(int(numd)) #2^n - 1 - x
print(numd)
absnumd = abs(int(numd))
numb = bin(absnumd)
bnumb = numb[2:]
outnum = '{:0>8}'.format(bnumb)
print(outnum)
onesComplement(input("Enter(-127to127):"))

和Signed Magnitude一样, 用one’s complement(反码)表示也会浪费一个数.(0被表示了两次,+0和-0)


Two’s Complement (2的补码)(中文又称补码)

Two’s Complement (2的补码), 中文又称补码. 对于n-bit表示法, 数$x$的two’s complement(补码)即为:

随后把$x$转化成相应的二进制数。

其实就是在反码上+1, 即, two’s complement(补码) = one’s complement(反码) + 1. 这样就可以解决one’s complement(反码)出现-0的情况, 使-128 ~ -1都能被表示, 不产生浪费.
举一个例子:

$x=-20$
$|x|=20=$ 00010100
one’s complement(反码) = 11101011
two’s complement(补码) = 11101011 + 1 = 11101100
-20 = 11101100

对于8-bit来说, 用two’s complement(补码)来表示, 其取值范围是-128 ~ +127.

binary of: Signed Magnitude One’s Complement (反码) Two’s Complement(补码 )
00000000 0 0 0
00000001 1 1 1
00000010 2 2 2
01111111 127 127 127
10000000 -0 -127 -128
10000001 -1 -126 -127
11111111 -127 -0 -1

以下是Python实现:

1
2
3
4
5
6
7
8
9
10
11
def twosComplement(numd):
outnum = ""
if numd[0:1] == "-":
numd = 256 - abs(int(numd)) #2^n - x
print(numd)
absnumd = abs(int(numd))
numb = bin(absnumd)
bnumb = numb[2:]
outnum = '{:0>8}'.format(bnumb)
print(outnum)
twosComplement(input("Enter(-128to127):"))

如何快速获得补码:
以-12为例

  1. 写出其绝对值的二进制数: 00001100
  2. 从右向左开始, 找到第一个1(00001 1 00), 反转其左边所有位(11110 1 00)并保持其右边位不变

因此:

+12 = 00001100
-12 = 11110100


注意

原码、反码、补码是用作负数的二进制表示, 对于正数的表示没有影响, 从下面的表格就可以看出这一点:

binary of: Signed Magnitude One’s Complement (反码) Two’s Complement(补码)
00000000 0 0 0
00000001 1 1 1
00000010 2 2 2
01111111 127 127 127
10000000 -0 -127 -128
10000001 -1 -126 -127
11111111 -127 -0 -1

但是接下来的Excess方法对被转换的所有整数都有影响.


Excess (Biased)

excess与上面所说的三种方法有很大的不同, 最明显的不同就是, 在excess方法中, 最高位的1代表正数, 0代表负数, 以下是excess的转换方法:

  1. 在原来的数字上加上一个常量(bias);
  2. 再把得出来的数转成二进制.

举例:

Excess 128 表示加的数是128;
+12 -> +12 + 128 = 140 = 10001100
-12 -> -12 + 128 = 116 = 01110100
0 -> 0 + 128 = 128 = 10000000

对于8-bit来说, 用Excess128来表示, 其取值范围是-128 ~ +127.

binary of: Signed Magnitude One’s Complement (反码) Two’s Complement(补码) Excess 128
00000000 0 0 0 -128
00000001 1 1 1 -127
00000010 2 2 2 -126
01111111 127 127 127 -1
10000000 -0 -127 -128 0
10000001 -1 -126 -127 1
11111111 -127 -0 -1 127

以下是Python实现:

1
2
3
4
5
6
7
8
def excessEightbits(numd):
outnum = ""
biasednum = int(numd) + 128
numb = bin(biasednum)
bnumb = numb[2:]
outnum = '{:0>8}'.format(bnumb)
print(outnum)
excessEightbits(input("Enter(-128to127):"))

从上面这个表可以看出, 用excess的方法能够让二进制数和十进制数保持一样的大小顺序, 这样能够在之后计算时保持和值和差的的统一,方便计算.
总的来说, excess方法是让负号“消失”, 通过给每个数加上一个数, 让某一范围内的数整体向正数方向移动, 直到没有负数. 而移动的长度就是加的数的大小.
对于一个N-bit的数, 一共可以表示2^N个数, 其中有 2^(N-1) 个负数, 2^(N-1) 个正数(包括0), 能够表示的范围就是 -2^(N-1) ~ 2^(N-1)-1.


代码分享

以下Python代码实现用四种方法使整数在二进制数和十进制数之间转换

代码下载链接

也欢迎到我的开发性页面使用UltraFish Plus - 有符号二进制数转换器 Signed binary number convertor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#Made by Mike_Zhang
def signedMagnitude(numd):
outnum = ""
absnumd = abs(int(numd))
numb = bin(absnumd)
bnumb = numb[2:]
fixed = '{:0>7}'.format(bnumb)
if numd[0:1] == "-":
outnum = "1" + fixed
else:
outnum = "0" + fixed
print(outnum)

def onesComplement(numd):
outnum = ""
if numd[0:1] == "-":
numd = 256 - 1 - abs(int(numd))
print(numd)
absnumd = abs(int(numd))
numb = bin(absnumd)
bnumb = numb[2:]
outnum = '{:0>8}'.format(bnumb)
print(outnum)

def twosComplement(numd):
outnum = ""
if numd[0:1] == "-":
numd = 256 - abs(int(numd))
print(numd)
absnumd = abs(int(numd))
numb = bin(absnumd)
bnumb = numb[2:]
outnum = '{:0>8}'.format(bnumb)
print(outnum)

def excessEightbits(numd):
outnum = ""
biasednum = int(numd) + 128
numb = bin(biasednum)
bnumb = numb[2:]
outnum = '{:0>8}'.format(bnumb)
print(outnum)

def signedMagnitude_re(numb):
if numb[0] == "0":
print(int(numb, 2))
elif numb[0] == "1":
print("-", int(str(numb[1:]), 2),sep="")

def onesComplement_re(numb):
if numb[0] == "0":
print(int(numb, 2))
elif numb[0] == "1":
numd = 256 - 1 - int(str(numb), 2)
print("-" , numd, sep="")

def twosComplement_re(numb):
if numb[0] == "0":
print(int(numb, 2))
elif numb[0] == "1":
numd = 256 - int(str(numb), 2)
print("-" , numd, sep="")

def excessEightbits_re(numb):
numd = int(str(numb), 2)
print(numd - 128)

def main():
choice1 = input("Enter(d = D to B; b = B to D):")
choice2 = input("Enter(s = Signed Magnitude; 1 = 1s Complement; 2 = 2sComplement; e = excess):")
if choice1 == "d":
if choice2 == "s":
signedMagnitude(input("Enter(-127to127):"))
elif choice2 == "1":
onesComplement(input("Enter(-127to127):"))
elif choice2 == "2":
twosComplement(input("Enter(-127to128):"))
elif choice2 == "e":
excessEightbits(input("Enter(-127to128):"))
elif choice1 == "b":
if choice2 == "s":
signedMagnitude_re(input("Enter(8bit binary):"))
elif choice2 == "1":
onesComplement_re(input("Enter(8_bit binary):"))
elif choice2 == "2":
twosComplement_re(input("Enter(8bit binary):"))
elif choice2 == "e":
excessEightbits_re(input("Enter(8_bit binary):"))
main()
#Made by Mike_Zhang

写在最后

原码、反码、补码中可以深挖的东西还有好多,都十分有趣,之后会继续记录。
最后,希望大家一起交流,分享,指出问题,谢谢!


原创文章,转载请标明出处
Made by Mike_Zhang




感谢你的支持

有符号二进制数表示方法Signed binary number representation
https://ultrafish.io/post/Signed-binary-number-representation/
Author
Mike_Zhang
Posted on
September 19, 2020
Licensed under