二进制
所谓二进制(binary system),就是一套只使用 0 和 1 来表示数值的系统,逢二进一。
二进制在第三次科技革命中扮演着十分重要的角色,因为对于电路开关而言,通常只有两个状态:开,关。如果我们用电路开关的开表示 1,关表示 0,那么我们就可以使用电路来进行二进制数(binary)的计算了,这就有计算机的基本运行逻辑。不过很可惜,我这个教程并不打算介绍那些什么加法器,乘法器,除法器之类的电路元件的原理,有兴趣可以自行了解,我们这里要介绍的是更加根本性的东西。
将二进制数转换为十进制数(decimal),我们只要给第 n 位乘上 2^(n-1)
,之后再全部相加即可。例如:二进制数 1101 转换为十进制数是 1x2^3 + 1x2^2 + 0x2^1 + 1x2^0 = 13
。
将十进制数转换为二进制数,我们可以采用除二取余的方式,例如求 302 的二进制,我们先让它不断除 2:
- 302÷2 = 151 余0
- 151÷2 = 75 余1
- 75÷2 = 37 余1
- 37÷2 = 18 余1
- 18÷2 = 9 余0
- 9÷2 = 4 余1
- 4÷2 = 2 余0
- 2÷2 = 1 余0
- 1÷2 = 0 余1
之后,我们将所有余数从下往上写,得到:100101110
,这就是 302 的二进制。注意是从下往上写。
八进制与十六进制
二进制因为只使用 0 和 1,通常又长又难以估量数值大小。如果我们把二进制数每三位合并成一位,用 0-7 来表示,那就成了八进制数(octal)。如果我们把二进制数每四位合并成一位,用 0-9,a-f(即对应 10-15)表示,那就成了十六进制数(Hexadecimal)。
由于我们使用的电脑,通常都以 8 比特作为一个字节,而 8 比特用十六进制来表示刚好需要 8÷4=2 位数,但是用八进制就不能刚好表示出一个字节。因此,八进制现在基本处于英雄无用武之地的状况,我们这篇教程也不会再去讨论八进制。
十六进制是逢十六进一,由于数字只有十个(即 0-9),因此超出的部分使用字母 abcdef 来表示(分别代表10, 11, 12, 13, 14, 15),字母可以使用大写字母,含义不变。由于 16 是 2 的 4 次方,因此每一位十六进制刚好代表四位二进制,因此从十六进制转换为二进制或者二进制转换为十六进制相比于十进制而言是一件非常轻松的事情。
1
2
3
4
0 -- 0000 1 -- 0001 2 -- 0010 3 -- 0011
4 -- 0100 5 -- 0101 6 -- 0110 7 -- 0111
8 -- 1000 9 -- 1001 a -- 1010 b -- 1011
c -- 1100 d -- 1101 e -- 1110 f -- 1111
比如,我们把十六进制数 5d71
转换为二进制数,就是 0101110101110001
。
我们要把十六进制数转换为十进制数的话,和二进制转换为十进制是类似的道理,我们给第 n 位乘上 16^(n-1)
再全部相加,例如,十六进制数 5d71
转换为十进制就是 5x16^3 + 13x16^2 + 7x16^1 + 1x16^0 = 23921
。
记住一个特殊值,ff
等于 16x16-1
即 255
,这是一个字节能储存的数值大小。
下面是一个将十进制数转换为十六进制字符串的 GM 脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// hex(number)
{
var _number, _remain, _str;
_number = argument0;
_str = "";
do
{
_remain = _number mod 16;
_number = _number div 16;
if (_remain < 10)
_str = string(_remain) + _str;
else
_str = chr(ord('A') + _remain - 10) + _str;
}
until (_number == 0);
return _str;
}
至于将十六进制字符串转换成十进制数值,可以借用神奇的 execute_string 来完成:
1
2
3
4
// from_hex(str)
{
return execute_string("return $" + argument0);
}