Come posso ottenere un conteggio del numero totale di cifre in un numero?


114

Come posso ottenere un conteggio del numero totale di cifre di un numero in C #? Ad esempio, il numero 887979789 ha 9 cifre.


6
prova a usare .Length se non funziona convertilo prima in una stringa
Breezer

Diciamo x = 887979789; x.ToString () Count ().; te lo darà.
nPcomp

Risposte:


175

Senza convertire in una stringa potresti provare:

Math.Ceiling(Math.Log10(n));

Correzione a seguito del commento di ysap:

Math.Floor(Math.Log10(n) + 1);

10
Temo ceil (log10 (10)) = ceil (1) = 1, e non 2 come dovrebbe essere per questa domanda!
ysap

3
Grazie, è un bel metodo. Sebbene non sia più veloce di int count = 0; do {count ++; } while ((i / = 10)> = 1); :(
Puterdo Borato

3
Se il tuo intervallo di numeri include negativi, dovrai usare Math.Floor (Math.Log10 (Math.Abs ​​(n)) + 1);
mrcrowl

1
Bene, se nè 0può semplicemente restituire 1:) Troppo gestire i valori negativi basta sostituire ncon Math.Abs(n).
Umair

3
@Puterdo Borato: il mio test delle prestazioni ha effettivamente dimostrato che il tuo metodo è più veloce quando il numero di cifre è <5. Superalo, Steve's Math.floor è più veloce.
stack247

83

Prova questo:

myint.ToString().Length

Funziona ?


