如何获得数字中位数的总数?


Answers:


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); :(
Puterdo Borato,2012年

3
如果您的数字范围包含负数,则需要使用Math.Floor(Math.Log10(Math.Abs​​(n))+1);
mrcrowl 2012年

1
那么,如果n就是0可以只返回1:)太手柄负值只需更换nMath.Abs(n)
乌迈尔

3
@Puterdo Borato:我的性能测试实际上表明,当位数小于5时,您的方法会更快。通过该操作,Steve的Math.floor会更快。
stack247

83

试试这个:

myint.ToString().Length

那样有用吗 ?


25
值得指出的是,如果要处理负数,则可能会遇到这种方法的问题。(而且显然是小数,但该示例使用int,所以我认为这不是问题。)
Cody Gray

2
@Krythic字符串分配是.NET世界中的新潮流。
nawfal

1
新?几乎不。早在2010年,我就在分配字符串。大声笑。你说得对。这太脏了!
Andiih '16

3
@Krythic不是1980年代,您的计算机有足够的RAM可以在一次操作期间将10个字符串保存到内存中。
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.000随机取样int /long数字。在执行测试之前,将随机数据集预处理为一个数组。

4种不同方法之间的一致性测试也在执行,MinValue负边界的情况下,-101,正面边框的情况下,MaxValue和也为整群随机数据集。对于以上提供的方法,没有一致性测试会失败,但对于LOG10方法除外(稍后讨论)。

测试是在.NET Framework 4.7.2和上执行的.NET Core 2.2;对于x86x64平台,一个64位Intel处理器的机器上,与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在注释部分中指出的那样,对于输入值是或的情况,必须使用double内部强制转换来固定LOG10方法。Math.Abs()int.MinValuelong.MinValue

关于我在编辑此问题之前已经实施的早期性能测试(必须进行一百万次编辑),@GyörgyKőszeg指出了一个特定案例,其中IF-CHAIN方法的执行速度比LOG10方法慢。

尽管@AlanSingfield指出的问题解决之后,差异的幅度变得低得多,但这仍然会发生。double当输入值准确时,此修复程序(向中添加强制类型转换)会导致计算错误-999999999999999999:LOG10方法返回20而不是19if当输入值为零时,LOG10方法还必须具有保护措施。

LOG10方法很难处理所有值,这意味着您应该避免使用它。如果有人找到使它在以下所有一致性测试中都能正常工作的方法,请发表评论!

WHILE方法还提供了一个最近的重构版本,该版本更快,但是它仍然很慢Platform = x86(直到现在我都找不到原因)。

STRING方法一直很慢:它贪婪地分配了太多的内存,却一无所获。有趣的是,在.NET Core中,字符串分配似乎比.NET Framework中的要快得多。很高兴知道。

在99.99%的情况下,IF-CHAIN方法应胜过所有其他方法。根据我个人的观点,这是您的最佳选择(考虑使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
为什么没有将此标记为解决方案?性能很重要,这似乎是最广泛的答案。
马丁·德·琼

有趣的是,我得到了不同的结果。对于随机值,Log10和蛮力几乎相同,但对于long.MaxValueLog10则要好得多。还是仅在.NET Core中?
捷尔吉科塞格

@GyörgyKőszeg:我已经为Int64添加了测试。请注意,这些测试针对Int32Int64生成了不同的数据集,这可能解释了为什么Int64速度比Int32某些情况更快。尽管在Int32测试内和测试内,Int64在测试不同的计算方法时数据集不会更改。现在,关于.NET Core,我怀疑数学库中是否有任何魔术优化会改变这些结果,但是我希望听到更多有关此的信息(我的回答已经很大,可能是SO中最大的回答之一;-)
sɐunıɔןɐqɐp19年

@GyörgyKőszeg:而且,低水平的性能测试非常棘手。我通常喜欢保持代码尽可能简单(我喜欢简单for循环过enumerations,我预处理随机数据集,并避免使用泛型,任务,的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)实际上是未定义的。但是,您是正确的,因为这是一种特殊情况,需要进行单独测试和处理。对于任何非正整数也是如此。请参阅对史蒂夫答案的评论。
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,否则不能使用Log10计算负数的位数。但是然后Math.Abs(int.MinValue)抛出一个异常(long.MinValue对于Int64也是如此)。如果我们在将数字传递给Log10之前将其转换为两倍,那么它将适用于除-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

如果精度很重要,则可以将输入类型从更改doubledecimal,但是十进制也有限制。


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,

当时,将引发Exception 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
当时,将引发Exception 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链的解决方案中考虑整数值的分布。+1
sɐunıɔןɐqɐp

对于内存访问不是瓶颈的情况,LookUpTable方法似乎超级快。我坚信对于频繁访问内存的情况,LookUpTable的速度要比类似if链的方法(如您建议的BinSearch)慢。顺便说一句,您有Int64LookUpTable 的实现吗?还是您认为实施起来太复杂了?我想稍后在整套设备上运行性能测试。
sɐunıɔןɐqɐp

嘿,还没到64位。该原理必须稍有不同,因为您需要4倍级别,而不仅仅是hiword和loword。绝对同意,在现实世界中,您的CPU缓存将对空间有很多其他竞争需求,并且在缩小查找大小方面还有很多改进的余地(>> 1则仅考虑偶数) 。给定随机数据集的分布,可以通过偏向9、10、8位而不是1、2、3、4位来改善二进制搜索。
艾伦·辛菲尔德

1

将数字除以10将得到最左边的数字,然后对该数字进行10模运算得到的数字不包含第一个数字,然后重复该操作,直到获得所有数字



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 '16

-2

这取决于您到底想用数字做什么。您可以像这样从最后一个到第一个数字进行迭代:

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

1
您的逻辑相反。您需要使用%获取数字,然后/=将其削减。
julealgon


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.