mikewzp

 

整数在内存中的天才存储方案

加法和减法是计算机中最基本的运算,计算机时时刻刻都离不开它们,所以它们由硬件直接支持。为了提高加减法的运算效率,硬件电路要设计得尽量简单。对于有符号数,内存要区分符号位和数值位,对于人脑来说,很容易辨别,但是对于计算机来说,就要设计专门的电路:有符号加、减,这无疑增加了硬件的复杂性,增加了计算的时间。要是能把符号位和数值位等同起来,让它们一起参与运算,不再加以区分,这样硬件电路就变得简单了。

另外,加法和减法也可以合并为一种运算,就是加法运算,因为减去一个数相当于加上这个数的相反数,例如,5 - 3 等价于 5 + (-3),10 - (-9) 等价于 10 + 9。如果能够实现上面的两个目标,那么只要设计一种简单的、不用区分符号位和数值位的加法电路,就能同时实现加法和减法运算,并且非常高效。实际上,这两个目标都已经实现了,真正的计算机硬件电路就是如此简单。

然而,简化硬件电路是有代价的,这个代价就是有符号数在存储和读取时都要进行转化——为了计算相反数,必然需要转化。那么,这个转换过程究竟是怎样的呢?接下来我们就详细地讲解一下。

首先,需要弄清下面几个概念:原码、反码、补码。参考:补码、原码、反码 - 武装部 - 博客园 (cnblogs.com)

补码到底是如何简化硬件电路的

假设 6 和 18 都是 short 类型的,现在我们要计算 6 - 18 的结果,根据运算规则,它等价于 6 + (-18)。
如果采用原码计算,那么运算过程为:

6 - 18 = 6 + (-18)
= [0000 0000 0000 0110] + [1000 0000 0001 0010]
= [1000 0000 0001 1000]
= -24

直接用原码表示整数,让符号位也参与运算,对于类似上面的减法来说,结果显然是不正确的。

于是就考虑用反码代替原码参与计算行不行?下面就演示了反码运算的过程:

6 - 18 = 6 + (-18)
= [0000 0000 0000 0110] + [1111 1111 1110 1101]
= [1111 1111 1111 0011]
= [1000 0000 0000 1100]
= -12

这样一来,计算结果就正确了。

然而,若是将减数和被减数交换下位置:18 - 6

18 - 6 = 18 + (-6)
= [0000 0000 0001 0010] + [1111 1111 1111 1001]
= [1 0000 0000 0000 1011]
= [0000 0000 0000 1011]
= [0000 0000 0000 1011]
= 11

按照反码计算的结果是 11,而真实的结果应该是 12 才对,它们相差了 1。

蓝色的 1 是加法运算过程中的进位,它溢出了,内存容纳不了了,所以直接截掉。

6 - 18 的结果正确,18 - 6 的结果就不正确,相差 1。后来发现这么个规律:按照反码计算,小数减大数正确;大数减小数就不对了,始终相差1。好奇的话可以自己去试一试,如:5 - 13 和 13 - 5 。

于是开始设计补码,规则很简单就是通过打补丁来纠正差的这个1。补码 = 反码 + 1。

下面演示了按照补码计算的过程:

6 - 18 = 6 + (-18)
= [0000 0000 0000 0110] + [1111 1111 1110 1110]
= [1111 1111 1111 0100]
=  [1111 1111 1111 0011]
= [1000 0000 0000 1100]
= -12
## 这里由于符号位是负数,所以在补码转换回反码的过程中,要进行减一操作;一加一减,等于没变。
18 - 6 = 18 + (-6)
= [0000 0000 0001 0010] + [1111 1111 1111 1010]
= [1 0000 0000 0000 1100]
= [0000 0000 0000 1100]
= [0000 0000 0000 1100]
= [0000 0000 0000 1100]
= 12
## 这里由于符号位是正数,故补码/反码/原码都是一个,因此在反码转换补码时添加的那个计算完毕就被保留下来;于是便把大数减小数时差的那个1给补了上来。神不神奇?

在计算机内存中,整数一律采用补码的形式来存储,在读取整数时则进行一个由补到原的转换即可。这种天才般的设计一举达成了本文开头提到的两个目标,简化了硬件电路。

posted on 2023-06-02 21:02  武装部  阅读(18)  评论(0编辑  收藏  举报

导航