¿Cómo puedo contar el número total de dígitos de un número?


114

¿Cómo puedo obtener un recuento de la cantidad total de dígitos de un número en C #? Por ejemplo, el número 887979789 tiene 9 dígitos.


6
intente usar .Length si no funciona, conviértalo en una cadena primero
Breezer

Digamos x = 887979789; x.ToString (). Count (); te dará eso.
nPcomp

Respuestas:


174

Sin convertir a una cadena, puede intentar:

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

Corrección siguiendo el comentario de ysap:

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

10
¡Me temo que ceil (log10 (10)) = ceil (1) = 1, y no 2 como debería ser para esta pregunta!
ysap

3
Gracias, es un buen método. Aunque no es más rápido que int count = 0; hacer {contar ++; } mientras ((i / = 10)> = 1); :(
Puterdo Borato

3
Si su rango de números incluye negativos, deberá usar Math.Floor (Math.Log10 (Math.Abs ​​(n)) + 1);
mrcrowl

1
Bueno, si nse 0puede devolver 1:) Para manejar valores negativos, simplemente reemplácelos ncon Math.Abs(n).
Umair

3
@Puterdo Borato: mi prueba de rendimiento mostró que tu método es más rápido cuando el número de dígitos es <5. Pasa eso, Steve's Math.floor es más rápido.
stack247

83

Prueba esto:

myint.ToString().Length

Eso funciona ?


25
Vale la pena señalar que es probable que tenga problemas con este método si se trata de números negativos. (Y obviamente decimales, pero el ejemplo usa un int, así que supongo que eso no es un problema).
Cody Gray

2
La asignación de cadenas @Krythic es la nueva moda en el mundo .NET.
nawfal

1
¿nuevo? Apenas. Estuve asignando cuerdas de manera atroz en 2010. Qué marca de tendencias. Jajaja Aunque tienes razón. ¡Esto está sucio!
Andiih

3
@Krythic No es la década de 1980, su computadora tiene suficiente RAM para guardar una cadena de 10 caracteres en la memoria durante una operación.
MrLore

2
@MrLore En aplicaciones simples, esto puede ser cierto, pero en el mundo del desarrollo de juegos, es una bestia completamente diferente.
Krythic

48

La solución

Cualquiera de los siguientes métodos de extensión funcionará. Todos ellos consideran el signo menos como un dígito y funcionan correctamente para todos los valores de entrada posibles. También funcionan para .NET Framework y .NET Core. Sin embargo, existen diferencias de rendimiento relevantes (que se analizan a continuación), según su elección de plataforma / marco.

Versión 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;
}

Versión 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;
}

Discusión

Esta respuesta incluye pruebas realizadas para ambos tipos Int32y Int64, utilizando una matriz de números / 100.000.000muestreados aleatoriamente . El conjunto de datos aleatorio se procesa previamente en una matriz antes de ejecutar las pruebas.intlong

También se ejecutaron pruebas de coherencia entre los 4 métodos diferentes, por MinValue, casos fronterizos negativos, -1, 0, 1, casos fronterizos positivas, MaxValuey también para todo el conjunto de datos al azar. Ninguna prueba de consistencia falla para los métodos proporcionados anteriormente, EXCEPTO para el método LOG10 (esto se analiza más adelante).

Las pruebas se ejecutaron en .NET Framework 4.7.2y .NET Core 2.2; para plataformas x86y x64, en una máquina con procesador Intel de 64 bits, con Windows 10y con VS2017 v.15.9.17. Los siguientes 4 casos tienen el mismo efecto en los resultados de rendimiento:

.NET Framework (x86)

  • Platform = x86

  • Platform = AnyCPU, Prefer 32-bitestá marcado en la configuración del proyecto

.NET Framework (x64)

  • Platform = x64

  • Platform = AnyCPU, Prefer 32-bitestá desmarcado en la configuración del proyecto

.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

Resultados

Las pruebas de rendimiento siguientes producen una distribución uniforme de valores entre la amplia gama de valores que podría asumir un número entero. Esto significa que existe una probabilidad mucho mayor de probar valores con una gran cantidad de dígitos. En escenarios de la vida real, la mayoría de los valores pueden ser pequeños, por lo que IF-CHAIN ​​debería funcionar aún mejor. Además, el procesador almacenará en caché y optimizará las decisiones de IF-CHAIN ​​de acuerdo con su conjunto de datos.

Como @AlanSingfield señaló en la sección de comentarios, el método LOG10 tuvo que arreglarse con una conversión hacia doubleadentro Math.Abs()para el caso en que el valor de entrada es int.MinValueo long.MinValue.

Con respecto a las primeras pruebas de rendimiento que implementé antes de editar esta pregunta (ya tenía que ser editado un millón de veces), hubo un caso específico señalado por @ GyörgyKőszeg , en el que el método IF-CHAIN ​​funciona más lento que el método LOG10.