25
Vale la pena sottolineare che probabilmente incontrerai problemi con questo metodo se hai a che fare con numeri negativi. (E ovviamente decimali, ma nell'esempio viene utilizzato un int, quindi presumo che non sia un problema.)
Cody Gray

2
L'allocazione di stringhe @Krythic è la nuova mania nel mondo .NET.
nawfal

1
nuovo? Quasi. Stavo egregiamente allocando le stringhe nel 2010. Che trend setter. Lol. Hai ragione però. Questo è sporco!
Andiih

3
@Krythic Non sono gli anni '80, il tuo computer ha abbastanza RAM per salvare una stringa di 10 caratteri in memoria per la durata di un'operazione.
MrLore

2
@MrLore Nelle applicazioni semplici questo può essere vero, ma nel mondo dello sviluppo di giochi è una bestia completamente diversa.
Krythic

48

La soluzione

Uno dei seguenti metodi di estensione farà il lavoro. Tutti considerano il segno meno come una cifra e funzionano correttamente per tutti i possibili valori di input. Funzionano anche per .NET Framework e .NET Core. Esistono tuttavia differenze di prestazioni rilevanti (discusse di seguito), a seconda della scelta della piattaforma / framework.

Versione Int32:

public static class Int32Extensions
{
    // IF-CHAIN:
    public static int Digits_IfChain(this int n)
    {
        if (n >= 0)
        {
            if (n < 10) return 1;
            if (n < 100) return 2;
            if (n < 1000) return 3;
            if (n < 10000) return 4;
            if (n < 100000) return 5;
            if (n < 1000000) return 6;
            if (n < 10000000) return 7;
            if (n < 100000000) return 8;
            if (n < 1000000000) return 9;
            return 10;
        }
        else
        {
            if (n > -10) return 2;
            if (n > -100) return 3;
            if (n > -1000) return 4;
            if (n > -10000) return 5;
            if (n > -100000) return 6;
            if (n > -1000000) return 7;
            if (n > -10000000) return 8;
            if (n > -100000000) return 9;
            if (n > -1000000000) return 10;
            return 11;
        }
    }

    // USING LOG10:
    public static int Digits_Log10(this int n) =>
        n == 0 ? 1 : (n > 0 ? 1 : 2) + (int)Math.Log10(Math.Abs((double)n));

    // WHILE LOOP:
    public static int Digits_While(this int n)
    {
        int digits = n < 0 ? 2 : 1;
        while ((n /= 10) != 0) ++digits;
        return digits;
    }

    // STRING CONVERSION:
    public static int Digits_String(this int n) =>
        n.ToString().Length;
}

Versione Int64:

public static class Int64Extensions
{
    // IF-CHAIN:
    public static int Digits_IfChain(this long n)
    {
        if (n >= 0)
        {
            if (n < 10L) return 1;
            if (n < 100L) return 2;
            if (n < 1000L) return 3;
            if (n < 10000L) return 4;
            if (n < 100000L) return 5;
            if (n < 1000000L) return 6;
            if (n < 10000000L) return 7;
            if (n < 100000000L) return 8;
            if (n < 1000000000L) return 9;
            if (n < 10000000000L) return 10;
            if (n < 100000000000L) return 11;
            if (n < 1000000000000L) return 12;
            if (n < 10000000000000L) return 13;
            if (n < 100000000000000L) return 14;
            if (n < 1000000000000000L) return 15;
            if (n < 10000000000000000L) return 16;
            if (n < 100000000000000000L) return 17;
            if (n < 1000000000000000000L) return 18;
            return 19;
        }
        else
        {
            if (n > -10L) return 2;
            if (n > -100L) return 3;
            if (n > -1000L) return 4;
            if (n > -10000L) return 5;
            if (n > -100000L) return 6;
            if (n > -1000000L) return 7;
            if (n > -10000000L) return 8;
            if (n > -100000000L) return 9;
            if (n > -1000000000L) return 10;
            if (n > -10000000000L) return 11;
            if (n > -100000000000L) return 12;
            if (n > -1000000000000L) return 13;
            if (n > -10000000000000L) return 14;
            if (n > -100000000000000L) return 15;
            if (n > -1000000000000000L) return 16;
            if (n > -10000000000000000L) return 17;
            if (n > -100000000000000000L) return 18;
            if (n > -1000000000000000000L) return 19;
            return 20;
        }
    }

    // USING LOG10:
    public static int Digits_Log10(this long n) =>
        n == 0L ? 1 : (n > 0L ? 1 : 2) + (int)Math.Log10(Math.Abs((double)n));

    // WHILE LOOP:
    public static int Digits_While(this long n)
    {
        int digits = n < 0 ? 2 : 1;
        while ((n /= 10L) != 0L) ++digits;
        return digits;
    }

    // STRING CONVERSION:
    public static int Digits_String(this long n) =>
        n.ToString().Length;
}

Discussione

Questa risposta include test eseguiti per entrambi Int32e Int64tipi, utilizzando una matrice di 100.000.000campione casuale int/ longnumeri. Il dataset casuale viene preelaborato in un array prima di eseguire i test.

Test di coerenza tra i 4 metodi differenti sono stati eseguiti, per MinValue, casi di confine negativi, -1, 0, 1, casi di confine positivi, MaxValuee anche per l'intero insieme di dati casuali. Nessun test di consistenza fallisce per i metodi sopra forniti, TRANNE il metodo LOG10 (questo verrà discusso più avanti).

I test sono stati eseguiti il .NET Framework 4.7.2e .NET Core 2.2; per x86e x64piattaforme, su una macchina con processore Intel a 64 bit, con Windows 10e con VS2017 v.15.9.17. I seguenti 4 casi hanno lo stesso effetto sui risultati delle prestazioni:

.NET Framework (x86)

  • Platform = x86

  • Platform = AnyCPU, Prefer 32-bitè controllato nelle impostazioni del progetto

.NET Framework (x64)

  • Platform = x64

  • Platform = AnyCPU, Prefer 32-bitè deselezionato nelle impostazioni del progetto

.NET Core (x86)

  • "C:\Program Files (x86)\dotnet\dotnet.exe" bin\Release\netcoreapp2.2\ConsoleApp.dll

  • "C:\Program Files (x86)\dotnet\dotnet.exe" bin\x86\Release\netcoreapp2.2\ConsoleApp.dll

.NET Core (x64)

  • "C:\Program Files\dotnet\dotnet.exe" bin\Release\netcoreapp2.2\ConsoleApp.dll

  • "C:\Program Files\dotnet\dotnet.exe" bin\x64\Release\netcoreapp2.2\ConsoleApp.dll

risultati

I test delle prestazioni di seguito producono una distribuzione uniforme dei valori tra l'ampia gamma di valori che un numero intero potrebbe assumere. Ciò significa che c'è una possibilità molto più alta di testare valori con un numero elevato di cifre. In scenari di vita reale, la maggior parte dei valori può essere piccola, quindi IF-CHAIN ​​dovrebbe funzionare ancora meglio. Inoltre, il processore memorizzerà nella cache e ottimizzerà le decisioni IF-CHAIN ​​in base al set di dati.

Come ha sottolineato @AlanSingfield nella sezione dei commenti, il metodo LOG10 doveva essere corretto con un casting doubleall'interno Math.Abs()per il caso in cui il valore di input è int.MinValueo long.MinValue.

Per quanto riguarda i primi test delle prestazioni che ho implementato prima di modificare questa domanda (doveva essere già modificata un milione di volte), c'era un caso specifico segnalato da @ GyörgyKőszeg , in cui il metodo IF-CHAIN ​​è più lento del metodo LOG10.

Ciò accade ancora, anche se l'entità della differenza è diventata molto inferiore dopo la correzione del problema segnalata da @AlanSingfield . Questa correzione (l'aggiunta di un cast a double) causa un errore di calcolo quando il valore di input è esattamente -999999999999999999: il metodo LOG10 restituisce 20invece di 19. Il metodo LOG10 deve anche avere una ifprotezione per il caso in cui il valore di input è zero.

