第5章 基础——5.7. 进制.1

5.7. 进制.1

我女儿读幼儿园时,我就开始关心起她的算术能力。于是我了解到,她在小班的时候,就会个位数的加法,但一晃两年,到大班要结束了,才见她拿着算盘在那里炫她的百位数以内加减。

我 提到了“个位”、“百位”,您懂吧——不懂?不会吧,我的书居然也有幼儿园的小朋友读者了。没错,平常我们用惯的数,是逢十进一,称为“十进制”数。十进 制数的最低位,称为“个位”,而后是“十位”,“百位”,“千位”。不过,今天我们学习的“二进制”数,它的最低位,称为“1位”,而后是“2位”,“8 位”,“16位”,“32位”,“64位”……

一个1,对于十进制数,在百位上,它代表100,在十位上,代表10,在个位上,才代表1。所以,一个十进数的123,它之所以是“一百二十三”,是因为……请看大屏幕:

123 = 1×100 + 2×10 + 3×1。

 

〖轻松一刻〗:二进制其实很简单

我知道性急的读者读到此处,已经直接打电话到出版社咆哮了:“我要的是一本计算机编程教材,而不是一本幼儿算术启蒙读物!”

有关“十进制”的知识,确实很简单,比“十进制”还要简单5倍的,是“二进制”。 可是,真的有很多程序员,一提到“二进制”就晕乎,所以大家就耐心一点,我们的知识,马上就会上升到初中代数的水平了。

 

另外,如果我们把一个数,从右到左,依次称为“第0位”、“第1位”、“第2位”……的话,那么,“十进制”各位的权值,以“10”为基数,以位为指数。请再次看大屏幕:

123 = 1×(10的2次方) + 2×(10的1次方) + 3×(10的0次方)

其中,10的0次方为1,因为初一代数告诉我们:“除0以外,任何数的0次方为1”。

这是十进制,那么,二进制呢?

 

5.7.1. 正整数和零

“二进制”各位的权值,则以“2”为基础。这样,二进制数123——且慢,二进制数中,是不可能出现2,3的,因为它逢2就进1了,所以只有0~1,这和十进制数中只有0~9是一个道理。那我们以二进制数“110”为例。

再啰嗦一下,既然是二进制数,那就不能把110读成“一百一十”。为了避免混淆,我们用110(2)明确表示一个二进制数。

110(2)= 1×(2的2次方) + 1×(2的1次方) + 0×(2的0次方)

= 1×4 + 1×2 + 0×1

= 4 + 2 + 0

= 6

原来,二进制的110,不过是十进制的6。如果我女儿读幼儿园时学的是二进制数的话,那么她读小班时,就可以掌握3位数的加减了。

 

〖课堂作业〗:练习二进制数换算成十进制数

1、写以下二进制数的十进制值: 0000、0001、0010、0110、0011、1000、1010、0111、1111、1001。

2、请将以上二进制数从小到大排列,然后补充出同样处于0000~1111范围的其它二进制整数,并将新增的二进制数换算成十进制。

 

看 来,二进制挺“耗位置”的。一个小小的十进制数6,用二进制表达,就得用到3位数。之前我们在说机型,提过16位、32位、64位,没错,这里的“位 /bit”,指的是二进制数。当前大多数人用的PC机是32位机,在32位机上,一个最大的二进制整数是多大呢?拍拍脑袋就知道了:

11111111 11111111 11111111 11111111

这 就是最大的32位无符号正整数,换算成十进制是多大呢?答:4294967295,读作:“四十二亿九千四百九十六万又七千两百九十五”。如果问的是: 32位的二进制,最多可以表达几个整数?则回答:可以表达4294967296个(例如:0~4294967295)。在这么多数中,二进制和十进制来表 达一致的,只有0和1。

至此,我们在说的整数,都是0和正整数,负整数如何用二进制表达呢?比如-1?

 

5.7.2. 负整数(原码、反码、补码)

