スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2進数、8進数、10進数、16進数を互いに変換するC言語プログラムの例 (2)

前回の続き。10進数を2進数に変換するC言語プログラムの例。

10進数を2進数に変換

2で繰り返し割る方法

2で繰り返し割ることで、数値から2進数の各桁を算出します。最も基本的な方法です。

RDXの値を変更すれば、10進数だけでなく、8進数や16進数からの変換にも対応できます。

#include <stdio.h>
#include <stdlib.h>  /* strtoul */
#include <string.h>  /* strchr */

/* RDT: 入力する数字の基数 */
#define RDX 10

/* DGTS_MAX: 入力数字の上限桁数 */
/* FMT: 書式(printf関数で使用) */
#ifndef RDX
  #define RDX 10
#endif

#if RDX == 8
  #define DGTS_MAX 11
  #define FMT "%lo"
#elif RDX == 10
  #define DGTS_MAX 10
  #define FMT "%lu"
#elif RDX == 16
  #define DGTS_MAX 8
  #define FMT "%lX"
#else
  #undef RDX
  #define RDX 10
  #define DGTS_MAX 10
  #define FMT "%lu"
#endif

/* 2進数の上限桁数 */
#define BITS_MAX 32

int main(void)
{
  unsigned long in_num, rstr_num, num;  /* 数値 */
  int bits[BITS_MAX];    /* 2進数 */
  char buf[DGTS_MAX+1];  /* 入力バッファ */
  int i;
  char *p1, *p2;

  /* 数字の入力 */
  printf("%d進数で数字を入力してください: ", RDX);
  if(fgets(buf, sizeof(buf), stdin) == NULL)
    exit(1);
  p1 = strchr(buf, '\n');
  if(p1)
    *p1 = '\0';  /* 改行文字の除去 */
  else
    while(getchar() != '\n');  /* 入力ストリームのクリア */

  /* 文字列を数値へ変換 */
  in_num = strtoul(buf, &p2, RDX);
  if(p2 == buf || (*p2 != '\0' && *p2 != '\n')) 
    exit(1); 

  /* 数値から2進数の各桁を算出 */
  /* bits[0]が最下位桁、bits[BITS_MAX-1]が最上位桁 */
  num = in_num;
  for(i = 0; i < BITS_MAX; i++){
    bits[i] = num % 2;
    num = num / 2;
  }

  /* 元の数値の表示 */
  printf("%2d進数:", RDX);
  printf(FMT, in_num);
  printf("\n");

  /* 変換結果の表示 */
  printf("%2d進数 -> %2d進数: ", RDX, 2);
  i = BITS_MAX - 1;
  while(i > 0 && bits[i] == 0) i--;  /* 先行する0は表示しない */
  while(i > 0)
    printf("%d", bits[i--]);
  printf("%d\n", bits[0]);  /* 最下位桁は必ず表示 */

  /* 求めた2進数から元の進数を復元 */
  rstr_num = 0;
  for(i = BITS_MAX - 1; i >= 0; i--)
    rstr_num = rstr_num * 2 + bits[i];
  printf("%2d進数 -> %2d進数(復元): ", 2, RDX);
  printf(FMT, rstr_num);
  printf("\n");

  return 0;
}

ビット演算による方法

コンピュータの内部では、数値はビット列で表現されます。C言語では、数値に対してビットシフトなどのビット演算による操作が可能です。それを利用して、数値から2進数の各桁を算出します。

RDXの値を変更すれば、10進数だけでなく、8進数や16進数からの変換にも対応できます。

#include <stdio.h>
#include <stdlib.h>  /* strtoul */
#include <string.h>  /* strchr */

/* RDT: 入力する数字の基数 */
#define RDX 10

/* DGTS_MAX: 入力数字の上限桁数 */
/* FMT: 書式(printf関数で使用) */
#ifndef RDX
  #define RDX 10
#endif

#if RDX == 8
  #define DGTS_MAX 11
  #define FMT "%lo"
#elif RDX == 10
  #define DGTS_MAX 10
  #define FMT "%lu"
#elif RDX == 16
  #define DGTS_MAX 8
  #define FMT "%lX"
#else
  #undef RDX
  #define RDX 10
  #define DGTS_MAX 10
  #define FMT "%lu"
#endif

/* 2進数の上限桁数 */
#define BITS_MAX 32

