谈谈二进制(二)——四则运算

算法青霭客
• 阅读 6315

(作者注:Segmentfault里文章的数学公式有BUG,预览时是好好的,文章发出来就都显示有问题,如果你们看的时候也有问题的话,可以移步http://www.dubingxuan.com/article/8/

0. 序

上一篇谈谈二进制(一)中,我们花了巨量的篇幅,从最基本的计数开始,认识了各种进制的原理,接着通过对我们最熟悉的十进制的分解组合,推演了其他进制和十进制之间的相互转换过程。

本篇将继续深入二进制,来探究一下二进制的四则运算过程,也就是加、减、乘、除,看看二进制和十进制在计算上又有多少差异。

1. 加法

1.1 整数

加法一般是四则运算中首先被提及的,它是计数这一基础行为的延申,从一个一个地累加,延申到一堆一堆地相加,使计数这一行为提升了一个维度。按照上一篇文章的推演逻辑,我们依然从十进制开始,首先探究一下十进制的加法。

我们取两个十进制数:62185,我们用简单的口算就能算出来他们俩的和是247,但这个运算过程是怎么样的呢?我们仔细想一下刚才在计算时大脑的计算过程,然后来看看小时候初学加法时用到的竖式表达式:

谈谈二进制(二)——四则运算

从上式中我们可以看到,这个加法的整个过程其实分为了四步:

  1. 个位相加,得到的结果没有进位,依然是个位;
  2. 十位相加,6 + 8 = 14,进位了,因为是十位的相加结果,所以进到百位上,因此十位上结果是4,百位上是1
  3. 百位相加,这里百位只有185162的百位是0,所以是0 + 1 = 1
  4. 最后,把上面三步得到的结果再进行相加,数字不存在的空白处都看作是0,最终得到了247这个结果。

总结上面十进制的加法过程,其实就是两个数字从低位开始相加,如果有进位,就将进的位数加到下一位的计算结果上,然后重复这个过程,直到两个数字的所有位数遍历完成。

在上一篇文章中我们提到过,不同进制之间的区别几乎仅仅是进位方式的区别,所以上面这套加法法则在十进制数上成立,那么在二进制上自然也是成立的。我们取两个二进制数字1011110,把它们相加:

谈谈二进制(二)——四则运算

得到了10011这个结果,我们将三个二进制转化成十进制验证一下:1110 = 14101 = 510011 = 19,完全正确。

当我们熟悉了二进制的计算过程后我们会发现,其实二进制的加法计算要比十进制更简单,因为二进制的最低位的计算只有三种:1 + 0 = 10 + 0 = 01 + 1 = 10,比十进制要少得多,毕竟二进制一共也只由01两种数字组成。

1.2 浮点数

上面说的例子是整数的运算,精通十进制计算的我们知道,在十进制的计算中,小数的加法和整数大同小异,依然是从最右边开始相加,往左边进位,直到整个数字的位数遍历相加完毕,譬如:5.875 + 3.75 = 9.625。十进制是如此,二进制也不例外,我们把上式中的前两个数字换成二进制:5.875 = 101.1113.75 = 11.11,然后把这两个二进制浮点数相加:

谈谈二进制(二)——四则运算

注意,带小数的计算,我们需要将小数点对齐。经过计算,101.111 + 11.11得到的结果1001.101对应的十进制数正是9.625

2 减法

讲完了加法,下面来看减法。众所周知,减法是加法的逆运算,并且减法的运算也依然是从最右边开始,逐渐往左。由于是加法的逆运算,我们需要注意的是,在减法中,没有进位,取而代之的是退位。我们直接来看上面加法部分的最后一个式子101.111 + 11.11 = 1001.101的减法逆运算:

谈谈二进制(二)——四则运算

上面的竖式就是1001.101 - 101.111的过程了,式中的每一步我都直接计算了退位。这个式子比较特殊,它除了右边第一位外,一路往左全部都在做退位计算,所以整个竖式看起来有点诡异。但总之,我们得到了11.11这个结果,和上文中的加法吻合。

关于减法还有一点,就是减数>被减数的情况,我们知道,这种情况的结果为负数,而我们在计算的时候,其实只需要把不存在的位数都用0去替换,该怎么退位怎么退位就行了,这里就不再展开。

3 乘法

3.1 整数

从加减到乘除,我们的计算又提升了一个维度,而乘法实际上也是加法的进阶,所谓乘,其实就是倍数,也就是几个几相加,如4 × 3,实际上就是43或者34相加。

还是先来看十进制,取两个数:8457,我们用竖式来看一下他们的运算过程:

谈谈二进制(二)——四则运算

嗯,看上去比加减法复杂多了。但仔细观察我们就会发现,每一行数字其实就是两个因数上各个数字遍历相乘的结果,唯一要注意的,就是相乘后所在的位置,我们逐行来看。

首先第一行,4 × 7 = 28,这个没有任何问题,47都是个位数,也就是\(10^0\)位置上的数,因此它们之间相乘,就是两个个位数相乘,结果的最低位在个位上。然后是第二行,这里的567 × 8的结果,但实际上,式中的8并不是8,而是80,是十位(\(10^1\))上的数字,因此它和个位上的7相乘,得到的应该是560,而我们在式中把0给省略了。或者换一种角度,56这个数字最低位所在的位数,实际上是8的位数和7的位数相乘的结果:\(10^0times10^1=10^1\),因此56就被写到了\(10^1\)的位置上,如果把个位看作是第0位,那么56所在的十位就是第1位。

下面一行的20和上面56一样。

最后一行,40,经过前面的分析我们知道,它其实是5080相乘的结果,后面两个0省略了。但为了下面二进制讲解时方便,我这里更愿意使用所在位数来解释,也就是\(10^1times10^1=10^2\),也可以直接指数部分相加1 + 1 = 2,所以40就被写在了从0数起的第2位上。

最后把上面四行所得的数字按照位置相加,不存在的地方用0补充,就得到了最终结果4788

再来看二进制,实际上二进制的乘法要比十进制省事儿得多,原因和加法一样,因为二进制只有01两个数,因此它的基础乘法计算只有三种:0 × 0 = 00 × 1 = 01 × 1 = 1,连进位都省了,全是一位。

我们取两个二进制整数110(6)101(5),来看看它们相乘后的结果:

谈谈二进制(二)——四则运算

由于二进制的基础乘法实在太简单,我这里没有像上面十进制一样,遍历每个数字相乘,而是将第一个因数110看作一个整体,将第二个因数101的三个数拆开,分别去乘110,所以我们看第一行结果110,是1 × 110 = 110,由于1在第0位,所以结果也写在第0位。

接着是第二行,0 × 110 = 000。这个0101的\(2^1\)所在的位置上,也就是第1位,而看做整体的110的最低位为\(2^0\),也就是第0位,因此他们相乘的积也写在第0 + 1 = 0位。

第三行,1 × 110 = 1101在第2位,所以结果写在第0 + 2 = 2位。

最终把上面三个数相加,就得到了11110,也就是30,正好是5 × 6的结果。

3.2 浮点数

完成了整数的乘法后,我们来看小数带小数的乘法,鉴于二进制实在是太好算了,我们跳过十进制,直接来到二进制的例子。取两个数,1.01(1.25)11.1(3.5),来看他们的竖式计算:

谈谈二进制(二)——四则运算

这里我们同样将第二个因数11.1拆开,将第一个因数1.01看做一个整体来计算,得到了最后的结果100.011(4.375)。这个浮点数相乘的式子里,我们同样要注意,并且特别注意位数的问题。

首先第一行,我们看到被我们看做整体的第一个因数1.01的最低位为\(2^{-2}\)位置上,即-2位,而与之相乘的0.1-1位上,因此,第一行相乘的结果我们要写在第-1 + -2 = -3位上,所以它整个都在小数点后面。以此类推,第二行和第三行分别把结果最低位写在了-2-1位上。最后把三个结果相加,不存在的位置用0补充,就得到了最终结果。

整体而言,二进制的乘法要比十进制的乘法简单的多得多,这回我们连九九乘法表都不用背了。

4 除法

除法是乘法的逆运算,但相对于加法的逆运算减法来说,除法就相对要复杂一些了。我们还是先从十进制开始,但这次就不区分整数和小数了,直接从浮点数开始。

取两个十进制数,被除数185.65,除数2.5,我们来计算\(185.65div2.5\)的结果:

谈谈二进制(二)——四则运算

选的数字有点大,整个计算过程有点长。这里有几个点我们需要注意:

  1. 在计算带小数的除法时,我们会习惯先将除数(2.5)变成整数,也就是除数和被除数同时乘\(10^n\),这里两个数在除之前都先乘了10:\(185.65times10div(2.5times10)\),使2.5变成了25。这样做可以方便计算,原因就是前文乘法中我们强调过很多次的位数问题,因为在除法的过程中也涉及乘法,即被除数=除数×商,如果我们把除数的小数点去掉了,那么除数的最低位变成了第0位,我们在计算时就会方便很多,不用担心位数搞错的问题。
  2. 除法在计算时,从最高位开始计算,以被除数为基准数,每一位上的数除以整个除数,若当前的数小于除数,则结果为0,并将当前计算的数减去0后与下一位相结合(作为下一位的高一位)成新的数字,再除以整个除数。如上式中第一步和第二步,结果都是0,然后把两步的结果与下一位结合,变成185,此时可以除以2525 × 7 = 175185 - 175 = 10,然后下一步将这个10和被除数的下一位6结合,得到了106。上式的计算过程部分中,所有商与除数相乘的结果都是红色字,新的每一步的被除数都是黑色字。这样一直计算,直到最后剩下的结果为0,我们就得到了最终的商,上式中写在被除数上方的74.26

上面我文字写的有点绕,但其实只要我们大概回忆一下十进制的除法,通过上面这个式子,就能看明白是怎么回事了。

二进制同理,我们取两个二进制数11.11(3.75)10.1(2.5),来看一下\(11.11div10.1\)的结果:

谈谈二进制(二)——四则运算

过程和上面十进制一样,最终我们得到了1.1(1.5)这个结果。

5 结尾

其实在原本的计划中,这篇(二)里还打算把二进制的位运算给放进来,这样凑成一个完整的运算篇。但吃了上一篇文章篇幅过长的教训后,觉得还是先不写运运算了,在下一篇专门来讲运运算,以节省篇幅,提升一点阅读体验。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
美凌格栋栋酱 美凌格栋栋酱
8个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Stella981 Stella981
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Easter79 Easter79
3年前
SpringBoot整合Redis乱码原因及解决方案
问题描述:springboot使用springdataredis存储数据时乱码rediskey/value出现\\xAC\\xED\\x00\\x05t\\x00\\x05问题分析:查看RedisTemplate类!(https://oscimg.oschina.net/oscnet/0a85565fa
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
算法青霭客
算法青霭客
Lv1
长江悲已滞,万里念将归。
文章
3
粉丝
0
获赞
0