【C言語】整数の桁数を求める

C言語で整数の桁数を求める方法の解説ページアイキャッチ

このページにはプロモーションが含まれています

このページではC言語での「整数の桁数の求め方」について説明していきます。

C言語では整数の桁数を求めることを目的とする標準ライブラリ関数は存在しません。ですが、別の目的で用意されている標準ライブラリ関数を利用したり四則演算を利用したりすることで整数の桁数を簡単に求めることが出来ます。

この「整数の桁数の求め方」として下記の3つの方法を紹介していきます。

  • 10 で割れる回数をカウントして桁数を求める
  • log10 関数の結果を利用して桁数を求める
  • 文字列長から桁数を求める

これらの方法で整数の桁数が求まる仕組みについてもしっかり理解していただけるよう分かりやすく説明していきますので、是非最後までページを読んでみていただければと思います!

10 で割れる回数をカウントして桁数を求める

整数の桁数を求める方法の1つ目は「整数を 10 で割れる回数をカウントして桁数を求める」になります。要は、特定の整数が 0 になるまで 10 で割り続けます。そして、0 になるまでに割った回数が、その整数の桁数となります。

整数同士の割り算の場合、一桁の整数は必ず1回 10 で割れば 0 になります。さらに、N 桁の整数は 110 で割れば桁数が N - 1 となりますので、10 での割り算を N - 1 回繰り返せば必ず1桁の整数になり、それに対して再度 10 で割れば 0 になります。

したがって、この「整数に対して “その整数の桁数” 回分の 10 での割り算を行えば必ず結果は 0 になる」という性質を利用すれば、整数の値に関わらず、割り算結果が  0 になるまでに 10 で割った回数をカウントすることで桁数を求めることが可能です。

10での割り算を整数の桁数分繰り返せば結果が0になる様子を示す図

この考え方で整数の桁数を求めるプログラムのソースコード例は下記のようになります。

10で割れる回数をカウント
#include <stdio.h>
#include <stdlib.h>

unsigned int get_num_digit(int x) {
    unsigned int abs_x = abs(x);
    unsigned int num_digit = 0;

    do {
        abs_x = abs_x / 10;
        num_digit++;
    } while (abs_x > 0);

    return num_digit;
}
int main(void) {
    int x;
    unsigned int num_digit;
    printf("x = ");
    scanf("%d", &x);

    num_digit = get_num_digit(x);

    printf("number = %d : num_digit = %u\n", x, num_digit);
}

main 関数では scanf でユーザーから整数の入力を受け付け、その入力された整数を引数として get_num_digit 関数を実行しています。最後に入力された整数と、get_num_digit 関数の返却値を printf で出力しています。

そして、get_num_digit 関数が引数で指定された整数の桁数を求める関数となります。引数で指定された整数が 0 の場合は桁数を 1 として考えるようになっています。

get_num_digit 関数では最初に abs 関数で引数で指定された整数の絶対値を求め、それを abs_x に格納しています。絶対値に変換したところで桁数は変わりませんし、絶対値の場合は必ず正の値となって負の値の考慮が不要となるため、処理を簡単にするため絶対値を求めるようにしています。このあたりは今後紹介する関数でも共通となります。

そして、絶対値を求めた後は do while ループの中で下記の2つを実行しており、1度ループ内の処理が実行されるたびに abs_x10 分の 1 の値になり、さらに桁数を管理する num_digit の値が 1 増えることになります。

ループ内の処理
abs_x = abs_x / 10;
num_digit++;

これが繰り返されるたびに abs_x の値は 10 分の 1 の値 となり、いずれは abs_x0 となります。そして、その時の num_digitの値 が “引数で指定された整数の桁数” ということになります。

do while ループを while ループに変更すれば、引数で指定された整数が 0 の場合にループ内の処理が実行されなくなるので、関数の返却値は 0 となります。したがって、0 の桁数を 0 としたい場合は while ループの方が適切であるということになります。ここは 0 の桁数の扱いに応じて使い分けていただければ良いと思います。

whileへの置き換え
while (abs_x > 0) {
    abs_x = abs_x / 10;
    num_digit++;
}

この方法のポイントは下記を利用するという点になると思います。

整数同士の割り算
整数 / 整数の結果は「整数」となる(小数点以下の値は切り捨てられる)

ループの中で abs_x = abs_x / 10 を実行していますが、abs_xunsigned int 型で整数を扱う変数であり、さらに 10 も整数なので、整数 / 整数 の計算が行われることになります。したがって、割り算の結果の小数点以下の値が切り捨てられることになるため、上記のループ内の処理を繰り返すことで桁数が正しく求められるようになっています。

たとえば abs_x789 の場合、下記のように各変数の値が変化していくことになり、789 の桁数である 3 が正しく求められることになります。

  • ループ1回目
    • abs_x78
    • num_digit1
  • ループ2回目
    • abs_x7
    • num_digit2
  • ループ3回目
    • abs_x0
    • num_digit3

ですが、もし abs_xfloatdouble 型の場合は割り算が 整数 / 整数 ではなくなるため、割り算結果に小数点以下の値が残ってしまうことになります。その結果、abs_x がループの3回目では 0 にならず、余計にループが実行されて算出される桁数(num_digit)も多くなってしまいます。私が実際に試したら求まる桁数が 329 になってしまいました…。

  • ループ1回目
    • abs_x78.9
    • num_digit1
  • ループ2回目
    • abs_x7.89
    • num_digit2
  • ループ3回目
    • abs_x0.789
    • num_digit3
  • ループ4回目
    • abs_x0.0789
    • num_digit4
  • ・・・・

こんな感じで、この方法は 整数 / 整数 の結果が整数となることを利用したものとなります。整数 / 整数 の結果が意図せず 0 になってしまって理由が分からず苦労された方も多いのではないかと思いますが、このように桁数を求める際には、この 整数 / 整数 の性質が活躍することになります。

log10 関数の結果を利用して桁数を求める

次はもうちょっとスマートな桁数の求め方になります。

この方法では標準ライブラリ関数 log10 を利用して整数の桁数を求めます。log10 関数は math.h で定義されており、リンク時に -lm オプションが必要になる場合があるので注意してください。

log10 関数は引数で指定された値の常用対数を求める関数になります。数学等で log を習った方も多いと思います。log が苦手だった人もおられるかもしれませんし、常用対数と聞いても意味不明と感じる方もおられると思います。ただ、整数の桁数を求めることが目的であれば、log10 は引数で指定された値が “10 の何乗となるか” を計算する関数であると単純に考えてよいです。

そして、log10 関数を利用すれば、特定の正の整数 x (0 は含まない) の桁数は下記の3つの手順で求めることが出来ます。単純ですね!

  1. log10(x) を求める
  2. 1. の結果の小数点以下を切り捨てる
  3. 2. の結果に +1 する

例えば log10(100) を実行した場合、100102 乗なので返却値は 2 となります。これに +1 すれば 100 の桁数である 3 が求まりますね!

log10(1000) の場合は 3 が返却されるので、これに +1 すれば桁数 4 が求まります。

じゃあ 500 の場合はどうでしょう?

こういった 500 のように 10 の整数乗で表せない数であっても、log10 関数は結果を返却してくれます。具体的には、 2 よりも大きく 3 よりも小さい値が返却されることになりますので、返却値の小数点以下を切り捨てしてやれば log10(100) と同じ結果が得られます。500 は桁数としては 100 と同じですので、あとは上記手順に基づいて +1 してやれば正解となる桁数 3 が得られることになります。

この 500 のように 100 よりも大きく 1000 よりも小さな値を引数に指定した場合、必ず log10 関数の返却値は 2 よりも大きく 3 よりも小さな値となります。

これは、log10 関数が単調増加の関数であるためになります。要は、引数の数が大きくなれば必ず log10 の返却値も大きくなり、引数の数が小さくなれば必ず log10 の返却値も小さくなります。log10 関数が単調増加の関数であるため、例えば 100 よりも大きくて 1000 よりも小さな値を引数に指定したのに返却値が log10(100) よりも大きくなったり log10(1000) よりも小さくなったりすることはありません。

log10が単調増加関数であることを示す図

そして、log10 関数のこの性質は、引数で指定する値に関わらず当てはまります(0 以下の値を除いて)。なので、log10 が引数で指定した値が 10 の何乗であるかを求める関数であること、および log10 が単調増加であることを考えれば、整数の値に関わらず前述の下記の手順で整数の桁数を求めることが出来ることになります。

  1. log10(x) を求める
  2. 1. の結果の小数点以下を切り捨てる
  3. 2. の結果に +1 する

そして、この手順を関数化したものが下記となります。10 で割れる回数をカウントして桁数を求める で示した get_num_digit 関数を下記に置き換えてやれば、プログラム全体のソースコードとしても成立してコンパイルも通るようになります。環境によっては math ライブラリを明示的にリンクする必要があり、この場合はコンパイル時のオプションに -lm を追加する必要があるので注してください。

log10で桁数を求める
#include <math.h>

unsigned int get_num_digit(int x) {
    unsigned int abs_x = abs(x);
    unsigned int num_digit = 0;

    if (abs_x == 0) {
        num_digit = 1;
    } else {
        num_digit = log10(abs_x) + 1;
    }

    return num_digit;
}

スポンサーリンク

文字列長から桁数を求める

最後に、整数を文字列に変換し、文字列長から桁数を求める方法を紹介します。

冒頭でも説明したように、C言語には標準ライブラリ関数には整数の桁数を直接求めるようなものはありません。

ですが、文字列長を求める関数は存在します。具体的には、strlen 関数が文字列長を求める関数となります。

そして、正の整数を文字列に変換すれば、その文字列長はその整数の桁数と一致します。したがって、整数を文字列に変換し、その文字列を引数として strlen 関数で文字列長を求めてやれば、結果として整数の桁数も求めることが出来ることになります。

また、数値の文字列への変換は snprintf 関数によって実現できます。この詳細は下記ページで解説していますので、詳しくは下記ページを参照していただければと思います。

数値を文字列に変換する方法の解説ページアイキャッチ 【C言語】数値を文字列に変換する(snprintf関数の活用)

要は、snprintf 関数を利用して整数を文字列に変換し、その文字列の文字列長を strlen 関数で求めてやれば、整数の桁数を求めることができることになります。

そして、この手順を関数化したものが下記となります。10 で割れる回数をカウントして桁数を求める で示した get_num_digit 関数を下記に置き換えてやれば、プログラム全体のソースコードとしても成立してコンパイルも通るようになります。

文字列長を求めて桁数を求める
#include <string.h>

unsigned int get_num_digit(int x) {
    unsigned int abs_x = abs(x);
    unsigned int num_digit = 0;
    char buf[256];

    sprintf(buf, "%u", abs_x);
    num_digit = strlen(buf);

    return num_digit;
}

まとめ

このページでは、C言語で「整数の桁数を求める方法」について説明しました!

具体的には、「整数の桁数の求め方」として下記の3つの方法を紹介しました。

  • 10 で割れる回数をカウントして桁数を求める
  • log10 関数の結果を利用して桁数を求める
  • 文字列長から桁数を求める

一番オススメの方法は真ん中の「log10 関数の結果を利用して桁数を求める」になります。log に苦手意識を持っている方もおられるかもしれませんが、桁数を求めたい整数が 10 の何乗であるかを求めることによって桁数を求めることが可能で、これは log10 関数で一発で調べることが出来ます。

数学で学んだときは log なんて一生使わないと思った方も多いと思いますが、プログラミングをやっていると思わぬことろで活躍することもありますので、log の性質、具体的には「特定の数値が “底” の何乗であるか」を調べることが可能であるという性質を持っていることは覚えておきましょう!log10 の場合は底の値が 10 となりますが、log2 の場合は底の値が 2 となるため、2 の何乗であるかを調べることが可能となります。そして、これによって、特定の整数を2進数に変換したときの桁数を求めることが可能となります。

また、最初に説明した方法は考え方は単純ですが、ループを組んだりする必要があってバグが発生しやすいかなぁと思います。最後に説明した方法もスマートではあるのですが、文字列格納用のバッファを用意する必要がある点がちょっと面倒ですかね。

いずれにせよ、今回紹介した方法を利用すれば整数の桁数を求めることが可能となります。そして、今回紹介したような考え方はいろんな場面で役に立つと思いますので、是非このページの解説内容は頭の片隅にでも置いておいてください!

オススメの参考書(PR)

C言語学習中だけど分からないことが多くて挫折しそう...という方には、下記の「スッキリわかるC言語入門」がオススメです!

まず学習を進める上で、参考書は2冊持っておくことをオススメします。この理由は下記の2つです。

  • 参考書によって、解説の仕方は異なる
  • 読み手によって、理解しやすい解説の仕方は異なる

ある人の説明聞いても理解できなかったけど、他の人からちょっと違った観点での説明を聞いて「あー、そういうことね!」って簡単に理解できた経験をお持ちの方も多いのではないでしょうか?

それと同じで、1冊の参考書を読んで理解できない事も、他の参考書とは異なる内容の解説を読むことで理解できる可能性があります。

なので、参考書は2冊持っておいた方が学習時に挫折しにくいというのが私の考えです。

特に上記の「スッキリわかるC言語入門」は、他の参考書とは違った切り口での解説が豊富で、他の参考書で理解できなかった内容に対して違った観点での解説を読むことができ、オススメです。題名の通り「なぜそうなるのか?」がスッキリ理解できるような解説内容にもなっており、C言語入門書としてもかなり分かりやすい参考書だと思います。

もちろんネット等でも色んな観点からの解説を読むことが出来ますので、分からない点は別の人・別の参考書の解説を読んで解決していきましょう!もちろん私のサイトも参考にしていただけると嬉しいです!

入門用のオススメ参考書は下記ページでも紹介していますので、こちらも是非参考にしていただければと思います。

https://daeudaeu.com/c_reference_book/

同じカテゴリのページ一覧を表示