问题 获取无符号长整数c#中的位数


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

有没有人遇到过这个问题?

我看过类似的帖子有Java重点@ 为什么log(1000)/ log(10)与log10(1000)不同? 还有一个帖子@ 如何获取int数字的单独数字? 这表明我如何使用%运算符实现相同的功能,但代码更多

这是我用来模拟这个的代码

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

提前致谢


11811
2018-02-09 19:15


起源

你不需要 % 运算符,整数除法就足够了。而“更多代码”是一个单语句循环。不是那么多代码。 - Konrad Rudolph
是ToString()。长度真的那么贵吗?也许它的头发较慢,但对任何价值都是准确的。对我而言,准确性比效率更令人满意。 - jlafay
在使用时,你开始得到不正确的结果是有道理的 Log10 功能,因为该功能使用 double type,它不能完全代表非常大的整数(参见 en.wikipedia.org/wiki/Double_precision_floating-point_format) - Justin
我同意,我认为使用时没有特别的错误 ToString().Length。此外,你可以改变你的 Action 只要签名正确,就可以使用常规方法。 - Daniel T.
请参见 stackoverflow.com/questions/4143000/...  - 如果你想要“速度”,可以看到“级联if”版本(可以分成几个除了迭代方法类似的部分)。否则可能就好了 ToString 这里。该 if 可以移动到List的快速迭代迭代中。


答案:


log10将涉及浮点转换 - 因此舍入错误。对于double来说,错误非常小,但对于精确整数来说这是一个大问题!

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

整数除以10.结果> 0?如果是这样的话。如果没有,停止。 位数是所需的迭代次数。

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

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


7
2018-02-09 19:23



只是一个戏弄:这种方法的略微修改实际上只能用 整数乘法 并比较。在我的回复中查看查找表应该有一些见解(前一句不需要查找表)。
当然,反过来做。如果乘法运算速度更快,这可能更好。 - winwaed
它 是 更快。问题是,如果它是可感知的;-)我只是想留下评论,因为这在我完成我的帖子之后的另一个晚上让我感到震惊 - 并且意识到我之前从未见过它建议作为这个问题的解决方案(这可能意味着几件事之一...)。


我会用 ToString().Length 除非你知道这将被召唤数百万次。

“过早的优化是所有邪恶的根源” - 唐纳德克努特


5
2018-02-09 20:09





来自 文件

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

我怀疑你正在达到精确限制。您的值999,999,999,999,998可能处于精度限制。而且自从 ulong 必须转换为 double 在打电话之前 Math.Log10,你看到这个错误。


3
2018-02-09 19:24





其他答案已发布 为什么 有时候是这样的。

下面是一个确定整数“长度”的快速方法示例(某些情况除外)。这本身并不是很有趣 - 但我在这里包含它因为使用这种方法 与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分支分成“组”可能更快,但这对我的口味来说太多重复键入。)

快乐的编码。


0
2018-02-09 20:32



使查找表静态并使用二进制搜索而不是直接迭代可以加快这个解决方案的速度。如果需要种子更多推出二进制搜索到if语句系列可能是最快的。 - Alexei Levenkov