作为信息技术开发人员,了解二进制是我们的基本功。二进制,就是基数为2的数字系统,在这个系统中只有0和1两个数字,所有其它数字都使用0和1的组合来表示,与我们日常使用的十进制有很大不同,在十进制中所有的数字是使用0到9这10个数字的组合来表示的。
位权
首先,让我们来看看位权的概念。在十进制中,从低位到高位(右边是低位),每一位的位权是10的0次方、10的1次方、10的2次方,以此类推,如1、10、100、1000等。而在二进制中,每一位的位权则是2的0次方、2的1次方、2的2次方,如1、2、4、8等。这个概念非常重要,因为它帮助我们理解了不同位置的数字所代表的权重。
正负数
接下来,我们谈谈正负数在二进制中的表示。正数的最高位为0,而负数的最高位为1。负数除了最高位的符号位外,其余部分是对应正数的反码加1,这种表示方法称为"补码"。
这里说下补码的概念,对于正数,其补码就是其本身;而对于负数,其补码是对应的正数按位取反得到的数值(即1变0,0变1)再加1。以-2为例,首先,我们看一下2的二进制表示,即00000010。然后按位取反,得到11111101,再对非符号位的部分加1,得到11111110,这就是-2在二进制中的表示。
一个非常有趣的点是,二进制中的减法可以通过加法来实现,比如2-2=2+(-2),用二进制计算相加正好为0。这样计算机就可以用同样的硬件电路进行加法和减法运算,大大简化了经算计的硬件设计。
移位运算
然后,我们再看看移位运算。左移时,产生的空位用0填充;右移时,分为逻辑右移和算术右移,逻辑右移左侧空位用0填充,算术右移左侧空位用符号位填充。另外,当二进制数需要位数扩充时,左侧通常用符号位填充。
让我们通过一些具体的例子来理解移位运算及其意义。
-
左移运算:左移就是将二进制数的所有位向左移动。例如,如果我们有一个二进制数00001011(十进制中的11),我们可以对它进行左移运算。比如,让它左移一位,结果就是00010110(十进制中的22),右边多出来的位置用0填充。在这个例子中,你可以看到左移一位实际上等同于将原数乘以2。因此,左移运算可以用来高效地进行乘法计算。
-
右移运算:对于右移,我们有两种方式,逻辑右移和算术右移。
-
- 逻辑右移:逻辑右移是将所有位向右移动,并用0填充左侧空位。例如,假设我们有一个二进制数00001011(十进制中的11),我们对它进行逻辑右移一位,结果就是00000101(十进制中的5)。逻辑右移实际上等同于将原数除以2(向下取整)。
-
- 算术右移:算术右移也是将所有位向右移动,但是它会用符号位填充左侧空位。例如,假设我们有一个二进制数1111 1011(十进制中的-5),我们对它进行算术右移一位,结果就是11111101(十进制中的-3)。算术右移可以保留负数的符号,因此在处理有符号数的右移运算时,通常会使用算术右移。
-
位数扩充:当我们需要扩充一个二进制数的位数时,通常会用符号位来填充左侧。例如,我们有一个8位的二进制数10101010(最高位为符号位,1代表这是一个负数),如果我们需要将它扩充到16位,那么我们就在左侧添加8个1,结果就是1111111110101010。位数扩充通常用于处理不同位宽的数据,比如从一个8位系统升级到一个16位系统。
逻辑运算
再来说说逻辑运算,包括NOT(取反)、OR(有1取1,无1取0)、AND(全1取1,否则取0)和XOR(不同取1,相同取0)。这些运算在计算机内部被广泛使用,是计算机处理信息的基础。它们被用于实现各种算术运算,如加法、减法等,也被用于条件判断、循环控制等逻辑操作。此外,逻辑运算也是构建更复杂的电路结构,如加法器、乘法器等的基础。
举几个例子:
NOT运算(取反):它只对一个二进制位进行操作。NOT运算会将1变为0,将0变为1。在计算机中,NOT运算常常用于改变二进制数的值,当我们想要将一个布尔值从真变为假,或者从假变为真时,就可以使用NOT运算。
OR运算(有1取1,无1取0):输入两个运算数,对两个运算数中对应的两个二进制位进行操作。只要其中一个位是1,结果就是1,否则结果是0。例如,对两个二进制数1010和0011进行OR运算,结果为1011。在计算机中,OR运算常用于设置二进制数的特定位,当我们想要将一个数的某一位设置为1时,就可以使用OR运算。
AND运算(全1取1,否则取0):输入两个运算数,只有两个运算数中对应的两个位都是1时,结果才是1,否则结果是0。例如,对二进制数1010和0011进行AND运算,结果为0010。在计算机中,AND运算常用于清除二进制数的特定位,当我们想要将一个数的某一位清除(设置为0)时,就可以使用AND运算。
XOR运算(不同取1,相同取0):输入两个运算数,只有两个运算数中对应的两个位不同时,结果才是1,否则结果是0。例如,对二进制数1010和0011进行XOR运算,结果为1001。在计算机中,XOR运算常用于切换二进制数的特定位,当我们想要反转一个数的某一位时,就可以使用XOR运算。
小数表示
最后,我们谈谈小数的二进制表示。小数在二进制中表示时,通常由符号位、指数位和尾数位组成,这样可以节省空间,并可以表达很大的数。比如,32位单精度浮点数由1个符号位、8个指数位和23个尾数位组成。
小数的科学计数法表达方式为(+-)m*2的n次方,其中m是小数点前只有1个1的二进制小数。为了在指数位中去掉指数的符号位,取指数位的中间数为0,8位指数位的中间数是127,所以指数位可以表达-127至128的范围。
这里有个中间数的概念,简单介绍下:中间数是为了去掉指数位中的符号位,有时候也称为"偏移"。偏移是一种处理负数的方法,让我们可以使用全部的无符号二进制数来表示负数和正数。对于8位的指数位,中间数(偏移)是127。也就是说,我们将实际的指数值加上127,然后将结果存储在指数位中。
例如,如果我们想表示的指数是10,我们会在指数位中存储10+127=137。同样,如果我们想表示的指数是-10,我们会在指数位中存储-10+127=117。
因此,8位的指数位可以表示的范围是0到255(包括0和255)。但由于我们使用了127的偏移,所以实际的指数范围是-127到128。
在上边的例子使用符号位和偏移可以表示的指数范围是差不多的。我们可以再思考下为什么不使用符号位,而使用偏移?主要有以下几个原因:
-
简化浮点数的比较和排序:在偏移表示法中,较大的浮点数总是有较大的二进制表示。这意味着我们可以像比较无符号整数那样比较浮点数,而无需关心它们的符号位和大小。如果我们直接在指数位中使用符号位,那么比较和排序浮点数就会变得更复杂。
-
避免特殊情况:在IEEE 754标准中,当所有的指数位都为0或者都为1时,表示的是一些特殊的值,如无穷大、无穷小、NaN(Not a Number)等。如果我们直接在指数位中使用符号位,那么我们就需要找其他的方式来表示这些特殊的值。
-
避免浪费:如果我们在指数位中直接使用符号位,那么有一半的指数位值(即所有以1开头的值)将用于表示负指数,而另一半的值(即所有以0开头的值)将用于表示正指数。然而,在实际应用中,我们可能并不需要这么多的负指数。使用偏移表示法,我们可以根据实际需求来调整正指数和负指数的范围。
另外需要注意,某些小数用二进制表示时会无限循环,所以有限的位数会出现算不准的问题。
关于小数的完整详细介绍,可以看我另一篇文章:开发必备知识:浮点数的工作原理与使用注意事项)
以上就是对二进制基础知识的概述,希望能对各位开发的同学有所帮助。理解二进制,就像理解计算机的"语言",会让我们在开发过程中更加得心应手。
欢迎关注微/信/公/众/号:萤火架构,及时获取更多架构经验分享。