如有错误,请不吝指正对于代码行内没有对齐的问题,电脑版或者手机app上查看应该就不会出现了。

常见进制的英文名

首先要清楚秀的对象是人是狗,接着才能开始你的表演。

  • Binary:二进制
  • Quaternary:四进制
  • Octal:八进制
  • Decimal:十进制
    (同时也是进制的单词,通常提到进制默认十进制)
  • Hexadecimal:十六进制

多个进制的情况下,我们需要有一种统一的规定辨识一个数是何种进制,否则将会引起解读歧义。计算机界通常有两种最普遍的规定:

  • 狭义规定

之所以称其为狭义规定,是因为这种规定只能辨识编程过程中最常用的四种进制:2进制、8进制、10进制和16进制。

  1. 2进制:在所要表示的数之前加0b;如0b10表示一个2进制数。
  2. 8进制:在所要表示的数之前加0;如012表示一个8进制数。
  3. 10进制:遵从日常习惯;如12默认为10进制。
  4. 16进制:在所要表示的数之前加0x;如0x1A表示一个16进制数。
  • 广义规定

广义规定使用N_(x)表示x进制(其中_(x)应该为N的下标,此处迫于无奈只能用下划线表示);如:10进制中的12,我们表示为12_(10)。可以看到这种规定可以明确标识任意进制数

声明:狭义、广义非专业规定,只在本文为方便理解使用。

为了表述方便,我们在下文计算过程中使用广义规定。


进制之间的转换

在这之前,希望你能忘掉以前所学过的所有数学,如同倚天屠龙记中学太极剑法的张无忌一般,无招胜有招。现在你是一个连1 + 1都不会的少年!

记得我们最开始学数学的时候,是从加、乘法表开始的,下面让我们重新温习一遍这部分的知识……

  • N_(10)

鉴于10进制加、乘法表大家都已熟悉,这里就不予给出。(OxO)

  • N_(2)、N_(3)、N_(5)
//N_(2)      //N_(3)         //N_(5)
//加法表     //加法表        //加法表
+  0  1      +  0  1  2      +  0  1  2  3  4
0  0  1      0  0  1  2      0  0  1  2  3  4
1  1  10     1  1  2  10     1  1  2  3  4  10
             2  2  10 11     2  2  3  4  10 11
                             3  3  4  10 11 12
                             4  4  10 11 12 13

//乘法表     //乘法表        //乘法表 
*  0  1      *  0  1  2      *  0  1  2  3  4
0  0  0      0  0  0  0      0  0  0  0  0  0
1  0  1      1  0  1  2      1  0  1  2  3  4
             2  0  2  11     2  0  2  4  11 13
                             3  0  3  11 14 22
                             4  0  4  13 22 31

推导这么弱智的东西到底有什么用呢?进制转换中用到这些了?我读书少别骗我。很不巧,还真用到了!


EXAMPLE:

M_(2) -> N_(10)

我们先来看一个最经典的例子,2进制转换10进制。

//看到0、1字符串中间的空格莫慌,类比电话号码中的空格
0110 0001_(2) = N_(10)
//step1:从右往左给bit位编码,编码从0开始;当你熟练之后可以直接跳过
//该步骤……怕被人打,下方给出熟练之后应该怎么做
0|1|1|0|0|0|0|1
7|6|5|4|3|2|1|0
//step2:对应位的数值乘以进制数的编码次方
N = 1 * 2^0 + 1 * 2^5 + 1 * 2^6
  = 1 + 32 + 64
  = 97
//即
0110 0001_(2) = 97_(10)

高手一般是这样干的

0110 0001_(2) = N_(10)
//step1:从右往左直接写2的对应幂方
  0| 1| 1| 0| 0|0|0|1
128|64|32|16| 8|4|2|1
//step2:把1对应的幂方统统加起来
N = 1 + 32 + 64 = 97
//即
0110 0001_(2) = 97_(10)

与之相对的一个镜像问题是:

M_(10) -> N_(2)

97_(10) = N_(2);
//step1:97除以2,余1,得48;循环此操作,直到商为0;足够熟悉者可以
//跳过此步,直接目测得出结果……为了不挨打,下面也会给出
  | 97
2 |____
  | 48  ...  1
2 |____
  | 24  ...  0
2 |____
  | 12  ...  0
2 |____
  | 6   ...  0  
2 |____
  | 3   ...  0
2 |____
  | 1   ...  1
2 |____
    0   ...  1
//step2:从下往上将余数自左向右写出来,就是所得2进制结果,如果有需
//要,可在结果前加上补位0
N = 0110 0001
//即
97_(10) = 0110 0001_(2);

当然高手是这样干的:

97_(10) = N_(2);
//step1:若规定了bit位数,全部用0补足。没有规定就大概估量一下
//对于用0补足这个问题上,二进制常常用于计算机计算,而最小的存储单
//位是字节byte,1byte = 8 bit,所以我们需要进行补0占位的操作
  0| 0| 0| 0| 0|0|0|0
