Maneira de obter o número de dígitos em um int?

Existe uma maneira mais simples de obter o tamanho de um int?

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

Sua solução baseada em String está perfeitamente bem, não há nada “limpo” sobre isso. Você tem que perceber que, matematicamente, os números não têm comprimento, nem têm dígitos. Comprimento e dígitos são ambas as propriedades de uma representação física de um número em uma base específica, isto é, uma String.

Uma solução baseada em logaritmo faz (algumas) as mesmas coisas que a baseada em String faz internamente, e provavelmente o faz (insignificantemente) mais rápido porque produz apenas o tamanho e ignora os dígitos. Mas eu não consideraria isso mais claro em intenção – e esse é o fator mais importante.

O logaritmo é seu amigo:

 int n = 1000; int length = (int)(Math.log10(n)+1); 

NB: válido apenas para n> 0.

A abordagem mais rápida: dividir e conquistar.

Supondo que seu intervalo seja de 0 a MAX_INT, você tem de 1 a 10 dígitos. Você pode abordar esse intervalo usando dividir e conquistar, com até quatro comparações para cada input. Primeiro, você divide [1..10] em [1..5] e [6..10] com uma comparação e, em seguida, cada intervalo de comprimento 5 é dividido usando uma comparação em um intervalo de comprimento 3 e um intervalo de comprimento 2. O intervalo de comprimento 2 requer mais uma comparação (total de 3 comparações), o intervalo de comprimento 3 pode ser dividido em intervalo de comprimento 1 (solução) e um intervalo de comprimento 2. Então, você precisa de 3 ou 4 comparações.

Sem divisões, sem operações de ponto flutuante, sem logaritmos caros, apenas comparações de números inteiros.

Código (longo mas rápido):

 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; } } } 

Benchmark (após o aquecimento da JVM) - veja o código abaixo para ver como o benchmark foi executado:

  1. método de linha de base (com String.length): 2145ms
  2. método log10: 711ms = 3,02 vezes mais rápido que a linha de base
  3. divisão repetida: 2797ms = 0,77 vezes mais rápido que a linha de base
  4. dividir e conquistar: 74ms = 28,99
    vezes mais rápido que a linha de base

Código completo:

 public static void main(String[] args) throws Exception { // validate methods: for (int i = 0; i < 1000; i++) if (method1(i) != method2(i)) System.out.println(i); for (int i = 0; i < 1000; i++) if (method1(i) != method3(i)) System.out.println(i + " " + method1(i) + " " + method3(i)); for (int i = 333; i < 2000000000; i += 1000) if (method1(i) != method3(i)) System.out.println(i + " " + method1(i) + " " + method3(i)); for (int i = 0; i < 1000; i++) if (method1(i) != method4(i)) System.out.println(i + " " + method1(i) + " " + method4(i)); for (int i = 333; i < 2000000000; i += 1000) if (method1(i) != method4(i)) System.out.println(i + " " + method1(i) + " " + method4(i)); // work-up the JVM - make sure everything will be run in hot-spot mode allMethod1(); allMethod2(); allMethod3(); allMethod4(); // run benchmark Chronometer c; c = new Chronometer(true); allMethod1(); c.stop(); long baseline = c.getValue(); System.out.println(c); c = new Chronometer(true); allMethod2(); c.stop(); System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline"); c = new Chronometer(true); allMethod3(); c.stop(); System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline"); c = new Chronometer(true); allMethod4(); c.stop(); System.out.println(c + " = " + StringTools.formatDouble((double)baseline / c.getValue() , "0.00") + " times faster than baseline"); } private static int method1(int n) { return Integer.toString(n).length(); } private static int method2(int n) { if (n == 0) return 1; return (int)(Math.log10(n) + 1); } private static int method3(int n) { if (n == 0) return 1; int l; for (l = 0 ; n > 0 ;++l) n /= 10; return l; } private static int method4(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; } } } } private static int allMethod1() { int x = 0; for (int i = 0; i < 1000; i++) x = method1(i); for (int i = 1000; i < 100000; i += 10) x = method1(i); for (int i = 100000; i < 1000000; i += 100) x = method1(i); for (int i = 1000000; i < 2000000000; i += 200) x = method1(i); return x; } private static int allMethod2() { int x = 0; for (int i = 0; i < 1000; i++) x = method2(i); for (int i = 1000; i < 100000; i += 10) x = method2(i); for (int i = 100000; i < 1000000; i += 100) x = method2(i); for (int i = 1000000; i < 2000000000; i += 200) x = method2(i); return x; } private static int allMethod3() { int x = 0; for (int i = 0; i < 1000; i++) x = method3(i); for (int i = 1000; i < 100000; i += 10) x = method3(i); for (int i = 100000; i < 1000000; i += 100) x = method3(i); for (int i = 1000000; i < 2000000000; i += 200) x = method3(i); return x; } private static int allMethod4() { int x = 0; for (int i = 0; i < 1000; i++) x = method4(i); for (int i = 1000; i < 100000; i += 10) x = method4(i); for (int i = 100000; i < 1000000; i += 100) x = method4(i); for (int i = 1000000; i < 2000000000; i += 200) x = method4(i); return x; } 

Mais uma vez, benchmark:

  1. método de linha de base (com String.length): 2145ms
  2. método log10: 711ms = 3,02 vezes mais rápido que a linha de base
  3. divisão repetida: 2797ms = 0,77 vezes mais rápida que a linha de base
  4. dividir e conquistar: 74ms = 28,99
    vezes mais rápido que a linha de base

Edit: Depois que eu escrevi o benchmark, eu dei uma olhadinha no Integer.toString do Java 6, e descobri que ele usa:

 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; } 

