数値の合計桁数を取得するにはどうすればよいですか?


114

C#で数値の合計桁数を取得するにはどうすればよいですか?たとえば、887979789の数字は9桁です。


6
機能しない場合は.Lengthを使用してみてください。最初に文字列に変換してください
Breezer

xを887979789としましょう。x.ToString()。Count(); あなたにそれを与えるでしょう。
nPcomp 2017年

回答:


174

文字列に変換せずに、次のことを試すことができます。

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

ysapのコメントに続く修正:

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

10
私はceil(log10(10))= ceil(1)= 1と恐れています。この質問の場合は2ではありません。
ysap 2010

3
ありがとう、それは素晴らしい方法です。ただし、int count = 0よりも速くはありません。{count ++; } while((i / = 10)> = 1); :(
プテルドボラート

3
数値範囲に負数が含まれている場合は、Math.Floor(Math.Log10(Math.Abs​​(n))+ 1);を使用する必要があります。
mrcrowl

1
ええと、それだけで返すことn0でき1ます:)負の値を処理しすぎるnと、単にに置き換えMath.Abs(n)ます。
Umair 2013年

3
@Puterdo Borato:桁数が5未満の場合、私のパフォーマンステストでは実際にメソッドが高速であることが示されました。合格すると、SteveのMath.floorが高速になります。
stack247

83

これを試して:

myint.ToString().Length

それは動作しますか ?


25
負の数を扱っている場合、このメソッドで問題が発生する可能性が高いことを指摘する価値があります。(そして明らかに小数ですが、例ではを使用しているintので、それは問題ではないと思います。)
Cody Grey

2
@Krythic文字列割り当ては、.NETの世界で新しい流行です。
nawfal

1
新着?ほとんどありません。私は2010年にひどく文字列を割り当てていました。笑。あなたは正しいです。これは汚い!
Andiih

3
@Krythic 1980年代ではありません。コンピュータには、1回の操作で10文字の文字列をメモリに保存するのに十分なRAMがあります。
MrLore 2018

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

討論

この回答には、ランダムにサンプリングされた/ 数値の配列を使用して、Int32およびの両方に対して実行されたテストが含まれます。ランダムデータセットは、テストを実行する前に前処理されて配列になります。Int64100.000.000intlong

4つの異なる方法のうちの整合性テストは、また、実行されたためMinValue、負の境界場合、、 、-101正の境界ケース、MaxValueおよびまた全体ランダムデータセットの。LOG10メソッドを除いて、上記で提供されたメソッドの整合性テストは失敗しません(これについては後で説明します)。

テストは.NET Framework 4.7.2およびで実行されました.NET Core 2.2x86およびx64プラットフォーム用、64ビットインテルプロセッサーマシン、、Windows 10およびVS2017 v.15.9.17。次の4つのケースは、パフォーマンス結果に同じ影響を及ぼします。

.NET Framework(x86)

  • Platform = x86

  • Platform = AnyCPUPrefer 32-bitプロジェクト設定でチェックされます

.NET Framework(x64)

  • Platform = x64

  • Platform = AnyCPUPrefer 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

この質問を編集する前に私が実装した初期のパフォーマンステスト(すでに100万回編集する必要がありました)に関して@GyörgyKőszegによって指摘された特定のケースがあり、IF-CHAINメソッドの実行がLOG10メソッドよりも低速でした。

@AlanSingfieldによって指摘された問題の修正後、差の大きさははるかに低くなりましたが、これはまだ起こります。この修正(キャストをに追加double)すると、入力値が正確に-999999999999999999次の場合に計算エラーが発生します。LOG10メソッドはの20代わりに戻ります19。LOG10メソッドには、ifには、入力値がゼロの場合のガードです。

LOG10メソッドは、すべての値に対して機能するように非常にトリッキーです。つまり、それを回避する必要があります。誰かが以下のすべての一貫性テストで正しく機能する方法を見つけたら、コメントを投稿してください!

WHILEメソッドは最近のリファクタリングバージョンも高速ですが、それでもまだ遅いです Platform = x86(理由は今までわかりませんでした)。

STRINGメソッドは一貫して低速です。貪欲にメモリを割り当てすぎて何もしません。興味深いことに、.NET Coreでは、文字列の割り当ては.NET Frameworkよりもはるかに高速であるように見えます。知ってよかった。

