このページではC言語での「整数の桁数の求め方」について説明していきます。
C言語では整数の桁数を求めることを目的とする標準ライブラリ関数は存在しません。ですが、別の目的で用意されている標準ライブラリ関数を利用したり四則演算を利用したりすることで整数の桁数を簡単に求めることが出来ます。
この「整数の桁数の求め方」として下記の3つの方法を紹介していきます。
10
で割れる回数をカウントして桁数を求めるlog10
関数の結果を利用して桁数を求める- 文字列長から桁数を求める
これらの方法で整数の桁数が求まる仕組みについてもしっかり理解していただけるよう分かりやすく説明していきますので、是非最後までページを読んでみていただければと思います!
10
で割れる回数をカウントして桁数を求める
整数の桁数を求める方法の1つ目は「整数を 10
で割れる回数をカウントして桁数を求める」になります。要は、特定の整数が 0
になるまで 10
で割り続けます。そして、0
になるまでに割った回数が、その整数の桁数となります。
整数同士の割り算の場合、一桁の整数は必ず1回 10
で割れば 0
になります。さらに、N
桁の整数は 1
回 10
で割れば桁数が N - 1
となりますので、10
での割り算を N - 1
回繰り返せば必ず1桁の整数になり、それに対して再度 10
で割れば 0
になります。
したがって、この「整数に対して “その整数の桁数” 回分の 10
での割り算を行えば必ず結果は 0
になる」という性質を利用すれば、整数の値に関わらず、割り算結果が 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_x
が 10
分の 1
の値になり、さらに桁数を管理する num_digit
の値が 1
増えることになります。
abs_x = abs_x / 10;
num_digit++;
これが繰り返されるたびに abs_x
の値は 10
分の 1
の値 となり、いずれは abs_x
が 0
となります。そして、その時の num_digit
の値 が “引数で指定された整数の桁数” ということになります。
do while
ループを while
ループに変更すれば、引数で指定された整数が 0
の場合にループ内の処理が実行されなくなるので、関数の返却値は 0
となります。したがって、0
の桁数を 0
としたい場合は while
ループの方が適切であるということになります。ここは 0
の桁数の扱いに応じて使い分けていただければ良いと思います。
while (abs_x > 0) {
abs_x = abs_x / 10;
num_digit++;
}
この方法のポイントは下記を利用するという点になると思います。
整数 / 整数
の結果は「整数」となる(小数点以下の値は切り捨てられる)ループの中で abs_x = abs_x / 10
を実行していますが、abs_x
は unsigned int
型で整数を扱う変数であり、さらに 10
も整数なので、整数 / 整数
の計算が行われることになります。したがって、割り算の結果の小数点以下の値が切り捨てられることになるため、上記のループ内の処理を繰り返すことで桁数が正しく求められるようになっています。
たとえば abs_x
が 789
の場合、下記のように各変数の値が変化していくことになり、789
の桁数である 3
が正しく求められることになります。
- ループ1回目
abs_x
:78
num_digit
:1
- ループ2回目
abs_x
:7
num_digit
:2
- ループ3回目
abs_x
:0
num_digit
:3
ですが、もし abs_x
が float
や double
型の場合は割り算が 整数 / 整数
ではなくなるため、割り算結果に小数点以下の値が残ってしまうことになります。その結果、abs_x
がループの3回目では 0
にならず、余計にループが実行されて算出される桁数(num_digit
)も多くなってしまいます。私が実際に試したら求まる桁数が 329
になってしまいました…。
- ループ1回目
abs_x
:78.9
num_digit
:1
- ループ2回目
abs_x
:7.89
num_digit
:2
- ループ3回目
abs_x
:0.789
num_digit
:3
- ループ4回目
abs_x
:0.0789
num_digit
:4
- ・・・・
こんな感じで、この方法は 整数 / 整数
の結果が整数となることを利用したものとなります。整数 / 整数
の結果が意図せず 0
になってしまって理由が分からず苦労された方も多いのではないかと思いますが、このように桁数を求める際には、この 整数 / 整数
の性質が活躍することになります。
log10
関数の結果を利用して桁数を求める
次はもうちょっとスマートな桁数の求め方になります。
この方法では標準ライブラリ関数 log10
を利用して整数の桁数を求めます。log10
関数は math.h
で定義されており、リンク時に -lm
オプションが必要になる場合があるので注意してください。
log10
関数は引数で指定された値の常用対数を求める関数になります。数学等で log を習った方も多いと思います。log が苦手だった人もおられるかもしれませんし、常用対数と聞いても意味不明と感じる方もおられると思います。ただ、整数の桁数を求めることが目的であれば、log10
は引数で指定された値が “10
の何乗となるか” を計算する関数であると単純に考えてよいです。
そして、log10
関数を利用すれば、特定の正の整数 x
(0
は含まない) の桁数は下記の3つの手順で求めることが出来ます。単純ですね!
log10(x)
を求める- 1. の結果の小数点以下を切り捨てる
- 2. の結果に
+1
する
例えば log10(100)
を実行した場合、100
は 10
の 2
乗なので返却値は 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
関数のこの性質は、引数で指定する値に関わらず当てはまります(0
以下の値を除いて)。なので、log10
が引数で指定した値が 10
の何乗であるかを求める関数であること、および log10
が単調増加であることを考えれば、整数の値に関わらず前述の下記の手順で整数の桁数を求めることが出来ることになります。
log10(x)
を求める- 1. の結果の小数点以下を切り捨てる
- 2. の結果に
+1
する
そして、この手順を関数化したものが下記となります。10 で割れる回数をカウントして桁数を求める で示した get_num_digit
関数を下記に置き換えてやれば、プログラム全体のソースコードとしても成立してコンパイルも通るようになります。環境によっては math
ライブラリを明示的にリンクする必要があり、この場合はコンパイル時のオプションに -lm
を追加する必要があるので注してください。
#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
関数によって実現できます。この詳細は下記ページで解説していますので、詳しくは下記ページを参照していただければと思います。
要は、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/