【问题标题】:How can I get a count of the total number of digits in a number?如何计算一个数字的总位数?
【发布时间】:2011-05-27 21:37:06
【问题描述】:

如何计算 C# 中数字的总位数?例如,数字 887979789 有 9 位。

【问题讨论】:

标签: c# math numbers


【解决方案1】:

不用转换成字符串也可以试试

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

【讨论】:

  • 恐怕 ceil(log10(10)) = ceil(1) = 1,而不是这个问题应该的 2!
  • 谢谢,这是一个不错的方法。虽然它并不比 int count = 0; 快。做{计数++; } 而 ((i /= 10) >= 1); :(
  • 如果您的数字范围包括负数,您需要使用 Math.Floor(Math.Log10(Math.Abs​​(n)) + 1);
  • @Puterdo Borato:我的性能测试实际上表明,当位数小于 5 时,您的方法更快。通过,Steve 的 Math.floor 更快。
  • 这不适用于 0。Math.Floor(Math.Log10(0) + 1) = -2147483648(负无穷大 + 1)。请参阅docs.microsoft.com/en-us/dotnet/api/… 文档。
【解决方案2】:

试试这个:

myint.ToString().Length

这样行吗?

【讨论】:

  • 值得指出的是,如果您正在处理负数,您可能会遇到此方法的问题。 (显然是小数,但该示例使用int,所以我认为这不是问题。)
  • 我到处都能看到这样的代码。查看我的答案的性能测试。
  • @Krythic 这不是 1980 年代,您的计算机有足够的 RAM 可以在一次操作期间将 10 个字符的字符串保存到内存中。
  • @MrLore 在简单的应用程序中这可能是真的,但在游戏开发世界中,它是完全不同的野兽。
【解决方案3】:

解决方案

以下任何扩展方法都可以完成这项工作。它们都将减号视为一个数字,并且对于所有可能的输入值都能正常工作。它们也适用于 .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 数字组成的数组。随机数据集在执行测试之前被预处理成一个数组。

还针对MinValue、负边界情况、-101、正边界情况、MaxValue 以及整个随机数据集执行了 4 种不同方法之间的一致性测试.上述提供的方法没有一致性测试失败,LOG10 方法除外(稍后讨论)。

测试在.NET Framework 4.7.2.NET Core 2.2 上执行;对于x86x64 平台,在64 位Intel 处理器机器上使用Windows 10VS2017 v.15.9.17。以下4种情况对性能结果的影响相同:

.NET 框架 (x86)

  • Platform = x86

  • Platform = AnyCPU, Prefer 32-bit 已在项目设置中选中

.NET 框架 (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 在评论部分指出的那样,对于输入值为 int.MinValuelong.MinValue.

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

这仍然会发生,尽管在修复 @AlanSingfield 指出的问题后,差异的幅度变得小得多。当输入值正好是 -999999999999999999 时,此修复(向 double 添加强制转换)会导致计算错误:LOG10 方法返回 20 而不是 19。 LOG10 方法还必须有一个if 保护,用于输入值为零的情况。

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

【讨论】:

  • 我喜欢这个解决方案,它比数学技巧更具可读性,速度不言自明,赞。
  • 为什么没有将其标记为解决方案?性能很重要,这似乎是最广泛的答案。
  • 有趣,我得到了different results。对于随机值,Log10 和蛮力几乎相同,但对于long.MaxValue,Log10 明显更好。还是仅在 .NET Core 中?
  • @GyörgyKőszeg:我为 Int64 添加了测试。请注意Int32Int64 的测试生成不同的数据集,这可以解释为什么Int64 在某些情况下比Int32 更快。虽然,在Int32 测试和Int64 测试中,在测试不同的计算方法时数据集没有改变。现在关于 .NET Core,我怀疑数学库中是否有任何神奇的优化会改变这些结果,但我很想听听更多关于这个的信息(我的答案已经很大,可能是 SO 中最大的答案之一 ;-)
  • @GyörgyKőszeg:此外,低级性能测量非常棘手。我通常更喜欢使代码尽可能简单(我更喜欢简单的 for 循环而不是 enumerations,我预处理随机数据集,并避免使用泛型、任务、Function&lt;&gt;Action&lt;&gt; 或任何黑盒测量框架)。总之,保持简单。我还关闭了所有不必要的应用程序(Skype、Windows Defender、禁用防病毒、Chrome、Microsoft Office 缓存等)。
【解决方案4】:

不是直接用C#,而是公式是:n = floor(log10(x)+1)

【讨论】:

  • log10(0) 是-无穷大
  • @Klaus - log10(0) 实际上是未定义的。但是,您是正确的,因为它是一种特殊情况,需要单独测试和处理。对于任何非正整数也是如此。请参阅史蒂夫的答案。
  • @ysap:Log10 很难正常工作。您对如何为所有可能的输入值范围正确实施它有任何想法吗?
  • @sɐunıɔןɐqɐp - log10 在大多数情况下是一个库函数。你为什么要自己实现它,你遇到了什么问题? log10(x) = log2(x) / log2(10),或一般logA(x) = logB(x) / logB(A)
  • 我不是要再次实现 Log10,我的意思是 Log10(0) 是 -infinity。 Log10 不能用于计算负数的位数,除非您在将值传递给 Log10 之前使用Math.Abs()。但随后Math.Abs(int.MinValue) 抛出异常(long.MinValue,在 Int64 的情况下)。如果我们在将数字传递给 Log10 之前将其转换为 double,那么它几乎适用于除 -999999999999999999 之外的所有数字(在 Int64 的情况下)。您是否知道任何计算位数的公式,该公式使用 log10 并接受任何 int32 或 int64 值作为输入并仅输出有效值?
【解决方案5】:

答案已经在这里适用于无符号整数,但我还没有找到从小数和双精度数中获取位数的好解决方案。

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,但小数也有限制。

【讨论】:

    【解决方案6】:

    Steve is correct的答案,但对小于1的整数不起作用。

    这里有一个更新版本,确实适用于否定:

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

    【讨论】:

    • 你缺少一个 castto int:digits = n == 0 ? 1 : (int)Math.Floor(Math.Log10(Math.Abs(n)) + 1);
    • 我没有使用 if 语句:digits = (int)Math.Floor(Math.Abs​​(Math.Log10(Math.Abs​​(n))) + 1)
    • n = int.MinValue 时会抛出异常。
    【解决方案7】:

    使用递归(有时在面试中被问到)

    public int CountDigits(int number)
    {
        // In case of negative numbers
        number = Math.Abs(number);
    
        if (number >= 10)
            return CountDigits(number / 10) + 1;
        return 1;
     }
    

    【讨论】:

    • number = int.MinValue 时会抛出异常。
    【解决方案8】:
    static void Main(string[] args)
    {
        long blah = 20948230498204;
        Console.WriteLine(blah.ToString().Length);
    }
    

    【讨论】:

    • 提防负数:-1 = 2
    【解决方案9】:

    这是一个使用二分搜索的实现。看起来是迄今为止 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
    • 对于内存访问不是瓶颈的场景,LookUpTable 方法似乎超级快。我坚信对于频繁访问内存的场景,LookUpTable 比 if-chain-like 方法慢,比如你建议的 BinSearch 方法。顺便问一下,你有 LookUpTable 的Int64 实现吗?还是您认为实施起来太复杂了?我想稍后在整套设备上运行性能测试。
    • 嘿,还没有达到 64 位。原理必须略有不同,因为您需要 4x 级别,而不仅仅是 hiword 和 loword。绝对同意,在现实世界中,您的 CPU 缓存将有许多其他竞争性的空间需求,并且在减少查找大小方面还有很大的改进空间(>>1 然后只想到偶数) .考虑到随机数据集的分布,可以通过偏向 9、10、8 位而不是 1、2、3、4 位来改进二分搜索。
    【解决方案10】:

    将一个数字除以 10 将得到最左边的数字,然后对该数字执行 mod 10 得到没有第一个数字的数字,然后重复此操作直到获得所有数字

    【讨论】:

      【解决方案11】:
      int i = 855865264;
      int NumLen = i.ToString().Length;
      

      【讨论】:

      • 对于负整数和像 23.00 这样的数字失败。做一个string.TrimStart('-') 更好
      【解决方案12】:

      整数的总位数:

            double input = Convert.ToDouble(Console.ReadLine());
            double b = Math.Floor(Math.Log10(input) + 1);          
            int c = Convert.ToInt32(b);
      

      【讨论】:

        【解决方案13】:

        创建一个返回所有数字的方法,以及另一个计算它们的方法:

        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 )。

        请记住,它不会将负号视为数字。

        【讨论】:

          【解决方案14】:

          这取决于你想对数字做什么。您可以像这样遍历从最后一个到第一个的数字:

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

          【讨论】:

          • 你的逻辑颠倒了。您需要使用% 获取数字,然后使用/= 将其减少。
          【解决方案15】:

          转换成字符串,然后你可以通过.length方法计算总数。 喜欢:

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

          【讨论】:

          • 完全没有必要分配字符串。
          【解决方案16】:

          如果它只是为了验证你可以这样做:887979789 &gt; 99999999

          【讨论】:

            【解决方案17】:

            假设您的问题是指一个 int,以下适用于负/正和零:

            Math.Floor((decimal) Math.Abs(n)).ToString().Length
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-08-30
              • 1970-01-01
              • 2022-01-03
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2021-05-23
              • 2021-10-09
              相关资源
              最近更新 更多