Il metodo LOG10 è abbastanza complicato da far funzionare per tutti i valori, il che significa che dovresti evitarlo. Se qualcuno trova un modo per farlo funzionare correttamente per tutti i test di coerenza di seguito, per favore pubblica un commento!

Il metodo WHILE ha anche ottenuto una recente versione refactoring che è più veloce, ma è ancora lento Platform = x86(non sono riuscito a trovare il motivo, fino ad ora).

Il metodo STRING è costantemente lento: assegna avidamente troppa memoria per niente. È interessante notare che in .NET Core, l'allocazione delle stringhe sembra essere molto più veloce rispetto a .NET Framework. Buono a sapersi.

Il metodo IF-CHAIN ​​dovrebbe superare tutti gli altri metodi nel 99,99% dei casi; e, a mio parere personale, è la scelta migliore (considerando tutte le regolazioni necessarie per far funzionare correttamente il metodo LOG10 e le cattive prestazioni degli altri due metodi).

Infine, i risultati sono:

inserisci qui la descrizione dell'immagine

Poiché questi risultati dipendono dall'hardware, consiglio comunque di eseguire i test delle prestazioni di seguito sul tuo computer se hai davvero bisogno di essere sicuro al 100% nel tuo caso specifico.

Codice di prova

Di seguito è riportato il codice per il test delle prestazioni e anche il test di coerenza. Lo stesso codice viene utilizzato sia per .NET Framework che per .NET Core.

using System;
using System.Diagnostics;

namespace NumberOfDigits
{
    // Performance Tests:
    class Program
    {
        private static void Main(string[] args)
        {
            Console.WriteLine("\r\n.NET Core");

            RunTests_Int32();
            RunTests_Int64();
        }

        // Int32 Performance Tests:
        private static void RunTests_Int32()
        {
            Console.WriteLine("\r\nInt32");

            const int size = 100000000;
            int[] samples = new int[size];
            Random random = new Random((int)DateTime.Now.Ticks);
            for (int i = 0; i < size; ++i)
                samples[i] = random.Next(int.MinValue, int.MaxValue);

            Stopwatch sw1 = new Stopwatch();
            sw1.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_IfChain();
            sw1.Stop();
            Console.WriteLine($"IfChain: {sw1.ElapsedMilliseconds} ms");

            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_Log10();
            sw2.Stop();
            Console.WriteLine($"Log10: {sw2.ElapsedMilliseconds} ms");

            Stopwatch sw3 = new Stopwatch();
            sw3.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_While();
            sw3.Stop();
            Console.WriteLine($"While: {sw3.ElapsedMilliseconds} ms");

            Stopwatch sw4 = new Stopwatch();
            sw4.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_String();
            sw4.Stop();
            Console.WriteLine($"String: {sw4.ElapsedMilliseconds} ms");


            // Start of consistency tests:
            Console.WriteLine("Running consistency tests...");
            bool isConsistent = true;

            // Consistency test on random set:
            for (int i = 0; i < samples.Length; ++i)
            {
                int s = samples[i];
                int a = s.Digits_IfChain();
                int b = s.Digits_Log10();
                int c = s.Digits_While();
                int d = s.Digits_String();
                if (a != b || c != d || a != c)
                {
                    Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
                    isConsistent = false;
                    break;
                }
            }

            // Consistency test of special values:
            samples = new int[]
            {
                0,
                int.MinValue, -1000000000, -999999999, -100000000, -99999999, -10000000, -9999999, -1000000, -999999, -100000, -99999, -10000, -9999, -1000, -999, -100, -99, -10, -9, - 1,
                int.MaxValue, 1000000000, 999999999, 100000000, 99999999, 10000000, 9999999, 1000000, 999999, 100000, 99999, 10000, 9999, 1000, 999, 100, 99, 10, 9,  1,
            };
            for (int i = 0; i < samples.Length; ++i)
            {
                int s = samples[i];
                int a = s.Digits_IfChain();
                int b = s.Digits_Log10();
                int c = s.Digits_While();
                int d = s.Digits_String();
                if (a != b || c != d || a != c)
                {
                    Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
                    isConsistent = false;
                    break;
                }
            }

            // Consistency test result:
            if (isConsistent)
                Console.WriteLine("Consistency tests are OK");
        }