如果让你来设计,你认为在计算机中,二进制的负整数该如何表达呢?有丁小明同学站起来回答:

“前面加个负号就行了呗!比如,十进制的-1,还是-1(2),十进制的6,就是-110(2)……”

 

〖轻松一刻〗:丁小明同学罚洗校厕三天

早就说过,在计算机中,一切都是用数字表达。虽然电脑键盘是有负号正号,电脑显示屏上也能打出负号正号,但这都是给人看的,一切符号对于机器来说,最终都需要用二进制数表达。倘使“负的二进制数”需要用“负号”表达,但“负号”本身又需要用二进制表达……

 

没辙,既然计算机中一切都是0和1,那么负号肯定也得用0或1来表达了。所以,可能除丁小明同学以外,大家最容易想到的方法,就是0表示正号,1表示负号。对于一个32位的数来说:

+1还是:00000000 00000000 00000000 00000001

-1则是:10000000 00000000 00000000 00000001

咦?真的很直观噢,并且感觉很有创意啊……哈哈哈哈!然而,数学家们对我们这个创意很不满意。他们说,“+1加-1 应该等于0啊。你且把那两个数加加!” 得,居然是“-2”。

我 们把目光转向计算机学家,更惨,计算机学家基本也是一位数学家,所以他们有双重的不满意:“0怎么表达啊?我们制造计算机,第一要求就是准确无二义!你搞 这样一个正负表达,请问,0是00000000 00000000 00000000 00000000好呢?还是10000000 00000000 00000000 00000000好?还是两者都可以?”

的确噢,(+1) + (-1) 应等于0,并且+0应该等于-0……这要求不算高啊,怎么办呢?当初我也曾为此抓耳挠腮,终于得出结论:干嘛呢?!我只是要来打个酱油,你凭什么让我回答这个问题!为什么不直接告诉我先哲们是如何处理的?

先哲出场了!他说:“年轻人,用二进制数中最右边的一位0表示正号,最右边的一位1表示负号,其实是完全正确的思路!只不过,如果是负数的话,我们需要用补码来表达就一切OK了……”先哲的声音越来越远,越来越远。

“等等啊,什么叫补码啊?”

“至于什么叫补码,就让《白话 C++》来告诉你吧……”

计算机中,负数是用补码表示的。什么叫补码?让我且从“原码”说起……

  • 原码

前面一直在说的正整数和0的表示法,就叫做原码。例如,以32位的0和1为例:

0:00000000 00000000 00000000 00000000

1:00000000 00000000 00000000 00000001

我们仅用原码来表达正整数和0,而不用来表达负整数,因为,假设用原码表示-0和-1的话:

-0:10000000 00000000 00000000 00000000

-1:10000000 00000000 00000000 00000001

这就是我们刚才失败的创意。虽然失败,但却是后面变换过程的开始。

  • 反码

将原码中的0变成1,1变成0,就叫做反码。因为我们已经肯定要用“原码”来表达0和正整数了,所以让我们看看反码如何表达-0和-1。

先看原码:

-0的原码:10000000 00000000 00000000 00000000

-1的原码:10000000 00000000 00000000 00000001

然后分别取反:

-0的反码:11111111 11111111 11111111 11111111

-1的反码:11111111 11111111 11111111 11111110

反码的“-0”和“+0”长得更不像了(完全相反嘛)!再试一下(+1) + (-1),我们排一下竖式:

  00000000 00000000 00000000 00000001

+ 11111111 11111111 11111111 11111110

---------------------------------------

  11111111 11111111 11111111 11111111

结果成了那个正大的无符号整数:4294967295。看来,反码只是一个中间状态,我们既不用它表达正整数或0,也不用它表达负整数。

  • 补码

将反码加1得到的数,就叫做补码。

这么简单?不过,也得注意一点小事情:溢出。

〖轻松一刻〗:世界上最大数是多少?