Eu comparei isso com minha solução de divisão e conquista:

  1. dividir-e-conquistar: 104ms
  2. Solução Java 6 - iterar e comparar: 406ms

O meu é cerca de 4x mais rápido.

Dois comentários sobre seu benchmark: Java é um ambiente complexo, com compilation just-in-time e garbage collection e assim por diante, então para obter uma comparação justa, sempre que executo uma referência, sempre: (a) incluo os dois testes em um loop que os executa na seqüência 5 ou 10 vezes. Muitas vezes o tempo de execução na segunda passagem pelo loop é bem diferente do primeiro. E (b) Após cada “abordagem”, eu faço um System.gc () para tentar acionar uma garbage collection. Caso contrário, a primeira abordagem pode gerar vários objects, mas não o suficiente para forçar uma garbage collection, então a segunda abordagem cria alguns objects, o heap é esgotado e a garbage collection é executada. Então a segunda abordagem é “carregada” para pegar o lixo deixado pela primeira abordagem. Muito injusto!

Dito isto, nenhum dos acima fez uma diferença significativa neste exemplo.

Com ou sem essas modificações, obtive resultados muito diferentes do que você. Quando eu corri isso, sim, a abordagem toString deu tempos de execução de 6400 a 6600 millis, enquanto a abordagem de log atingiu 20.000 a 20.400 millis. Em vez de ser um pouco mais rápida, a abordagem de log foi 3 vezes mais lenta para mim.

Observe que as duas abordagens envolvem custos muito diferentes, portanto, isso não é totalmente chocante: a abordagem toString criará muitos objects temporários que precisam ser limpos, enquanto a abordagem de log precisa de cálculos mais intensos. Então talvez a diferença seja que em uma máquina com menos memory, toString requer mais rodadas de garbage collection, enquanto que em uma máquina com um processador mais lento, o cálculo extra de log seria mais doloroso.

Eu também tentei uma terceira abordagem. Eu escrevi essa pequena function:

 static int numlength(int n) { if (n == 0) return 1; int l; n=Math.abs(n); for (l=0;n>0;++l) n/=10; return l; } 

Isso foi executado em 1600 a 1900 millis – menos de 1/3 da abordagem toString e 1/10 da abordagem de log na minha máquina.

Se você tivesse uma ampla gama de números, você poderia acelerar ainda mais, começando a dividir por 1.000 ou 1.000.000 para reduzir o número de vezes através do loop. Eu não brinquei com isso.

Como o número de dígitos na base 10 de um inteiro é apenas 1 + truncado (log10 (number)) , você pode fazer:

 public class Test { public static void main(String[] args) { final int number = 1234; final int digits = 1 + (int)Math.floor(Math.log10(number)); System.out.println(digits); } } 

Editado porque minha última edição corrigiu o exemplo de código, mas não a descrição.