Esto todavía sucede, aunque la magnitud de la diferencia se redujo mucho después de la solución del problema señalado por @AlanSingfield . Esta corrección (agregar una conversión a double) provoca un error de cálculo cuando el valor de entrada es exactamente -999999999999999999: el método LOG10 devuelve en 20lugar de 19. El método LOG10 también debe tener una ifprotección para el caso en que el valor de entrada sea cero.

El método LOG10 es bastante complicado para trabajar con todos los valores, lo que significa que debe evitarlo. Si alguien encuentra una manera de hacer que funcione correctamente para todas las pruebas de coherencia a continuación, ¡publique un comentario!

El método WHILE también obtuvo una versión refactorizada reciente que es más rápida, pero aún es lenta Platform = x86(no pude encontrar la razón por la cual, hasta ahora).

El método STRING es consistentemente lento: asigna con avidez demasiada memoria por nada. Curiosamente, en .NET Core, la asignación de cadenas parece ser mucho más rápida que en .NET Framework. Bueno saber.

El método IF-CHAIN ​​debería superar a todos los demás métodos en el 99,99% de los casos; y, en mi opinión personal, es su mejor opción (considerando todos los ajustes necesarios para que el método LOG10 funcione correctamente y el mal desempeño de los otros dos métodos).

Finalmente, los resultados son:

ingrese la descripción de la imagen aquí

Dado que estos resultados dependen del hardware, recomiendo de todos modos ejecutar las pruebas de rendimiento a continuación en su propia computadora si realmente necesita estar 100% seguro en su caso específico.

Código de prueba

A continuación se muestra el código para la prueba de rendimiento y también la prueba de coherencia. El mismo código se usa para .NET Framework y .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
Me gusta esta solución, es mucho más legible que los trucos matemáticos y la velocidad habla por sí sola, felicitaciones.
MrLore

3
¿Por qué esto no está marcado como la solución? El rendimiento importa y esta parece ser la respuesta más extensa.
Martien de Jong

Interesante, obtengo resultados diferentes . Para valores aleatorios, Log10 y la fuerza bruta son casi iguales, pero para long.MaxValueLog10 es significativamente mejor. ¿O es solo en .NET Core?
György Kőszeg

@ GyörgyKőszeg: He agregado pruebas para Int64. Tenga en cuenta que las pruebas Int32y Int64generan diferentes conjuntos de datos, lo que puede explicar por qué Int64fue más rápido que Int32en algunos casos. Aunque, dentro de la Int32prueba y dentro de la Int64prueba, los conjuntos de datos no se modifican cuando se prueban los diferentes métodos de cálculo. Ahora, con respecto a .NET Core, dudo que haya alguna optimización mágica en la biblioteca de matemáticas que cambie estos resultados, pero me encantaría saber más sobre eso (mi respuesta ya es enorme, probablemente una de las más grandes en SO ;-)
sɐunıɔ ןɐ qɐp

@ GyörgyKőszeg: Además, las mediciones de rendimiento de bajo nivel son muy complicadas. Por lo general prefieren mantener el código tan simple como sea posible (yo prefiero simples forbucles sobre enumerations, I conjuntos de datos aleatorios pre-proceso, y evitar el uso de genéricos, Tareas Function<>, Action<>o cualquier marco de medición negro-caja). En resumen, manténgalo simple. También elimino todas las aplicaciones innecesarias (Skype, Windows Defender, deshabilito Anti-Virus, Chrome, caché de Microsoft Office, etc.).
sɐunıɔ ןɐ qɐp

13

No directamente en C #, pero la fórmula es: n = floor(log10(x)+1)


2
log10 (0) es -infinity
Alex Klaus

2
@Klaus - log10 (0) en realidad no está definido. Pero tiene razón en que se trata de un caso especial que debe analizarse y tratarse por separado. Esto también es cierto para cualquier número entero no positivo. Vea los comentarios a la respuesta de Steve.
ysap

@ysap: Log10 es bastante complicado para que funcione correctamente. ¿Tiene alguna idea de cómo implementarlo correctamente para todo el rango de posibles valores de entrada?
sɐunıɔ ןɐ qɐp

@ sɐunıɔ ןɐ qɐp - log10es en la mayoría de los casos una función de biblioteca. ¿Por qué querría implementarlo usted mismo y qué problemas encuentra? log10(x) = log2(x) / log2(10), o en general logA(x) = logB(x) / logB(A).
ysap

No quise implementar Log10 nuevamente, quiero decir Log10(0)es -infinito. Log10 no se puede usar para calcular el número de dígitos de números negativos a menos que lo use Math.Abs()antes de pasar el valor a Log10. Pero luego Math.Abs(int.MinValue)arroja una excepción ( long.MinValuetambién, en el caso de Int64). Si convertimos el número al doble antes de pasarlo a Log10, entonces funciona para casi todos los números excepto para -999999999999999999(en el caso de Int64). ¿Conoce alguna fórmula para calcular la cantidad de dígitos que usa log10 y acepta cualquier valor int32 o int64 como entrada y genera solo valores válidos?
sɐunıɔ ןɐ qɐp

