進位制

平常我們用的數字系統是十進位制的,而計算機使用的是二進位制,而時、分、秒這樣的時間單位,用的是六十進位制……,這裡是進位制的轉換和一些性質。

接下來我會用 這樣的方式來表示括號中的數字 是用 進位制表示的,例如 表示十進位的 表示十六進位的 ,也就是十進位的

基本轉換

一個十進位的數字 表示一個位數)可以表示為: 舉例來說,可以表示為:

相同的,非整數的十進位數字 可以表示為: 也就是

進位制的數字 表示的值就是:

舉例來說 轉換為十進位會是: 轉換為十進位會是:

浮點數精確性

在算十進位除法的時候,經常會出現循環小數,例如: 仔細看一下,,由此可知,有些在十進位會出現循環小數的數值,在其他進位制可能就不會有循環小數的狀況,反過來也一樣,在十進位看起來很簡單的值,在其他進位制可能會是很複雜的循環小數,更重要的是,二進位很容易發生這種狀況: 這會導致一個很討厭的結果,在儲存浮點數時,只能儲存有限的位數,因此有小數時很容易發生不精準的狀況,在整數與浮點數會介紹更多。

進位制的互相轉換

進位的每一位範圍是 ,這恰好跟二進位的 個 bit 能表示的範圍一樣,因此,在進行 進位的互相轉換時,可以先轉成二進位。

舉例來說, 要轉換為四進位,那麼我們可以先轉成二進位,以 3 個 bit 表示八進位下的一個位數:

1 4 7
001 100 111

接下來,用 2 個 bit 表示四進位下的一個位數:

01 10 01 11
1 2 1 3

我們得出

事實上, 進位的一個位數都可以用 進位的 個位數去表示,因為 進位的每一位數範圍都是 進位的 個位數能表示的範圍恰好也是這樣。

進位制的互轉

當一個數字乘以 時,它在 進位下的小數點會右移一位;而除以 時,它在 進位下的小數點會左移一位;對整數部分 時,可以得到它的個位數。

這是一種常見的的十進位轉二進位的方法:有一個數字 ,整數部分是 ,小數部分是 ,將 不斷除以 並向下取整,直到 為止,在這個過程中得到的餘數,倒過來寫就會是 的二進位,例如:

然後我們可以得到

可以這麼做的原因是,每次取 時,我們會得到它在二進位下的個位數,接著把 除以 後, 在二進位下會右移一位,重複操作就可以依序得到從個位數開始往左的每一位數。

接著是小數部分 ,將 不斷乘以 ,記錄它的整數部分後,把整數部分扣掉,再繼續重複這個動作,直到小數後沒東西為止,然後將記錄下來的整數部分依序寫下來就是 的二進位。例如:(箭頭後方是整數部分) 我們得到

可以這麼做是因為,每次乘以 時, 在二進位下的小數點後第一位會變成個位數,如此一來我們就可以依序得到小數點後第一位開始往右的位數。

至於 ,就把 加起來就好了,以上面的舉例來說,我們會得到

其實無論多少進位,都可以使用這種方式互相轉換,且這個方式無論在手動計算或寫程式計算都很方便,像這樣可以將 轉為 進位:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <string>
#include <algorithm>
using namespace std;
string convert(double n, int k){
int a = (int) n;
double b = n - a;
string ans;
while(a > 0){
ans += a % k + '0';
a /= k;
}
reverse(ans.begin(), ans.end());
if(b == 0) return ans;
ans += '.';
while(b > 0){
b *= k;
ans += (int)b + '0';
b -= (int)b;
}
return ans;
}

需要注意的是,有可能會遇到某進位制無法精確表示的狀況(見上方的浮點數精確性),此時在 while(b > 0) 這個迴圈就會卡住,例如 convert(0.375, 3) 就會發生這個狀況,可以加個位數限制之類的,至於手算時會比較容易注意到這個問題,就沒有太大影響。