一、计算机该怎么做减法

比如 2-1=1,1-1=0 由于种种原因,加法电路难度和成本已经很高了,当时的计算机理论条件下,再去设计一个减法电路,费力又费钱,前辈们想用加法电路来解决减法运算问题。思路如下,熟悉的可以跳过。

 

假设当前时针指向8点,而准确时间是5点,调整时间可有以下两种拨法:

一是倒拨3小时:8-3=5

二是顺拨9小时:8+9=12+5=5

可以看出在以模为12的系统中,减3和加9效果是一样的,因此减3运算,可以用加9来代替。

对“模”12而言,3和9互为补数(二者相加等于模)

“模”是计量器产生“溢出”的量。

从上面的事例中,我们可以得出结论,即在有模的计量系统中,减一个数等于加上它的补数(可以看下面的实例)。

这就可以将减法运算转化为加法运算,达到用加法电路来解决减法运算的目的。

例如:

减3和加253是一样的,8-3=8+253=256+5=5(对应的二进制数是[0000 0101])

就是说-3用253来表示,可以得到正确结果。这就是计算机做减法的思路。

 

二、这种算法的结果永远是正数

新的问题是 3-8=3+(256-8)=3+248=251,

显然3-8=-5。这种小的数字减去大的数字该怎么办?

例如:

3-8=3+(256-8)=3+248=251 (对应的二进制码是[1111 1011])

但是很显然,我们想要的是[1111 1011]=-5。

那我们有两种选择:

1)保留这种处理减法的思路,就让[1111 1011]=-5,然后再找其他的二进制码表示251

2)放弃这种处理减法的思路,确认[1111 1011]=251,并且找其他的二进制码表示-5

不管是哪种选择都必须舍弃一个数字,-5和251不能同时存在,有没有发现byte中没有正128,只有-128?

我们可以算一算,-5到-1是5个数,0算一个数,1到251是251个数,总共就有5+1+251=257个数,这可不行。因为8位二进制码最多只能表示256个数,如果-5和251都要表示,就会有257个数(不要少算了0),超出了表示范围。同理,-1和255 只能选一个,-2和254只能选一个,··· ,-128和128也只能选一个。

最终,为了避免2-3=2+(256-3)=255 = 2^8-1 ,即 255 = [1111 1111] 这样的尴尬。

我们确定将[1111 1111]定义为-1,而不是255,同理,[1111 1110]不再是254,定为-2,第一位为符号位,1为负号。

类推下去,0-128=-128,但是按原先的算法,0-128=0+(256-128)=128[1000 0000],尴尬了,因此128也光荣牺牲了,把[1000 0000]让位给-128,

至此,Java中byte的范围确定为-128~127,而不是-127~128。

 

三、拓展知识与常见问题

1、字、字节、位

字     word 
字节  byte 
位     bit 

1字节=8位 (1 byte = 8bit),一个字节的字长是8

1字=2字节 (1 word = 2 byte) ,一个字的字长为16

字节通常简写为“B”,而位通常简写为小写“b”,计算机存储器的大小通常用字节来表示。

字长计算机的每个字所包含的位数称为字长,计算的字长是指它一次可处理的二进制数字的数目。一般地,大型计算机的字长为32-64位,小型计算机为12-32位,而微型计算机为4-16位。字长是衡量计算机性能的一个重要因素。

 

bps( bits per second ),带宽、网络通讯的传输速率都是以「bps」为单位,例如 56Kbps、100.0Mbps

Bps( Byte per second ,电脑一般都以Bps 显示速度,例如 1Mbps 大约等同 128 KBps

bit,电脑记忆体中最小的单位,在二进位电脑系统中,每一bit 可以代表0 或 1 的数位讯号,正负电极表示 

Byte,一个Byte由8 bits 所组成,可代表一个字元(A~Z)、数字(0~9)、或符号(,.?!%&+-*/),是记忆体储存资料的基本单位,每个中文字和中文符号则须要两 Bytes

 

bit - Byte - KB - MB - GB - TB - PB - EB - ZB - YB - BB

字节 (Byte) = 8位(bit)

1KB ( Kilobyte,千字节) = 1024B

1MB ( Megabyte,兆字节) = 1024KB

1GB ( Gigabyte,吉字节,千兆) = 1024MB

1TB ( Trillionbyte,万亿字节,太字节) = 1024GB

1PB ( Petabyte,千万亿字节,拍字节) = 1024TB

1EB ( Exabyte,百亿亿字节,艾字节) = 1024PB

1ZB (Zettabyte,十万亿亿字节,泽字节) = 1024EB

1YB ( Yottabyte,一亿亿亿字节,尧字节) = 1024ZB

BB ( Brontobyte,千亿亿亿字节) = 1024YB

 

信息存储量是度量存储器存放程序和数据的数量。

主要度量单位是字节,1个字节(Byte)等于8位(b)二进制。

位(bit,Binary Digits):存放一位二进制数,即0或1,为最小的存储单位,8个二进制位为一个字节单位。

一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。

英文标点占一个字节,中文标点占两个字节。

 

2、原码、反码、补码的概念是拿来干什么的?

答:现在唯一的问题是,用户输入-5后,计算机怎么得到[1111 1011]并保存

计算机中的有符号数有三种表示方法,即原码、反码、补码。三种表示方法均有符号位和数值位两部分,符号位都是用0表示“正”,用1表示“负”,而数值位,三种表示方法各不相同。在计算机系统中,数值一律用补码来表示和存储。原因在于,使用补码,可以将符号位和数值域统一处理;同时,加法和减法也可以统一处理,用加法来替代减法。

整型数据在内存中的存放,是以补码表示的:

1)正数的补码,和原码相同

2)负数的补码,将该数的绝对值得二进制形式按位取反再加1

-5 = 1000 0101[原] = 1111 1010[反] + 1 = 1111 1011[补] (计算机内存的都是补码)

根据上面的理论,-5的补码就是256-5=251[1111 1011],但问题是我们正在解决减法问题,现在计算机还不会做减法,计算机怎么得到[1111 1011],

于是先辈们发明了一种算法,先得出原码[1000 0101],然后符号位不变,其余位取反(逻辑非),得到反码[1111 1010],再加1,得到了[1111 1011]。就是说,原反补码可以跳过计算直接得出2进制码。

 

3、计算机为什么不用原码表示负数?

答:

一是原码的作用本来就不是保存负数,也不保存正数,而是便于我们理解二进制。

二是因为原码做减法得到值是错误的。在减法计算中,2-1=[0000 0010]原+[1000 0001]原 = [1000 0011]原=-3,结果是不正确的。编码不仅要能表示数字,还要求运算结果和10进制不矛盾。

 

3.1  计算机为什么不用反码表示负数?

答:和原码同理,用反码也不能做减法:

2-1=2+(-1)=[0000 0010]原+[1000 0001]原= [0000 0010]反+[1111 1110]反=[0000 0000]反=[0111 1111]原= 127,并不等于正确结果1。(符号位也参与运算)

注:上面计算转二进制中,遵循了:

1)正数的补码,和原码相同

2)负数的补码,将该数的绝对值得二进制形式按位取反再加1

 

3.2 补码可以做减法吗?

答:可以

2-1=2+(-1)=[0000 0010]原+[1000 0001]原= [0000 0010]反+[1111 1110]反

= [0000 0010]补+[1111 1111]补 = [0000 0001]补 =1。

就是说只有补码可以做减法,所以计算机中采用补码来表示负数

注:上面计算转二进制中,遵循了:

1)正数的反码与补码,和原码相同

2)负数的补码,将该数的绝对值得二进制形式按位取反再加1

 

3、8位二进制补码具体对应的10进制是多少?

答:

补码 ....  对应的实际数值

