After it was mentioned out (thx Fabian Lange) that my benchmark was flawed i have done some additional research on that matter and corrected my benchmark. I also add another method to calculate which is mentioned here.
As lesson learned: Use constant data for all benchmarks and use blackholes or method returns to prevent JIT optimizations like dead code elimination.

I have been working with JMH for 4 weeks now and this week we had a small problem @SiteOS for which it is perfect. The question was what is the fastest way to calculate the digits of a number which is shown in this post.

I considered the following four solutions

  • Conversion to String and make use of String.length
  • Division by ten while result is greater zero
  • Use the Math.log10
  • Use an binary if else construct

The JMH Benchmark is written within a couple of minutes

@State(Scope.Thread)
public class NumberDigitsBenchmark {
    private int[] data;
    @Setup(Level.Trial)
    public void init() throws IOException {
        Random random = new Random();
        Path path = get("/tmp/numbers.txt");
        if (!exists(path)) {
            write(path,
                    (Iterable<String>) random.ints().filter(i -> i > 0).limit(1_000_000).mapToObj(String::valueOf)::iterator);
        }
        data = readAllLines(path).stream().mapToInt(Integer::valueOf).toArray();
    }
    @Benchmark
    public void log(Blackhole bh) throws InterruptedException {
        for (int nr : data) {
            bh.consume((int) (log10(nr) + 1));
        }
    }
    @Benchmark
    public void viaString(Blackhole bh) throws InterruptedException {
        for (int nr : data) {
            bh.consume(("" + nr).length());
        }
    }
    @Benchmark
    public void divide(Blackhole bh) throws InterruptedException, IOException {
        for (int nr : data) {
            bh.consume(division(nr));
        }
    }
    private int division(int nr) {
        int digits = 0;
        while (nr > 0) {
            digits++;
            nr /= 10;
        }
        return digits;
    }
    @Benchmark
    public void theIfWay(Blackhole bh) {
        for (int nr : data) {
            bh.consume(ifSolution(nr));
        }
    }
    private static int ifSolution(int n) {
        if (n < 100000) {
            // 5 or less
            if (n < 100) {
                // 1 or 2
                if (n < 10)
                    return 1;
                else
                    return 2;
            } else {
                // 3 or 4 or 5
                if (n < 1000)
                    return 3;
                else {
                    // 4 or 5
                    if (n < 10000)
                        return 4;
                    else
                        return 5;
                }
            }
        } else {
            // 6 or more
            if (n < 10000000) {
                // 6 or 7
                if (n < 1000000)
                    return 6;
                else
                    return 7;
            } else {
                // 8 to 10
                if (n < 100000000)
                    return 8;
                else {
                    // 9 or 10
                    if (n < 1000000000)
                        return 9;
                    else
                        return 10;
                }
            }
        }
    }
    public static void main(String[] args) throws RunnerException, IOException {
        Options options = new OptionsBuilder()
                .include(NumberDigitsBenchmark.class.getSimpleName()).forks(1).build();
        new Runner(options).run();
    }
}

On my machine the if-else construct was the fastest solution but keep in mind that this implementation is an integer specialized version. So if you want to use it for longs you have to code a little bit more.

Benchmark                   Mode    Cnt         Score         Error  Units
NumberDigitsBenchmark.divide     thrpt   20   40.986 ± 1.029  ops/s
NumberDigitsBenchmark.log        thrpt   20   31.435 ± 2.213  ops/s
NumberDigitsBenchmark.theIfWay   thrpt   20  118.407 ± 7.492  ops/s
NumberDigitsBenchmark.viaString  thrpt   20   29.617 ± 1.613  ops/s