数值信息在计算机内是采用二进制编码表示。数有正负之分,一般情况下,用"0"表示正号,"1"表示负号,符号位放在数的最高位。
例如,8位二进制数A = (+1011011),B = (-1011011),它们在机器中可以表示为:
A:01011011
B:11011011
计算机中几种常用的编码----原码、反码和补码。
1.原码
将符号位数字化为0或1,数的绝对值与符号一起编码,即所谓"符号-绝对值表示"的编码,称为原码。
例如:
整数:
X = +1011011,原码:[X]原 = 01011011;
X = -1011011,原码:[X]原= 11011011;
小数:
对于一个带符号的纯小数,它的原码表示就是把小数点左边一位用作符号位。
X = 0.1011,原码:[X]原 = 0.1011;
X = -0.1011,原码:[X]原 = 1.1011;
当采用原码表示法时,编码简单直观,与真值转换方便。但原码也存在一些问题,一是零的表示不唯一,因为:[+0] = 000···0,[-0] = 100···0 零有二义性,
给机器判零带来麻烦。二是用原码进行四则运算时,符号位需要单独处理,且运算规则复杂。例如加法运算,若两数同号,两数相加,结果取共同的符号;
若两数异号,则要大数减去小数,结果冠以大号的符号。此外,借位操作如果用计算机硬件来实现是很困难的。
2.反码
反码很少使用,但作为一种编码方式和求补码的中间码,我们还是要学习一下的。
正数的反码与原码表示相同。
负数反码的符号位与原码相同(仍用1表示),其余各位取反(0变1,1变0)。例如:
X = +1100110, [X]原 = 01100110,[X]反 = +1100110;
X = -1100110,[X]原 = 11100110,[X]反 = 10011001;
X = +0000000,[X]原 = 00000000,[X]反 = 00000000;
X = -0000000,[X]原 = 10000000,[X]反 = 111111111;
和原码一样,反码中的零的表示也不唯一。
当X为纯小数时,反码的表示如下:
X = 0.1011,[X]原 = 0.1011,[X]反 = 0.1011;
X = -0.1011,[X]原 = 1.1011,[X]反 = 1.0100;
3.补码
(1)模数的概念
模数从物理意义上讲,是某种计量器的容量。例如,我们日常生活中用到的钟表,模数就是12。钟表计时的方式就是达到12就从零开始(扔掉一个12),这在数学上是一种"取模(或取余)运算(mod)"。例如:14%12 = 2。
如果现在的准确时间是6点整,而你的手表指向8点,怎样把表拨准呢?可以有两种方法:把表往后拨2小时,或把表往前拨10小时,效果是一样的。即:
8-2 = 6
(8+10)%12 = 6
在模数系统中,8-2 = 8+10 (mod 12)
上式之所以成立,是因为2与10对模数12是互为补数的(2+10 = 12)。因此,可以认可这样一个结论:在模数系统中,一个数减去另一个数,或者一个数加上一个负数,等于第一个数加上第二个数的补数:
8+(-2) = 8+10 (mod 12)
我们称10为-2在模12下的"补码"。负数采用补码表示后,可以使加减法统一为加法运算。
在计算机中,机器表示数据的字长是固定的。对于n位数来说,模数的大小是:n位数全为1,且末位再加1。实际上模数的值已经超过了机器所能表示的数的范围,因此模数在机器中是表示不出来的。若运算结果大于模数,则模数自动丢掉,也就等于实现了取模运算。
如果有n位整数(包括一位符号位),则它的模数为2^n;如果有n位小数,小数点前一位为符号位,则它的模数为2。
***(2)补码表示法***
由以上讨论得知,对于一个二进制负数,可用其模数与真值做加法(模减去该数的绝对值)求得其补码。
例: X = -0110 [X]补 = 2^4 + (-0110) = 1010
X = -0.1011 [X]补 = 2 + (-0.1011) = 1.0101
由于机器中不存在数的真值形式,用上述公式求补码在机器中不易实现,但从上式可推导出一个简便方法。
"对于一个负数,其补码由该数反码的最末位加1求得。"
"对于正数来说,其原码、反码、补码形式相同。”
补码的特点之一就是零的表示唯一。
[+0]补 = 0 0 ···0 [-0]补 = 1 1 ··· 1 + 1 = 1 0 0 ··· 0
|_____| |______| | |______|
n位 n位 | n位
|_ _ _ _ _ _ _ 自动丢失
这种简便的求补码方法经常被简称为"求反加1"。
(3)补码的运算规则
采用补码表示的另一个好处就是当数值信息参与算术运算时,采用补码方式是最方便的。首先,"符号位可作为数值参加运算,"最后仍可得到正确的结果符号,符号无需单独处理;其次,采用补码进行运算时,减法运算可转换为加法运算,简化了硬件中的运算电路。
例如:
计算67-10=?
[+67]原 = 01000011, [+67]补 = [+67]原
[-10]原 = 10001010, [-10]补 = 11110110
0 1 0 0 0 0 1 1 [+67]补
+ 1 1 1 1 0 1 1 0 [-10]补
——————————————
1 0 0 1 1 1 0 0 1 = 57
|____________________________ 最高位的进位自然丢失
由于字长只有8位,因此加法最高位的进位自然丢失,达到了取模效果(即丢掉了一个模数)。
应当指出,"补码运算的结果仍为补码。" 上例中,从结果符号位得知,结果为正,所以补码即原码,转换成十进制数为57。
如果结果为负,则是负数的补码形式,若要变成原码,需要对补码再求补,即可还原为原码。
例如:
10 - 67 = ?
[+10]原 = 00001010 = [+10]补
[-67]原 = 11000011 [-67]补 = 10111101
0 0 0 0 1 0 1 0
+ 1 0 1 1 1 1 0 1
——————————
1 1 0 0 0 1 1 1
[结果]补 = 11000111,[结果]原 = 10111001
所以结果的真值为-0111001,十进制为-57。
以上两个例子是否就可以说明补码运算的结果总是正确的呢?请看下面的例子:
例如:
85 + 44 = ?
0 1 0 1 0 1 0 1
+ 0 0 1 0 1 1 0 0
———————————
1 0 0 0 0 0 0 1
从结果的符号位可以看出,结果是一负数。但两个整数相加不可能是负数,问题出在什么地方呢?原来这是由于"溢出"造成的,即结果超出了一定位数的二进制数所能表示的数的范围。
以上内容节选自清华大学出版社《C++语言程序设计(第4版)》
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。