[c#] 숫자의 총 자릿수를 어떻게 구할 수 있습니까?

C #에서 숫자의 총 자릿수를 어떻게 구할 수 있습니까? 예를 들어, 숫자 887979789는 9 자리 숫자입니다.



답변

문자열로 변환하지 않고 시도해 볼 수 있습니다.

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

ysap의 의견에 따른 수정 :

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


답변

이 시도:

myint.ToString().Length

작동합니까?


답변

해결책

다음 확장 방법 중 하나가 작업을 수행합니다. 모두 빼기 기호를 숫자로 간주하고 가능한 모든 입력 값에 대해 올바르게 작동합니다. .NET Framework 및 .NET Core에서도 작동합니다. 그러나 선택한 플랫폼 / 프레임 워크에 따라 관련 성능 차이가 있습니다 (아래에서 설명).

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

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

토론

이 답변에는 무작위로 샘플링 된 / 숫자 의 배열을 사용하여 Int32Int64유형 모두에 대해 수행 된 테스트가 포함 됩니다. 무작위 데이터 세트는 테스트를 실행하기 전에 배열로 사전 처리됩니다.100.000.000intlong

4 개 가지 방법 중 일관성 검사도 수행되었다위한 MinValue네거티브 테두리 경우,, -1, 0, 1, 포지티브 테두리 케이스 MaxValue, 또한 전체 랜덤 데이터 셋. LOG10 방법을 제외하고 위에 제공된 방법에 대한 일관성 테스트는 실패하지 않습니다 (이 내용은 나중에 설명합니다).

테스트는 .NET Framework 4.7.2및 에서 실행되었습니다 .NET Core 2.2. 에 x86x64플랫폼, 64 비트 인텔 프로세서 시스템에서,와 Windows 10,와와 VS2017 v.15.9.17. 다음 4 가지 경우는 성능 결과에 동일한 영향을 미칩니다.

.NET Framework (x86)

  • Platform = x86

  • Platform = AnyCPU, Prefer 32-bit프로젝트 설정에서 확인 됨

.NET Framework (x64)

  • Platform = x64

  • Platform = AnyCPU, Prefer 32-bit프로젝트 설정에서 선택 취소됨

.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

결과

아래의 성능 테스트는 정수가 가정 할 수있는 광범위한 값 사이에 균일 한 값 분포를 생성합니다. 즉, 자릿수가 많은 값을 테스트 할 가능성이 훨씬 더 높습니다. 실제 시나리오에서는 대부분의 값이 작을 수 있으므로 IF-CHAIN이 더 잘 수행되어야합니다. 또한 프로세서는 데이터 세트에 따라 IF-CHAIN ​​결정을 캐시하고 최적화합니다.

마찬가지로 @AlanSingfield는 주석 섹션에서 지적 상기 LOG10 방법은 주조에 의해 고정되어야했다 double내부 Math.Abs()입력 값이 때 경우에 int.MinValue또는 long.MinValue.

이 질문을 편집하기 전에 구현 한 초기 성능 테스트와 관련하여 (이미 백만 번 편집해야 함) @ GyörgyKőszeg가 지적한 특정 사례 가 있는데, IF-CHAIN ​​방법이 LOG10 방법보다 느리게 수행됩니다.

@AlanSingfield가 지적한 문제에 대한 수정 이후 차이의 크기가 훨씬 낮아졌지만 여전히 발생합니다 . 이 수정 (캐스트 추가 double)은 입력 값이 정확히 -999999999999999999다음과 같을 때 계산 오류를 발생시킵니다 . 20대신 LOG10 메서드가 반환 됩니다 19. LOG10 메서드 if에는 입력 값이 0 인 경우에 대한 가드 도 있어야합니다 .

LOG10 메서드는 모든 값에 대해 작동하기가 매우 까다롭기 때문에 피해야합니다. 누군가 아래의 모든 일관성 테스트에서 올바르게 작동하도록하는 방법을 찾으면 댓글을 달아주세요!

WHILE 메서드는 또한 더 빠른 최신 리팩토링 버전을 얻었지만 여전히 느립니다 Platform = x86(지금까지 이유를 찾을 수 없었습니다).

STRING 메서드는 지속적으로 느립니다. 탐욕스럽게 너무 많은 메모리를 할당합니다. 흥미롭게도 .NET Core에서 문자열 할당은 .NET Framework에서보다 훨씬 빠릅니다. 알아 둘만 한.

IF-CHAIN ​​방법은 99.99 %의 경우에서 다른 모든 방법보다 성능이 우수해야합니다. 그리고 내 개인적인 의견으로는 LOG10 방법이 올바르게 작동하도록 필요한 모든 조정과 다른 두 방법의 나쁜 성능을 고려할 때 최선의 선택입니다.

마지막으로 결과는 다음과 같습니다.

여기에 이미지 설명 입력

이러한 결과는 하드웨어에 따라 다르기 때문에 특정 경우에 100 % 확신해야하는 경우 자신의 컴퓨터에서 아래 성능 테스트를 실행하는 것이 좋습니다.

테스트 코드

아래는 성능 테스트와 일관성 테스트를위한 코드입니다. .NET Framework 및 .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");
        }
    }
}


답변

직접 C #은 아니지만 공식은 다음과 같습니다. n = floor(log10(x)+1)


답변

이미 여기에있는 답변은 부호없는 정수에 대해 작동하지만 소수와 두 배에서 자릿수를 얻는 좋은 솔루션을 찾지 못했습니다.

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

정밀도가 중요한 경우 입력 유형을에서 double로 변경할 수 decimal있지만 십진수도 제한이 있습니다.


답변

Steve 의 대답 은 맞지만 1보다 작은 정수에서는 작동하지 않습니다.

다음은 네거티브에 대해 작동하는 업데이트 된 버전입니다.

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


답변

재귀 사용 (간혹 인터뷰에서 요청 됨)

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

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