        // Int64 Performance Tests:
        private static void RunTests_Int64()
        {
            Console.WriteLine("\r\nInt64");

            const int size = 100000000;
            long[] samples = new long[size];
            Random random = new Random((int)DateTime.Now.Ticks);
            for (int i = 0; i < size; ++i)
                samples[i] = Math.Sign(random.Next(-1, 1)) * (long)(random.NextDouble() * long.MaxValue);

            Stopwatch sw1 = new Stopwatch();
            sw1.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_IfChain();
            sw1.Stop();
            Console.WriteLine($"IfChain: {sw1.ElapsedMilliseconds} ms");

            Stopwatch sw2 = new Stopwatch();
            sw2.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_Log10();
            sw2.Stop();
            Console.WriteLine($"Log10: {sw2.ElapsedMilliseconds} ms");

            Stopwatch sw3 = new Stopwatch();
            sw3.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_While();
            sw3.Stop();
            Console.WriteLine($"While: {sw3.ElapsedMilliseconds} ms");

            Stopwatch sw4 = new Stopwatch();
            sw4.Start();
            for (int i = 0; i < size; ++i) samples[i].Digits_String();
            sw4.Stop();
            Console.WriteLine($"String: {sw4.ElapsedMilliseconds} ms");

            // Start of consistency tests:
            Console.WriteLine("Running consistency tests...");
            bool isConsistent = true;

            // Consistency test on random set:
            for (int i = 0; i < samples.Length; ++i)
            {
                long s = samples[i];
                int a = s.Digits_IfChain();
                int b = s.Digits_Log10();
                int c = s.Digits_While();
                int d = s.Digits_String();
                if (a != b || c != d || a != c)
                {
                    Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
                    isConsistent = false;
                    break;
                }
            }

            // Consistency test of special values:
            samples = new long[] 
            {
                0,
                long.MinValue, -1000000000000000000, -999999999999999999, -100000000000000000, -99999999999999999, -10000000000000000, -9999999999999999, -1000000000000000, -999999999999999, -100000000000000, -99999999999999, -10000000000000, -9999999999999, -1000000000000, -999999999999, -100000000000, -99999999999, -10000000000, -9999999999, -1000000000, -999999999, -100000000, -99999999, -10000000, -9999999, -1000000, -999999, -100000, -99999, -10000, -9999, -1000, -999, -100, -99, -10, -9, - 1,
                long.MaxValue, 1000000000000000000, 999999999999999999, 100000000000000000, 99999999999999999, 10000000000000000, 9999999999999999, 1000000000000000, 999999999999999, 100000000000000, 99999999999999, 10000000000000, 9999999999999, 1000000000000, 999999999999, 100000000000, 99999999999, 10000000000, 9999999999, 1000000000, 999999999, 100000000, 99999999, 10000000, 9999999, 1000000, 999999, 100000, 99999, 10000, 9999, 1000, 999, 100, 99, 10, 9,  1,
            };
            for (int i = 0; i < samples.Length; ++i)
            {
                long s = samples[i];
                int a = s.Digits_IfChain();
                int b = s.Digits_Log10();
                int c = s.Digits_While();
                int d = s.Digits_String();
                if (a != b || c != d || a != c)
                {
                    Console.WriteLine($"Digits({s}): IfChain={a} Log10={b} While={c} String={d}");
                    isConsistent = false;
                    break;
                }
            }

            // Consistency test result:
            if (isConsistent)
                Console.WriteLine("Consistency tests are OK");
        }
    }
}

