如果你在网上搜索 How to convert from decimal to binary,会找到四个简单的算法:两个用于整数,两个用于小数。它们连同示例都放在本文第一部分。不过,虽然只记住算法在绝大多数情况下已经够用,我还是决定试着弄懂它们为什么成立。第二部分讲解每个算法背后那些非常基础的数学。懂了这些,万一你哪天突然忘了某个算法,也能把它推回来。我强烈建议你拿出纸笔,跟着我一起做一遍运算,这样能更好地记住其中的数学。下面就是网上能找到的那四个算法及示例。
把十进制整数转成二进制
要把整数转成二进制,从这个整数开始,把它除以 2,记下商和余数。继续用 2 去除商,直到商为零。然后把各次的余数按逆序写出来即可。
下面用整数 12 举例。先把这个数除以二,写出商和余数:
12 : 2 = 6 + 0 6 : 2 = 3 + 0 3 : 2 = 1 + 1 1 : 2 = 0 + 1 \begin{aligned}
12 : 2 &= 6 + 0 \\
6 : 2 &= 3 + 0 \\
3 : 2 &= 1 + 1 \\
1 : 2 &= 0 + 1
\end{aligned} 12 : 2 6 : 2 3 : 2 1 : 2 = 6 + 0 = 3 + 0 = 1 + 1 = 0 + 1
现在只需把余数按逆序写出来——1100 。所以十进制的 12 在二进制中表示为 1100 。
把十进制小数转成二进制
要把小数转成二进制,从这个小数开始,把它乘以 2 ,记下所得的整数部分和小数部分。继续乘以 2,直到所得的小数部分等于零。然后把每次乘法结果中的整数部分写出来即可。
下面用小数 0.375 举例。
0.375 ⋅ 2 = 0 + 0.75 0.75 ⋅ 2 = 1 + 0.5 0.5 ⋅ 2 = 1 + 0 \begin{aligned}
0.375 \cdot 2 &= 0 + 0.75 \\
0.75 \cdot 2 &= 1 + 0.5 \\
0.5 \cdot 2 &= 1 + 0
\end{aligned} 0.375 ⋅ 2 0.75 ⋅ 2 0.5 ⋅ 2 = 0 + 0.75 = 1 + 0.5 = 1 + 0
现在只需把每一步所得的整数部分写出来——0.011 。所以十进制的 0.375 在二进制中表示为 0.011 。
把二进制整数转成十进制
要把二进制整数转成十进制,从左边开始。取当前的累计值,乘以二,再加上当前这一位数字。继续下去,直到没有数字剩下。下面用整数 1011 举例。
2 ⋅ 0 + 1 = 1 2 ⋅ 1 + 0 = 2 2 ⋅ 2 + 1 = 5 2 ⋅ 5 + 1 = 11 \begin{aligned}
2 \cdot 0 + 1 &= 1 \\
2 \cdot 1 + 0 &= 2 \\
2 \cdot 2 + 1 &= 5 \\
2 \cdot 5 + 1 &= 11
\end{aligned} 2 ⋅ 0 + 1 2 ⋅ 1 + 0 2 ⋅ 2 + 1 2 ⋅ 5 + 1 = 1 = 2 = 5 = 11
把二进制小数转成十进制
要把二进制小数转成十进制,从右边开始,累计值取 0。取当前的累计值,加上当前这一位数字,再把结果除以 2。继续下去,直到没有数字剩下。下面用小数 0.1011 举例。我把除以 2 直接换成了乘以 1/2 。
1 2 ⋅ ( 1 + 0 ) = 0.5 1 2 ⋅ ( 1 + 0.5 ) = 0.75 1 2 ⋅ ( 0 + 0.75 ) = 0.375 1 2 ⋅ ( 1 + 0.375 ) = 0.6875 \begin{aligned}
\frac{1}{2} \cdot (1 + 0) &= 0.5 \\
\frac{1}{2} \cdot (1 + 0.5) &= 0.75 \\
\frac{1}{2} \cdot (0 + 0.75) &= 0.375 \\
\frac{1}{2} \cdot (1 + 0.375) &= 0.6875
\end{aligned} 2 1 ⋅ ( 1 + 0 ) 2 1 ⋅ ( 1 + 0.5 ) 2 1 ⋅ ( 0 + 0.75 ) 2 1 ⋅ ( 1 + 0.375 ) = 0.5 = 0.75 = 0.375 = 0.6875
这就是能让你在二进制和十进制之间来回转换的 4 个简单算法。
数的 q 进制展开
理解这些算法为何成立的关键,是数的 q 进制展开 。任意进制中的整数都可以写成如下形式:
N = x n ⋅ q n + … + x 1 ⋅ q 1 + x 0 ⋅ q 0 N = x_n \cdot q^n + \ldots + x_1 \cdot q^1 + x_0 \cdot q^0 N = x n ⋅ q n + … + x 1 ⋅ q 1 + x 0 ⋅ q 0
其中 N 是整数,x 是数位 (十进制中为 0 到 9,二进制中为 0 和 1) ,q 是基数 (十进制为 10,二进制为 2) 。
本文中这种形式称为 数 N 的 q 进制展开 ,或简称 q 进制展开 。我们看看数 12 在十进制和二进制中的样子:
12 10 = 1 ⋅ 10 1 + 2 ⋅ 10 0 1100 2 = 1 ⋅ 2 3 + 1 ⋅ 2 2 + 0 ⋅ 2 1 + 0 ⋅ 2 0 \begin{aligned}
12_{10} &= 1 \cdot 10^1 + 2 \cdot 10^0 \\
1100_2 &= 1 \cdot 2^3 + 1 \cdot 2^2 + 0 \cdot 2^1 + 0 \cdot 2^0
\end{aligned} 1 2 10 110 0 2 = 1 ⋅ 1 0 1 + 2 ⋅ 1 0 0 = 1 ⋅ 2 3 + 1 ⋅ 2 2 + 0 ⋅ 2 1 + 0 ⋅ 2 0
同样地,任意进制中的小数也可以写成如下形式:
N = x 1 ⋅ q − 1 + x 2 ⋅ q − 2 + … + x n ⋅ q n N = x_1 \cdot q^{-1} + x_2 \cdot q^{-2} + \ldots + x_n \cdot q^n N = x 1 ⋅ q − 1 + x 2 ⋅ q − 2 + … + x n ⋅ q n
其中 N 是小数,x 是数位 (十进制中为 0 到 9,二进制中为 0 和 1) ,q 是基数 (十进制为 10,二进制为 2) 。
数 0.375 在十进制和二进制中的表示如下:
0.375 10 = 3 ⋅ 10 − 1 + 7 ⋅ 10 − 2 + 5 ⋅ 10 − 3 0.011 2 = 0 ⋅ 2 − 1 + 1 ⋅ 2 − 2 + 1 ⋅ 2 − 3 \begin{aligned}
0.375_{10} &= 3 \cdot 10^{-1} + 7 \cdot 10^{-2} + 5 \cdot 10^{-3} \\
0.011_2 &= 0 \cdot 2^{-1} + 1 \cdot 2^{-2} + 1 \cdot 2^{-3}
\end{aligned} 0.37 5 10 0.01 1 2 = 3 ⋅ 1 0 − 1 + 7 ⋅ 1 0 − 2 + 5 ⋅ 1 0 − 3 = 0 ⋅ 2 − 1 + 1 ⋅ 2 − 2 + 1 ⋅ 2 − 3
把十进制整数转成二进制
事实证明,我们可以用这个 q 进制展开形式 把一个数从十进制转成二进制。就用同一个数 12 来做。首先假装我们不知道它在二进制中怎么写,把未知的数位用 x 代替写出来:
12 10 = x n ⋅ 2 n + … + x 1 ⋅ 2 1 + x 0 ⋅ 2 0 12_{10} = x_n \cdot 2^n + \ldots + x_1 \cdot 2^1 + x_0 \cdot 2^0 1 2 10 = x n ⋅ 2 n + … + x 1 ⋅ 2 1 + x 0 ⋅ 2 0
我们的任务是求出所有的 x 。看看这里能做什么。首先要注意的是:除最后一项外,所有加数都会是偶数,因为它们都是 2 的倍数。利用这一点,我们就能推出 x0 这一位——如果被转换的整数是偶数,那么 x0 等于 0 ;如果是奇数,那么 x0 必然是 1 。这里的数 12 是偶数,所以 x0 为零。把这个信息记下来:
12 10 = x n ⋅ 2 n + … + x 1 ⋅ 2 1 + 0 12_{10} = x_n \cdot 2^n + \ldots + x_1 \cdot 2^1 + 0 1 2 10 = x n ⋅ 2 n + … + x 1 ⋅ 2 1 + 0
接下来要求 x1 的值。既然从 x1 到 xN 的所有加数都是 2 的倍数,我们就可以把 2 提到括号外,从而把 x1 单独分出来。来做一下:
12 10 = 2 ( x n ⋅ 2 n − 1 + … + x 1 ⋅ 2 0 ⏟ 6 ) + 0 12_{10} = 2(\underbrace{x_n \cdot 2^{n-1} + \ldots + x_1 \cdot 2^0}_{6}) + 0 1 2 10 = 2 ( 6 x n ⋅ 2 n − 1 + … + x 1 ⋅ 2 0 ) + 0
也很容易看出括号内各值之和等于 6 。于是第一步可以写成:
12 = 2 ⋅ 6 + 0 12 = 2 \cdot 6 + 0 12 = 2 ⋅ 6 + 0
继续求剩下的 x 。括号里的多项式可以单独写成一个式子:
6 10 = x n ⋅ 2 n − 1 + … + x 2 ⋅ 2 1 + x 1 ⋅ 2 0 6_{10} = x_n \cdot 2^{n-1} + \ldots + x_2 \cdot 2^1 + x_1 \cdot 2^0 6 10 = x n ⋅ 2 n − 1 + … + x 2 ⋅ 2 1 + x 1 ⋅ 2 0
在这里,套用上面同样的逻辑就能看出 x1 等于 0 。把它重写一遍,并再次把 2 提到括号外:
6 10 = 2 ⋅ ( x n ⋅ 2 n − 2 + … + x 2 ⋅ 2 0 ⏟ 3 ) + 0 6_{10} = 2 \cdot (\underbrace{x_n \cdot 2^{n-2} + \ldots + x_2 \cdot 2^0}_{3}) + 0 6 10 = 2 ⋅ ( 3 x n ⋅ 2 n − 2 + … + x 2 ⋅ 2 0 ) + 0
于是第二步是:
6 = 2 ⋅ 3 + 0 6 = 2 \cdot 3 + 0 6 = 2 ⋅ 3 + 0
现在能看出规律了。我们可以一直把 2 提出来,直到商为零。顺着这个规律走下去,看看会得到什么。
3 10 = 2 ⋅ ( x n ⋅ 2 n − 3 + … + x 3 ⋅ 2 0 ⏟ 1 ) + 1 3_{10} = 2 \cdot (\underbrace{x_n \cdot 2^{n-3} + \ldots + x_3 \cdot 2^0}_{1}) + 1 3 10 = 2 ⋅ ( 1 x n ⋅ 2 n − 3 + … + x 3 ⋅ 2 0 ) + 1
既然商等于 1,就只剩下一个加数了,于是把前面的式子重写一下:
3 10 = 2 ⋅ ( x 3 ⋅ 2 0 ⏟ 1 ) + 1 3_{10} = 2 \cdot (\underbrace{x_3 \cdot 2^0}_{1}) + 1 3 10 = 2 ⋅ ( 1 x 3 ⋅ 2 0 ) + 1
于是第三步是:
3 = 2 ⋅ 1 + 1 3 = 2 \cdot 1 + 1 3 = 2 ⋅ 1 + 1
最后我们得到:
1 10 = x 3 ⋅ 2 0 1_{10} = x_3 \cdot 2^0 1 10 = x 3 ⋅ 2 0
显然 x3 等于 1 。但由于我们的算法需要一个商,把前面的式子重写成含有商的形式:
1 10 = 2 ⋅ 0 + 1 1_{10} = 2 \cdot 0 + 1 1 10 = 2 ⋅ 0 + 1
既然得到的商是 0 ,就再没有可处理的东西了,这就是最后一步。写出来:
1 = 2 ⋅ 0 + 1 1 = 2 \cdot 0 + 1 1 = 2 ⋅ 0 + 1
到这里转换就完成了。整个过程按步骤看是这样:
12 = 2 ⋅ 6 + 0 6 = 2 ⋅ 3 + 0 3 = 2 ⋅ 1 + 1 1 = 2 ⋅ 0 + 1 \begin{aligned}
12 &= 2 \cdot 6 + 0 \\
6 &= 2 \cdot 3 + 0 \\
3 &= 2 \cdot 1 + 1 \\
1 &= 2 \cdot 0 + 1
\end{aligned} 12 6 3 1 = 2 ⋅ 6 + 0 = 2 ⋅ 3 + 0 = 2 ⋅ 1 + 1 = 2 ⋅ 0 + 1
现在很清楚了:每一步的余数对应相应位置上 x 的值——第一个余数对应第一个 x,第二个余数对应第二个 x,以此类推。所以按上面描述的算法,数 12 在二进制中表示为 1100 。
别忘了我们一开始是想说明,那个「除以 2 」的算法为什么成立。把上面这些步骤中的 2 移到式子左边:
12 = 2 ⋅ 6 + 0 → 12 : 2 = 6 + 0 6 = 2 ⋅ 3 + 0 → 6 : 2 = 3 + 0 3 = 2 ⋅ 1 + 1 → 3 : 2 = 1 + 1 1 = 2 ⋅ 0 + 1 → 1 : 2 = 0 + 1 \begin{aligned}
12 = 2 \cdot 6 + 0 &\rightarrow 12 : 2 = 6 + 0 \\
6 = 2 \cdot 3 + 0 &\rightarrow 6 : 2 = 3 + 0 \\
3 = 2 \cdot 1 + 1 &\rightarrow 3 : 2 = 1 + 1 \\
1 = 2 \cdot 0 + 1 &\rightarrow 1 : 2 = 0 + 1
\end{aligned} 12 = 2 ⋅ 6 + 0 6 = 2 ⋅ 3 + 0 3 = 2 ⋅ 1 + 1 1 = 2 ⋅ 0 + 1 → 12 : 2 = 6 + 0 → 6 : 2 = 3 + 0 → 3 : 2 = 1 + 1 → 1 : 2 = 0 + 1
这样你就能看到,我们是如何得到开头那个算法的。这四步的计算也可以合并成一个表达式,像这样:
12 = 2 ⋅ ( 2 ⋅ ( 2 ⋅ ( 2 ⋅ 0 + 1 ⏟ 1 ) + 1 ⋅ 2 0 ⏞ 3 ) + 0 ⋅ 2 0 ⏟ 6 ) + 0 ⋅ 2 0 12 = 2 \cdot (\underbrace{2 \cdot (\overbrace{2 \cdot (\underbrace{2 \cdot 0 + 1}_{1}) + 1 \cdot 2^0}^{3}) + 0 \cdot 2^0}_{6}) + 0 \cdot 2^0 12 = 2 ⋅ ( 6 2 ⋅ ( 2 ⋅ ( 1 2 ⋅ 0 + 1 ) + 1 ⋅ 2 0 3 ) + 0 ⋅ 2 0 ) + 0 ⋅ 2 0
请务必弄清我们是怎么得到这个表达式的——在探究二进制转十进制的算法如何成立时,我们会用到它。
把十进制小数转成二进制
为了说明把小数转成二进制时为什么要乘以 2 并取整数部分,我同样会使用小数的 q 进制展开形式 。我用本文第一部分中的小数 0.375 。和整数部分一样,先假装我们不知道这个数在二进制中怎么写,把未知的数位用 x 代替写出来:
0.375 10 = x 1 ⋅ 2 − 1 + x 2 ⋅ 2 − 2 + … + x n ⋅ 2 n 0.375_{10} = x_1 \cdot 2^{-1} + x_2 \cdot 2^{-2} + \ldots + x_n \cdot 2^n 0.37 5 10 = x 1 ⋅ 2 − 1 + x 2 ⋅ 2 − 2 + … + x n ⋅ 2 n
和整数一样,我们的任务是通过逐个分离出 x 来求出所有的 x 。看看该怎么做。首先要注意的是:2 的负次幂给出的是分母为 2 的正次幂的分数。把上面的式子重写一下:
0.375 10 = x 1 ⋅ 1 2 + x 2 ⋅ 1 2 2 + … + x n ⋅ 1 2 n 0.375_{10} = x_1 \cdot \frac{1}{2} + x_2 \cdot \frac{1}{2^2} + \ldots + x_n \cdot \frac{1}{2^n} 0.37 5 10 = x 1 ⋅ 2 1 + x 2 ⋅ 2 2 1 + … + x n ⋅ 2 n 1
一眼就能看出,式子右边可以直接把 1/2 提出来。来做一下:
0.375 10 = 1 2 ⋅ ( x 1 + x 2 ⋅ 1 2 + … + x n ⋅ 1 2 n − 1 ) 0.375_{10} = \frac{1}{2} \cdot (x_1 + x_2 \cdot \frac{1}{2} + \ldots + x_n \cdot \frac{1}{2^{n-1}}) 0.37 5 10 = 2 1 ⋅ ( x 1 + x 2 ⋅ 2 1 + … + x n ⋅ 2 n − 1 1 )
然后把 1/2 移到左边
0.375 ⋅ 2 = x 1 + x 2 ⋅ 1 2 + … + x n ⋅ 1 2 n − 1 0.375 \cdot 2 = x_1 + x_2 \cdot \frac{1}{2} + \ldots + x_n \cdot \frac{1}{2^{n-1}} 0.375 ⋅ 2 = x 1 + x 2 ⋅ 2 1 + … + x n ⋅ 2 n − 1 1
好,这样我们就把 x1 分离出来了,而且知道它只能是 1 或 0 。要确定它是哪一位,先看看剩下的那些加数:
x 2 ⋅ 1 2 + … + x n ⋅ 1 2 n − 1 x_2 \cdot \frac{1}{2} + \ldots + x_n \cdot \frac{1}{2^{n-1}} x 2 ⋅ 2 1 + … + x n ⋅ 2 n − 1 1
想一想这些数之和最大能有多大。如果 x 各位的最大值是 1,那就可以直接把 x 全换成 1,把这个和写成:
1 2 + 1 2 2 + 1 2 3 + 1 2 4 + … + 1 2 n \frac{1}{2} + \frac{1}{2^2} + \frac{1}{2^3} + \frac{1}{2^4} + \ldots + \frac{1}{2^n} 2 1 + 2 2 1 + 2 3 1 + 2 4 1 + … + 2 n 1
这是一个分数构成的等比数列,这类数列的和落在 [0 < 和 < 1] 的范围内,所以这个和最大能给出的值就是 1。现在再看我们的式子:
0.375 ⋅ 2 = x 1 + x 2 ⋅ 1 2 + … + x n ⋅ 1 2 n − 1 ⏟ 最多为 1 0.375 \cdot 2 = x_1 + \underbrace{x_2 \cdot \frac{1}{2} + \ldots + x_n \cdot \frac{1}{2^{n-1}}}_{\text{最多为 } 1} 0.375 ⋅ 2 = x 1 + 最多为 1 x 2 ⋅ 2 1 + … + x n ⋅ 2 n − 1 1
现在应该清楚了:如果右边小于 1,那么 x1 就不可能等于 1 ,因此它等于 0 ,而剩下的部分等于 0.75 。
0.375 ⋅ 2 = 0 + x 2 ⋅ 1 2 + … + x n ⋅ 1 2 n − 1 ⏟ 0.75 0.375 \cdot 2 = 0 + \underbrace{x_2 \cdot \frac{1}{2} + \ldots + x_n \cdot \frac{1}{2^{n-1}}}_{0.75} 0.375 ⋅ 2 = 0 + 0.75 x 2 ⋅ 2 1 + … + x n ⋅ 2 n − 1 1
这与开头给出的算法第一步完全一致:
0.375 ⋅ 2 = 0 + 0.75 0.375 \cdot 2 = 0 + 0.75 0.375 ⋅ 2 = 0 + 0.75
取出 0.75 的小数部分,再提出一个 1/2 ,把 x2 分离出来:
0.75 = 1 2 ⋅ ( x 2 + … + x n ⋅ 1 2 n − 2 ) 0.75 = \frac{1}{2} \cdot (x_2 + \ldots + x_n \cdot \frac{1}{2^{n-2}}) 0.75 = 2 1 ⋅ ( x 2 + … + x n ⋅ 2 n − 2 1 )
并把 1/2 移到左边:
0.75 ⋅ 2 ⏟ 1.5 = x 2 + x 3 ⋅ 1 2 + … + x n ⋅ 1 2 n − 2 \underbrace{0.75 \cdot 2}_{1.5} = x_2 + x_3 \cdot \frac{1}{2} + \ldots + x_n \cdot \frac{1}{2^{n-2}} 1.5 0.75 ⋅ 2 = x 2 + x 3 ⋅ 2 1 + … + x n ⋅ 2 n − 2 1
此时,如果 x2 等于 0 ,那么式子右边之和不可能大于 1 ,但左边是 1.5 ,所以 x2 必须是 1 ,剩下的部分是 0.5 。写出来:
0.75 ⋅ 2 ⏟ 1.5 = 1 + x 3 ⋅ 1 2 + … + x n ⋅ 1 2 n − 2 ⏟ 0.5 \underbrace{0.75 \cdot 2}_{1.5} = 1 + \underbrace{x_3 \cdot \frac{1}{2} + \ldots + x_n \cdot \frac{1}{2^{n-2}}}_{0.5} 1.5 0.75 ⋅ 2 = 1 + 0.5 x 3 ⋅ 2 1 + … + x n ⋅ 2 n − 2 1
这同样符合开头给出的算法的规律:
0.75 ⋅ 2 = 1 + 0.5 0.75 \cdot 2 = 1 + 0.5 0.75 ⋅ 2 = 1 + 0.5
对剩下的小数部分 0.5 重复同样的操作。
0.5 ⋅ 2 ⏟ 1 = x 3 + … + x n ⋅ 1 2 n − 3 \underbrace{0.5 \cdot 2}_{1} = x_3 + \ldots + x_n \cdot \frac{1}{2^{n-3}} 1 0.5 ⋅ 2 = x 3 + … + x n ⋅ 2 n − 3 1
用上面同样的逻辑可以看出 x3 等于 1 ,并且没有剩余的小数部分:
0.5 ⋅ 2 ⏟ 1 = 1 + … + x n ⋅ 1 2 n − 3 ⏟ 0 \underbrace{0.5 \cdot 2}_{1} = 1 + \underbrace{\ldots + x_n \cdot \frac{1}{2^{n-3}}}_{0} 1 0.5 ⋅ 2 = 1 + 0 … + x n ⋅ 2 n − 3 1
既然剩余的小数部分等于 0,我们最后一步就是这样:
0.5 ⋅ 2 = 1 + 0 0.5 \cdot 2 = 1 + 0 0.5 ⋅ 2 = 1 + 0
把所有步骤再写一遍:
0.375 ⋅ 2 = 0 + 0.75 0.75 ⋅ 2 = 1 + 0.5 0.5 ⋅ 2 = 1 + 0 \begin{aligned}
0.375 \cdot 2 &= 0 + 0.75 \\
0.75 \cdot 2 &= 1 + 0.5 \\
0.5 \cdot 2 &= 1 + 0
\end{aligned} 0.375 ⋅ 2 0.75 ⋅ 2 0.5 ⋅ 2 = 0 + 0.75 = 1 + 0.5 = 1 + 0
这正是我在开头给出的算法。和处理整数时一样,这三步的计算也可以合并成一个表达式:
0.375 = 1 2 ⋅ ( 0 + 1 2 ⋅ ( 1 + 1 2 ⋅ ( 1 + 0 ) ) ) 0.375 = \frac{1}{2} \cdot (0 + \frac{1}{2} \cdot (1 + \frac{1}{2} \cdot (1 + 0))) 0.375 = 2 1 ⋅ ( 0 + 2 1 ⋅ ( 1 + 2 1 ⋅ ( 1 + 0 )))
同样,请务必完全掌握这个表达式——在探讨二进制转十进制时我们会用到它。
为什么并非所有小数都能在二进制中有限表示
有些在十进制中能有限表示的小数,在二进制中却无法有限表示——这一点让很多开发者感到意外。但恰恰是这个困惑,是 0.1 加 0.2 会得出看似古怪结果的根源。那么,是什么决定了一个小数能否在某个进制中被有限表示呢?要让一个数能有限表示,分数的分母必须是该进制基数的幂。例如在十进制中,分母必须是 10 的幂——这就是为什么 0.625 在十进制中可以有限表示:
625 10 3 = 0.625 \frac{625}{10^3} = 0.625 1 0 3 625 = 0.625
而 1/3 无法有限表示:
1 3 = 0.3333 … = 0. 3 ‾ \frac{1}{3} = 0.3333\ldots = 0.\overline{3} 3 1 = 0.3333 … = 0. 3
二进制也是同理:
625 1000 = 5 8 = 5 2 3 \frac{625}{1000} = \frac{5}{8} = \frac{5}{2^3} 1000 625 = 8 5 = 2 3 5
但如果看 0.1,它的分母是 10,而 10 不是 2 的幂,所以 0.1 在二进制中会是一个无限小数。用上面学到的算法验证一下:
0.1 ⋅ 2 = 0 + 0.2 0.2 ⋅ 2 = 0 + 0.4 0.4 ⋅ 2 = 0 + 0.8 0.8 ⋅ 2 = 1 + 0.6 0.6 ⋅ 2 = 1 + 0.2 0.2 ⋅ 2 = 0 + 0.4 0.4 ⋅ 2 = 0 + 0.8 … \begin{aligned}
0.1 \cdot 2 &= 0 + 0.2 \\
0.2 \cdot 2 &= 0 + 0.4 \\
0.4 \cdot 2 &= 0 + 0.8 \\
0.8 \cdot 2 &= 1 + 0.6 \\
0.6 \cdot 2 &= 1 + 0.2 \\
0.2 \cdot 2 &= 0 + 0.4 \\
0.4 \cdot 2 &= 0 + 0.8 \\
&\ldots
\end{aligned} 0.1 ⋅ 2 0.2 ⋅ 2 0.4 ⋅ 2 0.8 ⋅ 2 0.6 ⋅ 2 0.2 ⋅ 2 0.4 ⋅ 2 = 0 + 0.2 = 0 + 0.4 = 0 + 0.8 = 1 + 0.6 = 1 + 0.2 = 0 + 0.4 = 0 + 0.8 …
这样可以无限进行下去,不过我们把它写成循环小数:
0.1 10 = 0.00011001100110011 … 2 = 0.0 0011 ‾ 0.1_{10} = 0.00011001100110011\ldots_2 = 0.0\overline{0011} 0. 1 10 = 0.00011001100110011 … 2 = 0.0 0011
把二进制整数转成十进制
我用第一节中同一个二进制整数 1011 ,来说明那个「乘以 2」的算法为什么成立。这里同样要用到数的 q 进制展开形式 。先把它写成这种形式:
1011 2 = 1 ⋅ 2 3 + 0 ⋅ 2 2 + 1 ⋅ 2 1 + 1 ⋅ 2 0 1011_2 = 1 \cdot 2^3 + 0 \cdot 2^2 + 1 \cdot 2^1 + 1 \cdot 2^0 101 1 2 = 1 ⋅ 2 3 + 0 ⋅ 2 2 + 1 ⋅ 2 1 + 1 ⋅ 2 0
既然所有加数都是 2 的倍数,我们就可以不断把 2 提出来,直到商为零。来做一下:
1011 2 = 2 ⋅ ( 2 ⋅ ( 2 ⋅ ( 2 ⋅ 0 + 1 ) + 0 ) + 1 ) + 1 1011_2 = 2 \cdot (2 \cdot (2 \cdot (2 \cdot 0 + 1) + 0) + 1) + 1 101 1 2 = 2 ⋅ ( 2 ⋅ ( 2 ⋅ ( 2 ⋅ 0 + 1 ) + 0 ) + 1 ) + 1
现在,只要按照数学运算的顺序算下去,你得到的就正是我在开头展示的那些步骤,具体来说:
11 10 = 2 ⋅ ( 2 ⋅ ( 2 ⋅ ( 2 ⋅ 0 + 1 ⏟ 1 ) + 0 ⏞ 2 ) + 1 ⏟ 5 ) + 1 11_{10} = 2 \cdot (\underbrace{2 \cdot (\overbrace{2 \cdot (\underbrace{2 \cdot 0 + 1}_{1}) + 0}^{2}) + 1}_{5}) + 1 1 1 10 = 2 ⋅ ( 5 2 ⋅ ( 2 ⋅ ( 1 2 ⋅ 0 + 1 ) + 0 2 ) + 1 ) + 1
2 ⋅ 0 + 1 = 1 2 ⋅ 1 + 0 = 2 2 ⋅ 2 + 1 = 5 2 ⋅ 5 + 1 = 11 \begin{aligned}
2 \cdot 0 + 1 &= 1 \\
2 \cdot 1 + 0 &= 2 \\
2 \cdot 2 + 1 &= 5 \\
2 \cdot 5 + 1 &= 11
\end{aligned} 2 ⋅ 0 + 1 2 ⋅ 1 + 0 2 ⋅ 2 + 1 2 ⋅ 5 + 1 = 1 = 2 = 5 = 11
这样,二进制的 1011 就是十进制的 11 。
把二进制小数转成十进制
现在我们来到了最后一个算法。也许你已经自己想明白它的机理了。如果还没有,我们就看看它为什么成立。数的 q 进制展开形式 在这里同样是关键。我们取第一节中的数 0.1011 ,把它写成展开形式:
0.1011 2 = 1 ⋅ 1 2 + 0 ⋅ 1 2 2 + 1 ⋅ 1 2 3 + 1 ⋅ 1 2 4 0.1011_2 = 1 \cdot \frac{1}{2} + 0 \cdot \frac{1}{2^2} + 1 \cdot \frac{1}{2^3} + 1 \cdot \frac{1}{2^4} 0.101 1 2 = 1 ⋅ 2 1 + 0 ⋅ 2 2 1 + 1 ⋅ 2 3 1 + 1 ⋅ 2 4 1
同样,既然所有加数都是 1/2 的倍数,我们就可以不断把 1/2 提出来,直到没有剩余的小数部分。来做一下:
0.1011 2 = 1 2 ⋅ ( 1 + 1 2 ⋅ ( 0 + 1 2 ⋅ ( 1 + 1 2 ⋅ ( 1 + 0 ) ) ) ) 0.1011_2 = \frac{1}{2} \cdot (1 + \frac{1}{2} \cdot (0 + \frac{1}{2} \cdot (1 + \frac{1}{2} \cdot (1 + 0)))) 0.101 1 2 = 2 1 ⋅ ( 1 + 2 1 ⋅ ( 0 + 2 1 ⋅ ( 1 + 2 1 ⋅ ( 1 + 0 ))))
按照数学运算顺序算下去,就得出开头所述的算法:
0.6875 10 = 1 2 ⋅ ( 1 + 1 2 ⋅ ( 0 + 1 2 ⋅ ( 1 + 1 2 ⋅ ( 1 + 0 ) ⏟ 0.5 ) ) ⏞ 0.75 ) ⏟ 0.375 0.6875_{10} = \underbrace{\frac{1}{2} \cdot (1 + \overbrace{\frac{1}{2} \cdot (0 + \frac{1}{2} \cdot (1 + \underbrace{\frac{1}{2} \cdot (1 + 0)}_{0.5}))}^{0.75})}_{0.375} 0.687 5 10 = 0.375 2 1 ⋅ ( 1 + 2 1 ⋅ ( 0 + 2 1 ⋅ ( 1 + 0.5 2 1 ⋅ ( 1 + 0 ) )) 0.75 )
1 2 ⋅ ( 1 + 0 ) = 0.5 1 2 ⋅ ( 1 + 0.5 ) = 0.75 1 2 ⋅ ( 0 + 0.75 ) = 0.375 1 2 ⋅ ( 1 + 0.375 ) = 0.6875 \begin{aligned}
\frac{1}{2} \cdot (1 + 0) &= 0.5 \\
\frac{1}{2} \cdot (1 + 0.5) &= 0.75 \\
\frac{1}{2} \cdot (0 + 0.75) &= 0.375 \\
\frac{1}{2} \cdot (1 + 0.375) &= 0.6875
\end{aligned} 2 1 ⋅ ( 1 + 0 ) 2 1 ⋅ ( 1 + 0.5 ) 2 1 ⋅ ( 0 + 0.75 ) 2 1 ⋅ ( 1 + 0.375 ) = 0.5 = 0.75 = 0.375 = 0.6875
这样,二进制的 0.1011 就是十进制的 0.6875 。
Subscribe for future articles: