首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Java中计算整数的日志基2?

如何在Java中计算整数的日志基2?
EN

Stack Overflow用户
提问于 2010-07-22 01:03:48
回答 10查看 256.3K关注 0票数 155

我使用以下函数计算整数的日志基2:

代码语言:javascript
复制
public static int log2(int n){
    if(n <= 0) throw new IllegalArgumentException();
    return 31 - Integer.numberOfLeadingZeros(n);
}

它有最佳的性能吗?

有人知道已准备好的J2SE API函数吗?

UPD1对我来说出人意料的是,浮点算法似乎比整数算法更快。

由于UPD2的评论,我将进行更详细的调查。

UPD3 I的整数算术函数比Math.log(n)/Math.log(2)快10倍。

EN

回答 10

Stack Overflow用户

回答已采纳

发布于 2010-07-22 02:38:40

如果你在考虑使用浮点数来帮助计算整数,你必须小心。

我通常在可能的情况下尽量避免FP计算。

浮点运算不是精确的.您永远不可能确切地知道(int)(Math.log(65536)/Math.log(2))将评估什么。例如,Math.ceil(Math.log(1<<29) / Math.log(2))在我的PC上是30,在数学上应该是29。我没有找到(int)(Math.log(x)/Math.log(2))失败的x值(仅仅是因为只有32个“危险”值),但这并不意味着它在任何PC上都会以相同的方式工作。

这里通常的技巧是在四舍五入时使用"epsilon“。就像(int)(Math.log(x)/Math.log(2)+1e-10)不应该失败一样。选择这种"epsilon“并不是一项琐碎的任务。

更多演示,使用更一般的任务--尝试实现int log(int x, int base)

测试代码:

代码语言:javascript
复制
static int pow(int base, int power) {
    int result = 1;
    for (int i = 0; i < power; i++)
        result *= base;
    return result;
}

private static void test(int base, int pow) {
    int x = pow(base, pow);
    if (pow != log(x, base))
        System.out.println(String.format("error at %d^%d", base, pow));
    if(pow!=0 && (pow-1) != log(x-1, base))
        System.out.println(String.format("error at %d^%d-1", base, pow));
}

public static void main(String[] args) {
    for (int base = 2; base < 500; base++) {
        int maxPow = (int) (Math.log(Integer.MAX_VALUE) / Math.log(base));
        for (int pow = 0; pow <= maxPow; pow++) {
            test(base, pow);
        }
    }
}

如果我们使用最直接的对数实现,

代码语言:javascript
复制
static int log(int x, int base)
{
    return (int) (Math.log(x) / Math.log(base));
}

这些指纹:

代码语言:javascript
复制
error at 3^5
error at 3^10
error at 3^13
error at 3^15
error at 3^17
error at 9^5
error at 10^3
error at 10^6
error at 10^9
error at 11^7
error at 12^7
...

为了完全消除错误,我必须添加epsilon,它介于1e-11和1e-14之间。你能在测试前告诉我这个吗?我绝对不能。

票数 83
EN

Stack Overflow用户

发布于 2010-07-22 04:05:18

这是我用于此计算的函数:

代码语言:javascript
复制
public static int binlog( int bits ) // returns 0 for bits=0
{
    int log = 0;
    if( ( bits & 0xffff0000 ) != 0 ) { bits >>>= 16; log = 16; }
    if( bits >= 256 ) { bits >>>= 8; log += 8; }
    if( bits >= 16  ) { bits >>>= 4; log += 4; }
    if( bits >= 4   ) { bits >>>= 2; log += 2; }
    return log + ( bits >>> 1 );
}

它略快于Integer.numberOfLeadingZeros() (20-30%),比基于Math.log()的实现快近10倍(JDK1.6 x64),如下所示:

代码语言:javascript
复制
private static final double log2div = 1.000000000001 / Math.log( 2 );
public static int log2fp0( int bits )
{
    if( bits == 0 )
        return 0; // or throw exception
    return (int) ( Math.log( bits & 0xffffffffL ) * log2div );
}

这两个函数对所有可能的输入值返回相同的结果。

更新:Java1.7服务器JIT能够用基于本质的替代实现取代一些静态的数学函数。其中一个函数是Integer.numberOfLeadingZeros()。因此,对于1.7个或更高版本的服务器VM,类似问题中的实现实际上比上面的binlog略快一些。不幸的是,客户端JIT似乎没有这种优化。

代码语言:javascript
复制
public static int log2nlz( int bits )
{
    if( bits == 0 )
        return 0; // or throw exception
    return 31 - Integer.numberOfLeadingZeros( bits );
}

此实现还将对所有2^32可能的输入值返回与我前面发布的其他两个实现相同的结果。

以下是我的PC上的实际运行时(Sandy i7):

JDK 1.7 32位客户端VM:

代码语言:javascript
复制
binlog:         11.5s
log2nlz:        16.5s
log2fp:        118.1s
log(x)/log(2): 165.0s

JDK1.7 x64服务器VM:

代码语言:javascript
复制
binlog:          5.8s
log2nlz:         5.1s
log2fp:         89.5s
log(x)/log(2): 108.1s

这是测试代码:

代码语言:javascript
复制
int sum = 0, x = 0;
long time = System.nanoTime();
do sum += log2nlz( x ); while( ++x != 0 );
time = System.nanoTime() - time;
System.out.println( "time=" + time / 1000000L / 1000.0 + "s -> " + sum );
票数 99
EN

Stack Overflow用户

发布于 2010-07-22 01:09:43

试试Math.log(x) / Math.log(2)

票数 47
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3305059

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档