IF-CHAINメソッドは、99.99%のケースで他のすべてのメソッドよりも優れているはずです。そして、私の個人的な意見では、あなたの最良の選択です(LOG10メソッドを正しく機能させるために必要なすべての調整と、他の2つのメソッドのパフォーマンスの低下を考慮してください)。

最後に、結果は次のとおりです。

ここに画像の説明を入力してください

これらの結果はハードウェアに依存するため、特定のケースで本当に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 2018

3
これが解決策としてマークされていないのはなぜですか?パフォーマンスが重要であり、これが最も広範な答えのようです。
Martien de Jong 2018

興味深いことに、私は異なる結果を得る。ランダムな値の場合、Log10とブルートフォースはほぼ同じですが、long.MaxValueLog10の方がはるかに優れています。それとも.NET Coreだけですか?
ジェルジKőszeg

@GyörgyKőszeg:Int64のテストを追加しました。異なるデータセットをテストしInt32Int64生成することに注意してください。これにより、一部のケースInt64よりも速くなっInt32た理由が説明される場合があります。ただし、Int32テスト内およびテスト内ではInt64、さまざまな計算方法をテストしてもデータセットは変更されません。.NET Coreに関しては、これらの結果を変えるような魔法の最適化がMathライブラリにあるとは思えませんが、それについてもっと聞きたいです(私の答えはすでに巨大で、おそらくSOで最大のものの1つです;-)
sɐunıɔןɐqɐp19年

@GyörgyKőszeg:また、低レベルのパフォーマンス測定は非常にトリッキーです。私は通常、可能な限り単純な(私はシンプル好むようにコードを維持することを好む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
@クラウス-log10(0)は実際には未定義です。しかし、それはテストされ、個別に処理される必要がある特別なケースであるという点で正しいです。これは、任意の正でない整数にも当てはまります。スティーブの回答へのコメントを参照してください。
ysap 2014年

@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)-infinityだ。Math.Abs()値をLog10に渡す前に使用しない限り、Log10 を使用して負の数字の桁数を計算することはできません。ただしMath.Abs(int.MinValue)、例外がスローされます(long.MinValueInt64の場合も同様)。Log10に渡す前に数値を2倍にキャストすると、-999999999999999999(Int64の場合を除いて)ほとんどすべての数値で機能します。log10を使用し、int32またはint64値を入力として受け入れ、有効な値のみを出力する、桁数を計算するための式を知っていますか?
sɐunıɔןɐqɐp

9

すでにここで回答は符号なし整数で機能しますが、10進数と2進数から桁数を取得するための適切な解決策は見つかりませんでした。

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が、10進数にも制限があります。


7

スティーブの答えは正しいですが、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ステートメントなしで実行しました:digits =(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 2018

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-likeソリューションで常に考慮される必要があります。1
sɐunıɔןɐqɐp

LookUpTableアプローチは、メモリアクセスがボトルネックではないシナリオでは非常に高速であるようです。メモリアクセスが頻繁なシナリオでは、LookUpTableは、提案したBinSearchのようなif-chainのようなメソッドよりも遅くなると強く信じています。ところで、Int64LookUpTableの実装はありますか?それとも、実装するには複雑すぎると思いますか?完全なセットで後でパフォーマンステストを実行したいと思います。
sɐunıɔןɐqɐp

ねえ、64ビットのものまで行きませんでした。原則は、hiwordとlowordだけでなく、4倍のレベルが必要になるという点で少し異なる必要があります。現実の世界では、CPUキャッシュにはスペースに関する他の多くの競合するニーズがあり、ルックアップのサイズを削減することで改善の余地がたくさんあることに間違いなく同意します(>> 1の場合、偶数のみが頭に浮かびます)。 。ランダム検索セットの分布を考えると、2、1、2、3、4の代わりに9、10、8桁にバイアスをかけることで、バイナリ検索を改善できます。
アランSingfield

1

数値を10で除算すると、左端の桁が得られます。次に、数値に対してmod 10を実行すると、最初の桁のない数値が得られ、すべての桁が揃うまでこれを繰り返します


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

2
負の整数の場合、および23.00のような数値の場合は失敗します。行い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 2016

-2

それはあなたが数字で何をしたいかによります。次のように、最後から最初の数字まで反復できます。

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

1
あなたの論理は逆になります。を使用%して数字を取得し、それ/=を削減する必要があります。
julealgon


-3

あなたの質問がintを参照していると仮定すると、以下は負/正とゼロでも機能します:

Math.Floor((decimal) Math.Abs(n)).ToString().Length
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.