程序员联盟计算机系统C++

计算机系统002 - 数值运算

2017-06-16  本文已影响121人  SniperPan

写在开头,本篇的唯一主题是二进制计算,其他都是铺垫。为了便于阅读和管理,将单独开一篇作为系列文章目录,位置如下。

目录

1. 传统进制

1.1 十进制

当人类进化稳定开始衣食足而知礼节时,通常情况下使用最多的工具是双手。双手十指,随身携带,遇到要计数时,自然会就近原则,伸出双手开始掐手指。

甲骨文十进制

1.2 斤两

到了货币时代,买卖不仅要计数,还要计量。量有量纲,简称单位。旧时中国有官职“司马”,粮秣管理时需要称重,因此执着了统一度量衡。因此旧时斤也称“司马斤”,一司马斤为十六司马两,简称一斤十六两,自然“半斤八两”。1930年《中华民国度量衡法》仍采用十六两一斤的算法,一斤500g,一两为十六分之一斤,即31.25g。1959年《关于统一计量制度的命令》中说明由于两这算麻烦,一律改为十两一斤。

1.3 历法

随着交易范围的扩大(或是任何一项需要多人参与的项目),为了避免浪费较多时间在集结这件事情上,出现了历法。

指定历法之前,要确定的是地球绕太阳公转一周要365.24天,月球绕地球公转一周要29.53天,365.24/29.83 = 12.37。

目前使用的公历源自西元历法,它是从罗马时代凯撒时期制定下来,当时定的每个月有30天,与众神有关的5个月额外增加一天,二月每四年闰月多一天,每百年不闰。当然这不可能是凭空想出来的,而是当时的天文学、地理学等学科有所突破,人们对地球自转、公转有所认知的结果。至于后面月份天数出现变化并不重要,总之雏形已定。

无独有偶,在东亚地区也有一种历法,叫做农历(也称阴历、黄历)。农历有配合太阳阳光的二十四节气,有根据塑望月为基准的月历(十五月圆),有以回归年为基准的岁。同样的,农历也是依照天文数据计算而来,在清初时期,还有西方传教士参与修订《时宪书》。

1.4 时刻

我曾努力思考,为什么时间是60进制的,60这个数既不想888那么喜庆,也不想13那么邪乎,何德何能就占据了时刻这么重要的位置?
后来我了解到,天文观测中,需要对天体的位置进行标定,在一个或半个圆周面上标定出若干位置,为了便于定位,就需要将圆周进行等分。等分时,公约数越多的数字越适合作为进制的单位,当然也不能太大,而60就恰好符合这两个条件。60有12个因素,分别为1、2、3、4、5、6、10、12、15、20、30、60,且2、3和5还是质数。60进制的数可以被较多数整除,也就可以分成多个不同的时间长度,如15分钟为一刻。

这一套,在农历中依然有体现,六十一甲子,天干配地支呈60对,循环往复。

1.5 小结

标题明明是数学运算,我却扯了半天几斤几两、几时几分,为什么?
因为要先建立一个前提,进制的发明是基于应用环境的。很多资料在介绍二进制时,总止步于二进制适合计算机计算。为什么适合?凭什么适合?计算机这么厉害,不能直接按十进制算么?
那么下面我就告诉你,为什么计算机必须使用二进制计算。

2. 数值表示

2.1 数值表示的局限

首先不得不承认,世界上没有两片完全相同的叶子。也就是说,当我们想通过摆两片叶子、两块石头、两根手指来说明数量为2的时候,我们并没有想象中那么较真,我们并没有严格限定两件物品必须完全一致才能代表数量2。换言之,在数值表示时,适当的抽象是可以被接受的

为了方便表示促进交流,在接受实物表示数量的基础上,我们做了进一步抽象,用虚拟的、概念的数字表示数量。这就是为什么生活中可以将大把钞票交给银行,只换来几个数字变化也能让人深感欣慰的源泉。

不过很抱歉,任何试图描述物体长度、重量、体积等数值表示都是经过美化的,毕竟大家都承认测量误差是客观存在的,更何况世界上还有一条测不准原理,所谓的数值表示也只是自我催眠对事物已有所了解。既然只能用数值近似表示某一物体的实际情况,那么也就没有谁敢拍胸脯保证某一数值就是物体实际情况。

换成人话,也就是今天我不能在测量了某一电池电压值为5V就敢言之凿凿其电压值一定为5V,因为说不定下一秒它就是5.0000001V,在下一秒就变成了4.9999999V。也许很多人觉得这一点点误差我可以接受,那我想问一下,如果银行告诉你,你账户里有50万,等你取的时候却告诉你,不好意思,数值表示仅供参考,在你取钱的这一秒,账户上少了几块钱是否可以接受?再严重点,导弹发射时给的坐标同样有精度限制,是否可以接受因为精度导致发射时坐标便宜一毫米而波及了你?

数值表示,是数值运算的根本。无法精确表示,何谈精确运算,更别提产出精确结果。结果不精确、不可靠,那运不运算又有什么意义?

2.2 求精不求多的二进制表示法

模拟电路

对于模拟电路而言,要表示0~9十个数值可通过电压、电流或者其他方式近似表示,如[-0.0001, 0.00001]表示0,甚至将容错能力在提升一把,[-0.49999, 0.4999]表示0,只要将元器件质量控制好,不因时间、电场、磁场、温度等复杂因素变化而变化,还是能勉强实现十进制表示的。最多就是多弄一些保护电路、多供一些电以保证不会因为串联引起分压、多搞一些散热设施保证大家散发的热能即使撤走。最多,也就做一台这样的计算机,毕竟至少是有了。

ENIAC

