【问题标题】:How can I do an integer log2() in Fortran?如何在 Fortran 中执行整数 log2()?
【发布时间】:2016-02-21 21:22:17
【问题描述】:

一个明显的方法是:

integer function log2_i(val) result(res)
    implicit none
    integer, intent(IN) :: val

    if (val<0) then
        print *, "ERROR in log2_i(): val cannot be negative."
    else if (val==0) then
        print *, "ERROR in log2_i(): todo: return the integer equivalent of (-inf)..."
    else if (val==1) then
        res = 0
    else
        res = FLOOR( LOG(val+0.0d0)/LOG(2.0d0) )
    endif
end function log2_i

有没有更好的方法使用 Fortran 的移位运算符?

This question 几乎相同,但使用无符号整数。不幸的是,符号位会禁止使用相同的算法。

【问题讨论】:

  • 非负操作数上的 RSHIFT 相当于逻辑右移,所以我看不出有什么问题
  • 看一下标准函数ILEN,应该和你需要的很接近(+/- 1)。
  • @njuffa 那是 HPF 内在函数,而不是 Fortran 内在函数?所以它可能不会被广泛支持。
  • @francescalus 好点。我不经常使用 Fortran,并认为这是 Fortran 2003 的一部分,但也许它只是 HPF 扩展。我近年来使用的所有 Fortran 编译器都可以使用它,所以如果它是一个扩展,它似乎是一个常见的扩展(可能需要检查编译器标志设置以启用 HPF 功能)。
  • 你可以在循环中使用btest

标签: performance fortran bit-manipulation fortran90 logarithm


【解决方案1】:

正如@harold 提到的,这应该不是问题:因为对数只为正数定义,所以符号位始终为零(参见相应的Wikipedia article)。所以linked answer中的算法可以直接移植到Fortran(2008标准):

module mod_test
contains
  function ilog2_b(val ) result( res )
    integer, intent(in) :: val
    integer             :: res
    integer             :: tmp

    res = -1 
    ! Negativ values not allowed
    if ( val < 1 ) return

    tmp = val
    do while (tmp > 0)
      res = res + 1
      tmp = shiftr( tmp, 1 )
    enddo
  end function
end module

program test
  use mod_test
  print *,'Bitshifting: ', ilog2_b(12345)
  print *,'Formula:     ', floor( log(real(12345) ) / log(2.) )
end program

这是一个基于 @agentp 建议的 Fortran 95 内在 BTEST 的解决方案:

module mod_test
contains
  function ilog2_b2(val ) result( res )
    integer, intent(in) :: val
    integer             :: res
    integer             :: i

    res = -1 
    ! Negativ values not allowed
    if ( val < 1 ) return
    do i=bit_size(val)-1,0,-1
      if ( btest(val, i) ) then
        res = i
        return
      endif
    enddo

  end function
end module

program test
  use mod_test
  print *,'Testing bits:', ilog2_b2(123456)
  print *,'Formula:     ', floor( log(real(123456) ) / log(2.) )
end program

感谢@IanH 将我指向bit_size... 如果您的编译器支持shiftr,我会改用第一个变体。


@IanH 提到了另一种使用 leadz 的方法,这是 Fortran 2008 的一个特性:

module mod_test
contains
  function ilog2_b3(val ) result( res )
    integer, intent(in) :: val
    integer             :: res

    res = -1 
    ! Negativ values not allowed
    if ( val < 1 ) return

    res = bit_size(val)-leadz(val)-1
  end function
end module

program test
  use mod_test
  print *,'Testing bits:', ilog2_b3(123456)
  print *,'Formula:     ', floor( log(real(123456) ) / log(2.) )
end program

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多