一直想系统的总结一下计算机关于定点数的存储与表示,能清楚直白的阐述其中的原理。个人认为这是非常重要的内容,可以算是真正的内功心法,基础中的基础,能提升对技术的理解能力。
本文重点解决两个问题:
为什么计算机对于定点数要用补码表示
怎样理解反码等于原码保持符号位不变,数据位取反;补码等于反码加一
如果你能解释以上两个问题,请飘过!如果有点儿犹豫,不妨静下心来看看;如果0基础,毋庸置疑,赶紧往下撸呀!
前面会介绍一些铺垫性的概念和表述方式,都是为了对核心内容更好的表达。
位、字节、字
我们知道二进制数就只有0和1,而计算机对信息的存储就是使用二进制数字。通常,存储二值信号的单元叫做位(bit)。单个的位只能表示两种状态,没有太大的作用。但是,如果多个位组合起来,那就能表示非常丰富的含义。一般位的单位使用小写字母b表示,例如,10b表示10个bit
计算机中将8个位组成一组,称作字节(byte),作为最小的可寻址的存储单元。存储器的每个字节都由一个唯一的数字来标识,这个标识称为地址
。所有可能地址的集合称为虚拟地址空间。我们常说的32位机器,就是指该计算机的最小可寻址的存储单元为4字节,那么,虚拟地址空间就是[0,2^32 -1],也就是4GB。一般字节的单位使用大写字母B表示,例如,10B表示10个字节。
早些年的计算机大多数是32位,如今很多计算机都是64位的。"32位","64位"还有一个名称叫做字长(word size),字长为4表示32位机器,字长为8表示64位机器。
字节
是8个位
的组合形成的一个单元;字
便是n个字节
的组合形成的一个单元,n的大小一般为4或8,32位机器的字长为4个字节,64位机器的字长为8字节。
位、字节、字之间的大小关系是:word > byte > bit。
由于字大小不同,程序的平台移植性需要考虑。
进制
10进制是我们在日常生活种使用了一千多年的数字表示方法,某种角度来说,10进制也是一种"编码"方式。以10为基数,逢10进1。
2进制和16进制是在计算机的世界里,常用的数字"编码"方式。
从上表可以看出,数字0-15有三种不同的"编码"方式。这里之所以反复强调"编码"这个概念,是为了后面的内容做准备。要表示16个数字,若以10进制为度量,则为0-15;如果16进制为度量,则为0-F;若以2进制为度量,则为0000-1111;三种不同的表示形式,但表达的内容都是相同的。
大小端字节序
大小端问题其实可以看做快递存取问题。从哪儿开始存,怎么存?从哪儿开始取,怎么取?
假设有一个空的快递柜,每一层有4个柜子,按照常识,每个柜子都会有一个编号,假设第一层的4柜子的编号依次为0x100-0x103,现在,我们来玩一个游戏,将4种水果放进柜子里面,按照依次存取方式,保证4种水果的摆放顺序不变。每个水果也有编号,苹果(1号)、香蕉(2号)、草莓(3号)、葡萄(4号)
存之前4种水果的摆放顺序是:苹果(1) 香蕉(2) 草莓(3) 葡萄(4)
方法一(类似 大端)
存入:
0x100 0x101 0x102 0x103
苹果(1) 香蕉(2) 草莓(3) 葡萄(4)
取出(从编号小柜子先取):1 2 3 4
方法二(类似 小端)
存入:
0x100 0x101 0x102 0x103
葡萄(4) 草莓(3) 香蕉(2) 苹果(1)
取出(从编号大柜子先取):1 2 3 4
在计算机中,对于占用多字节的对象,同样也要考虑,这个对象的地址
在哪儿,该对象的内容按照什么顺序排列在多字节的space中。
前面举了一个实际生活中的栗子,下面再举一个计算机世界的例子:
对于16进制数0x01234567,该数占4个字节,那么以小端序该数在计算机中怎样存储?以大端序该数在计算机中怎样存储?
大端(big endian):(把数值的高位字节放在内存的低位地址上,把数值的低位字节放在内存的高位地址上)
小端(little endian):(把数值的高位字节放在高位地址上,低位字节放在低位地址上)
总之,小端序是数值高位对应地址高位,数值低位对应地址低位;而大端序则数值高位对应地址低位,数值低位对应地址高位。小端序在阅读时,通常是将数值字节按照(与我们书写)相反的顺序显示。
书写字节序列的自然方式是最低位字节在左边,而最高位字节在右边,这正好和通常书写数字时最高有效位在左边,最低有效位在右边的方式相反。
由于字节序的不同,要保证读取和写入的字节序相同,发送和接收的字节序相同。
因此,由于字节序和字大小平台差异性,所以二进制数据的可移植性较文本数据弱,需要两端统一。
位向量
位向量只是一种数学上的表示方式,也是为了后文内容做铺垫。所谓位向量就是固定位宽为w,由0和1组成的串。位向量的运算可以定义成向量的每个对应元素之间的运算。而对应元素之间的运算也就是位运算。
移位运算
整数表示
在c语言中支持多种整型
数据类型,不同数据类型所表示的取值范围也各不相同,但是其背后的规律值得我们探索。
32位机器的c的整型数据类型的取值范围:
long long(8字节)是在ISO C99标准引入的,所以在此之前的标准中都是占4个字节。
64位机器的c的整型数据类型的取值范围:
在64位机器中,long数据类型占8字节。
观察上述两个表格的取值范围,发现最小值和最大值的绝对值不对称,负数的绝对值比整数的绝对值大1。这是一个有意思的现象,后文在讲解补码时会解释缘由。
无符号整数编码
补码编码
如果你觉得上面的公式还不够直观,下面再展示一张表:
行文至此,又产生一个问题,为什么计算机表示整数要使用补码而不是使用其他编码方式,如,原码编码、反码编码呢?
原码编码
反码编码
总结
通常定点数(相对于浮点数)就是这三种表示方法:原码、反码、补码。
而我们常使用的口诀:反码等于原码保持符号位不变,数据位取反;补码等于反码加一(现在再来看这句结论是否豁然开朗呢!)。在原码与补码的心算转换过程中,常用反码作为过渡。
前面的内容,从数学层面解释了原码、反码、补码的原理。我相信,以后再看到关于整数的表示、原码、反码、补码相关的结论性描述,就不再迷惑了,彩!
另外,在c语言中,有时候当有符号数特别大或特别小,对于初学者处理不好可能对得不到意料之中的值;那是因为c中存在强制类型转换(显示/隐式),一般出问题是由于隐式强制类型转换导致,因为这种问题程序员不容易发现。针对这种问题,只需要记住一点即可,强制类型转换可能导致数值变化,但位模式不变。