128|64|32|16| 8|4|2|1
//step2:64是所有比97小的幂方中最大的,那么把64对应的bit位标为1,并
//且97 - 64 = 33
  0| 1| 0| 0| 0|0|0|0
128|64|32|16| 8|4|2|1
//step3:32是所有比33小的幂方中最大的,那么把32对应的bit位标为1,并
//且33 - 32 = 1。重复上述动作,直到97变为0
  0| 1| 1| 0| 0|0|0|1
128|64|32|16| 8|4|2|1
//即
97_(10) = 0110 0001_(2);

这也是大众熟知的转换方式,方便快捷且足以解决大多数问题。但需要注意,2进制和10进制相当特殊,并不具有代表性。


再来看看下面这个问题:

M_(3)->N(5)

似乎有点麻烦了。正常的做法是将3进制用上述办法转换成10进制,再用上述办法将所得10进制转换成5进制,进而间接得到结果。这样完全oxxk!

但问题来了,3进制和5进制之间的事,为什么要10进制跟着瞎参和?同样,好奇的宝宝可能也会问,2进制转换10进制主要用到的是乘法,为什么反过来会用除法?讲道理,进制是平等的,它们之间的转换应该也是统一的!


其实我们最常用的这种乘除转换法中省略了很多步骤,而且有很多省略由于“太过简单”以至于我们觉察不到,完整的算法步骤应该是如下所示:

0110 0001_(2) = N_(10)
//step1:从右往左给bit位用10进制数进行编码,编码从0开始;
0|1|1|0| |0|0|0|1
7|6|5|4| |3|2|1|0
//step2:对应位的数值乘以进制数的编码次方。其中对应位的数值、进制数
//和编码全部为10进制
N = 1_(10) * 2_(10)^0_(10) + 1_(10) * 2_(10)^5_(10) + 1_(10) * 2_(10)^6_(10)
  = 1_(10) + 32_(10) + 64_(10)
  = 97_(10)
//即
0110 0001_(2) = 97_(10)

同时我们可以将10进制转2进制这样改写:

97_(10) = N_(2);
//step1:从右往左给bit位用2进制数进行编码,编码从0开始;
9|7
1|0
//step2:对应位的数值乘以进制数的编码次方。其中对应位的数值、进制数
//和编码全部为2进制
N = 0111_(2) * 1010_(2)^0_(2) + 1001_(2) * 1010_(2)^1_(2)
  = 0111_(2) + 0101 1010_(2)
  = 0110 0001_(2)
//即
97_(10) = 0110 0001_(2);

这里便是用2进制加、乘法表的时候了。结合上表:

//我们需要计算1001_(2) * 1010_(2)
      1001
*     1010
_________________
      0000
     1001
    0000
   1001
_________________
   1011010
//加上补位0,即
0101 1010_(2)

于是,我们惊讶的发现,2进制转换10进制可以用乘法,10进制转换2进制同样用的是乘法!它们是同一个算法,尽管似乎变复杂了(这里必须吐槽一下简书对于数学公式的“弱”支持,我不得不用代码行进行表述,这让过程看起来相当复杂)。我们可以用这个算法解决任何进制转换的问题,理解它的关键就是对所有进制一视同仁。

就上述来看,这个算法并没有比常规算法来的有多方便。既然如此,它还有什么存在的意义?


我们接着之前3进制转5进制的问题:

M_(3)->N(5)

1021_(3) = N_(5)
//step1:从右往左给每个位用5进制数进行编码,编码从0开始;
1|0|2|1
3|2|1|0
//step2:对应位的数值乘以进制数的编码次方。其中对应位的数值、进制数
//和编码全部为5进制
N = 1_(5) * 3_(5)^0_(5) + 2_(5) * 3_(5)^1_(5) + 1_(5) * 3_(5)^3_(5) 
  = 1_(5) + 11_(5) + 102_(5)
  = 114_(5)
//即
1021_(3) = 114_(5)

很快就会有严谨的朋友对此提出质疑,怎么知道你这家伙算的对不对,且让我把它们分别转换成10进制检验一番。有这样想法的朋友应该不在少数,有质疑心和探索精神是好事,但是等等,小三和小五幽怨道:“为什么官人老是放不下十进制,她到底哪里好了嘛!”

我反复强调进制之间的平等关系,不应该仅仅因为对于我们对于10进制的熟悉,就将它置于一个判官般特殊的地位。想想在进行3547 + 2869 = 6416时,如果对结果持有怀疑,除了重复计算之外我们还会怎么做?可以通过计算2869 + 3547是否等于6416或者6416 - 3547是否等于2869来进行验证。但无论哪一种方法,我们都是在10进制的领域内跳舞,无需借助另一种进制来帮助我们检验。

