How do you calculate log base 2 in Java for integers?

简介:

今天遇到问题需要计算以2为底的对数值(需要整数),找了半天API中log函数有自然对数,以10为底的对数,没有以2为底的,~~o(>_<)o ~~,API中提供的方法返回的都是double类型的,可能你会觉得,不就这么简单嘛,其实这里面学问也挺大的呢,且听我慢慢叙来~~

  • 方法一:
我们都知道
            log[a]x
 log
[b]x = ---------
            log
[a]b
因此呢,我们可以用一下的方法计算 log2(或者用自然对数)
            log[10]x
 log
[2]x = ----------
            log
[10]2
即: (int) (Math.log(x) / Math.log(base));
 
如果只是计算以2为底的对数,上面的方法没有什么问题,如果以3或者其他数为底的时候,上面的方法就暗藏玄机了,强制装换成Int类型不一定可靠,以前写程序遇到过强制转后精度丢失的问题,结果查了半天才把这个bug找出来。上面的计算方法其实也是有问题的,无论什么时候,用浮点数运算去代替整数计算时都要小心。浮点数的运算是不确切的,你也不能很确切的知道(int)(Math.log(65536)/Math.log(2))的值是多少,例如:Math.ceil(Math.log(1<<29) / Math.log(2))计算的结果是30,而它的正确值应该是29,我不能很找到哪些值用(int)(Math.log(x)/Math.log(2))的时候会不正确, (just because there are only 32 "dangerous" values),
注:下面的与本主题无关,只是就这个问题说开去,证明一个问题,当底数不是2时,用上述方法是有问题的~~
下面就用遍历的方法把这些值打印出来:
 
 
 
  1. static int pow(int base,int power){ 
  2.      int result =1; 
  3.      for(int i =0; i < power; i++) 
  4.          result *= base; 
  5.      return result; 
  6.  } 
  7.  private static void test(int base,int pow){ 
  8.      int x = pow(base, pow); 
  9.      if(pow != log(x, base)) 
  10.          System.out.println(String.format("error at %d^%d", base, pow)); 
  11.      if(pow!=0&&(pow-1)!= log(x-1, base)) 
  12.          System.out.println(String.format("error at %d^%d-1", base, pow)); 
  13.  } 
  14.   
  15. static int log(int x,int base) 
  16.  { 
  17.      return(int)(Math.log(x)/Math.log(base)); 
  18.  } 
  19.  
  20.  public static void main(String[] args){ 
  21.      for(int base =2; base <500; base++){ 
  22.          int maxPow =(int)(Math.log(Integer.MAX_VALUE)/Math.log(base)); 
  23.          for(int pow =0; pow <= maxPow; pow++){ 
  24.              test(base, pow); 
  25.          } 
  26.      } 
  27.  } 
结果是:

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
...

 也就是说,当底为第一个数,指数为第二个数时,用(int)(Math.log(x)/Math.log(base))是有问题的,又有人提出用近似值("epsilon")来减小误差:(int)(Math.log(x)/Math.log(2)+1e-10),这种方法也是可以的;

  • 方法二:
 
 
  1. public static int log2(int n){ 
  2.     if(n <=0)throw new IllegalArgumentException(); 
  3.     return31-Integer.numberOfLeadingZeros(n); 
  4. } 

UPD: My integer arithmetic function is 10 times faster than Math.log(n)/Math.log(2).

上面的方法会不会有性能问题呢,可能你会不以为然,有人给出了更简便的方法,我们知道移位操作比遍历和运算要快得多;
  • 方法三:
 
 
  1. public static int log2X(int bits )// returns 0 for bits=0 
  2.  { 
  3.      int log =0; 
  4.      if(( bits &0xffff0000)!=0){ bits >>>=16; log =16;} 
  5.      if( bits >=256){ bits >>>=8; log +=8;} 
  6.      if( bits >=16 ){ bits >>>=4; log +=4;} 
  7.      if( bits >=4 ){ bits >>>=2; log +=2;} 
  8.      return log +( bits >>>1); 
  9.  } 
  10.  //自己慢慢理解吧^_^ 