9

Las respuestas aquí ya funcionan para enteros sin signo, pero no he encontrado buenas soluciones para obtener el número de dígitos de decimales y dobles.

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

Puede cambiar el tipo de entrada de doublea decimalsi la precisión importa, pero el decimal también tiene un límite.


7

La respuesta de Steve es correcta , pero no funciona para números enteros menores que 1.

Aquí una versión actualizada que funciona para negativos:

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

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

Lo hice sin la declaración if: dígitos = (int) Math.Floor (Math.Abs ​​(Math.Log10 (Math.Abs ​​(n))) + 1)
KOLRH

Esto arroja una excepción cuando n = int.MinValue.
sɐunıɔ ןɐ qɐp

5

Usar recursividad (a veces preguntado en entrevistas)

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
Esto arroja una excepción cuando number = int.MinValue.
sɐunıɔ ןɐ qɐp

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

2
Cuidado con los negativos: -1= 2
MrLore

2

Aquí hay una implementación que utiliza una búsqueda binaria. Parece ser el más rápido hasta ahora en int32.

La implementación de Int64 se deja como ejercicio para el lector (!)

Intenté usar Array.BinarySearch en lugar de codificar el árbol, pero eso fue aproximadamente la mitad de la velocidad.

EDITAR: Una tabla de búsqueda es mucho más rápida que la búsqueda binaria, a expensas de usar más memoria. De manera realista, probablemente usaría la búsqueda binaria en producción, la tabla de búsqueda es muy compleja para una ganancia de velocidad que probablemente sea eclipsada por otras partes del software.

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

Versión de la tabla de búsqueda:

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

Versión de búsqueda 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");

Enfoque muy interesante. De hecho, es más rápido que los métodos "Log10", "string.Length" y "While" para valores enteros distribuidos uniformemente. En escenarios de casos reales, la distribución de los valores enteros siempre se debe considerar en soluciones de tipo if-chain. +1
sɐunıɔ ןɐ qɐp

El enfoque LookUpTable parece ser súper rápido para escenarios donde el acceso a la memoria no es el cuello de botella. Creo firmemente que para escenarios con acceso frecuente a la memoria, LookUpTable se vuelve más lento que los métodos similares a if-chain, como el BinSearch que ha sugerido. Por cierto, ¿tiene la Int64implementación para LookUpTable? ¿O crees que es demasiado complicado implementarlo? Me gustaría ejecutar las pruebas de rendimiento más adelante en el conjunto completo.
sɐunıɔ ןɐ qɐp

Oye, no llegué tan lejos como el de 64 bits. El principio tendría que ser ligeramente diferente en el sentido de que necesitarías niveles 4x en lugar de solo hiword y loword. Definitivamente estoy de acuerdo en que, en el mundo real, la caché de su CPU tendrá muchas otras necesidades competitivas para el espacio, y hay mucho margen de mejora para reducir el tamaño de la búsqueda (>> 1, entonces los números pares solo me vienen a la mente) . La búsqueda binaria se podría mejorar al sesgar hacia 9,10,8 dígitos en lugar de 1,2,3,4, dada la distribución de su conjunto de datos aleatorios.
Alan Singfield

1

dividir un número por 10 le dará el dígito más a la izquierda, luego hacer un mod 10 en el número da el número sin el primer dígito y repetirlo hasta que tenga todos los dígitos


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

2
falla para int negativo y para números como 23.00. Hazlo string.TrimStart('-')mejor
nawfal

0

Cree un método que devuelva todos los dígitos y otro que los cuente:

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

Este me pareció el enfoque más intuitivo al abordar este problema. Probé elLog10 método primero debido a su aparente simplicidad, pero tiene una cantidad increíble de casos de esquina y problemas de precisión.

También encontré el if cadena propuesta en la otra respuesta a un poco fea de ver.

Sé que este no es el método más eficiente, pero le brinda la otra extensión para devolver los dígitos también para otros usos (puede marcarlo privatesi no necesita usarlo fuera de la clase).

Tenga en cuenta que no considera el signo negativo como un dígito.


-2

convertir en cadena y luego puede contar el número de tatal de dígitos por el método .length. Me gusta:

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

1
La asignación de una cadena es completamente innecesaria.
Krythic

-2

Depende de lo que quieras hacer exactamente con los dígitos. Puede iterar a través de los dígitos comenzando desde el último hasta el primero de esta manera:

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

1
Tu lógica se invierte. Necesitas usar %para obtener el dígito y luego /=cortarlo.
julealgon


-3

Suponiendo que su pregunta se refería a un int, lo siguiente también funciona para negativo / positivo y cero:

Math.Floor((decimal) Math.Abs(n)).ToString().Length
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.