Способ получить количество цифр в int?

есть ли более аккуратный способ получить длину int как это?

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

27 ответов


ваше строковое решение совершенно нормально, в нем нет ничего "не аккуратного". Вы должны понимать, что математически числа не имеют длины и не имеют цифр. Длина и цифры являются свойствами a физическое представление числа в определенной базе, т. е. строки.

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


логарифм-ваш друг:

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

NB: действует только для n > 0.


самый быстрый подход: разделяй и властвуй.

предполагая, что ваш диапазон от 0 до MAX_INT, у вас есть от 1 до 10 цифр. Вы можете подойти к этому интервалу, используя divide и conquer, С до 4 сравнениями на каждый вход. Во-первых, вы делите [1..10] в [1..5] и [6..10] с одним сравнением, а затем каждый интервал длины 5 вы делите с помощью одного сравнения на одну длину 3 и один интервал длины 2. Интервал длины 2 требует еще одного сравнения (всего 3 сравнения), длина Интервал 3 можно разделить на интервал длины 1 (решение) и интервал длины 2. Итак, вам нужно 3 или 4 сравнения.

никаких делений, никаких операций с плавающей запятой, никаких дорогостоящих логарифмов, только целочисленные сравнения.

код (длинный, но быстрый):

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 (после прогрева JVM) - см. код ниже, чтобы увидеть, как был запущен бенчмарк:

  1. базовый метод (со строкой.длина): 2145ms
  2. метод log10: 711ms = 3.02 раз быстрее, чем базовый
  3. повторное деление: 2797ms = 0,77 раза быстрее, чем базовый
  4. "разделяй и властвуй": = 28.99
    раз быстрее, чем исходная

полный код:

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

опять же, ориентир:

  1. базовый метод (со строкой.длина): 2145ms
  2. метод log10: 711ms = 3,02 раза быстрее, чем базовый
  3. повторное деление: 2797ms = 0,77 раза быстрее, чем базовая линия
  4. "разделяй и властвуй": = 28.99
    раз быстрее, чем исходная

Edit: После того, как я написал бенчмарк, я взял пик подхалима в Integer.toString из Java 6, и я обнаружил, что он использует:

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

Я сравнил его с моим решением "разделяй и властвуй":

  1. "разделяй и властвуй": 104ms
  2. решение Java 6-повторите и сравните: 406ms

шахты составляет около В 4 раза быстрее.


два комментария к вашему бенчмарку: Java-сложная среда, что с компиляцией just-in-time и сбором мусора и т. д., Поэтому, чтобы получить справедливое сравнение, всякий раз, когда я запускаю бенчмарк, я всегда: (a) заключаю два теста в цикл, который запускает их в последовательности 5 или 10 раз. Довольно часто время выполнения на втором проходе через цикл сильно отличается от первого. И (Б) после каждого "подхода" я делаю систему.gc (), чтобы попытаться запустить сборку мусора. В противном случае, первый подход может генерировать кучу объектов, но недостаточно для принудительной сборки мусора, затем второй подход создает несколько объектов, куча исчерпывается, и сборка мусора запускается. Затем второй подход "заряжается" за сбор мусора, оставленного первым подходом. Очень несправедливо!

тем не менее, ни то, ни другое не имело существенного значения в этом примере.

С этими изменениями или без них я получил совсем другие результаты, чем вы. Когда я побежал, Да, метод toString подход дал время от 6400 до 6600 Миллис, в то время как подход журнала обеспечить 20 000 до 20,400 Миллис. Вместо того, чтобы быть немного быстрее, подход к журналу был в 3 раза медленнее для меня.

обратите внимание, что два подхода связаны с очень разными затратами, поэтому это не совсем шокирует: подход toString создаст много временных объектов, которые должны быть очищены, в то время как подход log требует более интенсивных вычислений. Так что, возможно, разница в том, что на машине с меньшей памятью toString требует больше раундов сбора мусора, в то время как на машине с более медленным процессором дополнительное вычисление журнала было бы более болезненным.

Я также попробовал третий подход. Я написал эту маленькую функцию:

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

который работал в 1600 до 1900 Миллис - менее 1/3 подхода toString и 1/10 подхода log на моей машине.

Если бы у вас был широкий диапазон чисел, вы могли бы ускорить его, начав деления на 1000 или 1000000, чтобы уменьшить количество раз через петлю. Я с этим не играл.


Так как количество цифр в базе 10 целого числа просто 1 + усечение (log10 (число)), вы можете сделать:

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

редактировать потому что мое последнее редактирование исправило пример кода, но не описание.


решение Мэриан адаптировано для долго тип чисел (до 9,223,372,036,854,775,807), в случае если кто-то хочет скопировать и вставить его. В программе я написал это для чисел до 10000 были гораздо более вероятными, поэтому я сделал для них конкретную ветку. В любом случае, это не имеет значения.

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