int main(void)
{
  unsigned long in_num, rstr_num, num;  /* 数値 */
  char bits[BITS_MAX];   /* 2進数 */
  char buf[DGTS_MAX+1];  /* 入力バッファ */
  int i;
  char *b, *p1, *p2;

  /* 数字の入力 */
  printf("%d進数で数字を入力してください: ", RDX);
  if(fgets(buf, sizeof(buf), stdin) == NULL)
    exit(1);
  p1 = strchr(buf, '\n');
  if(p1)
    *p1 = '\0';  /* 改行文字の除去 */
  else
    while(getchar() != '\n');  /* 入力ストリームのクリア */

  /* 文字列を数値へ変換 */
  in_num = strtoul(buf, &p2, RDX);
  if(p2 == buf || (*p2 != '\0' && *p2 != '\n')) 
    exit(1); 

  /* 数値から2進数の各桁を算出 */
  /* bits[BITS_MAX-1]が最下位桁、bits[0]が最上位桁 */
  for(i = 0; i < BITS_MAX; i++)
    bits[BITS_MAX-1-i] = ((in_num >> i) & 1) + '0';

  /* 文字列の終端 */
  bits[BITS_MAX] = '\0';

  /* 元の数値の表示 */
  printf("%d進数:", RDX);
  printf(FMT, in_num);
  printf("\n");

  /* 変換結果の表示 */
  b = bits;
  while(*b == '0') {  /* 先行する0は非表示 */
    if(b == bits + BITS_MAX - 1)
      break;  /* 最下位桁は必ず表示 */
    b++;
  }
  printf("%2d進数 -> %2d進数: %s\n", RDX, 2, b);

  /* 求めた2進数から元の進数を復元 */
  rstr_num = 0;
  for(i = 0; i < BITS_MAX; i++)
    rstr_num = rstr_num * 2 + (bits[i] - '0');
  printf("%2d進数 -> %2d進数(復元): ", 2, RDX);
  printf(FMT, rstr_num);
  printf("\n");

  return 0;
}

負の数について

何進数であっても、基本的には、負の数は正の数にマイナスをつけて表します。よって、負の数を別のある進数に変換するときは、マイナスを除いた正数を変換し、最後にマイナスをつけます。

コンピュータで負の数を表す方法は、2の補数表現が一般的です。負の数x(<0)を32ビット(つまり、32桁の2進数)で表したときの2の補数は、2の32乗にxを足した数の2進数表示に一致します。例えば、-1の2の補数は、1を32個並べたものになりますが、これは4294967295の2進数表示に一致します。ただし、2の32乗は4294967296です。

参考リンク

JPCERT/CC:INT05-C. 可能性のあるすべての入力を処理できない入力関数を使って文字データを変換しない
JPCERT/CC:INT06-C. 文字列トークンを整数に変換するには strtol() 系の関数を使う
JPCERT/CC:FIO36-C. fgets() が改行文字を読み取ると仮定しない
株式会社きじねこ:C言語再入門 第6回 整数型の内部表現
株式会社きじねこ:[迷信] 2の累乗による割り算と右シフトは等価
株式会社きじねこ:[迷信] gets は単純に fgets に置き換えられる
株式会社きじねこ:[迷信] scanf ではバッファオーバーランを防げない
株式会社きじねこ:[迷信] fflush で入力バッファをクリア
Wikipedia:符号付数値表現

【theme : プログラミング
【genre : コンピュータ

プロフィール

よしいず

Author:よしいず
MATHEMATICS.PDFというウェブサイトを運営しています。

管理の都合上、トラックバックとコメントはオフにしてあります。ブログ経験者なら分かっていただけると思いますが、スパム(アダルトやその他の宣伝)ばかりなのが現実です。

リンクは自由です。当サイトの記事に対する間違いの指摘・意見・感想などを述べた記事からのリンクは歓迎です。ただし、ブログ記事アップ直後はミスが多く、頻繁に修正します。場合によっては削除する可能性もあります。その際、何も断りもなく修正・削除しますがご了承ください。内容を参考にする場合には投稿後一週間ほど様子を見てからにしてください(笑)。

記事の間違いを指摘するときは、その具体的箇所、理由(仕様に反するなど)・根拠(参考にした文献など)、代替案(同じ結果を得るための正しいやり方)も教えてください。そうしないと、(指摘される側および第三者はその時点では無知の状態なので、)どこが間違いなのか分かりませんし、本当に間違っているのかどうかが判断・検証できません。実際、間違いだと指摘されたことが結局は正しかったというケースもありますので。

このブログのタイトル一覧

リンク
月別アーカイブ
カテゴリ
最新記事
検索フォーム
RSSリンクの表示
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。