计算机原码、反码、补码的理解

(一)预备知识

(1) 二进制、八进制、十进制、十六进制之间的相互转换

一、 二进制与十进制之间的转换

1、二进制转十进制(不分整数和小数从最后一位算起,每一位上的数乘以2的几次方,这个次数由这个数字所在的位置决定,从零位开始,然后相加)

例:01101011.001转十进制

计算机原码、反码、补码的理解

 

 

 然后:1+2+0+8+0+32+64+0=107.125
01101011=107

2、十进制转二进制

整数:除2取余法是一个连续除2的过程,直到商出现0时位置,余数反向排列;
例:整数23转二进制:

计算机原码、反码、补码的理解

 

 

然后把余数反向排列:23=10111

小数:乘2取整法,即将小数部分乘以2,然后取整数部分,剩下的小数部分继续乘以2,然后取整数部分,剩下的小数部分又乘以2,一直取到小数部分 为零为止。如果永远不能为零,就同十进制数的四舍五入一样,按照要求保留多少位小数时,就根据后面一位是0还是1,取舍,如果是零,舍掉,如果是1,向入一位。换句话说就是0舍1入。读数要从前面的整数读到后面的整数。

例:0.125转二进制

0.125乘以2,得0.25,则整数部分为0,小数部分为0.25;
0.25乘以2,得0.5,则整数部分为0,小数部分为0.5;
0.5乘以2,得1.0,则整数部分为1,小数部分为0.0;
从第一位读起,读到最后一位,即为0.001。

23.125 转二进制10111.001

二、二进制与八进制之间的转换(基础还是二进制和十进制之间的转换)

取三合一法,即从二进制的小数点为分界点,向左(向右)每三位取成一位,接着将这三位二进制按权相加,得到的数就是一位八位二进制数,然后,按顺序进行排列,小数点的位置不变,得到的数字就是我们所求的八进制数。如果向左(向右)取三位后,取到最高(最低)位时候,如果无法凑足三位,可以在小数点最左边(最右边),即整数的最高位(最低位)添0,凑足三位)这里的最高位,最低位和十进制的一样,前面的是最高位,后面的是最低位。

三位二进制表示一位八进制, 因为三位二进制数最大(111)的十进制数也就是7,所以就保证每位数都是0-7之间的数

1、二进制转八进制

例:1100100转为八进制

1100100拆分成:001 100 100

计算机原码、反码、补码的理解

 

 

 依次读下来就是:144
1100100=144

2、八进制转二进制

八进制数与二进制数对应关系如下:

计算机原码、反码、补码的理解

 

 

 例:将八进制数653524转换为二进制

110 101 011 101 010 100

三、二进制与十六进制之间的转换(基础还是二进制和十进制之间的转换)

四位二进制表示一位十六进制, 因为四位二进制数最大(1111)也就是十进制的表示法15即十六进制的表示法F,所以就保证每位数都是0-F之间的数。

1、二进制转十六进制

例:1100100 拆分 0110 0100
0110=6
0100=4
1100100=64

2、十六进制转二进制

十六进制数与二进制数之间的对应关系:

计算机原码、反码、补码的理解

 

 

 四、十进制与十六进制之间的转换

其算法和二进制和十进制之间的算法一样只是,由2变成了16

1、十进制转十六进制
例:十进制数123 转成十六进制
123除16商7余B

7除16商0余7
结果就是7B
2、十六进制转十进制
例:十六进数 2AF5

计算机原码、反码、补码的理解

 

 

 2AF5=10997

参考原文:https://www.php.cn/faq/422257.html

(2)原码、反码、补码的由来

由计算机的硬件决定,任何存储于计算机中的数据,其本质都是以二进制码存储。

根据冯~诺依曼提出的经典计算机体系结构框架。一台计算机由运算器,控制器,存储器,输入和输出设备组成。其中运算器,只有加法运算器,没有减法运算器(据说一开始是有的,后来由于减法器硬件开销太大,被废了 )

所以,计算机中的没法直接做减法的,它的减法是通过加法来实现的。你也许会说,现实世界中所有的减法也可以当成加法的,减去一个数,可以看作加上这个数的相反数。当然没错,但是前提是要先有负数的概念。这就为什么不得不引入一个该死的符号位。

  1. 而且从硬件的角度上看,只有正数加负数才算减法。

  2. 正数与正数相加,负数与负数相加,其实都可以通过加法器直接相加。

计算机原码、反码、补码的理解

 

 

(二)原码、反码、补码的理解

(1)概念

1、机器数:

