【问题标题】:Get number of digits in an unsigned long integer c#获取无符号长整数c#中的位数
【发布时间】:2011-06-24 09:04:36
【问题描述】:

我正在尝试确定 c# ulong 数字中的位数,我正在尝试使用一些数学逻辑而不是使用 ToString().Length。我没有对这两种方法进行基准测试,但看过其他关于使用 System.Math.Floor(System.Math.Log10(number)) + 1 来确定位数的帖子。 似乎工作正常,直到我从 999999999999997 过渡到 999999999999998 时,我开始得到不正确的计数。

以前有人遇到过这个问题吗?

我看过类似的帖子,强调 Java 的 @Why log(1000)/log(10) isn't the same as log10(1000)? 和 @How to get the separate digits of an int number? 的帖子表明我如何使用 % 运算符实现相同的目标,但代码更多

这是我用来模拟的代码

Action<ulong> displayInfo = number => 
 Console.WriteLine("{0,-20} {1,-20} {2,-20} {3,-20} {4,-20}", 
  number, 
  number.ToString().Length, 
  System.Math.Log10(number), 
  System.Math.Floor(System.Math.Log10(number)),
  System.Math.Floor(System.Math.Log10(number)) + 1);

Array.ForEach(new ulong[] {
 9U,
 99U,
 999U,
 9999U,
 99999U,
 999999U,
 9999999U,
 99999999U,
 999999999U,
 9999999999U,
 99999999999U,
 999999999999U,
 9999999999999U,
 99999999999999U,
 999999999999999U,
 9999999999999999U,
 99999999999999999U,
 999999999999999999U,
 9999999999999999999U}, displayInfo);

Array.ForEach(new ulong[] {
 1U,
 19U,
 199U,
 1999U,
 19999U,
 199999U,
 1999999U,
 19999999U,
 199999999U,
 1999999999U,
 19999999999U,
 199999999999U,
 1999999999999U,
 19999999999999U,
 199999999999999U,
 1999999999999999U,
 19999999999999999U,
 199999999999999999U,
 1999999999999999999U
}, displayInfo);

提前致谢

拍拍

【问题讨论】:

  • 你不需要%运算符,整数除法就够了。 “更多代码”是一个单语句循环。没有那么多代码。
  • ToString().Length 真的那么贵吗?也许它的头发慢一些,但它对于任何值都是准确的。对我来说,准确性比效率更重要。
  • 使用Log10 函数时开始得到错误结果是有道理的,因为该函数使用double 类型,它不能准确表示非常大的整数(参见en.wikipedia.org/wiki/Double_precision_floating-point_format)跨度>
  • 我同意,我认为使用 ToString().Length 没有什么特别的问题。此外,您可以将Action 更改为常规方法,只要签名正确即可。
  • 请看stackoverflow.com/questions/4143000/…——如果你想要“速度”,请看“级联if”版本(可以分成几个部分,类似于迭代方法)。否则这里可能只是ToString。 if 可以移动到 List 的快速迭代中。

标签: c# algorithm numbers


【解决方案1】:

我会使用ToString().Length,除非你知道这会被调用数百万次。

“过早的优化是万恶之源”——Donald Knuth

【讨论】:

    【解决方案2】:

    其他答案已发布为什么会发生这种情况。

    这是一个确定整数“长度”的相当快速方法的示例(某些情况除外)。这本身并不是很有趣——但我将其包含在此处是因为使用此方法与 Log10 结合可以在无符号长整型的整个范围内获得“完美”的准确性,而无需第二次日志调用.

    // the lookup would only be generated once
    // and could be a hard-coded array literal
    ulong[] lookup = Enumerable.Range(0, 20)
        .Select((n) => (ulong)Math.Pow(10, n)).ToArray();
    ulong x = 999;
    int i = 0;
    for (; i < lookup.Length; i++) {
        if (lookup[i] > x) {
            break;
        }
    }
    // i is length of x "in a base-10 string"
    // does not work with "0" or negative numbers
    

    这种查找表方法可以很容易地转换为任何基础。 这种方法应该比迭代的除法方法更快,但分析留给读者作为练习。 (直接将 if-then 分支分成“组”可能会更快,但这对我来说太重复了。)

    编码愉快。

    【讨论】:

    • 将查找表设为静态并使用二分查找而不是直接迭代可能会加快此解决方案的速度。如果需要更多种子,则将二进制搜索推广到一系列 if 语句中可能是最快的。
    【解决方案3】:

    来自documentation:

    默认情况下,Double 值包含 15 精度的十进制数字,虽然 最多保持 17 位数字 内部。

    我怀疑您遇到了精度限制。您的值 999,999,999,999,998 可能处于精度极限。由于在调用 Math.Log10 之前必须将 ulong 转换为 double,因此您会看到此错误。

    【讨论】:

      【解决方案4】:

      log10 将涉及浮点转换 - 因此舍入误差。误差对于双精度数来说非常小,但对于精确整数来说就大了!

      排除 .ToString() 方法和浮点方法,是的,我认为您将不得不使用迭代方法,但我会使用整数除法而不是模数。

      整数除以10。结果是>0吗?如果是这样迭代。如果没有,请停止。 位数就是需要的迭代次数。

      例如。 5 -> 0; 1 次迭代 = 1 位数。

      1234 -> 123 -> 12 -> 1 -> 0; 4 次迭代 = 4 位。

      【讨论】:

      • 开个玩笑:实际上只需整数乘法 和比较即可对这种方法稍作修改。查看我回复中的查找表应该会有所了解(上一句不需要查找表)。
      • 当然,反过来做。如果乘法运算更快,这可能会更好。
      • 它是更快。问题是它是否可以感知;-) 我只是想留下评论,因为前几天晚上这让我印象深刻 - 在完成我的帖子之后 - 并意识到我以前从未见过它建议作为这个问题的解决方案(这可能意味着几件事之一......)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-12-29
      • 2020-03-05
      • 2012-07-12
      • 1970-01-01
      • 2021-08-17
      • 2012-02-11
      • 1970-01-01
      相关资源
      最近更新 更多