数値型の変数で扱える値の桁数を求める - C++ プログラミング

PROGRAM


数値型の変数で扱える値の桁数を求める

確実に表現できる桁数を調べる

正確に表現可能な桁数を取得する

C++ では、int 型や long 型などの数値型の変数で扱える値の桁数を、<limits> ヘッダーに定義されている std::numeric_limits<> クラスを使って知ることができます。

たとえば unsigned long long 型の変数が何桁の値(10 進数)を扱うことができるかは、次のようにして確認できます。

int validDigits = std::numeric_limits<unsigned long long>::digits10;

こうすることで、unsigned long long が 64 ビットの値であれば、19 という値を得ることができます。

 

得られた桁数についての注意点

注意したいのが、64 ビットの unsigned long long 型は最大で 0 から 18,446,744,073,709,551,615 の 20 桁の値を取るところです。std::numeric_limits<unsigned long long>::digits10 関数で得られた値は 19 です。

最大の桁数と聞くと「この変数が表現できる最大値の桁数」を想像してしまいがちですが、std::numeric_limits<>::digits10 が返すのは「その変数が正確に扱うことができる最大の桁数」です。

 

今回の unsigned long long では、10 進数で 19 桁までの値なら(unsigned なので正の値に限りますが)どんな値でも格納することができますが、20 桁になるとたとえば 20,000,000,000,000,000,000 のように表現できない値が出てきてしまいます。

そのため、std::numeric_limits<unsigned long long>::digits10 では 19 という値が返されます。

 

std::numeric_limits<>::digits10 がやっていること

ちなみにこの関数でやっていることは単純で、原則的には次の計算をすることで、表現可能な桁数を得ています。

std::floor(std::numeric_limits<unsigned long long>::digits * std::log10(2))

このこ std::floor の中の式は「2 を digits 乗した値の log10 を取ると、それが 10 進数では何桁で表現されるか」を知る公式です。

結果は 19.2659 というような小数点数で得られますが、つまり 19 桁以上 20 桁未満という感じです。

そして今回は「その変数で確実に表現できる桁数」を計算したいので、小数点数を切り捨てて「19 桁」が答えになります。

 

そして、面白いのが Xcode 4.6.2 + iOS SDK 6.1 の場合だと、実際の計算では次の式が使われていました。

std::numeric_limits<unsigned long long>::digits * 3 / 10

本来は "3 / 10" のところが、"std::log10(2)" であるのが正しいのですけど、std::log10(2) が 0.301029995663981... という値なので、それに近い値で支障のない 0.3 が使用されている様子でした。

きっと、コンピュータの値の範囲が有限という特徴を生かして対数 (log) による計算をなくし、処理速度の向上が図られているのでしょう。

 

最大値の桁数を知る

ある変数が扱える最大値の桁数を取得する

ある数値型が最大何桁の 10 進数で表現されるかを知りたい場合があります。

たとえば、変数の値を文字列に変換するためのバッファーを確保しておきたい場合などがそうですけど、その場合は次のようにして、その桁数を取得することができそうです。

int maxDigits = std::numeric_limits<unsigned long long>::digits10 + 1;

ここで std::numeric_limits<unsigned long long>::digits10 というのは、先ほども書いたように、その変数が正確に表現できる値の 10 進数での桁数です。そしてその式から、正確に表現できる桁数のうちの最大桁数と思って大丈夫でしょう。

 

実際のところ、digits 桁の 2 進数で表せる最大値を 10 進数にしたときの桁数は、次の公式で求められます。

std::ceil(std::numeric_limits<unsigned long long>::digits * std::log10(2))

std::numeric_limits<unsigned long long>::digits10 との違いは、"std::numeric_limits<unsigned long long>::digits * std::log10(2)" で得られた小数点数を切り捨てるか切り上げるかだけなので、切り捨てられた値に 1 を足してあげれば、その最大値の桁数を求めることができます。

ここでもし正確に表現できる桁数が 10 進数でちょうどぴったりその桁の最後だった場合、その桁数を切り上げてしまうと 1 つ多い桁になってしまいますけど、std::log10(2) は現実では無理数なので、ぴったりになることはなさそうです。

 

ある値の桁数を取得する

たとえば開始位置を揃えて数値を表示したい場合は、上記の方法だと、最大の値だった場合の桁数になってしまうので上手く行きません。

そのようなときも、先ほどと同じような式で桁数を求めることができます。

 

たとえば、変数 value に格納されている値が 10 進数で何桁になるかを知るには、次のようにします。

int valueDigits = std::ceil(std::log10(value));

これで、その値を 10 進数にしたときの桁数を計算することができました。


[ もどる ]