同样的道理,对于此处1021_(3)是否等于114_(5),只需要检验能否从114_(5)得到1021_(3),并不需要10进制瞎捣乱。

M_(5)->N(3)

114_(5) = N_(3)
//step1:从右往左给每个位用3进制数进行编码,编码从0开始;
1|1|4
2|1|0
//step2:对应位的数值乘以进制数的编码次方。其中对应位的数值、进制数
//和编码全部为3进制
N = 11_(3) * 12_(3)^0_(3) + 1_(3) * 12_(3)^1_(3) + 1_(3) * 12_(3)^2_(3) 
  = 11_(3) + 12_(3) + 221_(3)
  = 1021_(3)
//即
114_(5) =1021 _(3)

如此便可以证明我们的计算结果是对的,当然不排除“错错得对”的情况,但那毕竟是少数。另外,作为一个算法,如果不去推导出它的一般式,我们总是有足够的理由认为它只是在上述“特例”中才适用,但鉴于本文以应用为主,这种方法正确性的证明和一般公式就不予给出了(推导出来估计也没几个人愿意去看),有兴趣的童鞋可以自己加以研究。


到这里,我们关于进制转换的介绍就可以告一段落了。其实这个方法并非什么新鲜事,几乎是个搞计算机的都知道。写本文的目的也并非一味地夸大这个方法的优点,适合自己的才是最好的。

我只是想从一个统一的算法开始说起,为大家展示展示进制之间的平等关系。

进制之间不存在计算难易之分,因为所计算的数据量都是那么多。你之所以会觉得10进制简单,只是因为你对它足够的熟悉,你从小学数学到高等数学接触的全部都是10进制,大脑早已经习惯了10进制思维,看到1 + 4你下意识的是5_(10)而不是10_(5),自然会觉得10进制比5进制好算了不少。

进制之间也没有高低阶之分。这种偏见尤其体现在,进行3进制转换5进制的时候,人们总是习惯性的先转换成10进制,检查计算对错时亦如此。10进制就如同一个中介+标尺一般的存在。

当我们用一种根深蒂固的思维去看待另一种思维中存在的问题的时候,就会产生矛盾,而我们又不容易跳出这种思维定式,甚至很多时候我们意识不到自己处于思维定式当中,于是矛盾得到进一步的固化,以至于我们不得不想出各种奇奇怪怪的方法来解决矛盾,如同夏虫语冰焉。譬如,地心说理论中天体的运动规律、以太超距作用乃至于减法(我认为减法只是人们最初理解不了负数的产物,5+3应该写作+5+3,只是两个正数放在了一起,-5-3也只是两个负数放在一起,而“放在一起”这个动作我们可以统一用一个函数sum表示,这样它们应该写作sum(+5, +3)和sum(-5, -3))。

于是,男人不能理解女人,大人不能理解小孩,常人不能理解色盲,东方人不能理解西方人,人类不能理解动物,地球生物不能理解外星生物,甚至生物不能理解非生物。

世间本无象,人心生万象,受其便也受其困。

最后感谢看到这里的童鞋,送大家一颗小彩蛋


幂关系进制之间的快速转换

声明:幂关系进制非专有名词,只在本文中为了理解方便使用

若X进制和Y进制之间满足,Y = X^n,则称它们为幂关系进制

有没有童鞋在上课的时候纳闷过老师们为什么能瞬间写出0xAF = 0b10101111,反正笔者在刚开始学习的时候风中凌乱过不知道多少回,而且每次老师都一脸淡定的如同算了一个1+1……

其实而幂关系进制之间是存在着快速转换方法的。

典型的,如2进制、4进制、16进制……互为幂关系进制。以下举例说明:

//2进制->4进制
1010 1111_(2) = N_(4)
//step1:将2进制两两分组,分成4对
10|10|11|11
//step2:写出每对对应的4进制
10|10|11|11
 2| 2| 3| 3
//即
1010 1111_(2) = 2233_(4)

//4进制->16进制
2233_(4) = N_(4)
//step1:将2进制两两分组,分成2对
22|33
//step2:写出每对对应的16进制
22|33
 A| F
//即
2233_(4) = AF_(16)

//2进制->16进制
1010 1111_(2) = N_(16)
//step1:将2进制4个一组,分成2对
1010|1111
//step2:写出每对对应的16进制
1010|1111
   A|F
//即
1010 1111_(2) = AF_(16)

反过来也是同样的道理。

怎么样,是不是很简单了呀!希望看了我这篇文章的童鞋们再也不要像笔者之前一样蠢萌了,,,费尽心思先将16进制转换成10进制,再转换成2进制,,,真的伤不起!

再想想如果是3进制和9进制之间的问题呢?2进制和8进制呢?相信你一定可以自己找到它们的解法。