4
Mi piace questa soluzione, è molto più leggibile dei trucchi matematici e la velocità parla da sola, complimenti.
MrLore

3
Perché questo non è contrassegnato come la soluzione? Le prestazioni sono importanti e questa sembra essere la risposta più ampia.
Martien de Jong

Interessante, ottengo risultati diversi . Per valori casuali Log10 e forza bruta sono quasi gli stessi, ma per long.MaxValueLog10 è significativamente migliore. O è solo in .NET Core?
György Kőszeg

@ GyörgyKőszeg: ho aggiunto test per Int64. Tieni presente che i test per Int32e Int64generano diversi set di dati, il che potrebbe spiegare perché sono Int64stati più veloci rispetto Int32ad alcuni casi. Tuttavia, all'interno del Int32test e all'interno del Int64test i set di dati non vengono modificati durante il test dei diversi metodi di calcolo. Per quanto riguarda .NET Core, dubito che ci sia un'ottimizzazione magica nella libreria Math che cambierebbe questi risultati, ma mi piacerebbe saperne di più (la mia risposta è già enorme, probabilmente una delle più grandi in SO ;-)
sɐunıɔ ןɐ qɐp

@ GyörgyKőszeg: Inoltre, le misurazioni delle prestazioni di basso livello sono molto complicate. Io di solito preferisco mantenere il codice più semplice possibile (io preferisco semplici forloop oltre enumerations, I pre-processo set di dati casuali, e di evitare l'uso dei farmaci generici, Attività, Function<>, Action<>, o qualsiasi sistema di misurazione nero-scatola). In sintesi, mantienilo semplice. Uccido anche tutte le applicazioni non necessarie (Skype, Windows Defender, disabilito Anti-Virus, Chrome, cache di Microsoft Office, ecc.).
sɐunıɔ ןɐ qɐp

13

Non direttamente in C #, ma la formula è: n = floor(log10(x)+1)


2
log10 (0) è -infinity
Alex Klaus

2
@Klaus - log10 (0) in realtà non è definito. Ma hai ragione in quanto è un caso speciale che deve essere testato e trattato separatamente. Questo vale anche per qualsiasi numero intero non positivo. Vedi i commenti alla risposta di Steve.
ysap

@ysap: Log10 è abbastanza difficile da far funzionare correttamente. Hai qualche idea su come implementarlo correttamente per tutta la gamma di possibili valori di input?
sɐunıɔ ןɐ qɐp

@ sɐunıɔ ןɐ qɐp - log10è nella maggior parte dei casi una funzione di libreria. Perché dovresti implementarlo tu stesso e quali problemi incontri? log10(x) = log2(x) / log2(10), o in generale logA(x) = logB(x) / logB(A).
ysap

Non intendevo implementare nuovamente Log10, voglio dire Log10(0)è -infinity. Log10 non può essere utilizzato per calcolare il numero di cifre di numeri negativi a meno che non venga utilizzato Math.Abs()prima di passare il valore a Log10. Ma poi Math.Abs(int.MinValue)lancia un'eccezione ( long.MinValueanche in caso di Int64). Se eseguiamo il cast del numero per raddoppiarlo prima di passarlo a Log10, allora funziona per quasi tutti i numeri tranne -999999999999999999(nel caso di Int64). Conosci qualche formula per calcolare il numero di cifre che utilizza log10 e accetta qualsiasi valore int32 o int64 come input e restituisce solo valori validi?
sɐunıɔ ןɐ qɐp

9

Le risposte già qui funzionano per interi senza segno, ma non ho trovato buone soluzioni per ottenere il numero di cifre da decimali e doppi.

public static int Length(double number)
{
    number = Math.Abs(number);
    int length = 1;
    while ((number /= 10) >= 1)
        length++;
    return length;
}
//number of digits in 0 = 1,
//number of digits in 22.1 = 2,
//number of digits in -23 = 2

Puoi cambiare il tipo di input da doublea decimalse la precisione è importante, ma anche il decimale ha un limite.


7

La risposta di Steve è corretta , ma non funziona per numeri interi inferiori a 1.

Ecco una versione aggiornata che funziona per i negativi:

int digits = n == 0 ? 1 : Math.Floor(Math.Log10(Math.Abs(n)) + 1)

Ti manca un castingto int:digits = n == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(n)) + 1);
sɐunıɔ ןɐ qɐp

