C言語基礎|10進数の基数変換

10進数は人間にとって自然だけど、コンピュータの世界では 2進数・8進数・16進数が日常茶飯事です。
そこで大事になるのが、10進数を別の基数に変換する手順。やり方は意外とシンプルで、

  • 目的の基数で割る。
  • 剰余を集める。
  • 逆順に並べる。

この3点セットで完成します。
ここでは 2進数・8進数・16進数への変換を、表と図で「同じ仕組み」として整理していきますね。

10進数 → n進数は割り算が主役

10進数を n進数に変換するときの核はこれです。

10進数 → n進数の基本手順

手順すること意味
110進数を n で割るn進数の右に1桁ずらすイメージ
2剰余を記録する剰余が最下位桁になる
3商を次の入力にして繰り返す商が 0 になるまで続ける
4剰余を逆順に並べる上位桁からの表現が完成

この表のポイントは「剰余が下の桁」になること。だから逆順にする必要があるんです。

10進数 57 を 2進数に変換する

2で割り続けて剰余を集め、最後に逆順です。

57 → 2進数(割り算と剰余)

割る前割る剰余
572281
282140
14270
7231
3211
1201

剰余を上から書くと 1 0 0 1 1 1
逆順に読むので 111001(これが 57 の 2進表現)になります。

10進数 57 を 8進数に変換する

やることは同じで、割る数が 8 になるだけです。

57 → 8進数

割る前割る剰余
57871
7807

剰余を逆順に並べて 71(8進数)です。

10進数 57 を 16進数に変換する

16で割ると、剰余が 10〜15 になることがあるので、そこだけ注意。

57 → 16進数

割る前割る剰余
571639
31603

逆順で 39(16進数)です。

16進数の剰余が文字になる対応

101112131415
ABCDEF

この表は「剰余が10以上になったら文字になる」を確認するためのものです。

2進数と16進数/8進数はまとめて変換できる

ここ、気持ちいいところです。
2進数は桁数が長くなりがちなので、読みやすくするために 16進数や 8進数に「まとめて変換」することが多いです。

  • 2進数4桁 ↔ 16進数1桁
  • 2進数3桁 ↔ 8進数1桁

2進数4桁と16進数1桁の対応(抜粋)

2進数16進数2進数16進数
0000010008
0001110019
001021010A
001131011B
010041100C
010151101D
011061110E
011171111F

4桁ごとに区切るだけ(2進 → 16進)

0111 1010 0101 1100
  7    A    5    C

この図の意味は「計算しなくても置き換えるだけで済む」ということ。現場でも超よく使います。

C言語で基数変換を試す

「10進数を入力すると、2進・8進・16進をまとめて表示する」プログラムです。

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

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

// 10進数を 2進数 / 8進数 / 16進数 に変換して表示する

#include <stdio.h>

void print_in_base(unsigned int value, unsigned int base)
{
    char buf[33];
    const char digits[] = "0123456789ABCDEF";
    int idx = 0;

    if (value == 0) {
        putchar('0');
        return;
    }

    while (value > 0) {
        unsigned int r = value % base;
        buf[idx++] = digits[r];
        value /= base;
    }

    for (int i = idx - 1; i >= 0; i--) {
        putchar(buf[i]);
    }
}

int main(void)
{
    unsigned int n;

    puts("10進数を入力すると、2進・8進・16進に変換して表示します。");
    printf("入力してください(0以上): ");
    scanf("%u", &n);

    printf("2進数  : ");
    print_in_base(n, 2);
    putchar('\n');

    printf("8進数  : ");
    print_in_base(n, 8);
    putchar('\n');

    printf("16進数 : ");
    print_in_base(n, 16);
    putchar('\n');

    return 0;
}

登場する命令の書式と「何をするか」

puts

  • 書式:puts(文字列);
  • 何をする:文字列を表示して改行する(案内文に便利)

printf

  • 書式:printf(書式文字列, 値, ...);
  • 何をする:書式付きで表示する(ラベルや整形表示)

scanf

  • 書式:scanf(書式文字列, 変数のアドレス);
  • 何をする:入力を読み取る
    ・%u は unsigned int 用
    ・&n のように「格納先」を渡す

putchar

  • 書式:putchar(文字);
  • 何をする:1文字だけ出力する(変換結果を1桁ずつ出すのに最適)

% と /

  • value % base:剰余を求める(最下位桁)
  • value /= base:商に更新する(次の桁へ)

表と図でプログラムの動きを説明する

print_in_base の中で起きていること

処理目的例(n=57, base=2)
r = value % base最下位桁を取り出す57 % 2 → 1
buf に r を入れる下位桁からためるbuf に 1
value /= base次の計算へ57 / 2 → 28
逆順で出力表示は上位桁から111001

バッファに逆順でたまる

剰余が出る順: 1, 0, 0, 1, 1, 1
bufの中      : [1][0][0][1][1][1]
表示         : 111001  (逆から出す)