A solução de Marian adaptada para números de tipo longos (até 9.223.372.036.854.775.807), no caso de alguém querer copiá-lo e colá-lo. No programa que eu escrevi isso para números até 10000 eram muito mais prováveis, então eu fiz uma ramificação específica para eles. De qualquer forma, isso não fará uma diferença significativa.

 public static int numberOfDigits (long n) { // Guessing 4 digit numbers will be more probable. // They are set in the first branch. if (n < 10000L) { // from 1 to 4 if (n < 100L) { // 1 or 2 if (n < 10L) { return 1; } else { return 2; } } else { // 3 or 4 if (n < 1000L) { return 3; } else { return 4; } } } else { // from 5 a 20 (albeit longs can't have more than 18 or 19) if (n < 1000000000000L) { // from 5 to 12 if (n < 100000000L) { // from 5 to 8 if (n < 1000000L) { // 5 or 6 if (n < 100000L) { return 5; } else { return 6; } } else { // 7 u 8 if (n < 10000000L) { return 7; } else { return 8; } } } else { // from 9 to 12 if (n < 10000000000L) { // 9 or 10 if (n < 1000000000L) { return 9; } else { return 10; } } else { // 11 or 12 if (n < 100000000000L) { return 11; } else { return 12; } } } } else { // from 13 to ... (18 or 20) if (n < 10000000000000000L) { // from 13 to 16 if (n < 100000000000000L) { // 13 or 14 if (n < 10000000000000L) { return 13; } else { return 14; } } else { // 15 or 16 if (n < 1000000000000000L) { return 15; } else { return 16; } } } else { // from 17 to ...¿20? if (n < 1000000000000000000L) { // 17 or 18 if (n < 100000000000000000L) { return 17; } else { return 18; } } else { // 19? Can it be? // 10000000000000000000L is'nt a valid long. return 19; } } } } } 

Usando Java

 int nDigits = Math.floor(Math.log10(Math.abs(the_integer))) + 1; 

use import java.lang.Math.*; no início

Usando C

 int nDigits = floor(log10(abs(the_integer))) + 1; 

use inclue math.h no começo

Posso tentar? 😉

baseado na solução de Dirk

 final int digits = number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number)))); 

Não posso deixar um comentário ainda, então vou postar como uma resposta separada.

A solução baseada em logaritmo não calcula o número correto de dígitos para inteiros longos muito grandes, por exemplo:

 long n = 99999999999999999L; // correct answer: 17 int numberOfDigits = String.valueOf(n).length(); // incorrect answer: 18 int wrongNumberOfDigits = (int) (Math.log10(n) + 1); 

Solução baseada em logaritmo calcula o número incorreto de dígitos em inteiros grandes

Que tal a velha Matemática? Divida por 10 até chegar a 0.

 public static int getSize(long number) { int count = 0; while (number > 0) { count += 1; number = (number / 10); } return count; } 

Solução de Marian, agora com Ternary:

  public int len(int n){ return (n<100000)?((n<100)?((n<10)?1:2):(n<1000)?3:((n<10000)?4:5)):((n<10000000)?((n<1000000)?6:7):((n<100000000)?8:((n<1000000000)?9:10))); } 

Porque nós podemos.

Curioso, tentei fazer benchmark …

 import org.junit.Test; import static org.junit.Assert.*; public class TestStack1306727 { @Test public void bench(){ int number=1000; int a= String.valueOf(number).length(); int b= 1 + (int)Math.floor(Math.log10(number)); assertEquals(a,b); int i=0; int s=0; long startTime = System.currentTimeMillis(); for(i=0, s=0; i< 100000000; i++){ a= String.valueOf(number).length(); s+=a; } long stopTime = System.currentTimeMillis(); long runTime = stopTime - startTime; System.out.println("Run time 1: " + runTime); System.out.println("s: "+s); startTime = System.currentTimeMillis(); for(i=0,s=0; i< 100000000; i++){ b= number==0?1:(1 + (int)Math.floor(Math.log10(Math.abs(number)))); s+=b; } stopTime = System.currentTimeMillis(); runTime = stopTime - startTime; System.out.println("Run time 2: " + runTime); System.out.println("s: "+s); assertEquals(a,b); } } 

os resultados são:

 Tempo de execução 1: 6765
 s: 400000000
 Tempo de execução 2: 6000
 s: 400000000

Agora fico me perguntando se meu benchmark realmente significa alguma coisa, mas eu obtenho resultados consistentes (variações dentro de um ms) em várias execuções do próprio benchmark ... 🙂 Parece inútil tentar otimizar isso ...


