Número de dígitos en un entero en Java

1. Introducción

En este tutorial rápido, exploraremos diferentes formas de obtener el número de dígitos en un entero en Java.

También analizaremos esos diferentes métodos y averiguaremos qué algoritmo encajaría mejor en nuestra situación.

2. Número de dígitos en un entero

Para los métodos discutidos aquí, solo estamos considerando números enteros positivos. Si esperamos alguna entrada negativa, primero podemos hacer uso de Math.abs (número) antes de usar cualquiera de estos métodos.

2.1. Solución basada en cadenas

Quizás la forma más fácil de obtener el número de dígitos en un entero es convertirlo en String y llamar al método length () . Esto devolverá la longitud de la representación de cadena de nuestro número:

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

Pero, este puede ser un enfoque subóptimo, ya que esta declaración implica la asignación de memoria para una cadena, para cada evaluación . La JVM primero debe analizar nuestro número y copiar sus dígitos en una Cadena separada y realizar una serie de operaciones diferentes también (como mantener copias temporales, manejar conversiones Unicode, etc.).

Si solo tenemos unos pocos números para evaluar, entonces claramente podemos optar por esta solución, porque la diferencia entre este y cualquier otro enfoque será despreciable incluso para números grandes.

2.2. Enfoque logarítmico

Para los números representados en forma decimal, si tomamos su registro en base 10 y lo redondeamos, obtendremos el número de dígitos en ese número:

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

Tenga en cuenta que el log 10 0 de cualquier número no está definido. Entonces, si esperamos cualquier entrada con valor 0 , también podemos marcar eso.

El enfoque logarítmico es significativamente más rápido que el enfoque basado en cadenas , ya que no tiene que pasar por el proceso de conversión de datos. Solo implica un cálculo simple y directo sin ninguna inicialización o bucles de objetos adicionales.

2.3. Multiplicación repetida

En este método, tomaremos una variable temporal (inicializada en 1) y la multiplicaremos continuamente por 10 hasta que sea mayor a nuestro número. Durante este proceso, también usaremos una variable de longitud que mantendrá un registro de la longitud del número:

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

En este código, la línea temp * = 10 es lo mismo que escribir temp = (temp << 3) + (temp << 1) . Dado que la multiplicación suele ser una operación más costosa en algunos procesadores en comparación con los operadores de turno, este último puede ser un poco más eficiente.

2.4. Dividiendo con potencias de dos

Si conocemos el rango de nuestro número, entonces podemos usar una variación que reducirá aún más nuestras comparaciones. Este método divide el número por potencias de dos (por ejemplo, 1, 2, 4, 8, etc.):

Este método divide el número por potencias de dos (por ejemplo, 1, 2, 4, 8, etc.):

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;

Aprovecha el hecho de que cualquier número puede representarse mediante la suma de potencias de 2. Por ejemplo, 15 se puede representar como 8 + 4 + 2 + 1, que son todas potencias de 2.

Para un número de 15 dígitos, estaríamos haciendo 15 comparaciones en nuestro enfoque anterior, que hemos reducido a solo 4 en este método.

2.5. Divide y conquistaras

Este es quizás el enfoque más voluminoso en comparación con todos los demás descritos aquí, pero no hace falta decir que este es el más rápido porque no estamos realizando ningún tipo de conversión, multiplicación, suma o inicialización de objeto.

Obtenemos nuestra respuesta en solo tres o cuatro simples declaraciones 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; } } } }

De manera similar al enfoque anterior, podemos usar este método solo si conocemos el rango de nuestro número.

3. Evaluación comparativa

Ahora que tenemos una buena comprensión de las posibles soluciones, hagamos ahora una evaluación comparativa simple de todos nuestros métodos utilizando Java Microbenchmark Harness (JMH).

La siguiente tabla muestra el tiempo medio de procesamiento de cada operación (en nanosegundos):

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

La solución basada en cadenas , que es la más simple, también es la operación más costosa, ya que es la única que requiere conversión de datos e inicialización de nuevos objetos.

El enfoque logarítmico es significativamente más eficiente, en comparación con la solución anterior, ya que no implica ninguna conversión de datos. Y, al ser una solución de una sola línea, puede ser una buena alternativa al enfoque basado en cadenas .

La multiplicación repetida implica una multiplicación simple, proporcionalmente a la longitud del número; por ejemplo, si un número tiene quince dígitos, este método implicará quince multiplicaciones.

Sin embargo, el siguiente método aprovecha el hecho de que cada número puede ser representado por potencias de dos (el enfoque similar a BCD) y reduce lo mismo a operaciones de 4 divisiones, por lo que es incluso más eficiente que el primero.

Finalmente, como podemos inferir, el algoritmo más eficiente es la implementación detallada de Divide and Conquer , que ofrece la respuesta en solo tres o cuatro declaraciones if simples. Podemos usarlo si tenemos un gran conjunto de datos de números que necesitamos analizar.

4. Conclusión

En este breve artículo, describimos algunas de las formas de encontrar el número de dígitos en un entero y comparamos la eficiencia de cada enfoque.

Y, como siempre, puede encontrar el código completo en GitHub.