1000 0000 ... -128

1000 0001 ... -127

... ... ...

1111 1110 ... -2

1111 1111 ... -1

0000 0000 ... 0

0000 0001 ... 1

0000 0010 ... 2

... ... ...

0111 1110 ... 126

0111 1111 ... 127

 

上面的看不明白?没关系,我们来穷举法,找规律

正数,原码跟补码一样 
+127, 0111 1111 
+126, 0111 1110 
+125, 0111 1101 
+124, 0111 1100 
+123, 0111 1011 
+122, 0111 1010 
... 
  +4, 0000 0100 
  +3, 0000 0011 
  +2, 0000 0010 
  +1, 0000 0001 
   0, 0000 0000 (无正负之分) 

下面是负数了,值,原码,符号位不变其它取反,+1 

  -1, 1000 0001, 1111 1110, 1111 1111 
  -2, 1000 0010, 1111 1101, 1111 1110 
  -3, 1000 0011, 1111 1100, 1111 1101 
  -4, 1000 0100, 1111 1011, 1111 1100 
  -5, 1000 0101, 1111 1010, 1111 1011 
  -6, 1000 0110, 1111 1001, 1111 1010 
  -7, 1000 0111, 1111 1000, 1111 1001 
  -8, 1000 1000, 1111 0111, 1111 1000 
  -9, 1000 1001, 1111 0110, 1111 0111 
-10, 1000 1010, 1111 0101, 1111 0110 
-11, 1000 1011, 1111 0100, 1111 0101 
-12, 1000 1100, 1111 0011, 1111 0100 
-13, 1000 1101, 1111 0010, 1111 0011 
-14, 1000 1110, 1111 0001, 1111 0010 
-15, 1000 1111, 1111 0000, 1111 0001 
-16, 1001 0000, 1110 1111, 1111 0000 
-17, 1001 0001, 1110 1110, 1110 1111 
... 
-24, 1001 1000, 1110 0111, 1110 1000 
... 
-99, 1110 0011, 1001 1100, 1110 0100 
... 
-124, 1111 1100, 1000 0011, 1111 1101 
-125, 1111 1101, 1000 0010, 1000 0011 
-126, 1111 1110, 1000 0001, 1000 0010 
-127, 1111 1111, 1000 0000, 1000 0001 
看出点什么了没有? 反码还差一个二进制 1000 0000

-128,  1 1000 0000,   1 1111 1111,   1 1000 0000 (注:最高位溢出,并不存在,符号位总在第一位1) 

由此可见,-128的原码和补码,都是 1000 0000

回顾扩展,0 原码 0000 0000,非负数的原码、反码、补码都相同,都是 0000 0000

总结规律,发现很好玩的,如下:

数值   ,     补码(计算机真实存储的二进制是补码)

127    ,   0111 1111
.....    
5       ,   0000  0101
4       ,   0000  0100
3       ,   0000  0011
2       ,   0000  0010
1       ,   0000  0001

0       ,   0000  0000
-128  ,   1000  0000

-127  ,   1000  0001
-126  ,   1000  0010
....
-3  ,   1111  1101
-2  ,   1111  1110
-1  ,   1111  1111

 