L'ho fatto senza istruzione if: digits = (int) Math.Floor (Math.Abs ​​(Math.Log10 (Math.Abs ​​(n))) + 1)
KOLRH

Questo genera un'eccezione quando n = int.MinValue.
sɐunıɔ ןɐ qɐp

5

Utilizzo della ricorsione (a volte chiesto durante le interviste)

public int CountDigits(int number)
{
    // In case of negative numbers
    number = Math.Abs(number);

    if (number >= 10)
        return CountDigits(number / 10) + 1;
    return 1;
 }

1
Questo genera un'eccezione quando number = int.MinValue.
sɐunıɔ ןɐ qɐp

4
static void Main(string[] args)
{
    long blah = 20948230498204;
    Console.WriteLine(blah.ToString().Length);
}

2
Attenzione agli -1
aspetti

2

Ecco un'implementazione che utilizza una ricerca binaria. Sembra essere il più veloce finora su int32.

L'implementazione di Int64 viene lasciata come esercizio per il lettore (!)

Ho provato a utilizzare Array.BinarySearch invece di codificare l'albero, ma era circa la metà della velocità.

EDIT: una tabella di ricerca è molto più veloce della ricerca binaria, a scapito dell'utilizzo di più memoria. Realisticamente, probabilmente userei la ricerca binaria in produzione, la tabella di ricerca è molto complessa per un guadagno di velocità che probabilmente sarà oscurato da altre parti del software.

Lookup-Table: 439 ms
Binary-Search: 1069 ms
If-Chain: 1409 ms
Log10: 1145 ms
While: 1768 ms
String: 5153 ms

Versione tabella di ricerca:

static byte[] _0000llll = new byte[0x10000];
static byte[] _FFFFllll = new byte[0x10001];
static sbyte[] _hhhhXXXXdigits = new sbyte[0x10000];

// Special cases where the high DWORD is not enough information to find out how
// many digits.
static ushort[] _lowordSplits = new ushort[12];
static sbyte[] _lowordSplitDigitsLT = new sbyte[12];
static sbyte[] _lowordSplitDigitsGE = new sbyte[12];

static Int32Extensions()
{
    // Simple lookup tables for number of digits where value is 
    //    0000xxxx (0 .. 65535)
    // or FFFFxxxx (-1 .. -65536)
    precomputePositiveLo16();
    precomputeNegativeLo16();

    // Hiword is a little more complex
    precomputeHiwordDigits();
}

private static void precomputeHiwordDigits()
{
    int b = 0;

    for(int hhhh = 0; hhhh <= 0xFFFF; hhhh++)
    {
        // For hiword hhhh, calculate integer value for loword of 0000 and FFFF.
        int hhhh0000 = (unchecked(hhhh * 0x10000));  // wrap around on negatives
        int hhhhFFFF = hhhh0000 + 0xFFFF;

        // How many decimal digits for each?
        int digits0000 = hhhh0000.Digits_IfChain();
        int digitsFFFF = hhhhFFFF.Digits_IfChain();

        // If same number of decimal digits, we know that when we see that hiword
        // we don't have to look at the loword to know the right answer.
        if(digits0000 == digitsFFFF)
        {
            _hhhhXXXXdigits[hhhh] = (sbyte)digits0000;
        }
        else
        {
            bool negative = hhhh >= 0x8000;

            // Calculate 10, 100, 1000, 10000 etc
            int tenToThePower = (int)Math.Pow(10, (negative ? digits0000 : digitsFFFF) - 1);

            // Calculate the loword of the 10^n value.
            ushort lowordSplit = unchecked((ushort)tenToThePower);
            if(negative)
                lowordSplit = unchecked((ushort)(2 + (ushort)~lowordSplit));

            // Store the split point and digits into these arrays
            _lowordSplits[b] = lowordSplit;
            _lowordSplitDigitsLT[b] = (sbyte)digits0000;
            _lowordSplitDigitsGE[b] = (sbyte)digitsFFFF;

            // Store the minus of the array index into the digits lookup. We look for
            // minus values and use these to trigger using the split points logic.
            _hhhhXXXXdigits[hhhh] = (sbyte)(-b);
            b++;
        }
    }
}