这种方法要比 Integer.numberOfLeadingZeros() 快上20-30%,要比一个如下所示的基于Math.log()
的 方法几乎快10 倍:

 
 
  1. private static final double log2div =Math.log(2); 
  2.  public static int log2fp(int bits ) 
  3.  { 
  4.      if( bits ==0) 
  5.          return0;// or throw exception 
  6.      return(int)(Math.log( bits &0xffffffffL)/ log2div ); 
  7.  } 

我说的没错吧,可能你觉得太钻牛角尖了,也是,不过别人能用更优雅,效率更高的方法实现相 同的功能,也
值得借鉴一下,并且以后在用浮点数代替整数计算时也要晓得,会不会有“陷阱”~~










本文转自 breezy_yuan 51CTO博客,原文链接:http://blog.51cto.com/lbrant/469310,如需转载请自行联系原作者
相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
目录
相关文章
|
Java Apache 开发工具
【Azure 事件中心】 org.slf4j.Logger 收集 Event Hub SDK(Java) 输出日志并以文件形式保存
【Azure 事件中心】 org.slf4j.Logger 收集 Event Hub SDK(Java) 输出日志并以文件形式保存
414 1
|
Java Maven
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
在Java项目中,启动jar包时遇到“no main manifest attribute”错误,且打包大小明显偏小。常见原因包括:1) Maven配置中跳过主程序打包;2) 缺少Manifest文件或Main-Class属性。解决方案如下:
3402 8
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
|
SQL druid Oracle
【YashanDB知识库】yasdb jdbc驱动集成druid连接池,业务(java)日志中有token IDENTIFIER start异常
客户Java日志中出现异常,影响Druid的merge SQL功能(将SQL字面量替换为绑定变量以统计性能),但不影响正常业务流程。原因是Druid在merge SQL时传入null作为dbType,导致无法解析递归查询中的`start`关键字。
|
XML JSON Java
Java中Log级别和解析
日志级别定义了日志信息的重要程度,从低到高依次为:TRACE(详细调试)、DEBUG(开发调试)、INFO(一般信息)、WARN(潜在问题)、ERROR(错误信息)和FATAL(严重错误)。开发人员可根据需要设置不同的日志级别,以控制日志输出量,避免影响性能或干扰问题排查。日志框架如Log4j 2由Logger、Appender和Layout组成,通过配置文件指定日志级别、输出目标和格式。
|
Java
java do while 的语法怎么用?
java do while 的语法怎么用?
588 3
|
人工智能 Oracle Java
解决 Java 打印日志吞异常堆栈的问题
前几天有同学找我查一个空指针问题,Java 打印日志时,异常堆栈信息被吞了,导致定位不到出问题的地方。
629 2
|
XML Java 测试技术
《手把手教你》系列基础篇(九十)-java+ selenium自动化测试-框架设计基础-Logback实现日志输出-中篇(详解教程)
【7月更文挑战第8天】这篇教程介绍了如何使用Logback将Java应用的日志输出到文件中。首先,通过创建`logback.xml`配置文件,设置`FileAppender`来指定日志文件路径和格式。然后,提供了一个`RollingFileAppender`的例子,用于每日生成新的日志文件并保留一定天数的历史记录。文中包含配置文件的XML代码示例,并展示了控制台输出和生成的日志文件内容。教程最后提到了一些可能遇到的问题及解决建议。
297 1
《手把手教你》系列基础篇(九十)-java+ selenium自动化测试-框架设计基础-Logback实现日志输出-中篇(详解教程)
|
Java 应用服务中间件 HSF
Java应用结构规范问题之配置Logback以仅记录错误级别的日志到一个滚动文件中的问题如何解决
Java应用结构规范问题之配置Logback以仅记录错误级别的日志到一个滚动文件中的问题如何解决
364 7
|
Java 应用服务中间件 HSF
Java应用结构规范问题之配置Logback以在控制台输出日志的问题如何解决
Java应用结构规范问题之配置Logback以在控制台输出日志的问题如何解决
541 7
|
Java 应用服务中间件 HSF
Java应用结构规范问题之AllLoggers接口获取异常日志的Logger实例的问题如何解决
Java应用结构规范问题之AllLoggers接口获取异常日志的Logger实例的问题如何解决
281 3