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


114

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


6
.Length를 사용해보십시오. 작동하지 않으면 먼저 문자열로 변환하십시오
Breezer

x = 887979789; x.ToString (). Count (); 당신에게 줄 것입니다.
nPcomp 2017 년

답변:


175

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

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

ysap의 의견에 따른 수정 :

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

10
나는 ceil (log10 (10)) = ceil (1) = 1이 아니라 2가 아니라이 질문에 대해 두려워합니다!
ysap

3
감사합니다. 좋은 방법입니다. int count = 0보다 빠르지는 않지만; do {count ++; } while ((i / = 10)> = 1); :(
Puterdo Borato

3
숫자 범위에 음수가 포함 된 경우 Math.Floor (Math.Log10 (Math.Abs ​​(n)) + 1);
mrcrowl

1
잘 경우 nIS는 0단지 반환 할 수 있습니다 1:) 너무 핸들 음의 값은 대체 n와 함께 Math.Abs(n).
Umair 2013-06-13

3
@Puterdo Borato : 제 성능 테스트에서는 자릿수가 5 미만일 때 방법이 더 빠르다는 것이 실제로 나타났습니다. 통과하면 Steve의 Math.floor가 더 빠릅니다.
stack247

83

이 시도:

myint.ToString().Length

작동합니까?


25
음수를 처리하는 경우이 방법에 문제가 발생할 가능성이 있음을 지적 할 가치가 있습니다. (그리고 분명히 십진수이지만 예제에서는를 사용 int하므로 문제가되지 않는다고 가정합니다.)
Cody Gray

2
@Krythic 문자열 할당은 .NET 세계의 새로운 열풍입니다.
nawfal

1
새로운? 거의. 나는 2010 년에 엄청나게 문자열을 할당하고 있었다. 얼마나 트렌드 세터인가. Lol. 그래도 당신 말이 맞아요. 이것은 더럽다!
Andiih

3
@Krythic 1980 년대가 아닙니다. 컴퓨터에는 한 번의 작업 동안 10 개의 문자열을 메모리에 저장할 수있는 충분한 RAM이 있습니다.
MrLore

2
@MrLore 간단한 응용 프로그램에서는 이것이 사실 일 수 있지만 게임 개발 세계에서는 완전히 다른 짐승입니다.
Krythic

48

해결책

다음 확장 방법 중 하나가 작업을 수행합니다. 모두 빼기 기호를 숫자로 간주하고 가능한 모든 입력 값에 대해 올바르게 작동합니다. .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");
        }
    }
}

4
저는이 솔루션을 좋아합니다. 수학 트릭보다 훨씬 읽기 쉽고 속도 자체가 좋아요.
MrLore

3
이것이 솔루션으로 표시되지 않는 이유는 무엇입니까? 성능이 중요하며 이것이 가장 광범위한 답변 인 것 같습니다.
Martien de Jong

흥미롭게도 다른 결과를습니다 . 임의 값의 경우 Log10과 무차별 대입은 거의 동일하지만 long.MaxValueLog10의 경우 훨씬 더 좋습니다. 아니면 .NET Core에 있습니까?
György Kőszeg

@ GyörgyKőszeg : Int64에 대한 테스트를 추가했습니다. 테스트 는 다른 데이터 세트를 생성 Int32하고 Int64생성 Int64하므로 Int32어떤 경우 보다 빨라진 이유를 설명 할 수 있습니다 . 내 비록 Int32테스트 및 내 Int64다른 계산 방법을 테스트 할 때의 데이터 세트가 변경되지 않은 시험. 지금은 어떤 마법 최적화이 결과를 바꿀 것 수학 라이브러리가 의심, .NET 핵심에 대한,하지만 내 대답은 아마 이미 ;-) SO에서 가장 큰 중 하나 크다 (더 그것에 대해 듣고 싶어요
sɐunıɔ ןɐ qɐp nov.

@ GyörgyKőszeg : 또한 낮은 수준의 성능 측정은 매우 까다 롭습니다. 나는 보통 가능한 한 간단하게 코드를 유지하는 것을 선호 (I 간단 선호 for이상 루프를 enumerations, I 사전 처리 임의 데이터 세트, 제네릭, 작업, 사용하지 않도록 Function<>, Action<>또는 어떤 블랙 박스 측정 프레임 워크). 요약하면 간단하게 유지하십시오. 또한 불필요한 응용 프로그램 (Skype, Windows Defender, 안티 바이러스 비활성화, Chrome, Microsoft Office 캐시 등)을 모두 종료합니다.
sɐunıɔ ןɐ qɐp

13

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


2
log10 (0)은 -infinity
Alex Klaus

2
@Klaus-log10 (0)은 실제로 정의되지 않았습니다. 그러나 별도로 테스트하고 치료해야하는 특별한 경우라는 점에서 정확합니다. 양수가 아닌 정수에 대해서도 마찬가지입니다. Steve의 답변에 대한 의견을 참조하십시오.
ysap 2014-06-03

@ysap : Log10은 제대로 작동하기가 매우 까다 롭습니다. 가능한 입력 값의 모든 범위에 대해 올바르게 구현하는 방법에 대한 아이디어가 있습니까?
sɐunıɔ ןɐ qɐp

@ sɐunıɔ ןɐ qɐp- log10대부분의 경우 라이브러리 함수입니다. 직접 구현하려는 이유는 무엇이며 어떤 문제가 발생합니까? log10(x) = log2(x) / log2(10), 또는 일반적으로 logA(x) = logB(x) / logB(A).
ysap

Log10을 다시 구현하려는 Log10(0)것이 아니라 무한대입니다. Log10 Math.Abs()에 값을 전달하기 전에 사용하지 않으면 Log10 을 사용하여 음수의 자릿수를 계산할 수 없습니다 . 그러나 Math.Abs(int.MinValue)예외가 발생합니다 ( long.MinValueInt64의 경우에도). Log10에 전달하기 전에 숫자를 double로 캐스트하면 -999999999999999999(Int64의 경우)를 제외한 거의 모든 숫자에 대해 작동합니다 . log10을 사용하고 int32 또는 int64 값을 입력으로 받아들이고 유효한 값만 출력하는 자릿수를 계산하는 공식을 알고 있습니까?
sɐunıɔ ןɐ qɐp

9

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

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있지만 십진수도 제한이 있습니다.


7

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

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

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

당신은 castingto의 INT 누락 :digits = n == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(n)) + 1);
sɐunıɔ ןɐ qɐp

