C言語基礎|基数変換

「2進数」「8進数」「16進数」は、コンピュータの世界でしょっちゅう出てきます。
でも人間はふだん 10進数で考えるので、基数変換(別の進数へ変換すること)ができると一気に理解がラクになります。

ここでは、

  • 2進数・8進数・16進数 → 10進数(重みで足し算)
  • 10進数 → 2進数・8進数・16進数(割り算と剰余)

という「2つの王道パターン」を、表や図でしっかり整理していきますね。

基数変換で押さえる2つの基本ルール

基数変換の基本戦略

変換方向基本アイデアキーワード
n進数 → 10進数各桁に重み(nのべき乗)をかけて足す重み、べき乗
10進数 → n進数nで割って剰余を集め、逆順に並べる商と剰余、逆順

この表は「どっち向きの変換をしているか」で、手順がガラッと変わるのがポイントです。

n進数 → 10進数の変換(重みで足し算)

10進数の各桁が 10⁰, 10¹, 10²… の重みを持つのと同じで、
n進数なら n⁰, n¹, n²… の重みを持ちます。

図:重みの考え方(例:n進数)

桁:     上位 ……………………………… 下位
重み:            n3   n2   n1   n0

例1:2進数 101 を 10進数へ

  • 101 = 1×2² + 0×2¹ + 1×2⁰
  • = 1×4 + 0×2 + 1×1
  • = 5

例2:8進数 123 を 10進数へ

  • 123 = 1×8² + 2×8¹ + 3×8⁰
  • = 1×64 + 2×8 + 3×1
  • = 83

例3:16進数 1FD を 10進数へ

16進数は A〜F が登場します。ここが最初の山場!

16進の文字と値

文字
A10
B11
C12
D13
E14
F15
  • 1FD = 1×16² + 15×16¹ + 13×16⁰
  • = 1×256 + 15×16 + 13×1
  • = 256 + 240 + 13
  • = 509

10進数 → n進数の変換(割り算と剰余)

逆向きの変換は「割り算」です。
nで割った剰余が最下位桁になります。商をさらに割って…を繰り返し、剰余を逆順に並べます。

図:剰余が下の桁になるイメージ

10進数を n で割る
  剰余 → いちばん下の桁
  商   → 残りをさらに変換する対象
最後に剰余を逆順に並べる

例:10進数 45 を 2進数へ

45 を 2 で割り続けます。

割り算と剰余の記録(45 → 2進数)

手順剰余
45 ÷ 2221
22 ÷ 2110
11 ÷ 251
5 ÷ 221
2 ÷ 210
1 ÷ 201

剰余を下から逆に読む → 101101
つまり、45(10) = 101101(2)

変換手順をC言語で確かめよう

ここでは「入力した10進数を、指定した基数(2〜16)に変換して表示する」プログラムにします。

プロジェクト名:chap7-3-1 ソースファイル名:chap7-3-1.c

Visual Studio でこのプログラムを実行するには、SDLチェック設定を変更しておく必要があります。
1.プロジェクト名を右クリックして、「プロパティ」をクリックします。
2.「C/C++」→「全般」→「SDLチェック」を「いいえ」に切り替えて「OK」をクリックします。

// 10進数を指定した基数(2〜16)に変換して表示する

#include <stdio.h>

void print_base(unsigned int value, unsigned int base)
{
    char buf[33];                 // 2進数でも32ビット分 + 終端
    const char digits[] = "0123456789ABCDEF";
    int idx = 0;

    if (base < 2 || base > 16) {
        puts("基数は2〜16で指定してください。");
        return;
    }

    // value が 0 のときは特別扱い(ループが1回も回らないため)
    if (value == 0) {
        puts("変換結果: 0");
        return;
    }

    // 剰余を順に buf に詰める(下位桁から溜まる)
    while (value > 0) {
        unsigned int r = value % base;
        buf[idx++] = digits[r];
        value /= base;
    }

    // buf は逆順に入っているので、後ろから表示する
    printf("変換結果: ");
    for (int i = idx - 1; i >= 0; i--) {
        putchar(buf[i]);
    }
    putchar('\n');
}

int main(void)
{
    unsigned int n, base;

    puts("10進数を好きな基数に変換してみましょう。");
    printf("10進数(0以上): ");
    scanf("%u", &n);

    printf("基数(2〜16): ");
    scanf("%u", &base);

    print_base(n, base);

    return 0;
}

このプログラムに出てくる命令の書式と役割

printf

  • 書式:printf(書式文字列, 値, ...);
  • 役割:書式付きで表示する(案内や結果表示)

puts

  • 書式:puts(文字列);
  • 役割:文字列を表示して改行する(説明文に便利)

scanf

  • 書式:scanf(書式文字列, 変数のアドレス);
  • 役割:キーボード入力を読み取る
    • %u は unsigned int 用
    • &n のように変数のアドレスを渡す

putchar

  • 書式:putchar(文字);
  • 役割:文字を1文字だけ出力する(変換結果を1桁ずつ出すのに便利)

% と /

  • value % base
    役割:剰余(その基数の最下位桁)
  • value /= base
    役割:商に更新(右に1桁ずらすイメージ)

表と図でプログラムの動きを説明する(何が起きてる?)

whileループでやっていること

処理目的例(value=45, base=2)
r = value % base最下位桁を取り出す45 % 2 → 1
buf に r を保存下位桁から溜めるbuf に 1
value /= base次の桁へ進む45 / 2 → 22

buf に入る順番(逆順になる理由)

剰余(下位桁)から順に保存される
例:45 を 2進数へ

buf: [1][0][1][1][0][1]  ← この順で入る
表示:  101101            ← 逆から読む

この「逆順になる」は基数変換の鉄板ポイントなので、覚えておくと強いです。