Использование Java

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

использовать import java.lang.Math.*; в начале

Использование C

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

использовать inclue math.h в начале


можно попробовать? ;)

на основании решения

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

пока не могу оставить комментарий, поэтому я опубликую его как отдельный ответ.

решение на основе логарифма не вычисляет правильное количество цифр для очень больших длинных целых чисел, например:

long n = 99999999999999999L;

// correct answer: 17
int numberOfDigits = String.valueOf(n).length();

// incorrect answer: 18
int wrongNumberOfDigits = (int) (Math.log10(n) + 1); 

решение на основе логарифма вычисляет неправильное количество цифр в больших целых числах


Как насчет простой старой математики? Делите на 10, пока не достигнете 0.

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

решение Мэриан, теперь с тройным:

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

, потому что мы можем.


любопытно, я попытался сравнить его ...

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


    }
}

результаты :

Run time 1: 6765
s: 400000000
Run time 2: 6000
s: 400000000

Теперь мне остается задаться вопросом, действительно ли мой бенчмарк что-то значит, но я получаю последовательные результаты (вариации в ms) за несколько запусков самого бенчмарка ... :) Похоже, бесполезно пытаться оптимизировать это...


edit: после комментария ptomli Я заменил "номер" на "i" в коде выше и получил следующие результаты за 5 запусков скамейка:

Run time 1: 11500
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11485
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11469
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11500
s: 788888890
Run time 2: 8547
s: 788888890

Run time 1: 11484
s: 788888890
Run time 2: 8547
s: 788888890

Как насчет этого рекурсивного метода?

    private static int length = 0;

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

простое решение:

public class long_length {
    long x,l=1,n;
    for (n=10;n<x;n*=10){
        if (x/n!=0){
            l++;
        }
    }
    System.out.print(l);
}

очень простое решение:

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

или вместо длины вы можете проверить, является ли число больше или меньше, чем нужное число.

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

}


я еще не видел решения на основе умножения. Логарифм, дивисон и строковые решения станут довольно громоздкими против миллионов тестовых случаев, поэтому вот один для 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;
}

в базе 10 это работает, потому что n по существу сравнивается с 9, 99, 999... как минимум 9, 90, 900... и n вычитается на 9, 90, 900...

к сожалению, это не портативный long просто заменив каждый экземпляр int из-за переполнения. С другой стороны, так уж получилось!--10-->будет работа для баз 2 и 10 (но плохо работает для большинства других баз). Вам понадобится таблица поиска для точек переполнения (или тест деления... фу)

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

С конструкцией (основанной на проблеме). Это альтернатива разделяй и властвуй. Сначала мы определим перечисление (учитывая, что это только для unsigned int).

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

теперь мы определим класс, который проходит через значения перечисления и сравнивает и возвращает соответствующую длину.

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

время выполнения этого решения совпадает с подходом "разделяй и властвуй".


введите номер и создать Arraylist, и цикл while запишет все цифры в Arraylist. Затем мы можем вынуть размер массива, который будет длиной введенного вами целочисленного значения.

ArrayList<Integer> a=new ArrayList<>();

while(number > 0) 
{ 
    remainder = num % 10; 
    a.add(remainder);
    number = number / 10; 
} 

int m=a.size();

простой рекурсивный способ

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

Не проверял


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

    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+"]");
    }

другой строкой подход. Короткий и сладкий - для любого целого числа n.

int length = ("" + n).length();

    int num = 02300;
    int count = 0;
    while(num>0){
         if(num == 0) break;
         num=num/10;
         count++;
    }
    System.out.println(count);

вот очень простой метод, который я сделал, который работает для любого числа:

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

способ работы с переменной счетчика чисел заключается в том, что 10 = 1-значное пространство. Например.1 = 1 десятая => 1 цифра пространства. Поэтому если у вас есть int number = 103342; вы получите 6, потому что это эквивалент .Обратно 000001 пространства. Кроме того, у кого-нибудь есть лучшее имя переменной для numberCounter? Я не могу придумать ничего лучше.

Edit: просто подумал о лучшем объяснении. По сути этот цикл while делает это так, что вы делите свое число на 10, пока оно не станет меньше единицы. По сути, когда вы делите что-то на 10, вы перемещаете его на одно числовое пространство, поэтому вы просто делите его на 10, пока не достигнете

вот еще одна версия, которая может подсчитать количество чисел в десятичном:

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

один хочет сделать это в основном потому, что он хочет "представить" его, что в основном означает, что он, наконец, должен быть "toString-ed" (или преобразован другим способом) явно или неявно; прежде чем он может быть представлен (напечатан, например).

Если это так, то просто попробуйте сделать необходимый "toString" явным и подсчитать биты.


вы могли бы цифры, используя последовательное деление на десять:

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

попробуйте преобразовать int до строка а затем получить длину строка. Это должно получить длину int.

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