小 学一二年级时,最爱和同学抬杠的事之一,就是比谁的数大了。甲说自家养了5只鹅,乙就说他家有6只。甲一生气:10只!乙更夸张100只(他家的鹅繁殖得 可真快)!后面就更了不得了,千只,万只,十万只,最后有一方嘴累了,就来一句:“反正无论你有多少只,我就比你多1只。”

一方垂死挣扎:“我家鹅的只数,是世界上最大的数!”

另一方:“那我家的是世界上最大的数再加1那个数。”

 

如 果这世界是上真有一个最大数,那么,这个最大数再加1得到的数是什么呢?这听着怎么也不像是个算术问题,倒像是个哲学问题啊。还好,在计算机的世界里,我 们可以回答这个问题。32位机里,最大的数,不就是“四十二亿九千四百九十六万又七千两百九十五”,那么,这个数加上1,是什么呢?

  11111111 11111111 11111111 11111111

+ 00000000 00000000 00000000 00000001

-----------------------------------------

  00000000 00000000 00000000 00000000

 

哈 哈哈哈,重大发现啊,原来计算机世界里,最大数加上1以后,居然得到0。咦,不是一直进位,进到最高位那个1上哪去了——你是说第33位吗?专业一点! 32位机,哪来的33位。那1位数,就这样“机间消失”了,对了,这也有一个专业名词,叫“溢出/overflow”。

言归正传。既然,将反码加1得到的数,就叫做补码。我们来看看补码如何表达-0和-1。

先看反码:

-0的反码:11111111 11111111 11111111 11111111

-1的反码:11111111 11111111 11111111 11111110

然后分别取补:

-0的补码:00000000 00000000 00000000 00000000

-1的补码:11111111 11111111 11111111 11111111

用补码表示-0,则它正好就是用原码表示的+0。数学家们提的第一个要求满足了!

再来算一下(1) + (-1) :

  00000000 00000000 00000000 00000001

+ 11111111 11111111 11111111 11111111

----------------------------------------

  00000000 00000000 00000000 00000000

溢出再一次发生,而结果却正是我们想要的:正1加上负1,等于0了!

〖重要〗: 小结:正、负整数、及0的二进制表示

小结一下:二进制数中,最高位可以被用来做符号位,0表示正号,1表示负号。但负数还需要由此换算成补码表示。至于0,用原码表示或用补码表示,都是一致的。

 

5.7.3. 无符号数vs.有符号数

前面提到一件事,说32位的最大整数,是32位上全是1的二进制数:

11111111 11111111 11111111 11111111

但后来我们又说,最高位的1可以用来表示是一个负数。这样,这个最大的数,一下子变成是“-1”(补码形式)了。

显然,如果最高位拿来当符号位的话,32位整数的最大数,应该是:

01111111 11111111 11111111 11111111

换 算成十进制数,是:2147483647,读作:“二十一亿四千七四八万又三千六百四十七”。哇,一下子缩水不少啊。不过,如果问的是: 32位的二进制,最多可以表达几个整数?则还是回答:可以表达4294967296个(例如:-2147483648~2147483647)。

〖小提示〗:16位整数的表达范围

在上个世纪我学习C++时,还时个人电脑还多数是16位的,所以一个整数的表达范围,就是区区的:-32768~32767;真是一个令人捉襟见肘的可用范围。

 

考虑到现实生活中,有许多数量是不需要“负数”的,比如“天上星星有几颗?”,或者“房地产商的利润是多少?”,所以C++语言决定区分“有符号”和“无符号”两种数量类型。

比如,我们一直在用的“int”,它是有符号的,即区分正负的32位数。而“unsigned int”,就是无符号的,只能表达0和正整数。常见的有:

  • int 、 unsigned int :整数,32位;(依据编译时机型变化)
  • short int 、 unsigned short int :短整数,16位;
  • char、unsigned char :字符,8位。unsigned char也常被称为“byte”,因为它最直接地表达计算机中一个字节的状态。
  • long、unsigned long:长整型,但其实在32位机器上,它也只是32位。

你可能感兴趣的:(第5章 基础——5.7. 进制.1)