3.1 [1000 0000]补为什么表示-128?( 强烈推荐

答:上面已经解释了,这里只做验证推理。

[1000 0000]补不管是什么值,[1000 0000]补 + [0000 0001]补 = [1000 0001]补 是恒成立的

补码的原码,跟求原码的补码是一样的,即如下由补码求原码:

[1000 0000]补 = [1111 1111]反 = [1 1000 0000]原 = [1000 0000]原

同理,[1000 0000]原 来计算其补码:

[1000 0000]原 = [1111 1111]反 = [1 1000 0000]原 = [1000 0000]补

注意:符号位 1000 0000 的1一直不变,在二进制的位置总是第一位,第九位是溢出位,其为数值,舍弃。

所以,[1000 0000]补 = [1000 0000]原 = [1000 0000]补,[1000 0000] 是一个很特殊的编码

 

一步一步转化:

[1000 0000]补+[0000 0001]反(正数原码、反码、补码,三者都相同)= [1000 0000]反

[1000 0000]补+[0000 0001]反 = [1111 1111]反+[0000 0001]反 = [1 1000 0000]反 = [1000 0000]反

再转化:[1000 0000]补 + [0000 0001]原 = [1111 1111]原

[1000 0000]补 + [0000 0001]原 = [1111 1111]反 + [0000 0001]原 = [1000 0000]原 + [0000 0001]原

再转化:[1000 0000]补 + 1 = -127

很明显:[1000 0000]补 = -128

 

3.2 为什么是-128~127,而不是-127~128了?

答:如果是-127~128了,你会发现0-128=128

非负数有129个,负数只有127个(-1 ~ -127),补码不对称了。

 

3.3 网上有说因为-128和128同余,所以-128的补码是128,对吗?

答:首要问题不是-128的补码是多少,而是总共只有256个数,-128和128只能选一个,两个都存在不就是257个数了吗?

 

4、那教材上说原码中出现正0和负0又是怎么回事?

答:国内教材的狗屁理论。

 

5. 请问8位、16位、32位、64位CPU都是什么时间问世的?

答:CPU处理器8088是准十六位的、是继8086之后推出的,被IBM-PC机选作CPU

1979年6月1日,INTEL发布了 8位的8088微处理器。

1978年6月8日,INTEL发布其 16位微处理器8086。

1985年10月17日,x86系列CPU Intel 80386发布。80386的广泛应用,将PC从16位时代带入了 32位时代。

2003年才以x86-64和64位PowerPC处理器架构的形式引入到个人电脑领域的主流。

 

8088、8086 微型计算机的区别

1、CPU结构不同

1)8088只有8条数据信号引线。

2)8086有16条数据信号引线。

2、字节数不同

1)8088片内指令预取缓冲器深度只有4字节。

2)8086片内指令预取缓冲器深度为6字节。

 

段寄存器

一共有4个段地址寄存器,他们是:

CS (code segment register) 16位代码段寄存器
DS (data segment register) 16位数据段寄存器
SS (stack segment register) 16位堆栈段寄存器
ES (extra segment register ) 16为附加段寄存器

 

有待研究的问题:

1)-128的原码、反码各是多少?

2)计算机不可能懂-5,那我们点击“-”、“5”这两个键,cpu是否直接接受到了-5的源码了?

 

 

 

负数在计算机中的二进制表示

计算机中的有符号数有三种表示方法,即原码、反码、补码

整型数据在内存中的存放,是以补码表示的:

1)正数的补码,和原码相同。

2)负数的补码,将该数的绝对值得二进制形式按位取反再加1。

 

1、符号位

C语言规定,把内存的最高位作为符号位,0表示正数,1表示负数。

 

2、原码、反码、补码

在计算机中,正数的补码与原码相同,负数以其正值的补码形式表示

2.1 原码

一个整数,按照绝对值大小转换成的二进制数,称为原码,例如:6的4字节

00000000 00000000 00000000 00000110 是 6 的原码

2.2 反码

将二进制数按位取反,所得的新二进制数称为原二进制数的反码。

如 00000000 00000000 00000000 00000110 的反码是 11111111 11111111 11111111 11111001

2.3 补码

补码 = 反码 + 1 (仅限负数,正数补码是其本身)

反码加1称为补码。也就是说,要得到一个数的补码,先得到反码,然后反码加上1,所得数称为补码。

所以 -6 在计算机器的表示形式为:

# -6 的正值 6 的二进制:
00000000 00000000 00000000 00000110

# 取反得反码:
11111111 11111111 11111111 11111001

# +1 得补码,即 -6 在计算机中的二进制表示:
11111111 11111111 11111111 11111010

简易理解:若模数为256,则负数 -6 取模后的正数值为 -6+256=250

计算机的内存里,存储的都是整数(正数 + 0 + 负数)的补码

 

补码原理,负数为什么要用补码表示

1、模(Modulo)

In mathematics, modular arithmetic is a system of arithmetic for integers, where numbers “wrap around” upon reaching a certain value—the modulus (plural moduli).

模是指一个计量系统的计数范围,如时钟(12点、24点)、10进制、2进制、16进制、32进制、太极图等。

计算机也是一个计算器,它也是有一个计量范围,即都存在一个“模”。

如时钟的计量范围是0~11,模 = 12

32位计算机的计量范围是2^32,模 = 2^32

“模”是计量器产生“溢出”的量,它的值在计量器上表示不出来,计量器上只能表示出模的余数,

例如:

12点的余数有 0、1、2、3、4、5、6、7、8、9、10、11

2进制的余数有 0、1

10进制的余数有 0、1、2、3、4、5、6、7、8、9

 

2、补数

假设当前时针指向11点,而准确时间是8点,调整时间可有以下两种拨法:

1)倒拨3小时:11-3=8

2)顺拨9小时:11+9=12+8=8

在以模为12的系统中,加9和减3效果是一样的,因此凡是减3运算,都可以用加9来代替。对“模”12而言,9和3互为补数(二者相加等于模)。

所以,得出一个结论,即在有模的计量系统中,减一个数等于加上它的补数,从而实现将减法运算转化为加法运算的目的。

从上面的化减法为加法,以及所谓的溢出等等可以看到,“模”可以说就是一个太极万事万物,阴阳转化,周而复始,无始无终,循环往复

 

3、补码原理 强烈推荐

计算机上的补码就是算术里的补数。

设我们有一个 4 位的计算机,则其计量范围即模是 2^4 = 16,所以其能够表示的范围是0~15,现在以计算 5 - 3为例,我们知道在计算机中,加法器实现最简单,所以很多运算最终都要转为加法运算,因此5-3就要转化为加法:

 # 按以上理论,减一个数等于加上它的补数,所以
 5 - 3
 # 等价于 
 5 + (16 - 3)   // 算术运算单元将减法转化为加法
 # 用二进制表示则为:
 0101 + (10000 - 0011)
 # 等价于
 0101 + ((1 + 1111) - 0011)
 # 等价于
 0101 + (1 + (1111 - 0011))
 # 等价于
 0101 + (1 + 1100) // 括号内是3(0011)的反码+1,正是补码的定义
 # 等价于
 0101 + 1101
 # 所以从这里可以得到,正数5不变,-3实际用1101表示的,即:
 -3 = 1101
 # 即 `-3` 在计算机中的二进制表示为 `1101`,正是“ -3 的正值 3(`0011`)的补码(`1101`)”。
 # 最后一步 0101 + 1101 等于
 10010

自己来一遍推演:

1)正数补码

5 = 0101 ,正数补码是其自身,即 5 补码也为 0101

2)负数补码

负数补码 = 负数反码 + 1

-3 = 1011 = 1100[反] + 1 = 1101[补],成功验证了 -3 补码为 `1101`

最后,因为我们假设的计算机是 4 位的,10010的第一位“溢出”了,所以只保存了 4 位,即 0010,而当计算机去读取时这正是我们所期望的 2

叹为观止吧,天才般的设计!感恩 伏羲莱布尼兹冯诺依曼

 

 

参考推荐:

8位有符号数的范围为“-128 — +127”

8位二进制中,补码是什么?-128是约定的?

8位有符号数的范围为-128 ~ +127 深入浅出探究

MySQL 数据类型

MySQL 数据库 User表权限以及用户授权详解

Linux tcpdump命令详解

Java解惑(Java Puzzler)

运维人员常用软件总结

HTTP 协议的历史进化演变

MacBook Pro 修改 mac 地址

区块链的技术简史、应用场景、未来趋势