private static void precomputePositiveLo16()
{
    for(int i = 0; i <= 9; i++)
        _0000llll[i] = 1;

    for(int i = 10; i <= 99; i++)
        _0000llll[i] = 2;

    for(int i = 100; i <= 999; i++)
        _0000llll[i] = 3;

    for(int i = 1000; i <= 9999; i++)
        _0000llll[i] = 4;

    for(int i = 10000; i <= 65535; i++)
        _0000llll[i] = 5;
}

private static void precomputeNegativeLo16()
{
    for(int i = 0; i <= 9; i++)
        _FFFFllll[65536 - i] = 1;

    for(int i = 10; i <= 99; i++)
        _FFFFllll[65536 - i] = 2;

    for(int i = 100; i <= 999; i++)
        _FFFFllll[65536 - i] = 3;

    for(int i = 1000; i <= 9999; i++)
        _FFFFllll[65536 - i] = 4;

    for(int i = 10000; i <= 65535; i++)
        _FFFFllll[65536 - i] = 5;
}



public static int Digits_LookupTable(this int n)
{
    // Split input into low word and high word.
    ushort l = unchecked((ushort)n);
    ushort h = unchecked((ushort)(n >> 16));

    // If the hiword is 0000 or FFFF we have precomputed tables for these.
    if(h == 0x0000)
    {
        return _0000llll[l];
    }
    else if(h == 0xFFFF)
    {
        return _FFFFllll[l];
    }

    // In most cases the hiword will tell us the number of decimal digits.
    sbyte digits = _hhhhXXXXdigits[h];

    // We put a positive number in this lookup table when
    // hhhh0000 .. hhhhFFFF all have the same number of decimal digits.
    if(digits > 0)
        return digits;

    // Where the answer is different for hhhh0000 to hhhhFFFF, we need to
    // look up in a separate array to tell us at what loword the change occurs.
    var splitIndex = (sbyte)(-digits);

    ushort lowordSplit = _lowordSplits[splitIndex];

    // Pick the correct answer from the relevant array, depending whether
    // our loword is lower than the split point or greater/equal. Note that for
    // negative numbers, the loword is LOWER for MORE decimal digits.
    if(l < lowordSplit)
        return _lowordSplitDigitsLT[splitIndex];
    else
        return _lowordSplitDigitsGE[splitIndex];
}

Versione di ricerca binaria

        public static int Digits_BinarySearch(this int n)
        {
            if(n >= 0)
            {
                if(n <= 9999) // 0 .. 9999
                {
                    if(n <= 99) // 0 .. 99
                    {
                        return (n <= 9) ? 1 : 2;
                    }
                    else // 100 .. 9999
                    {
                        return (n <= 999) ? 3 : 4;
                    }
                }
                else // 10000 .. int.MaxValue
                {
                    if(n <= 9_999_999) // 10000 .. 9,999,999
                    {
                        if(n <= 99_999)
                            return 5;
                        else if(n <= 999_999)
                            return 6;
                        else
                            return 7;
                    }
                    else // 10,000,000 .. int.MaxValue
                    {
                        if(n <= 99_999_999)
                            return 8;
                        else if(n <= 999_999_999)
                            return 9;
                        else
                            return 10;
                    }
                }
            }
            else
            {
                if(n >= -9999) // -9999 .. -1
                {
                    if(n >= -99) // -99 .. -1
                    {
                        return (n >= -9) ? 1 : 2;
                    }
                    else // -9999 .. -100
                    {
                        return (n >= -999) ? 3 : 4;
                    }
                }
                else // int.MinValue .. -10000
                {
                    if(n >= -9_999_999) // -9,999,999 .. -10000
                    {
                        if(n >= -99_999)
                            return 5;
                        else if(n >= -999_999)
                            return 6;
                        else
                            return 7;
                    }
                    else // int.MinValue .. -10,000,000 
                    {
                        if(n >= -99_999_999)
                            return 8;
                        else if(n >= -999_999_999)
                            return 9;
                        else
                            return 10;
                    }
                }
            }
        }

        Stopwatch sw0 = new Stopwatch();
        sw0.Start();
        for(int i = 0; i < size; ++i) samples[i].Digits_BinarySearch();
        sw0.Stop();
        Console.WriteLine($"Binary-Search: {sw0.ElapsedMilliseconds} ms");

