【基础】二进制及编码

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/greatbody/article/details/54135534

意义

xxxx

几种编码及转换

几种编码都需要懂,而且分情况。使用这些编码的基本前提,是确定单位,比如这里探讨的是一个字节,还是int(例如java中的32位int)。

以下内容我们均假设为一个字节(8位)

原码及使用

几点特性:

  • 符号位固定为标定符号,不参与表示数字大小
  • 非符号位的位表示数字绝对值
  • 有正负零

想写出原码,需要先确定数字。

我们知道,1个字节的原码是支持正负127范围内的整数的,包括从-127到-0到+0再到+127的这些整数。

+1

  • 符号为正,所以符号位为0
  • 绝对值为1,所以非符号位为1的二进制形式1扩展6个0,即0000001

所以,可以得到+1原码的表示形式:
0 0000001

-1

  • 符号为负,所以符号位为1
  • 绝对值为1,所以非符号位为1的二进制形式1扩展6个0,即0000001

所以,可以得到-1原码的表示形式:
1 0000001

反码及使用

反码是不直接使用的,是原码和补码间的桥梁。

反码的计算方式

正数的反码=原码
负数的反码=在原码的基础上,符号位保持不变,其它位依次取反

补码及使用

补码直接用于计算机的计算,特点是将减法转换成加法。

补码的计算方式

正数的补码=原码
负数的补码=原码的反码+1

深入理解编码本质

编码,顾名思义是由人类编的。兼有存在,意义,功能多重属性。

原码编码

原码的特点就是规定,在把一定的数位作为一个单元后,取其中的最高位(即书写中最左边的位)为符号位,0表示正数,1表示负数。然后剩余的位数用于表示数字的大小(即绝对值)。

反码及转换

这里,我们从一个例子来看。假设以两位长度二进制作为单位,我们见微知著的来分析。

原码 反码 补码 逻辑意义
000 000 000 +0
001 001 001 +1
010 010 010 +2
011 011 011 +3
100 111 1([1]00)
101 110 111 -1
110 101 110 -2
111 100 101 -3

为什么我们要在原码的100对应的逻辑意义那里打一个问号呢?

因为我们看,逻辑意义中已经有了-3,-2,-1,+0,+1,+2,+3,再看这里的补码,其实是有一个进位的。

原码100的补码是这样计算的:

  • 先计算反码:1 00 -> 1 11
  • 再计算补码:1 11,对于其数值部分+1,即11(2)+1(2)=100(2),因为1进位了,而只保留两位,所以我们得到00。再加上预留的符号位1,就是:1 00

原码的100对应的补码是100,那么这个补码应该对应的意义也是-0么?

我们已知-2++2=+0=-0
计算:
-2的补码是110
+2的补码是010
相加得:1000,其中高位1溢出,剩下000
说明000应该是0的补码表现形式

我们还已知-2+-2=-4
计算:
-2的补码是110
-2的补码是110
相加得:1100,其中高位1溢出,剩下100
所以,为了让计算规则自恰,我们说补码100表示-4
所以,-4的原码就是100

总结

回头来看这个表,我们会发现,应该加一个列在最右边

原码 反码 补码 逻辑意义 原码意义
000 000 000 0 +0
001 001 001 +1 +1
010 010 010 +2 +2
011 011 011 +3 +3
100 111 1([1]00) -4 -0
101 110 111 -1 -1
110 101 110 -2 -2
111 100 101 -3 -3

这里,原码是计算机中实际存储的数据格式;原码意义所列出的数值并不会被计算机用于将二进制转换为10进制;原码都会被转换成补码计算,计算机展示数字的时候,会将二进制数据按照逻辑意义展现出来。

上表采用3位二进制数做说明,是为了方便列表理解。

考虑到实际使用中有大量使用到无符号字节,所以列一个简单的表格,方便大家理解,无符号字节(C语言中一般是unsigned char)和char类型表示的数值的区别

原码 反码 补码 作为unsigned char 作为char
0000 0000 0000 0000 0000 0000 0 +0
0000 0001 0000 0001 0000 0001 1 +1
0111 1111 0111 1111 0111 1111 127 +127
1000 0000 1111 1111 1000 0000 128 -128
1000 0001 1111 1110 1111 1111 129 -1
1111 1111 1000 0000 1000 0001 255 -127

番外篇:为什么计算需要用补码,理论上为什么行?

举个例子,某一个数:
001+111
我们将001111都视为无符号数。
我们可以知道这样一个事实:
111变成补码的过程是:
符号位不变,其余位取反:100
然后加1:101
我们将原码和补码进行3位以内的二进制加法:
111
+101
=1100
保留3位后,得到100
也就是:101+111=100
那么111=100-101
所以:
001+101=001+100-111 (1)
又因为110+110=100 (2)
(1)中带入(2)
001+101=001+110+110-111=。。。。。

好了,以上一小节是扯淡,后来发现真理了

拿8位二进制数做例子:

原码 反码 补码 作为unsigned char 作为char
0000 0000 0000 0000 0000 0000 0 +0
0000 0001 0000 0001 0000 0001 1 +1
0000 0010 0000 0010 0000 0010 2 +2
0111 1111 0111 1111 0111 1111 127 +127
1000 0000 1111 1111 1000 0000 128 -128
1000 0001 1111 1110 1111 1111 129 -1
1000 0010 1111 1101 1111 1110 130 -2
1111 1111 1000 0000 1000 0001 255 -127

看起来好像char数值的顺序和原码的顺序不一致,其实就是这样的,因为char的数值是根据补码来计算,而补码是一个完全独立的编码,只不过和原码恰恰有一半重合,现在我们通过下面的表格来直接看补码。

补码 作为char
0111 1111 +127
0111 1110 +126
0000 0010 +2
0000 0001 +1
0000 0000 +0
1111 1111 -1
1111 1110 -2
1000 0010 -126
1000 0001 -127
1000 0000 -128

可以看出,对于负数:
补码转化为数值是:-2^7×1+非符号位绝对值
对于正数:
补码转化为数值是:-2^7×0+非符号位绝对值

举个例子,补码:1000 0010
补码转化为数值是:-2^7×0+000 0010(2进制)=10(2进制)=2(10进制)

所以,为什么转化为补码可以正常计算?因为补码是编码完全覆盖了连续整数范围且符号位能参与计算的编码。

所以,总结一下:
对于原码来说

  • 两个正数的非符号位之和不产生向符号位的进位,即可使用加法器计算
  • 两个负数的非符号位之和不产生向符号位的进位,即可使用加法器计算
  • 一个正数一个负数,不可以直接使用加法器计算

对于补码来说

  • 两个数使用加法器计算的结果,与其真实值直接相差n*模值

举例(3位二进制数):

补码 作为char
100 -4
101 -3
110 -2
111 -1
000 0
001 +1
010 +2
011 +3

如上,可支持的数据范围是-4到+3
那么我们计算-2+-3
即:
110+101=011
011是+3,而-2+-3应该是-5,我们发现:
+3=-5+4×2
符合前面给定的特性。

结语

谢谢大家观看,有疑问欢迎留言。

猜你喜欢

转载自blog.csdn.net/greatbody/article/details/54135534