Количество цифр в целом числе в Java

Количество цифр в целом числе в Java

1. Вступление

В этом кратком руководстве мы исследуемdifferent ways of getting the number of digits in an Integer на Java.

Мы также проанализируем эти различные методы и выясним, какой алгоритм лучше всего подходит для нашей ситуации.

2. Количество цифр вInteger

Что касается методов, обсуждаемых здесь, мы рассматриваем только положительные целые числа. Если мы ожидаем отрицательного ввода, мы можем сначала использоватьMath.abs(number), прежде чем использовать любой из этих методов.

2.1. Решение на основеString

Возможно, самый простой способ получить количество цифр вInteger - это преобразовать его вString и вызвать методlength(). Это вернет длинуString представления нашего числа:

int length = String.valueOf(number).length();

But, this may be a sub-optimal approach, as this statement involves memory allocation for a String, for each evaluation. JVM должна сначала проанализировать наш номер и скопировать его цифры в отдельныйString, а также выполнить ряд различных операций (таких как сохранение временных копий, обработка преобразований Unicode и т. д.).

Если у нас есть только несколько чисел для оценки, то мы можем четко использовать это решение - потому что разница между этим и любым другим подходом будет пренебрежимо даже для больших чисел.

2.2. Логарифмический подход

Для чисел, представленных в десятичной форме, если мы возьмем их логарифм по основанию 10 и округлим в большую сторону, то мы получим количество цифр в этом числе:

int length = (int) (Math.log10(number) + 1);

Обратите внимание, чтоlog100 любого числа не определено. Так что, если мы ожидаем ввода со значением0, мы можем поставить проверку и на это.

The logarithmic approach is significantly faster than String based approach as it doesn’t have to go through the process of any data conversion. Это просто включает в себя простое и понятное вычисление без какой-либо дополнительной инициализации объекта или циклов.

2.3. Повторное умножение

В этом методе мы возьмем временную переменную (инициализированную на 1) и будем постоянно умножать ее на 10, пока она не станет больше нашего числа. Во время этого процесса мы также будем использовать переменнуюlength, которая будет отслеживать длину числа:

int length = 0;
long temp = 1;
while (temp <= number) {
    length++;
    temp *= 10;
}
return length;

В этом коде строкаtemp *= 10 совпадает с записьюtemp = (temp << 3) + (temp << 1). Поскольку на некоторых процессорах умножение обычно обходится дороже, чем на операциях сдвига, последний может быть немного более эффективным.

2.4. Деление степенями двойки

Если мы знаем о диапазоне нашего числа, то мы можем использовать вариацию, которая еще больше уменьшит наши сравнения. Этот метод делит число на степени двух (например, 1, 2, 4, 8 и т. Д.):

Этот метод делит число на степени двух (например, 1, 2, 4, 8 и т. Д.):

int length = 1;
if (number >= 100000000) {
    length += 8;
    number /= 100000000;
}
if (number >= 10000) {
    length += 4;
    number /= 10000;
}
if (number >= 100) {
    length += 2;
    number /= 100;
}
if (number >= 10) {
    length += 1;
}
return length;

Он использует тот факт, что любое число может быть представлено добавлением степеней 2. Например, 15 может быть представлено как 8 + 4 + 2 + 1, которые все являются степенями 2.

Для 15-значного числа мы будем делать 15 сравнений в нашем предыдущем подходе, который мы сократили до 4 в этом методе.

2.5. Разделяй и властвуй

Возможно, этоthe bulkiest approach по сравнению со всеми остальными, описанными здесь, но, разумеется, этоthis one is the fastest, потому что мы не выполняем никаких типов преобразования, умножения, сложения или инициализации объекта.

Мы получаем ответ в трех или четырех простых операторахif:

if (number < 100000) {
    if (number < 100) {
        if (number < 10) {
            return 1;
        } else {
            return 2;
        }
    } else {
        if (number < 1000) {
            return 3;
        } else {
            if (number < 10000) {
                return 4;
            } else {
                return 5;
            }
        }
    }
} else {
    if (number < 10000000) {
        if (number < 1000000) {
            return 6;
        } else {
            return 7;
        }
    } else {
        if (number < 100000000) {
            return 8;
        } else {
            if (number < 1000000000) {
                return 9;
            } else {
                return 10;
            }
        }
    }
}

Подобно предыдущему подходу, мы можем использовать этот метод, только если мы знаем о диапазоне нашего числа.

3. Бенчмаркинг

Теперь, когда у нас есть хорошее представление о возможных решениях, давайте проведем простой сравнительный анализ всех наших методов с использованиемJava Microbenchmark Harness (JMH).

Следующая таблица показывает среднее время обработки каждой операции (в наносекундах):

Benchmark                            Mode  Cnt   Score   Error  Units
Benchmarking.stringBasedSolution     avgt  200  32.736 ± 0.589  ns/op
Benchmarking.logarithmicApproach     avgt  200  26.123 ± 0.064  ns/op
Benchmarking.repeatedMultiplication  avgt  200   7.494 ± 0.207  ns/op
Benchmarking.dividingWithPowersOf2   avgt  200   1.264 ± 0.030  ns/op
Benchmarking.divideAndConquer        avgt  200   0.956 ± 0.011  ns/op

Решение на основеString, которое является самым простым, также является наиболее дорогостоящей операцией, поскольку это единственное решение, которое требует преобразования данных и инициализации новых объектов.

Логарифмический подход значительно более эффективен по сравнению с предыдущим решением, так как не требует преобразования данных. И, будучи однострочным решением, это может быть хорошей альтернативой подходу на основеString-.

Повторное умножение предполагает простое умножение пропорционально длине числа; например, если число имеет длину пятнадцать цифр, тогда этот метод будет включать пятнадцать умножений.

Однако в следующем методе используется тот факт, что каждое число может быть представлено степенями двойки (подход аналогичен BCD), и сводится к 4 операциям деления, так что он даже более эффективен, чем первый.

Наконец, как мы можем сделать вывод,the most efficient algorithm is the verbose Divide and Conquer implementation - дает ответ всего в трех или четырех простых операторах if. Мы можем использовать его, если у нас есть большой набор данных, который мы должны проанализировать.

4. Заключение

В этой короткой статье мы описали некоторые способы определения количества цифр вInteger и сравнили эффективность каждого подхода.

И, как всегда, вы можете найти полный кодover on GitHub.