나는 if 문없이했다 : numbers = (int) Math.Floor (Math.Abs ​​(Math.Log10 (Math.Abs ​​(n))) + 1)
KOLRH

이 경우 예외가 발생 n = int.MinValue합니다.
sɐunıɔ ןɐ qɐp

5

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

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
이 경우 예외가 발생 number = int.MinValue합니다.
sɐunıɔ ןɐ qɐp

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

2
네거티브주의 : -1= 2
MrLore

2

다음은 이진 검색을 사용한 구현입니다. int32에서 지금까지 가장 빠른 것 같습니다.

Int64 구현은 독자 (!)

트리를 하드 코딩하는 대신 Array.BinarySearch를 사용해 보았지만 속도의 절반 정도였습니다.

편집 : 조회 테이블은 더 많은 메모리를 사용하는 대신 이진 검색보다 훨씬 빠릅니다. 현실적으로 나는 아마도 프로덕션에서 이진 검색을 사용할 것입니다. 룩업 테이블은 소프트웨어의 다른 부분에 의해 가려 질 가능성이있는 속도 향상을 위해 매우 복잡합니다.

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

조회 테이블 버전 :

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

바이너리 검색 버전

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

매우 흥미로운 접근 방식입니다. 균일하게 분산 된 정수 값의 경우 실제로 "Log10", "string.Length"및 "While"메서드보다 빠릅니다. 실제 시나리오에서 정수 값의 분포는 if-chain과 유사한 솔루션에서 항상 고려되어야합니다. +1
sɐunıɔ ןɐ qɐp

LookUpTable 접근 방식은 메모리 액세스가 병목 현상이 아닌 시나리오에서 매우 빠릅니다. 메모리 액세스가 빈번한 시나리오의 경우 LookUpTable이 제안한 BinSearch와 같은 if-chain과 유사한 방법보다 느리다고 강력하게 믿습니다. 그런데 Int64LookUpTable에 대한 구현이 있습니까? 아니면 구현하기가 너무 복잡하다고 생각하십니까? 나중에 전체 세트에서 성능 테스트를 실행하고 싶습니다.
sɐunıɔ ןɐ qɐp

이봐, 64 비트만큼 멀리 가지 않았어. 원칙은 단순히 hiword와 loword가 아닌 4x 레벨이 필요하다는 점에서 약간 달라야합니다. 현실 세계에서 CPU 캐시에는 공간에 대한 다른 경쟁 요구 사항이 많이 있으며 조회 크기를 줄이는 개선의 여지가 많다는 점에 확실히 동의합니다 (>> 1 그러면 짝수 만 떠 오릅니다). . 이진 검색은 무작위 데이터 세트의 분포를 고려할 때 1,2,3,4 대신 9,10,8 자리로 편향하여 개선 할 수 있습니다.
Alan Singfield

1

숫자를 10으로 나누면 가장 왼쪽의 숫자가 나오고, 숫자에 mod 10을 입력하면 첫 번째 숫자가없는 숫자가 나오고 모든 숫자가 나올 때까지 반복합니다.


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

2
음의 정수 및 23.00과 같은 숫자에 대해서는 실패합니다. DO가 string.TrimStart('-')더 나은
nawfal

0

모든 숫자를 반환하는 메서드와 숫자를 계산하는 다른 메서드를 만듭니다.

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

이 문제를 해결할 때 나에게 더 직관적 인 접근 방식처럼 느껴졌습니다. 나는 Log10그것의 명백한 단순성 때문에 먼저 방법을 시도했지만, 엄청난 양의 코너 케이스와 정밀도 문제가 있습니다.

나는 또한 if보기에 약간 추한 다른 답변에서 제안 된 -chain 을 발견 했습니다.

이것이 가장 효율적인 방법은 아니지만 다른 용도로도 숫자를 반환하는 다른 확장을 제공합니다 ( private클래스 외부에서 사용할 필요가없는 경우 표시 할 수 있음 ).

음수 부호를 숫자로 간주하지 않는다는 점에 유의하십시오.


-2

문자열로 변환 한 다음 .length 방법으로 총 자리 수를 계산할 수 있습니다. 처럼:

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

1
문자열 할당은 완전히 불필요합니다.
Krythic

-2

숫자로 정확히 무엇을 하려는지에 따라 다릅니다. 다음과 같이 마지막부터 시작하여 첫 번째 숫자까지 반복 할 수 있습니다.

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

1
당신의 논리는 반대입니다. %숫자를 구한 다음 /=잘라 내기 위해 를 사용해야 합니다.
julealgon


-3

귀하의 질문이 int를 참조한다고 가정하면 다음은 음수 / 양수 및 0에서도 작동합니다.

Math.Floor((decimal) Math.Abs(n)).ToString().Length
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.