ENIAC包含了17468个真空管、7200个晶体二极管、1500个继电器、10000个电容器,还有大约五百万个手工焊接头。它的重量达27吨(30英吨),体积大约是2.4m×0.9m×30m(8×3×100英尺),占地167平方米(1800平方英尺),耗电150千瓦[11][12](导致有传言说,每当这台计算机启动的时候,费城的灯都变暗了[13])。
ENIAC有20个带符号的十位累加器,它们使用10的补码表示方法,每秒可在它们和数字源(例如另一个累加器,或者常数传送器)进行5000次简单加减操作。

但对于ENIAC,我想绝大多数人不要说是否买得起,交得起电费,我想要先考虑的是买房的钱。当然即使这样,还是有土豪可以支付得起的,但最大的弊端,可能就是运算频率实在有限,毕竟很多电子元器件都是机械式的,要想对一个机械开关每秒钟做几千甚至几G次开合,这就不太现实了。

数字电路

庆幸的是,人们发明了半导体。半导体元器件体积更小,一些特殊的元器件比如二极管、三极管甚至不用灯丝加热来运作。同样一个三极管,现在我们只要这么一小片位置即可。

三极管

但这样还不够,有人希望可以做的更小,反正外层都是包装和引脚,电子实质上是在半导体和导线间穿梭,不如去掉外壳,直接做一些模块。比如这个:

比如这样:

但是集成电路有一个问题,里面的电子元器件密集,各元器件本身体积有非常小,如果对这样一个集成电路加以高电压,那么烧毁是在所难免的了。可是如果只是用低电压,比如几十毫伏,在这种情况下,仍然要保证数字0~9表示的足够精确,那对元器件本身的要求就十分之高了。不仅要能工作,还要能抗干扰,不因外界任何变化而影响自身电压电流。这几乎是做不到的,哪怕能做到,良品率也是极低的,毕竟生产环境本身都存在诸多不确定因素的影响。

这种代价的前提下,是否还有意义坚持使用十进制数值运算?答案是没有。

其实完全可以换一种思维,做选择和判断题时通常更难的是选择,如果是不定项选择题,那就更难了。同样的,区分一个电压到底应该归类成4V还是5V,远比判断到底有没有电压要不准确的多。判断电压有没有无非两种结果,要么有,要么没有。管他误差大不大,不通电自然没有电,表示为0,通电了就是有电表示为1。至于什么23456789的,完全可以组合01表示出来,毕竟十进制里面也分个十百千(10的N次方,N从0开始)位,这里来个2的N次方(N从0开始)做单位也说得过去啊。

这就是为什么计算机里面非要用二进制,而不是十进制来做数值表示的原因。不是不想,是不能

3.数值运算

计算机之所以称为计算机,首先是要能计算。数学中最基本的是四则运算,再往上配合什么正弦、余弦、正切等等完成复杂运算,我们这里不深入数学,毕竟本人数学很一般,就说说计算机怎么用二进制完成四则运算。

要知道,计算机不通电就是一堆废铁,通电了也不过是一堆会电人的废铁。且先不讲操作系统原理之类的,单说一个加法器(事实上减法、乘法、除法均可通过加法器实现,只不过效率有所差异),到底是如何工作的。

  • A, B 被加数
  • Cin 低位进位, Cout本位进位
  • S 缩写自Sum,表示和

这个加法器是1bit的,它的运算规则如下:

事实上我们知道程序中通常会至少使用8bit来表示一个数值,比如ASCII码表。而所谓的全加器其实就是级联多个1bit加法器完成工作,前一加法器的进位Cout作为下一加法器的Cin。

简单加法器看起来实现也并不复杂,那我们来思考一个问题,数据从哪里来?

3.1 数据从哪里来

这里讲的数据来源不是问怎么从键盘或其他设备到加法器这一层,而是问如何从寄存器(这里先说明下存储功能本身要么通过反馈电路保持住电信号,如寄存器;要么写时转成磁信号,读时再由磁转换回来,如磁带、磁盘。)到加法器的两端?

这个过程叫做装载,以0+1为例,换成汇编语言来讲,表示如下:

    MOV 0. eax
    ADD 1, eax

但是电子元器件并没有灵性,他们不会知道何时该取装载,何时该去做加法,也就是说,如果希望这个由电子元器件组成的团队正常运作,就必须有统一秩序,正如历法、时间的发明,而在计算机内部,这叫做时序。时序作为统一指令,当时序行进到0时,哪些元器件应该去装载,当行进到1时,哪些元器件应该协同完成加操作。

完成加操作后,运算所得值同样应保存起来以备后续使用。

3.2 流水线:空间换时间

如上一小节所述,完成一次加法需要如下三个阶段:

假如现在需要做3个加法,如果依次运行,则需要9个阶段。假设每个阶段需要一个时序,那么总共耗费9个时序。是否有方法可以缩减所需时序呢?答案是流水线

流水线的核心是拆分步骤、模块化、并行执行。模块化带来的代价是必须引入存储器保存每个模块的结果以实现数据对接,相比单个模块直接完成,每拆分一步,就需要多消耗一个寄存器。因此,实际上是拿空间的开销增加换取时间上的节省,这在程序开发中很常见,甚至MMX、SIMD等多媒体指令集也是遵循这个思想加快数据处理速度。

4. 总结

本篇主要说明了计算机采用二进制运算的缘由,以及涉及到的基本步骤。至于更多的如CPU终端、Cache等技术,将于后续操作系统相关篇幅中展开。总的来讲,简单加法器只会与寄存器产生关联,本篇的目的也就只需要到这里即可。

上一篇下一篇

猜你喜欢

热点阅读