一个数在计算机中的表现形式叫做机器数,这个数有正负之分,在计算机中用一个数的最高位(符号位)用来表示它的正负,其中0表示正数,1表示负数。

例如正数7,在计算机中用一个8位的二进制数来表示,是00000111,而负数-7,则用10000111表示,这里的00000111和10000111是机器数

2、真数

计算机中的机器数对应的真实的值就是真数,对最高位(符号位)后面的二进制数转换成10进制,并根据最高位来确定这个数的正负。对于上面的00000111和10000111来说,对最高位后面的二进制数转换成10进制是7,在结合最高位的值,得出对应的真数分别是7和-7

3、原码

用第一位表示符号,其余位表示值。因为第一位是符号位,所以8位二进制数的取值范围就是:[1111_1111 , 0111_1111]  即 [-127 , 127] ,原码是容易被人脑所理解的表达方式

4、反码

正数的补码反码是其本身,负数的反码是符号位保持不变,其余位取反。例如正数1的原码是[0000_0001],它的反码是是其本身

[0000_0001],-1的原码是[1000_0001],其反码是[1111_1110]

5、补码

正数的补码是其本身,负数的补码是在其反码的基础上+1,例如正数1的原码是[0000_0001],他的补码是其本身[0000_0001],

-1的补码是[1111_1111]

(2)反码、补码的由来

因为人脑可以知道第一位是符号位,可以根据符号位对真值的绝对值进行加减乘除,但是对于计算机来说,加减乘除是最最最基本的运算,要设计的尽量简单,计算机辨别符号位会让计算机的设计电路变得很复杂,于是人们想出了让符号位也参与到运算上来。减去一个数,等于加上他的负数。

使用原码参数运算的缺陷
计算机原码、反码、补码的理解

 

 

从上面的原码表中可以看见左边每增加一个二进制单位对应的真数是递减的,而右边每增加一个二进制单位对应的真数是递增的,所以对于原码来说,能满足正数的加法,但无法满足负数的加法

2+1 = [0000_0010]原+[0000_0001]原=[0000_0011]原 = 3

1+-1=[0000_00001]原+[1000_0001]原=[1000_0010]原=-2

为了满足负数对加法的需求,就必须让负数与他对应的二进制码是同步递增或者同步递减

于是就通过符号位不变,其余位取反来满足这个同步递增或者递减的要求,由于正数本来就满足它本身的加法,所以不需要做任何改变。这就是反码的定义由来。
计算机原码、反码、补码的理解

 

 

从上图的反码表中可以看到在运算不跨过0的时候,正负数的加法已经能满足要求

-2+1=[1111_1101]反+[0000_0001]反=[1111_1110]反=-1

127+1=[1000_0000]反=-127=128 加法算出来是128,由于128超过最大值,余1,所以取最小值开始的第一位,也就是

最小值-127,但是这里有个不合理的地方,就是[1111_1111]和[0000_0000]都表示0,这导致在实际计算中每当跨过0一次,就有一个单位的误差

-1+2=[1111_1110]反+[0000_0010]反=[0000_0000]反=0

要解决这个问题就必须让反码中的[1111_1111]和[0000_0000]合并,

由于[1111_1111]+[0000_0001]=[0000_0000],所以在负数反码的基础上+1就可以解决反码中跨0的误差问题,同时不会对负数与它对应的二进制反码的同步递增产生影响,所以在反码的基础上+1就完美的解决了符号参与预算的问题,这就是补码为什么是在负数反码的基础上+1的由来。
计算机原码、反码、补码的理解

 

 

从上面的图中发现还有一个[1000_0000]的二进制没有对应任何真数,于是就规定了这个数的真数是-128

所以补码的表示范围是[-128~127] ,这样一来256个二进制正好表示256个整数,在实际二进制的运算中超过范围其实就是对256的取余预算(x+128)mod 256 - 128。

(三)总结

对于有符号的数而言:

1、二进制的最高位是符号位:0表示正数,1表示负数

2、正数的原码、反码、补码都一样:三码合一

3、负数的反码=原码的符号位不变,其它按位取反

4、负数的补码=反码+1,负数的反码=补码-1

5、0的反码、补码都是0

6、Java没有无符号数,换而言之、Java中的数都是有符号的

7、计算机对数的运算都是对数的补码进行运算

8、计算机输出结果时,输出的是数的原码

参考原文:1、https://blog.csdn.net/chenchao2017/article/details/79733278

                  2、https://blog.csdn.net/afsvsv/article/details/94553228

上一篇:04_HashMap


下一篇:ERROR: The partition with /var/lib/mysql is too full! failed!