整数の桁数の求め方

高校のころからのバリバリ文系はこういうことに弱い。こんなこと「普通はこうする」ってのがありそうなんだけど、そういう常識が無い。

何気に JDK のソースを見ていたら。
java.lang.Integer の
public static String toString(int i)
の中で、int 値を String に変換するために、何桁の char 配列を確保すればよいか求める関数である stringSize 関数の中で以下のような処理。

    final static int [] sizeTable = { 9, 99, 999, 9999, 99999, 999999, 9999999,
                                      99999999, 999999999, Integer.MAX_VALUE };

    // Requires positive x
    static int stringSize(int x) {
        for (int i=0; ; i++)
            if (x <= sizeTable[i])
                return i+1;
    }

まじっすかと。何でこんなことに引っかかってるのかというと、以前まったく同じような処理を実装する必要に迫られたことがあるのだ。実装言語は Java


簡単に求められるだろうと思ったら、これ意外に難しいじゃんと、ちょっと考えてまず思いついたのが、
log(X) + 1 の小数点切り捨てで求める方法。Java でいうところのこれ。

(int)Math.log10(X) + 1

しかし、当時の私にはこれが普通に現れる表現なのかどうかが分からなかった。それになんか、この計算って重い?いやいや、最近のCPUなら別に・・・。(苦悩)理系の同僚に「ぷっ」と言われるようなソースを書き上げるのもイタイタしい。


仕方なく恥をしのんで、理系っぽい同僚に相談したところ、


「・・・(苦笑)」


あ、普通はこういう書き方しないんだ、と空気を読んで、結局こう書き換えた。

String.valueOf(X).length()

だ、ださい・・・。しかし、今見てもやっぱりこのダサいやり方にして正解であったと思っている。この結果の値が適切な名前の変数名に代入されてさえいれば、これを一目見て何やってるかわかんない人はいないだろう。


まあ、そんな私の目の前に、第3の方法が現れたっちうわけである。こーれは非常にシンプル。軽いことは間違い無いだろう。最大で10回の比較が行われることになるが、比較はCPUのもっとも得意とするところ。しかも static な int 配列が相手だし。それにわかりやすい。


log10() のソースなんて、JNI 使って C で記述されている。OpenJDK7 のソースをみたところ(e_log10.c というのがそれのようだ)、文系が解読するにはキツイレベルだった。まーでも見た感じ、比較10回より速いってことはなさそうだ。


ということで、現在の桁数算出方法ランキングは、

1位:String.valueOf(X).length()
2位:sizeTable = { 9, 99, 999, 9999...
3位:(int)Math.log10(X) + 1


となった。結局1位は不動のままであるが、呼び出し回数が多少なりとも多い場合や、フレームワークや共通ライブラリの実装であれば、sizeTable を迷わず使うな。うん。