在 C# 中计算整数的 log2 的最快方法是什么?
如何最有效地计算 C# 中整数(以 2 为底的对数)所需的位数?例如:
int bits = 1 + log2(100);
=> bits == 7
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
如何最有效地计算 C# 中整数(以 2 为底的对数)所需的位数?例如:
int bits = 1 + log2(100);
=> bits == 7
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(6)
对 Guffa 答案的轻微改进...由于您添加到结果中的数量始终是 2 的幂,因此使用位运算可以对某些架构产生轻微的改进。此外,由于我们的上下文是位模式,因此使用十六进制更具可读性。在这种情况下,将算术移位 2 的幂很有用。
此外,应添加对 n==0 的检查,因为上面将产生 0 结果,并且 Log(0) 未定义(无论基数如何)。
在 ARM 汇编中,该算法生成非常紧凑的代码,因为可以使用条件指令消除比较后的分支,从而避免管道刷新。例如:
变为(让 R0 = n,R1 = 位)
Slight improvement to Guffa's answer... Since the amount you are adding to the result is always a power of two using bit operations can produce slight improvement on some architectures. Also since our context is bit patterns it is slightly more readable to use hexadecimal. In this case it is useful to shift the arithmetic by a power of 2.
Further a check for n==0 should be added since the above will yield a result of 0 and Log(0) is undefined (regardless of base).
In ARM assembly this algorithm produces very compact code as the branch after comparison can be eliminated with conditional instructions which avoids pipeline flushing. For Example:
becomes (let R0 = n, R1 = bits)
您可以简单地计算必须删除位的次数,直到值为零:
对于大数字更有效,您可以首先计算位组:
编辑:
最有效的方法是使用 Flynn1179 建议的二进制步骤(已投票)寻求灵感:),但将循环扩展为硬编码检查。这至少比上面的方法快两倍,但代码也更多:
You can simply count how many times you have to remove bits until the value is zero:
More efficient for large numbers, you can count groups of bits first:
Edit:
The most efficient method would be to use the binary steps that Flynn1179 suggested (upvoted for the inspiration :), but expanding the loop into hard coded checks. This is at least twice as fast as the method above, but also more code:
最干净、最快...(适用于 .Net core 3.1 及更高版本,从 .Net 5.0 开始性能领先)
亚军...(版本中最快)低于.Net 5,.Net 5中的第二位)
注释:
2009 年 3 月 22 日。
以下是一些基准:(此处代码:https://github.com/SunsetQuest/Fast-Integer- Log2)
基准测试说明: AMD Ryzen CPU,Release 模式,无调试器附加,.net core 3.1
我真的很喜欢 另一篇文章中的支出。该方法不存在潜在的架构问题,并且它还支持零,同时保持与 SPWorley 的浮点方法几乎相同的性能。
更新 3/13/2020: 史蒂夫注意到 Log2_SunsetQuest3 中存在一些被遗漏的错误。
更新 4/26/2020: 添加了新的 .Net Core 3 的 BitOperations.LeadingZeroCount(),如 phuclv 所指出的 。
The Cleanest and Quickest... (works in .Net core 3.1 and up and takes the performance lead starting in .Net 5.0)
Runner up... (fastest in versions below .Net 5, 2nd place in .Net 5)
Notes:
3/22/2009.
Here are some benchmarks: (code here: https://github.com/SunsetQuest/Fast-Integer-Log2)
Benchmark notes: AMD Ryzen CPU, Release mode, no-debugger attached, .net core 3.1
I really like the one created by spender in another post. This one does not have the potential architecture issue and it also supports Zero while maintaining almost the same performance as the float method from SPWorley.
Update 3/13/2020: Steve noticed that there were some errors in Log2_SunsetQuest3 that were missed.
Update 4/26/2020: Added new .Net Core 3's BitOperations.LeadingZeroCount() as pointed out by phuclv.
代码行数或运行时执行速度方面的效率?
代码很简单:
Math.log(n, 2)
。运行时速度有点棘手,但你可以通过一种“二分搜索”来做到这一点:
我不能 100% 确定我已经掌握了逻辑,但希望这个想法是清楚的。 .NET VM 中可能会有一些开销,但原则上它应该更快。
for 循环初始化程序中的
16
基于 int 所需位数的一半。如果您使用长整型,请从 32 开始,依此类推。Efficiency in terms of lines of code, or runtime execution speed?
Code's easy:
Math.log(n, 2)
.Runtime speed's a little trickier, but you can do it with a kind of 'binary search':
I'm not 100% certain I've got the logic right there, but hopefully the idea's clear. There might be some overheads in the .NET VM, but in principle it should be faster.
The
16
in the for loop initialializer is based on half the number of bits needed for an int. If you're working with longs, start it at 32, etc.在 .NET Core 3.0 中,有 BitOperations.LeadingZeroCount() 和 BitOperations.Log2。它们被映射到底层硬件指令,如 x86 的 LZCNT/BSR,因此这应该是最有效的解决方案
In .NET Core 3.0 there are BitOperations.LeadingZeroCount() and BitOperations.Log2. They're mapped to the underlying hardware instructino like x86's LZCNT/BSR so that should be the most efficient solution
直接转换为 IEEE754 32 位在大约 33554431
转换为 FP64 后有错误结果,ILogB 在 53 位后有错误结果,大约 18014398509481983
lg 和 ln 在大约 47 位后有错误,大约 281474976710655
lb 在 48 位后有错误,大约562949953421311
二分查找非常慢。
我的建议在这里:
快一:
简单一:
速度测试结果:
direct convert to IEEE754 32bit has wrong result after about 33554431
convert to FP64 and ILogB has wrong result after 53bits, about 18014398509481983
lg and ln has wrong after about 47bits, about 281474976710655
lb wrong after 48bits, about 562949953421311
Binary Search is very slow.
My suggestion is here:
Fast one:
Simple one:
Speed test result: