现实生活中的所有信息在计算机看来就是一堆数据,计算机的工作就是和数据打交道。简单来说,计算机最核心的功能就是两个:存储数据、处理数据。计算机首先得要能正确地存储用户数据,有了数据之后,计算机还要能够按照用户要求处理(运算)数据。今天痞子衡要讲的就是数据(整数)在计算机中是怎么存储(表示)的。
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家讲的是计算机原理知识点-整数表示。
本系列痞子衡会给大家介绍一些关于计算机原理的有趣的知识点,今天是系列第一节课,痞子衡要讲的是计算机中最基础的知识,即整数数据表示法。
现实生活中的所有信息在计算机看来就是一堆数据,计算机的工作就是和数据打交道。简单来说,计算机最核心的功能就是两个:存储数据、处理数据。计算机首先得要能正确地存储用户数据,有了数据之后,计算机还要能够按照用户要求处理(运算)数据。今天痞子衡要讲的就是数据(整数)在计算机中是怎么存储(表示)的。
一、何谓进制
学过数字电路的朋友肯定知道数据的进制表示法,常见的有二进制、八进制、十进制、十六进制表示法。在现实生活中我们所用的数据表示法是十进制,即由0-9共10个数字表示所有数值。
任意进制转换成十进制的公式为Xn-1X...X1X0=Xn-1*Rn-1+...X1*R+X0,其中R为当前被转换进制的权重值,二进制则R=2,八进制R=8,十进制R=10,十六进制R=16。X可取值当前被转换进制所有的单个数字(值)。
计算机就是大规模集成数字电路,数字电路只有两种电平状态(低/高),对应到数字表示就是0,1,所以实际上计算机只认二进制,所有保存在计算机中的数据都是由二进制来表示的。
二、整数编码
2.1 正负数表示(原码)
数据在计算机中都是用二进制来表示的。对于无符号整数(正整数),比如uint8_t型,8个bit均为数据位,用二进制表示其范围为8'b00000000 ~ 8'b11111111(0~255),这种所有bit均为数据位以表示无符号整数的编码方式即为原码。
但很多时候我们不仅要保存正整数,还要保存负整数,此时便涉及到有符号整数的表示方法了,此时编码规定最高bit表示符号(0为正,1为负),其余bit表示数据位,这种表示有符号整数的编码方式实际上也是原码。对于int8_t型,bit7为符号位,bit6-0为数据位,用二进制表示其范围为8'b10000000 ~ 8'b01111111(-128~127),有朋友可能会对这里有疑问,似乎按照编码规定能表示的整数范围应该是-127 ~ 127,但实际并不是如此,具体痞子衡在下面会解释。
2.2 减法的实现(反码)
数字电路里我们都听说过加法器,但没有减法器的说法,说明计算机实际上仅支持加法运算,对于正整数做加法运算用原码没有任何疑义。那么对于无符号整数做减法运算(等同于有符号数的加法运算)怎么实现?我们尝试着用原码来代入运算,比如129-7(即129+(-7)):
8'b10000001(129原码) + 8'b10000111(-7原码) -------------- = 8'b????????(符号位如何参与运算?) =9'b100001000(不区别符号位,直接运算,最高bit由于溢出被自动丢弃) = 8'b00001000(结果为8)
我们似乎遇到了困难,有符号运算数的符号位如何参与运算?如果我们不区别对待符号位,直接把它当做数据位进行计算,得出的结果显然是不对的,所以原码是无法被用作减法/有符号数的计算的。为了解决这个问题,此时引入了第二种编码方式规定(反码),即正数的反码与原码相同,负数的反码等于原码除了符号位外,其余数据位全部取反。有了反码,此时我们用反码重新计算129-7:
8'b10000001(129反码) + 8'b11111000(-7反码) -------------- =9'b101111001(最高bit由于溢出被自动丢弃) = 8'b01111001(结果为反码,由于最高bit为0,即表示正数,所以等同于原码,其值为122)
2.3 消除+/-0之争(补码)
反码看起来似乎解决了减法问题,不妨让我们再做一个减法运算,比如5-5,我们尝试再一次用反码解决问题:
8'b00000101(5反码) + 8'b11111010(-5反码) -------------- = 8'b11111111(结果为反码) = 8'b10000000(转换成原码,根据有符号数原码编码规定解析为-0)
-0是等于0的,运算的结果是对的,但是不是总感觉哪里不对?是的,原码8'b00000000表示+0,我们已经有+0来表示0了,再用-0表示0显得多此一举,浪费了一个编码值。怎么解决这个问题?此时引入第三种编码方式规定(补码),正数的补码与原码相同,负数的补码等于反码加1.那现在我们尝试用补码来重新计算5-5:
8'b00000101(5补码) + 8'b11111011(-5补码) -------------- =9'b100000000(最高bit由于溢出被自动丢弃) = 8'b00000000(结果为补码,由于最高bit为0,即表示正数,所以等同于反码,也等同于原码,其值为+0)
上述运算中使用补码消除了运算结果为-0的问题,但还是没有告诉我们8'b10000000的值到底是多少?显然不应该是-0,那么应该要怎么解析8'b10000000?有朋友说答案是-128,是的,前面我们已经在原码一节中给出了答案,那如何来理解这个答案?首先我们得要从标准原码定义的角度来说-128,按照定义-128的原码应该是9'b110000000,转换成补码还是9'b110000000,原码与补码一致,截断最高位后可得8'b10000000,讲到这里,我们似乎有点明白-128的由来了,但这样真的靠谱吗?代入运算会不会影响运算结果的正确性?我们来试试看-128+17:
8'b10000000(-128补码) + 8'b00010001(17补码) -------------- = 8'b10010001(结果为补码) = 8'b10010000(转换为反码) = 8'b11101111(转换为原码,由于最高bit为1,即表示负数,其值为-111)
由于用8'b10000000来表示-128参与运算并不会导致结果错误,那么何不直接约定用8'b10000000来表示-128呢?好,就这么愉快的决定了!这就是-128=8'b10000000的由来。其实就是计算机里的一个异常的人为规定而已!
2.4 编码小结
无论uint8_t/int8_t型数据全部使用补码的方式在计算机中进行存储,其中int8_t型数据范围内-128是个异常规定,无法用补码编码规则直接解释,但计算机可以自动理解识别。
为了巩固知识,我们做个小练习:
// a,b,c,d,e,f分别等于多少? uint8_t a = -1; uint8_t b = -128; uint8_t c = -129; int8_t d = 128; int8_t e = 129; int8_t f = -129; // 答案: a = 255, b = 128, c = 127, d = -128, e = -127, f = 127
至此,计算机原理知识点之整数表示痞子衡便介绍完毕了,掌声在哪里~~~