L02 Bit Byte Integer

8次阅读
没有评论

L02 Bit Byte Integer

对于十进制小数,如果用二进制表示,小数点右边第一位的权重是 2^-1, 第二位 2 ^-2 以此类推,用这类方法表示浮点数。

回顾C语言的数据类型以及大小(这里的都是单位字节 Byte)

L02 Bit Byte Integer

在这里我们只考虑 X86-64,本课程不考虑32位或者更低

异或运算(XOR)相同为0 不同为1

& 与 | 或 ^ 异或 ~ 非
如果用这些运算符对二进制数进行运算的话,&可以求得交集,|可获得并集,^可获得对称差,~可获得补集。

&& || 是短路逻辑运算,短路与以及短路或,会提前终止逻辑运算

<< 和>>是位移运算符,对二进制位进行操作。其中<<被称作左位移,例如 对于01100010 如果<< 3 就是向左移动三位,会整体向左移动,其中高位(溢出的部分)被清空,低位(空缺的部分)被0补齐。

同理>> 是向右移动,但这里需要区分两种类型:如果是逻辑移动,只需要向右移动,低位丢弃,高位补0;但如果是算术右移,则需要判断是正数还是复数来补符号位(也就是最左边的那一位)。如果是正数(符号位是 0​),高位补 0​。如果是负数(符号位是 1​),高位补 1。

6<<3 就是6这个二进制数向左移动
0000 0110 向左三位会变成 0011 0000

6>>2 是6向右移动2位
0000 0110 向右0000 0110 00 然后低位清空 变成0000 0001

‍

在这里我们需要辨析一个概念:有符号数和无符号数

有符号数的最高位是用来标记是正数还是负数的,比如对于32位的int,二进制从左往右数第32个数字就是标记为,如果这个数是1那么就是负数,如果是0就是正数。因此,int的最大正值就是从第一位到第31位全是1,第32位为0

$$\text{最大正值} = 2^{30} + 2^{29} + 2^{28} + \dots + 2^1 + 2^0$$

具体的数值就是

$$2^{31} – 1 = 2,147,483,647(约 21.47 亿)$$

但是对于负数,不仅仅只是因为看到了从左往右第32位是1就单纯加符号这么简单,计算机里面有两套系统,一套叫做原码,一套叫做补码。

如果采用原码,会带来一个巨大的结果:拥有两个 ​0​。

  • 0000...0000​ 算出来是 +0
  • 1000...0000​ 算出来是 -0

在数学上,+0​ 和 -0​ 是一样的,但计算机却要用两套不同的二进制来表示它们。这不仅白白浪费了一个珍贵的编码位置,还会让计算机底层的加法器电路变得无比复杂(因为每次算加减法都要单独判断一堆正负号)。

所以计算机引入了补码。在补码中,最左边的符号位(第 32 位)不再仅仅是一个简单的“正负号开关”,而是具有一个巨大的负权重。

  • 在 32 位 int 中,第 32 位的权重是:$-2^{31}$
  • 其余第 1 到 31 位的权重依然是正的:$2^0, 2^1, \dots, 2^{30}$

任何一个二进制数的值,就是​所有位置上的数值 $\times$ 其权重的累加和。

例子 A:第 32 位为 1,其余 31 位全为 0

  • 它的值 = $1 \times (-2^{31}) + 0 \times 2^{30} + \dots + 0 \times 2^0$
  • 计算结果 = $-2,147,483,648$

这就是 int​ 能表示的​最小负数(最大负值) !它的二进制写出来就是:

10000000 00000000 00000000 00000000

例子 B:第 32 位为 1,其余 31 位全为 1(全 1 状态)

  • 它的值 = $1 \times (-2^{31}) + (2^{30} + 2^{29} + \dots + 2^0)$
  • 我们知道后面的累加和等于 $2^{31} – 1$
  • 最终值 = $-2^{31} + (2^{31} – 1) = \mathbf{-1}$

所以,在计算机里,​全 ​1​​ 的二进制数代表的是 ​-1​​,而不是 -21.47亿!

通过这种设计,int(32位有符号整型)的表示范围完美地连在了一起:

  • 正数范围​:1​ 到 2,147,483,647(共 $2^{31}-1$ 个)
  • 零​:0​(只有唯一的 0000...0000)
  • 负数范围​:-1​ 到 -2,147,483,648(共 $2^{31}$ 个)

因为 -0​ 被消除了,多出来的那一个编码位置正好留给了负数。所以 ​​int​​ **能表示的负数极限(​$-2^{31}$**​ **)比正数极限(​$2^{31}-1$**​ )的绝对值刚好大 ​1​。

那么现在,如何快速的求一个正数对应的负数的二进制呢?假设这里用正数8 二进制表示就是 0000 1000

  1. 写出8的二进制 0000 1000

  2. 随后将0000 1000 和 1111 1111 进行异或运算(相同为0 不同为1)也就是按位取反

    得到 1111 0111 ,这时,权重之和为 -9

  3. 最后,再在此基础上+1 即1111 0111+ 0000 0001 得到 1111 1000

‍

符号扩展,假设现在有一个8位的符号数,如何在不改变值的情况下扩展到16位。

  1. 对于正数,假设为 0000 1000,那么需要将左侧扩充补0,变成 0000 0000 0000 1000
  2. 但是对于负数,由于编码位1,假设是-8 也就是 1111 1000,我们在进行补充的时候,需要扩充8位,随后在左侧补1,也就是
    1111 1111 1111 1000

‍

正文完
 0
评论(没有评论)