Approccio molto interessante. È effettivamente più veloce dei metodi "Log10", "string.Length" e "While" per valori interi distribuiti uniformemente. In scenari di casi reali, la distribuzione dei valori interi deve essere sempre considerata su soluzioni if-chain. +1
sɐunıɔ ןɐ qɐp

L'approccio LookUpTable sembra essere super veloce per gli scenari in cui l'accesso alla memoria non è il collo di bottiglia. Credo fermamente che per scenari con accesso frequente alla memoria, LookUpTable diventi più lento rispetto ai metodi if-chain, come quello BinSearch che hai suggerito. A proposito, hai l' Int64implementazione per LookUpTable? O pensi che sia troppo complicato implementarlo? Vorrei eseguire i test delle prestazioni in seguito sul set completo.
sɐunıɔ ןɐ qɐp

Ehi, non sono arrivato fino a quello a 64 bit. Il principio dovrebbe essere leggermente diverso in quanto avresti bisogno di 4x livelli piuttosto che solo hiword e loword. Sicuramente d'accordo sul fatto che nel mondo reale, la cache della CPU avrà molte altre esigenze in competizione per lo spazio, e c'è molto spazio per miglioramenti nel ridurre le dimensioni della ricerca (>> 1 quindi vengono in mente solo i numeri pari) . La ricerca binaria potrebbe essere migliorata polarizzando verso 9,10,8 cifre invece di 1,2,3,4, data la distribuzione del tuo set di dati casuali.
Alan Singfield

1

dividere un numero per 10 ti darà la cifra più a sinistra, quindi fare una mod 10 sul numero dà il numero senza la prima cifra e ripeti finché non hai tutte le cifre


0
int i = 855865264;
int NumLen = i.ToString().Length;

2
fallisce per int negativo e per numeri come 23.00. Fai un lavoro string.TrimStart('-')migliore
nawfal

0

Crea un metodo che restituisca tutte le cifre e un altro che le conteggi:

public static int GetNumberOfDigits(this long value)
{
    return value.GetDigits().Count();
}

public static IEnumerable<int> GetDigits(this long value)
{
    do
    {
        yield return (int)(value % 10);
        value /= 10;
    } while (value != 0);
}

Questo mi è sembrato l'approccio più intuitivo per affrontare questo problema. Ho provato il fileLog10 prima metodo a causa della sua apparente semplicità, ma ha una quantità folle di casi d'angolo e problemi di precisione.

Ho anche trovato il file if -chain proposta nell'altra risposta a un po 'brutta da guardare.

So che questo non è il metodo più efficiente, ma ti dà l'altra estensione per restituire le cifre anche per altri usi (puoi semplicemente contrassegnarlo privatese non hai bisogno di usarlo fuori dalla classe).

Tieni presente che non considera il segno negativo come una cifra.


-2

converti in stringa e poi puoi contare il numero totale di cifre con il metodo .length. Piace:

String numberString = "855865264".toString();
int NumLen = numberString .Length;

1
L'assegnazione di una stringa è completamente inutile.
Krythic

-2

Dipende esattamente da cosa vuoi fare con le cifre. Puoi scorrere le cifre partendo dall'ultima alla prima in questo modo:

int tmp = number;
int lastDigit = 0;
do
{
    lastDigit = tmp / 10;
    doSomethingWithDigit(lastDigit);
    tmp %= 10;
} while (tmp != 0);

1
La tua logica è invertita. È necessario utilizzare %per ottenere la cifra e quindi /=ridurla.
julealgon


-3

Supponendo che la tua domanda si riferisse a un int, quanto segue funziona anche per negativo / positivo e zero:

Math.Floor((decimal) Math.Abs(n)).ToString().Length
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.