电子说
首先,我们来思考最简单的情况,该怎样表示数字。
你可能会想这还不简单,统一用固定长度,比如用64个比特(8字节),这种方法可行,但问题是不论一个数字有多小,比方2,那么用这种方法表示2也需要占据64个比特(8字节):
明明只要一个字节就能表示而我们却用了8个,前面的全都是0,这也太奢侈太浪费了吧。
显然,在这里我们不能使用固定长度来表示数字,而需要使用变长方法来表示。
什么叫变长?意思是说如果数字本身比较大,那么其使用的比特位可以较多,但如果数字很小那么就应该使用较少的比特位来表示,这就叫变长,随机应变,不死板。
那怎样变长呢?
我们规定:对于每一个字节来说,第一个比特位如果是1那么表示接下来的一个比特依然要用来解释为一个数字,如果第一个比特为0,那么说明接下来的一个字节不是用来表示该数字的。
也就是说对于每个8个比特(1字节)来说,它的有效载荷是7个比特,第一个比特仅仅用来标记是否还应该把接下来的一个字节解析为数字。
根据这个规定假设来了这样一串01二进制:
1010110000000010
根据规定,我们首先取出第一个字节,也就是:
10101100
此时我们发现第一个比特位是1,因此我们知道接下来的一个字节也属于该数字,将当前字节的1去掉就是:
0101100
然后我们看下一个字节:
00000010
我们发现第一个bit为0,因此我们知道下一个字节不属于该数字了。
接下来我们将解析到的0101100(第一个字节去掉第一个比特位)以及第二个字节0000010(第二个字节去掉第一个比特位)翻转之后拼接到一起,这里之所以翻转是因为我们规定数字的高位在后。
这个过程就是:
1010110000000010
-> 10101100 | 00000010 // 解析得到两个字节
_ _
-> 0101100 | 0000010 // 各自去掉最高位
-> 0000010 | 0101100 // 两个字节翻转顺序
0000010 + 0101100
-> 100101100 // 拼接
最后我们得到了100101100,这一串二进制表示数字300。
这种数字的变长表示方法在protobuf中被称之为varint。
因此在这种表示方法下,如果数字较大,那么使用的比特就多,如果数字较小那么使用比特就少,聪明吧。
有的同学看到这里可能会问题,刚才讲解的方法只能表示无符号数字,那么有符号数字该怎么表示呢?比如-2该怎么表示?
按照刚才变长编码的思想,-2147483646使用的比特位应该比-2要少。
然而我们知道在计算机世界中负数使用补码表示的,也就是说最高位(最左侧的比特位)一定是1,假设我们使用64位来表示数字,那么如果我们依然用补码来表示数字的话那么无论这个负数有多大还是多小都需要占据10个字节的空间。
为什么是10个字节呢?
不要忘了varint每个字节的有效负荷是7个比特,那么对于需要64位表示的数字来说就需要64/7向上取整也就是10个字节来表示。
这显然不能满足我们对数字变长存储的要求。
该怎么解决这个问题呢?
既然无符号数字可以方便的进行变长编码,那么我们将有符号数字映射称为无符号数字不就可以了 ,这就是所谓的ZigZag编码,是不是很聪明,就像这样:
原始信息 编码后
0 0
-1 1
1 2
-2 3
2 4
-3 5
3 6
... ...
2147483647 4294967294
-2147483648 4294967295
这样我们就可以将有符号数字转为无符号数字,接收方接收到该数据后再恢复出有符号数字。
现在数字的问题彻底解决了,但这仅仅是万里长征第一步。
全部0条评论
快来发表一下你的评论吧 !