购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

2.2 二进制和十进制的数学等价性

在2.1节中,你已经看到了二进制数码是表示计算机内部开关状态的一种自然方式。此外你还得知,我们可以使用单个十六进制数码来表示4个开关的状态。本节将介绍二进制数字系统的一些数学特性,并说明它是如何与更熟悉的十进制(基数为10)数字系统进行转换的。

2.2.1 了解位置记数法

依据惯例,我们在书写数字的时候依据的是位置记数法(positional notation)。这意味着符号的值取决于其在一组符号中的位置。在熟悉的十进制数字系统中,我们使用符号0,1,…,9表示数字。

对于数字50,符号5的值是50,因为其位于十位,位于该位的任意数字都要乘以10。对于数字500,符号5的值是500,因为其位于百位。符号5在任意位置都是一个样子,但它的值则取决于其在数字中的位置。

再进一步说,在十进制数字系统中,整数123可以被认为是

1 × 100 + 2 × 10 + 3

或者

1 × 10 2 + 2 × 10 1 + 3 × 10 0

在这个例子中,最右侧的数码3是最低有效数码(least significant digit),因为它的值对于整个数字的值贡献最小。最左侧的数码1则是最高有效数码(most significant digit),因为它贡献了最多的值。

另一种数字系统

在发明位置记数法之前,人们使用计数系统(counting system)来记录数量。罗马数字系统(Roman numeral system)就是一种广为人知的计数系统。其中,用符号I代表1,V代表5,X代表10,L代表50,等等。要想表示数值2,你只需要用两个I即可:II。数值20可写作XX。

罗马数字系统有两个主要规则:代表较大值的符号先出现,如果代表较小值的符号放在较大值符号之前,则要将较小值从紧随其后的较大值中减去。例如,IV代表4,因为I(1)小于V(5),所以要将其将从V代表的值中减去。

罗马数字系统中没有代表0的符号,因为在该计数系统中并不需要。在位置记数系统中,我们需要一个符号来表示这个位置没有值,但是仍有位置意义。例如,500中的0告诉我们在十位和个位都没有值,只有百位上的值为5。

位置记数法的发明极大地简化了算术,并催生了我们今天所知的数学。如果你不信,试试用罗马数字系统中的60(LX)除以3(III)。(答案:XX。)

十进制数字系统的基数或底数(数码可用的符号数量)是10。这意味着表示数码0~9的符号共计10个。将数码向左移动一位会使其值扩大10倍;将它向右移动一位,其值会变为原来的1/10。位置记数法可以推广到任何基数 r ,如下所示:

其中,数字共有 n 个数码,每个 d i 代表单个数码(0≤ d i < r )。

这个表达式告诉了我们如何确定数字中每个数码的值。从右开始累计每个数码在数字中的位置,初始值为0。在每个位置,我们将基数 r 提升为该位置的幂,然后将得数乘以数码的值。将所有结果相加就得到了此数字所表示的值。

二进制数字系统的基数为2,所以表示数码的只有两个符号。这意味着 d i =0或1,我们可以将上述表达式写作:

其中,数字共有 n 个数码,每个 d i =0或1。

在2.2.2节中,我们将介绍如何在二进制数和无符号十进制数之间进行转换。有符号数可以是正数或负数,但是无符号数没有符号。我们将在第3章讨论有符号数。

2.2.2 将二进制数转换为无符号十进制数

通过计算特定位置上的2的幂,然后乘以该位置的比特值,就可以轻松地将二进制数转换为十进制数。来看一个例子:

以下算法总结了二进制到十进制的转换过程:

   Let result = 0
➊ Repeat for each i = 0,...,(n - 1)
       add ➋di x ➌2i to result

在每个比特位置➊,该算法计算2的幂➌并乘以该位置上的比特值(0或1)➋。

注意

虽然我们现在只考虑整数,但是该算法确实可以推广到小数值。只需让基数 r 的指数继续变成负值即可,也就是 r n - 1 , r n - 2 ,…, r 1 , r 0 , r - 1 , r - 2 ,…。详细讨论参见第19章。

动手实践

1.查看本节中的通用表达式,试回答对于十进制数29458254和十六进制数29458254, r n 和每个 d i 的值是多少?

2.将以下8位二进制数转换为十进制数:

a.1010 1010

b.0101 0101

c.1111 0000

d.0000 1111

e.1000 0000

f.0110 0011

g.0111 1011

h.1111 1111

3.将以下16位二进制数转换为十进制数:

a.1010 1011 1100 1101

b.0001 0011 0011 0100

c.1111 1110 1101 1100

d.0000 0111 1101 1111

e.1000 0000 0000 0000

f.0000 0100 0000 0000

g.0111 1011 1010 1010

h.0011 0000 0011 1001

4.设计一个算法,将以下十六进制数转换为十进制数:

a.a000

b.ffff

c.0400

d.1111

e.8888

f.0190

g.abcd

h.5555

2.2.3 将无符号十进制数转换为二进制数

如果我们想把一个无符号的十进制整数 N 转换为二进制数,设其等于先前的二进制数表达式,得到以下等式:

其中, d i =0或1。我们将等式两边除以2,右边底数为2的各项指数减1,于是得到以下结果:

其中, N 1 是整数部分,如果是偶数,余数 r 0 为0;如果是奇数,则为1。稍作改写,得到以下等价等式:

右边括号内的所有项都是整数。等式两边的整数部分和小数部分必须分别相等。也就是:

因此,我们看到 d 0 = r 0 。从展开的等式的两边减去 r 0 /2(等于 d 0 /2),得到以下结果:

两边再除以2:

使用与前面相同的推理, d 1 = r 1 。我们可以通过从右到左反复除以2,并使用余数作为相应位的值,以此产生一个数的二进制表示。这个过程可以总结为以下算法,其中正斜线(/)是整数除法运算符,百分号(%)是模运算符:

quotient = N
i = 0
di = quotient % 2
quotient = quotient / 2
While quotient != 0
    i = i + 1
    di = quotient % 2
    quotient = quotient / 2

一些编程任务(例如,硬件设备编程)需要特定的位模式。在这种情况下,使用位模式代替数值更为自然。我们可以将4位分成一组,用十六进制来表示每一组。例如,如果我们的算法要求0和1交错出现(0101 0101 0101 0101 0101 0101 0101 0101),我们可以将其转换为十进制值431655765,或者用十六进制表示为0x55555555(C/C++语法中的显示)。一旦记住了表2-1,你就会发现用十六进制来表示位模式要容易得多。

这两节的讨论只涉及无符号整数。有符号整数的表示方法依赖于CPU的某些架构特性,我们留待第3章讨论。

动手实践

1.将下列无符号十进制整数转换为十六进制(8位)表示:

a.100

b.123

c.10

d.88

e.255

f.16

g.32

h.128

2.将下列无符号十进制整数转换为十六进制(16位)表示:

a.1024

b.1000

c.32768

d.32767

e.256

f.65535

g.4660

h.43981

3.发明一种编码,允许我们存储带有加号或减号的字母评级(即评级A、A−、B+、B、B−等,直到字母F)。这种编码需要多少位? LYOJtjjre5PXUcLuuw9zOwhFE+qaC3cKI/q/6toEqvemkXQTE2ylm0kEC1gj4t5A

点击中间区域
呼出菜单
上一章
目录
下一章
×