edit: após o comentário de ptomli, substituí 'number' por 'i' no código acima e obtive os seguintes resultados em 5 runs do banco:

 Tempo de execução 1: 11500
 s: 788888890
 Tempo de execução 2: 8547
 s: 788888890

 Tempo de execução 1: 11485
 s: 788888890
 Tempo de execução 2: 8547
 s: 788888890

 Tempo de execução 1: 11469
 s: 788888890
 Tempo de execução 2: 8547
 s: 788888890

 Tempo de execução 1: 11500
 s: 788888890
 Tempo de execução 2: 8547
 s: 788888890

 Tempo de execução 1: 11484
 s: 788888890
 Tempo de execução 2: 8547
 s: 788888890

E esse método recursivo?

  private static int length = 0; public static int length(int n) { length++; if((n / 10) < 10) { length++; } else { length(n / 10); } return length; } 

solução simples:

 public class long_length { long x,l=1,n; for (n=10;n 

Uma solução muito simples:

 public int numLength(int n) { for (int length = 1; n % Math.pow(10, length) != n; length++) {} return length; } 

Ou, em vez disso, o comprimento você pode verificar se o número é maior ou menor que o número desejado.

  public void createCard(int cardNumber, int cardStatus, int customerId) throws SQLException { if(cardDao.checkIfCardExists(cardNumber) == false) { if(cardDao.createCard(cardNumber, cardStatus, customerId) == true) { System.out.println("Card created successfully"); } else { } } else { System.out.println("Card already exists, try with another Card Number"); do { System.out.println("Enter your new Card Number: "); scan = new Scanner(System.in); int inputCardNumber = scan.nextInt(); cardNumber = inputCardNumber; } while(cardNumber < 95000000); cardDao.createCard(cardNumber, cardStatus, customerId); } } 

}

Eu ainda não vi uma solução baseada em multiplicação. As soluções de logaritmo, divisão e baseadas em string tornar-se-ão bastante difíceis de lidar com milhões de casos de teste, então aqui está uma para ints :

 /** * Returns the number of digits needed to represents an {@code int} value in * the given radix, disregarding any sign. */ public static int len(int n, int radix) { radixCheck(radix); // if you want to establish some limitation other than radix > 2 n = Math.abs(n); int len = 1; long min = radix - 1; while (n > min) { n -= min; min *= radix; len++; } return len; } 

Na base 10, isso funciona porque n é essencialmente comparado com 9, 99, 999 … já que min é 9, 90, 900 … e n está sendo subtraído por 9, 90, 900 …

Infelizmente, isso não é portável para long apenas substituindo todas as instâncias de int devido ao estouro. Por outro lado, acontece que funcionará nas bases 2 e 10 (mas falha muito na maioria das outras bases). Você precisará de uma tabela de consulta para os pontos de overflow (ou um teste de divisão … ew)

 /** * For radices 2 &le r &le Character.MAX_VALUE (36) */ private static long[] overflowpt = {-1, -1, 4611686018427387904L, 8105110306037952534L, 3458764513820540928L, 5960464477539062500L, 3948651115268014080L, 3351275184499704042L, 8070450532247928832L, 1200757082375992968L, 9000000000000000000L, 5054470284992937710L, 2033726847845400576L, 7984999310198158092L, 2022385242251558912L, 6130514465332031250L, 1080863910568919040L, 2694045224950414864L, 6371827248895377408L, 756953702320627062L, 1556480000000000000L, 3089447554782389220L, 5939011215544737792L, 482121737504447062L, 839967991029301248L, 1430511474609375000L, 2385723916542054400L, 3902460517721977146L, 6269893157408735232L, 341614273439763212L, 513726300000000000L, 762254306892144930L, 1116892707587883008L, 1617347408439258144L, 2316231840055068672L, 3282671350683593750L, 4606759634479349760L}; public static int len(long n, int radix) { radixCheck(radix); n = abs(n); int len = 1; long min = radix - 1; while (n > min) { len++; if (min == overflowpt[radix]) break; n -= min; min *= radix; } return len; } 

Com design (baseado no problema). Esta é uma alternativa de dividir e conquistar. Primeiro definiremos um enum (considerando que é apenas para um int não assinado).

 public enum IntegerLength { One((byte)1,10), Two((byte)2,100), Three((byte)3,1000), Four((byte)4,10000), Five((byte)5,100000), Six((byte)6,1000000), Seven((byte)7,10000000), Eight((byte)8,100000000), Nine((byte)9,1000000000); byte length; int value; IntegerLength(byte len,int value) { this.length = len; this.value = value; } public byte getLenght() { return length; } public int getValue() { return value; } } 

Agora vamos definir uma class que passa pelos valores do enum e comparar e retornar o tamanho apropriado.

 public class IntegerLenght { public static byte calculateIntLenght(int num) { for(IntegerLength v : IntegerLength.values()) { if(num < v.getValue()){ return v.getLenght(); } } return 0; } } 

O tempo de execução desta solução é o mesmo da abordagem de divisão e conquista.

Digite o número e crie um Arraylist , e o loop while registrará todos os dígitos no Arraylist . Então podemos tirar o tamanho da matriz, que será o comprimento do valor inteiro que você digitou.

 ArrayList a=new ArrayList<>(); while(number > 0) { remainder = num % 10; a.add(remainder); number = number / 10; } int m=a.size(); 

Maneira recursiva fácil

 int get_int_lenght(current_lenght, value) { if (value / 10 < 10) return (current_lenght + 1); return (get_int_lenght(current_lenght + 1, value)) } 

não testado

Podemos conseguir isso usando um loop recursivo

  public static int digitCount(int numberInput, int i) { while (numberInput > 0) { i++; numberInput = numberInput / 10; digitCount(numberInput, i); } return i; } public static void printString() { int numberInput = 1234567; int digitCount = digitCount(numberInput, 0); System.out.println("Count of digit in ["+numberInput+"] is ["+digitCount+"]"); } 
  int num = 02300; int count = 0; while(num>0){ if(num == 0) break; num=num/10; count++; } System.out.println(count); 

Aqui está um método muito simples que fiz para qualquer número:

 public static int numberLength(int userNumber) { int numberCounter = 10; boolean condition = true; int digitLength = 1; while (condition) { int numberRatio = userNumber / numberCounter; if (numberRatio < 1) { condition = false; } else { digitLength++; numberCounter *= 10; } } return digitLength; } 

A maneira como funciona é com a variável do contador de números que é 10 = espaço de 1 dígito. Por exemplo, .1 = 1 décimo => espaço de 1 dígito. Portanto, se você tiver int number = 103342; você terá 6, porque é o equivalente a 0,000001 de espaço de volta. Além disso, alguém tem um nome de variável melhor para numberCounter ? Eu não consigo pensar em nada melhor.

Edit: Apenas pensei em uma melhor explicação. Essencialmente o que este loop while está fazendo é fazer com que você divida seu número por 10, até que seja menor que um. Essencialmente, quando você divide algo por 10, você está movendo um espaço numérico, então você simplesmente o divide por 10 até chegar a <1 para a quantidade de dígitos em seu número.

Aqui está outra versão que pode contar a quantidade de números em um decimal:

 public static int repeatingLength(double decimalNumber) { int numberCounter = 1; boolean condition = true; int digitLength = 1; while (condition) { double numberRatio = decimalNumber * numberCounter; if ((numberRatio - Math.round(numberRatio)) < 0.0000001) { condition = false; } else { digitLength++; numberCounter *= 10; } } return digitLength - 1; } 

Alguém quer fazer isso principalmente porque quer “apresentá-lo”, o que significa que ele precisa ser “toString-ed” (ou ser transformado de outra maneira) explicitamente ou implicitamente; antes de poder ser apresentado (impresso por exemplo).

Se for esse o caso, tente apenas tornar explícito o “toString” necessário e contar os bits.

Eu fiz assim e tenho o comprimento do número

 String Temp=IntegerVariable+""; System.out.println(Temp.length()); 

Você poderia os dígitos usando divisão sucessiva por dez:

 int a=0; if (no < 0) { no = -no; } else if (no == 0) { no = 1; } while (no > 0) { no = no / 10; a++; } System.out.println("Number of digits in given number is: "+a); 

Tente converter o int em uma string e, em seguida, obtenha o tamanho da string . Isso deve ter o comprimento do int .

 public static int intLength(int num){ String n = Integer.toString(num); int newNum = n.length(); return newNum; }